diff --git a/ast/ast.go b/ast/ast.go index 61f9670..72373a2 100644 --- a/ast/ast.go +++ b/ast/ast.go @@ -468,3 +468,40 @@ func (be *BindExpression) String() string { return out.String() } + +type FunctionDefineLiteral struct { + // Token holds the token + Token token.Token + + // Paremeters holds the function parameters. + Parameters []*Identifier + + // Defaults holds any default-arguments. + Defaults map[string]Expression + + // Body holds the set of statements in the functions' body. + Body *BlockStatement +} + +func (fl *FunctionDefineLiteral) expressionNode() {} + +// TokenLiteral returns the literal token. +func (fl *FunctionDefineLiteral) TokenLiteral() string { + return fl.Token.Literal +} + +// String returns this object as a string. +func (fl *FunctionDefineLiteral) String() string { + var out bytes.Buffer + params := make([]string, 0) + for _, p := range fl.Parameters { + params = append(params, p.String()) + } + out.WriteString(fl.TokenLiteral()) + out.WriteString("(") + out.WriteString(strings.Join(params, ", ")) + out.WriteString(") ") + out.WriteString(fl.Body.String()) + return out.String() + +} diff --git a/evaluator/evaluator.go b/evaluator/evaluator.go index e547b9a..22ff024 100644 --- a/evaluator/evaluator.go +++ b/evaluator/evaluator.go @@ -98,7 +98,12 @@ func Eval(node ast.Node, env *object.Environment) object.Object { params := node.Parameters body := node.Body return &object.Function{Parameters: params, Body: body, Env: env} - + case *ast.FunctionDefineLiteral: + params := node.Parameters + body := node.Body + // defaults := node.Defaults + env.Set(node.TokenLiteral(), &object.Function{Parameters: params, Env: env, Body: body}) + return NULL case *ast.BlockStatement: return evalBlockStatement(node, env) diff --git a/parser/parser.go b/parser/parser.go index 0a9b3da..b729be9 100644 --- a/parser/parser.go +++ b/parser/parser.go @@ -85,6 +85,7 @@ func New(L *lexer.Lexer) *Parser { p.registerPrefix(token.IDENT, p.parseIdentifier) p.registerPrefix(token.INT, p.parseIntegerLiteral) p.registerPrefix(token.BANG, p.parsePrefixExpression) + p.registerPrefix(token.DEF_FN, p.parseFunctionDefinition) p.registerPrefix(token.MINUS, p.parsePrefixExpression) p.registerPrefix(token.TRUE, p.parseBoolean) p.registerPrefix(token.FALSE, p.parseBoolean) @@ -660,3 +661,64 @@ func (P *Parser) parseBindExpression(exp ast.Expression) ast.Expression { return be } + +func (p *Parser) parseFunctionDefinition() ast.Expression { + p.nextToken() + lit := &ast.FunctionDefineLiteral{Token: p.currentToken} + if !p.expectPeek(token.LPAREN) { + return nil + } + lit.Defaults, lit.Parameters = p.parseFunctionDefParameter() + if !p.expectPeek(token.LBRACE) { + return nil + } + lit.Body = p.parseBlockStatement() + return lit +} + +// parseFunctionParameters parses the parameters used for a function. +func (p *Parser) parseFunctionDefParameter() (map[string]ast.Expression, []*ast.Identifier) { + + // Any default parameters. + m := make(map[string]ast.Expression) + + // The argument-definitions. + identifiers := make([]*ast.Identifier, 0) + + // Is the next parameter ")" ? If so we're done. No args. + if p.peekTokenLS(token.RPAREN) { + p.nextToken() + return m, identifiers + } + p.nextToken() + + // Keep going until we find a ")" + for !p.currentTokenLS(token.RPAREN) { + + if p.currentTokenLS(token.EOF) { + p.errors = append(p.errors, "unterminated function parameters") + return nil, nil + } + + // Get the identifier. + ident := &ast.Identifier{Token: p.currentToken, Value: p.currentToken.Literal} + identifiers = append(identifiers, ident) + p.nextToken() + + // If there is "=xx" after the name then that's + // the default parameter. + if p.currentTokenLS(token.ASSIGN) { + p.nextToken() + // Save the default value. + m[ident.Value] = p.parseExpressionStatement().Expression + p.nextToken() + } + + // Skip any comma. + if p.currentTokenLS(token.COMMA) { + p.nextToken() + } + } + + return m, identifiers +} diff --git a/test.eso b/test.eso index 5e319d5..84890c9 100644 --- a/test.eso +++ b/test.eso @@ -14,6 +14,15 @@ let fizzbuzz = fn(n, arr) { fizzbuzz(20, []) +func hello() { + printHi := fn() { + println("Hi") + } + printHi() +}; + + + let Person = fn(name, age, occupation) { let person = { @@ -36,3 +45,5 @@ name := p::name println(name) println(p::occupation) println(sec::Sum(50, 10)) + +hello() diff --git a/token/token.go b/token/token.go index 79f459f..ef4a859 100644 --- a/token/token.go +++ b/token/token.go @@ -28,6 +28,7 @@ const ( LBRACE = "{" RBRACE = "}" FUNCTION = "FUNCTION" + DEF_FN = "DEF_FUNTION" LET = "LET" BANG = "!" SLASH = "/" @@ -67,6 +68,7 @@ var keywords = map[string]TokenType{ "return": RETURN, "when": WHEN, "import": IMPORT, + "func": DEF_FN, } // LookupIdent checks if the identifier is a keyword