// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // All rights reserved. // parser.go package expr import ( "errors" "fmt" ) //-------- parser type parser struct { ctx exprContext } func NewParser(ctx exprContext) (p *parser) { p = &parser{ ctx: ctx, } return p } func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) { // name, _ := tk.Value.(string) // funcObj := self.ctx.GetFuncInfo(name) // if funcObj == nil { // err = fmt.Errorf("unknown function %s()", name) // return // } // maxArgs := funcObj.MaxArgs() // if maxArgs < 0 { // maxArgs = funcObj.MinArgs() + 10 // } // args := make([]*term, 0, maxArgs) args := make([]*term, 0, 10) lastSym := SymUnknown for lastSym != SymClosedRound && lastSym != SymEos { var subTree *ast if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil { if subTree.root != nil { args = append(args, subTree.root) } } else { break } lastSym = scanner.Previous().Sym } if err == nil { // TODO Check arguments if lastSym != SymClosedRound { err = errors.New("unterminate arguments list") } else { tree = newFuncCallTerm(tk, args) } } return } func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { // Example: "add = func(x,y) {x+y} var body *ast args := make([]*term, 0) tk := scanner.Next() for tk.Sym != SymClosedRound && tk.Sym != SymEos { if tk.Sym == SymIdentifier { t := newTerm(tk, nil) args = append(args, t) } else { err = tk.Errorf("invalid param %q, variable identifier expected", tk.source) break } tk = scanner.Next() } if err == nil && tk.Sym != SymClosedRound { err = tk.Errorf("unterminate function params list") } if err == nil { tk = scanner.Next() if tk.Sym == SymOpenBrace { body, err = self.parseGeneral(scanner, true, true, SymClosedBrace) } } if err == nil { // TODO Check arguments if scanner.Previous().Sym != SymClosedBrace { err = scanner.Previous().Errorf("not properly terminated function body") } else { tk = scanner.makeValueToken(SymExpression, "", body) tree = newFuncDefTerm(tk, args) } } return } func (self *parser) parseList(scanner *scanner, allowVarRef bool) (tree *term, err error) { args := make([]*term, 0) lastSym := SymUnknown for lastSym != SymClosedSquare && lastSym != SymEos { var subTree *ast if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil { if subTree.root != nil { args = append(args, subTree.root) } } else { break } lastSym = scanner.Previous().Sym } if err == nil { // TODO Check arguments if lastSym != SymClosedSquare { err = scanner.Previous().Errorf("unterminate items list") } else { tree = newListTerm(args) } } return } func (self *parser) parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) { return self.parseGeneral(scanner, true, false, termSymbols...) } func (self *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { return self.parseGeneral(scanner, false, allowVarRef, termSymbols...) } func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) { tree = NewAst() firstToken := true lastSym := SymUnknown for tk := scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = scanner.Next() { if tk.Sym == SymComment { continue } if tk.Sym == SymSemiColon { if allowForest { tree.ToForest() continue } else { err = tk.Errorf(`unexpected token %q, expected ",", "]", or ")"`, tk.source) break } } //fmt.Println("Token:", tk) if firstToken { if tk.Sym == SymMinus { tk.Sym = SymChangeSign } else if tk.Sym == SymPlus { tk.Sym = SymUnchangeSign } firstToken = false } switch tk.Sym { case SymOpenRound: var subTree *ast if subTree, err = self.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil { subTree.root.priority = priValue tree.addTerm(subTree.root) } case SymFuncCall: var funcCallTerm *term if funcCallTerm, err = self.parseFuncCall(scanner, allowVarRef, tk); err == nil { err = tree.addTerm(funcCallTerm) } case SymOpenSquare: var listTerm *term if listTerm, err = self.parseList(scanner, allowVarRef); err == nil { err = tree.addTerm(listTerm) } case SymEqual: if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil { err = tree.addToken(tk) } case SymFuncDef: var funcDefTerm *term if funcDefTerm, err = self.parseFuncDef(scanner); err == nil { err = tree.addTerm(funcDefTerm) } case SymIdentifier: if tk.source[0] == '@' && !allowVarRef { err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source) } else { err = tree.addToken(tk) } default: err = tree.addToken(tk) } lastSym = tk.Sym } return } func checkPrevSymbol(lastSym, wantedSym Symbol, tk *Token) (err error) { if lastSym != wantedSym { err = fmt.Errorf(`assign operator (%q) must be preceded by a variable`, tk.source) } return }