Skip to content

Commit

Permalink
feat(core): add function declaration without binding to let keyword
Browse files Browse the repository at this point in the history
  • Loading branch information
ahmedsaheed committed May 14, 2024
1 parent a81b335 commit d5f8449
Show file tree
Hide file tree
Showing 5 changed files with 118 additions and 1 deletion.
37 changes: 37 additions & 0 deletions ast/ast.go
Original file line number Diff line number Diff line change
Expand Up @@ -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()

}
7 changes: 6 additions & 1 deletion evaluator/evaluator.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)

Expand Down
62 changes: 62 additions & 0 deletions parser/parser.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down Expand Up @@ -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
}
11 changes: 11 additions & 0 deletions test.eso
Original file line number Diff line number Diff line change
Expand Up @@ -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 = {
Expand All @@ -36,3 +45,5 @@ name := p::name
println(name)
println(p::occupation)
println(sec::Sum(50, 10))

hello()
2 changes: 2 additions & 0 deletions token/token.go
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ const (
LBRACE = "{"
RBRACE = "}"
FUNCTION = "FUNCTION"
DEF_FN = "DEF_FUNTION"
LET = "LET"
BANG = "!"
SLASH = "/"
Expand Down Expand Up @@ -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
Expand Down

0 comments on commit d5f8449

Please sign in to comment.