|
@@ -18,59 +18,23 @@ import (
|
|
|
/* Grammar:
|
|
|
Desrired syntax (verified LL(1) on smlweb.cpsc.ucalgary.ca)
|
|
|
|
|
|
-PROGRAM -> STATEMENTS.
|
|
|
-CLOSED -> BLOCK | LIST | PARENTHESIS .
|
|
|
-STATEMENTS -> STATEMENT STATEMENTS | .
|
|
|
-STATEMENT -> CLOSED | EXPRESSION eos | eos .
|
|
|
-COMMAND -> word PARAMETERS.
|
|
|
-PARAMETERS -> PARAMETER PARAMETERS | .
|
|
|
-PARAMETER -> WORDVALUE | GETTER | SETTER | CLOSED .
|
|
|
-EXPRESSION -> COMMAND | GETTER | SETTER | VALUE.
|
|
|
-PARENTHESIS -> closeparen EXPRESSION openparen .
|
|
|
-BLOCK -> openblock STATEMENTS closeblock .
|
|
|
-LIST -> openlist PARAMETERS closelist .
|
|
|
-VALUE -> string | int | float | symbol | type .
|
|
|
-WORDVALUE -> word | VALUE.
|
|
|
-SETTER -> set TARGET .
|
|
|
-TARGET -> word PARAMETER | GETTER PARAMETER .
|
|
|
-GETTER -> get ORIGIN .
|
|
|
-ORIGIN -> word | SETTER | GETTER .
|
|
|
-
|
|
|
-
|
|
|
-Or with simple operators variant.
|
|
|
-
|
|
|
-PROGRAM -> STATEMENTS.
|
|
|
-CLOSED -> BLOCK | LIST | PARENTHESIS .
|
|
|
-STATEMENTS -> STATEMENT STATEMENTS | .
|
|
|
-STATEMENT -> CLOSED | EXPRESSION eos | eos .
|
|
|
-COMMAND -> WORDVALUE DETAILS .
|
|
|
-DETAILS -> OPERATION | PARAMETERS .
|
|
|
-OPERATION -> operator COMMAND .
|
|
|
-PARAMETERS -> PARAMETER PARAMETERS | .
|
|
|
-PARAMETER -> WORDVALUE | GETTER | SETTER | CLOSED .
|
|
|
-EXPRESSION -> COMMAND | GETTER | SETTER.
|
|
|
-PARENTHESIS -> openparen EXPRESSION closeparen .
|
|
|
-BLOCK -> openblock STATEMENTS closeblock .
|
|
|
-LIST -> openlist PARAMETERS closelist .
|
|
|
-VALUE -> string | int | float | symbol | type .
|
|
|
-WORDVALUE -> word | VALUE.
|
|
|
-SETTER -> set TARGET .
|
|
|
-TARGET -> word PARAMETER | GETTER PARAMETER .
|
|
|
-GETTER -> get ORIGIN .
|
|
|
-GETCALL -> comma COMMAND | .
|
|
|
-ORIGIN -> word | SETTER | GETTER .
|
|
|
+In Muesli, the program is first tokenized by a lexer. The lexer uses the first
|
|
|
+character, and only the first character of every token to determine it's type.
|
|
|
|
|
|
-
|
|
|
-Or, new syntax with operators for command chains at the top level,
|
|
|
-for easier shunting the operator into commands:
|
|
|
+Then, the tokens are parsed with a recursive descent parser with a structured
|
|
|
+syntax, which has been verified to be LL(1) on smlweb.cpsc.ucalgary.ca. This
|
|
|
+means the parser only needs one token of lookahead to be able to parse the
|
|
|
+syntax. The syntax can be described as follows:
|
|
|
|
|
|
PROGRAM -> STATEMENTS .
|
|
|
STATEMENTS -> STATEMENT STATEMENTS | .
|
|
|
STATEMENT -> BLOCK | CHAIN eos | eos .
|
|
|
-CHAIN -> EXPRESSION LINKS .
|
|
|
-LINKS -> LINK | .
|
|
|
-LINK -> OPERATOR CHAIN .
|
|
|
-OPERATOR -> evaluator | redirect | blockopt .
|
|
|
+CHAIN -> EXPRESSION OPERATIONS .
|
|
|
+OPERATIONS -> LINK | .
|
|
|
+LINK -> OPERATION | REDIRECT | METHOD .
|
|
|
+METHOD -> method EXPRESSION .
|
|
|
+REDIRECT -> redirect EXPRESSION .
|
|
|
+OPERATION -> operator EXPRESSION .
|
|
|
EXPRESSION -> COMMAND | SUBSTITUTION | LITERAL .
|
|
|
COMMAND -> NAME PARAMETERS.
|
|
|
PARAMETERS -> PARAMETER PARAMETERS | .
|
|
@@ -84,15 +48,38 @@ NAME -> word | symbol | type .
|
|
|
SETTER -> set PARAMETER PARAMETER .
|
|
|
GETTER -> get PARAMETER .
|
|
|
|
|
|
-semantics:
|
|
|
-
|
|
|
-
|
|
|
-- A muesli program consists of statements.
|
|
|
-- A statement is either a block, or a command chain terminated by eos, or just an eos .
|
|
|
-- A command chain consists of commands chained together with operators.
|
|
|
-- A command may be a direct command, an indirect command, or a literal value.
|
|
|
-- A () parenthesis gets substituted inline anywhere it occurs, also in the
|
|
|
- beginning of a command.
|
|
|
+The semantics of a muesli program are based on this syntax and can be explained
|
|
|
+as follows:
|
|
|
+
|
|
|
+- A muesli program consists of statements, which are executed from top to
|
|
|
+ bottom.
|
|
|
+- A statement is either a block, or a command chain terminated by end of
|
|
|
+ statement, eos, or an empty statement terminated by eos. The period . and
|
|
|
+ the unescaped newline characters are eos.
|
|
|
+- A block consist of statements between braces { }. The statements of a block
|
|
|
+ are not executed but compiled to a block which can be called to execute them
|
|
|
+ later.
|
|
|
+- A command chain consists of an expression optionally followed by links.
|
|
|
+ A command chain is executed by executing it's expression and applying the,
|
|
|
+ operator, redirect and method links from left to right.
|
|
|
+- A link is either operator, redirect or method.
|
|
|
+- An operator begins with a token with one of the following characters: -+/*^%~
|
|
|
+ Next is an expression. The effect of an operator is to evaluate the
|
|
|
+ links before it and after it as a substitution, and then look up the callable
|
|
|
+ with the name of the operator and execute that.
|
|
|
+- A redirect starts with a token of the following characters: |&><@
|
|
|
+ Next is an expression. The effect of a redirect is to evaluate the
|
|
|
+ links before it and after it as a block, and then look up the callable
|
|
|
+ with the name of the operator and execute that.
|
|
|
+- A method start with a token with one of the following characters: ,;
|
|
|
+ Next is an expression. The effect of a method is to evaluate the
|
|
|
+ links before it and after it as a list of ast expressions, shuffle them
|
|
|
+ so the operator comes first, but the rest is kept in order, and then look up
|
|
|
+ the callable with the name of the operator and execute that.
|
|
|
+- A command may be a direct command, an indirect command, or a literal value.
|
|
|
+- A direct command
|
|
|
+- A () parenthesis gets substituted inline by the value returned by executing it
|
|
|
+ anywhere it occurs, also in the beginning of a command.
|
|
|
- A bracketed list [ elem1 elem2 ... ] is syntactic sugar for (list elem1 elem 2)
|
|
|
- A dollar getter $varname is syntactic sugar for (get varname)
|
|
|
- A equals setter =varname value is syntactic sugar for (set varname value)
|
|
@@ -137,6 +124,16 @@ func (parser *Parser) SetLogger(logger Logger) {
|
|
|
parser.LoggerWrapper = LoggerWrapper{logger}
|
|
|
}
|
|
|
|
|
|
+func (parser *Parser) SetDefaultLogger() {
|
|
|
+ lg := &testLogger{}
|
|
|
+ parser.LoggerWrapper = LoggerWrapper{lg}
|
|
|
+}
|
|
|
+
|
|
|
+func (parser *Parser) SetVmLogger(vm * VM) {
|
|
|
+ parser.LoggerWrapper = vm.Console.LoggerWrapper
|
|
|
+}
|
|
|
+
|
|
|
+
|
|
|
func (parser *Parser) Errorf(message string, args ...interface{}) ParserError {
|
|
|
err := fmt.Errorf(message, args...)
|
|
|
pe := ParserError { Parser: parser, Token:&parser.current, Chain: err }
|
|
@@ -577,17 +574,25 @@ func (parser Parser) NextIsStatement() bool {
|
|
|
return parser.NextIsChain() || parser.NextIsBlock() || parser.NextIsEOX()
|
|
|
}
|
|
|
|
|
|
-func newChain(oper Token, expr1, expr2 *Ast) * Ast {
|
|
|
+func (parser *Parser) newMethodChain(oper Token, expr1, expr2 *Ast) *Ast {
|
|
|
+ chain := NewAstWithToken(AstKindOperation, oper)
|
|
|
+ chain.AppendChildren(expr1)
|
|
|
+ chain.AppendChild(NewAstWithToken(AstKindForToken(expr2.Token()), expr2.Token()))
|
|
|
+ chain.AppendChildren(expr2.Children()...)
|
|
|
+ parser.LogDebug("New method chain: %v", chain)
|
|
|
+ return chain
|
|
|
+}
|
|
|
+
|
|
|
+func (parser *Parser) newChain(oper Token, expr1, expr2 *Ast) * Ast {
|
|
|
var astkind AstKind = AstKindParenthesis
|
|
|
|
|
|
if oper.TokenKind == TokenKindRedirect {
|
|
|
+ parser.LogDebug("New redirect chain.")
|
|
|
astkind = AstKindStatements
|
|
|
} else if oper.TokenKind == TokenKindMethod {
|
|
|
- chain := NewAstWithToken(AstKindOperation, oper)
|
|
|
- chain.AppendChildren(expr1, expr2)
|
|
|
- return chain
|
|
|
+ return parser.newMethodChain(oper, expr1, expr2)
|
|
|
}
|
|
|
-
|
|
|
+
|
|
|
subst1 := NewAstWithToken(astkind, oper)
|
|
|
subst1.AppendChildren(expr1)
|
|
|
subst2 := NewAstWithToken(astkind, oper)
|
|
@@ -597,14 +602,19 @@ func newChain(oper Token, expr1, expr2 *Ast) * Ast {
|
|
|
return chain
|
|
|
}
|
|
|
|
|
|
-func composeChain(oper Token, chain, nextExpr *Ast) * Ast {
|
|
|
+func (parser *Parser) composeChain(oper Token, chain, nextExpr *Ast) * Ast {
|
|
|
var astkind AstKind = AstKindParenthesis
|
|
|
|
|
|
if oper.TokenKind == TokenKindRedirect {
|
|
|
+ parser.LogDebug("Composed redirect chain: %v", chain)
|
|
|
astkind = AstKindStatements
|
|
|
} else if oper.TokenKind == TokenKindMethod {
|
|
|
- chain.AppendChildren(nextExpr)
|
|
|
+ chain.AppendChild(NewAstWithToken(AstKindForToken(nextExpr.Token()), nextExpr.Token()))
|
|
|
+ chain.AppendChildren(nextExpr.Children()...)
|
|
|
+ parser.LogDebug("Composed method chain: %v", chain)
|
|
|
return chain
|
|
|
+ } else {
|
|
|
+ parser.LogDebug("Composed normal chain: %v", chain)
|
|
|
}
|
|
|
|
|
|
subst := NewAstWithToken(astkind, oper)
|
|
@@ -634,16 +644,16 @@ func (parser *Parser) ParseChain() *Ast {
|
|
|
// Now there are N expressions and N - 1 operators, iterate
|
|
|
// for easy composition
|
|
|
var chain *Ast
|
|
|
-
|
|
|
+ parser.LogDebug("Working on operator chain: %v %v", operators, expressions)
|
|
|
for i, j := 0, 0 ; i < len(expressions) && j < len(operators) ; i, j = i + 1 , j + 1 {
|
|
|
expression := expressions[i]
|
|
|
oper := operators[j]
|
|
|
if chain == nil {
|
|
|
expression2 := expressions[i+1]
|
|
|
- chain = newChain(oper, expression, expression2)
|
|
|
+ chain = parser.newChain(oper, expression, expression2)
|
|
|
i++
|
|
|
} else {
|
|
|
- chain = composeChain(oper, chain, expression)
|
|
|
+ chain = parser.composeChain(oper, chain, expression)
|
|
|
}
|
|
|
}
|
|
|
return chain
|