This feature is implemented by expansion, not by dedicated operators, e.g. a*=2+x is exapanded as a=a*(2+x)
531 lines
14 KiB
Go
531 lines
14 KiB
Go
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
|
|
// All rights reserved.
|
|
|
|
// parser.go
|
|
package expr
|
|
|
|
import (
|
|
"errors"
|
|
)
|
|
|
|
//-------- parser
|
|
|
|
type parser struct {
|
|
}
|
|
|
|
func NewParser() (p *parser) {
|
|
p = &parser{}
|
|
return p
|
|
}
|
|
|
|
func (parser *parser) Next(scanner *scanner) (tk *Token) {
|
|
for tk = scanner.Next(); tk.IsSymbol(SymComment); tk = scanner.Next() {
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) (tree *term, err error) {
|
|
args := make([]*term, 0, 10)
|
|
itemExpected := false
|
|
lastSym := SymUnknown
|
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
|
var subTree *ast
|
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err != nil {
|
|
break
|
|
}
|
|
prev := scanner.Previous()
|
|
if subTree.root != nil {
|
|
args = append(args, subTree.root)
|
|
} else if itemExpected {
|
|
err = prev.ErrorExpectedGot("function-param-value")
|
|
break
|
|
}
|
|
|
|
itemExpected = prev.Sym == SymComma
|
|
lastSym = scanner.Previous().Sym
|
|
}
|
|
if err == nil {
|
|
if lastSym != SymClosedRound {
|
|
err = errors.New("unterminated arguments list")
|
|
} else {
|
|
tree = newFuncCallTerm(tk, args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseFuncDef(scanner *scanner) (tree *term, err error) {
|
|
// Example: "add = func(x,y) {x+y}
|
|
var body *ast
|
|
args := make([]*term, 0)
|
|
lastSym := SymUnknown
|
|
defaultParamsStarted := false
|
|
itemExpected := false
|
|
tk := scanner.Previous()
|
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
|
tk = parser.Next(scanner)
|
|
if tk.IsSymbol(SymIdentifier) {
|
|
param := newTerm(tk)
|
|
args = append(args, param)
|
|
tk = parser.Next(scanner)
|
|
if tk.Sym == SymEqual {
|
|
var paramExpr *ast
|
|
defaultParamsStarted = true
|
|
if paramExpr, err = parser.parseItem(scanner, false, SymComma, SymClosedRound); err != nil {
|
|
break
|
|
}
|
|
param.forceChild(paramExpr.root)
|
|
} else if defaultParamsStarted {
|
|
err = tk.Errorf("can't mix default and non-default parameters")
|
|
break
|
|
}
|
|
} else if itemExpected {
|
|
prev := scanner.Previous()
|
|
err = prev.ErrorExpectedGot("function-param-spec")
|
|
break
|
|
}
|
|
lastSym = scanner.Previous().Sym
|
|
itemExpected = lastSym == SymComma
|
|
}
|
|
|
|
if err == nil && lastSym != SymClosedRound {
|
|
err = tk.ErrorExpectedGot(")")
|
|
}
|
|
if err == nil {
|
|
tk = parser.Next(scanner)
|
|
if tk.IsSymbol(SymOpenBrace) {
|
|
body, err = parser.parseGeneral(scanner, true, true, SymClosedBrace)
|
|
} else {
|
|
err = tk.ErrorExpectedGot("{")
|
|
}
|
|
}
|
|
if err == nil {
|
|
if scanner.Previous().Sym != SymClosedBrace {
|
|
err = scanner.Previous().ErrorExpectedGot("}")
|
|
} else {
|
|
tk = scanner.makeValueToken(SymExpression, "", body)
|
|
tree = newFuncDefTerm(tk, args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseList(scanner *scanner, parsingIndeces bool, allowVarRef bool) (subtree *term, err error) {
|
|
r, c := scanner.lastPos()
|
|
args := make([]*term, 0)
|
|
lastSym := SymUnknown
|
|
itemExpected := false
|
|
for lastSym != SymClosedSquare && lastSym != SymEos {
|
|
var subTree *ast
|
|
zeroRequired := scanner.current.Sym == SymColon
|
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedSquare); err == nil {
|
|
root := subTree.root
|
|
if root != nil {
|
|
if !parsingIndeces && root.symbol() == SymColon {
|
|
err = root.Errorf("unexpected range expression")
|
|
break
|
|
}
|
|
args = append(args, root)
|
|
if parsingIndeces && root.symbol() == SymColon && zeroRequired { //len(root.children) == 0 {
|
|
if len(root.children) == 1 {
|
|
root.children = append(root.children, root.children[0])
|
|
} else if len(root.children) > 1 {
|
|
err = root.Errorf("invalid range specification")
|
|
break
|
|
}
|
|
zeroTk := NewValueToken(root.tk.row, root.tk.col, SymInteger, "0", int64(0))
|
|
zeroTerm := newTerm(zeroTk)
|
|
zeroTerm.setParent(root)
|
|
root.children[0] = zeroTerm
|
|
}
|
|
} else if itemExpected {
|
|
prev := scanner.Previous()
|
|
err = prev.ErrorExpectedGot("list-item")
|
|
break
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
lastSym = scanner.Previous().Sym
|
|
itemExpected = lastSym == SymComma
|
|
}
|
|
if err == nil {
|
|
if lastSym != SymClosedSquare {
|
|
err = scanner.Previous().ErrorExpectedGot("]")
|
|
} else {
|
|
subtree = newListTerm(r, c, args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
|
tk := scanner.Previous()
|
|
args := make([]*term, 0)
|
|
lastSym := SymUnknown
|
|
itemExpected := false
|
|
for lastSym != SymClosedRound && lastSym != SymEos {
|
|
var subTree *ast
|
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil {
|
|
if subTree.root != nil {
|
|
args = append(args, subTree.root)
|
|
} else if itemExpected {
|
|
prev := scanner.Previous()
|
|
err = prev.ErrorExpectedGot("iterator-param")
|
|
break
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
lastSym = scanner.Previous().Sym
|
|
itemExpected = lastSym == SymComma
|
|
}
|
|
if err == nil {
|
|
if lastSym != SymClosedRound {
|
|
err = scanner.Previous().ErrorExpectedGot(")")
|
|
} else {
|
|
subtree = newIteratorTerm(tk, args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseDictKey(scanner *scanner) (key any, err error) {
|
|
tk := parser.Next(scanner)
|
|
if tk.Sym == SymError {
|
|
err = tk.Error()
|
|
return
|
|
}
|
|
if tk.Sym == SymClosedBrace || tk.Sym == SymEos {
|
|
return
|
|
}
|
|
if tk.Sym == SymInteger || tk.Sym == SymString {
|
|
tkSep := parser.Next(scanner)
|
|
if tkSep.Sym != SymColon {
|
|
err = tkSep.ErrorExpectedGot(":")
|
|
} else {
|
|
key = tk.Value
|
|
}
|
|
} else {
|
|
err = tk.ErrorExpectedGot("dictionary-key or }")
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree *term, err error) {
|
|
args := make(map[any]*term, 0)
|
|
lastSym := SymUnknown
|
|
itemExpected := false
|
|
for lastSym != SymClosedBrace && lastSym != SymEos {
|
|
var subTree *ast
|
|
var key any
|
|
if key, err = parser.parseDictKey(scanner); err != nil {
|
|
break
|
|
} else if key == nil {
|
|
tk := scanner.Previous()
|
|
lastSym = tk.Sym
|
|
if itemExpected {
|
|
err = tk.ErrorExpectedGot("dictionary-key")
|
|
}
|
|
break
|
|
}
|
|
if subTree, err = parser.parseItem(scanner, allowVarRef, SymComma, SymClosedBrace); err == nil {
|
|
if subTree.root != nil {
|
|
args[key] = subTree.root
|
|
} else /*if key != nil*/ {
|
|
prev := scanner.Previous()
|
|
err = prev.ErrorExpectedGot("dictionary-value")
|
|
break
|
|
}
|
|
} else {
|
|
break
|
|
}
|
|
lastSym = scanner.Previous().Sym
|
|
itemExpected = lastSym == SymComma
|
|
}
|
|
if err == nil {
|
|
if lastSym != SymClosedBrace {
|
|
err = scanner.Previous().ErrorExpectedGot("}")
|
|
} else {
|
|
subtree = newDictTerm(args)
|
|
// subtree = newMapTerm(args)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaultCase bool) (caseTerm *term, err error) {
|
|
var filterList *term
|
|
var caseExpr *ast
|
|
tk := parser.Next(scanner)
|
|
startRow := tk.row
|
|
startCol := tk.col
|
|
if tk.Sym == SymOpenSquare {
|
|
if defaultCase {
|
|
err = tk.Errorf("case list in default clause")
|
|
return
|
|
}
|
|
if filterList, err = parser.parseList(scanner, false, allowVarRef); err != nil {
|
|
return
|
|
}
|
|
tk = parser.Next(scanner)
|
|
startRow = tk.row
|
|
startCol = tk.col
|
|
} else if !defaultCase {
|
|
filterList = newListTerm(startRow, startCol, make([]*term, 0))
|
|
}
|
|
|
|
if tk.Sym == SymOpenBrace {
|
|
if caseExpr, err = parser.parseGeneral(scanner, true, allowVarRef, SymClosedBrace); err != nil {
|
|
return
|
|
}
|
|
} else {
|
|
err = tk.ErrorExpectedGot("{")
|
|
}
|
|
|
|
if err == nil {
|
|
caseTerm = newSelectorCaseTerm(startRow, startCol, filterList, caseExpr)
|
|
}
|
|
return
|
|
}
|
|
|
|
func addSelectorCase(selectorTerm, caseTerm *term) {
|
|
if len(selectorTerm.children) < 2 {
|
|
caseListTerm := newListTermA(caseTerm)
|
|
selectorTerm.children = append(selectorTerm.children, caseListTerm)
|
|
} else {
|
|
caseListTerm := selectorTerm.children[1]
|
|
caseList, _ := caseListTerm.value().([]*term)
|
|
caseList = append(caseList, caseTerm)
|
|
caseListTerm.tk.Value = caseList
|
|
}
|
|
caseTerm.parent = selectorTerm
|
|
}
|
|
|
|
func (parser *parser) parseSelector(scanner *scanner, tree *ast, allowVarRef bool) (selectorTerm *term, err error) {
|
|
var caseTerm *term
|
|
tk := scanner.makeToken(SymSelector, '?')
|
|
if selectorTerm, err = tree.addToken(tk); err != nil {
|
|
return
|
|
}
|
|
|
|
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, false); err == nil {
|
|
addSelectorCase(selectorTerm, caseTerm)
|
|
}
|
|
return
|
|
}
|
|
|
|
func (parser *parser) parseItem(scanner *scanner, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
|
return parser.parseGeneral(scanner, false, allowVarRef, termSymbols...)
|
|
}
|
|
|
|
func (parser *parser) Parse(scanner *scanner, termSymbols ...Symbol) (tree *ast, err error) {
|
|
return parser.parseGeneral(scanner, true, false, termSymbols...)
|
|
}
|
|
|
|
func couldBeACollection(t *term) bool {
|
|
var sym = SymUnknown
|
|
if t != nil {
|
|
sym = t.symbol()
|
|
}
|
|
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
|
|
}
|
|
|
|
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
|
|
// var areOut = false
|
|
// if ctxTerm != nil {
|
|
// areOut = tk.IsOneOf(syms)
|
|
// }
|
|
// return areOut
|
|
// }
|
|
|
|
func (parser *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
|
|
var selectorTerm *term = nil
|
|
var currentTerm *term = nil
|
|
var tk *Token
|
|
|
|
tree = NewAst()
|
|
firstToken := true
|
|
// lastSym := SymUnknown
|
|
for tk = parser.Next(scanner); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = parser.Next(scanner) {
|
|
// if tk.Sym == SymComment {
|
|
// continue
|
|
// }
|
|
|
|
if tk.Sym == SymSemiColon {
|
|
if allowForest {
|
|
tree.ToForest()
|
|
firstToken = true
|
|
currentTerm = nil
|
|
selectorTerm = nil
|
|
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 = parser.parseGeneral(scanner, false, allowVarRef, SymClosedRound); err == nil {
|
|
subTree.root.priority = priValue
|
|
err = tree.addTerm(newExprTerm(subTree.root))
|
|
currentTerm = subTree.root
|
|
}
|
|
case SymFuncCall:
|
|
var funcCallTerm *term
|
|
if funcCallTerm, err = parser.parseFuncCall(scanner, allowVarRef, tk); err == nil {
|
|
err = tree.addTerm(funcCallTerm)
|
|
currentTerm = funcCallTerm
|
|
}
|
|
case SymOpenSquare:
|
|
var listTerm *term
|
|
parsingIndeces := couldBeACollection(currentTerm)
|
|
if listTerm, err = parser.parseList(scanner, parsingIndeces, allowVarRef); err == nil {
|
|
if parsingIndeces {
|
|
indexTk := NewToken(listTerm.tk.row, listTerm.tk.col, SymIndex, listTerm.source())
|
|
indexTerm := newTerm(indexTk)
|
|
if err = tree.addTerm(indexTerm); err == nil {
|
|
err = tree.addTerm(listTerm)
|
|
}
|
|
} else {
|
|
err = tree.addTerm(listTerm)
|
|
}
|
|
currentTerm = listTerm
|
|
}
|
|
case SymOpenBrace:
|
|
if currentTerm != nil && currentTerm.symbol() == SymColon {
|
|
err = currentTerm.Errorf(`selector-case outside of a selector context`)
|
|
} else {
|
|
var mapTerm *term
|
|
if mapTerm, err = parser.parseDictionary(scanner, allowVarRef); err == nil {
|
|
err = tree.addTerm(mapTerm)
|
|
currentTerm = mapTerm
|
|
}
|
|
}
|
|
case SymEqual:
|
|
// if err = checkPrevSymbol(lastSym, SymIdentifier, tk); err == nil {
|
|
currentTerm, err = tree.addToken(tk)
|
|
// }
|
|
case SymFuncDef:
|
|
var funcDefTerm *term
|
|
if funcDefTerm, err = parser.parseFuncDef(scanner); err == nil {
|
|
err = tree.addTerm(funcDefTerm)
|
|
currentTerm = funcDefTerm
|
|
}
|
|
case SymDollarRound:
|
|
var iterDefTerm *term
|
|
if iterDefTerm, err = parser.parseIterDef(scanner, allowVarRef); err == nil {
|
|
err = tree.addTerm(iterDefTerm)
|
|
currentTerm = iterDefTerm
|
|
}
|
|
case SymIdentifier:
|
|
if tk.source[0] == '@' && !allowVarRef {
|
|
err = tk.Errorf("variable references are not allowed in top level expressions: %q", tk.source)
|
|
} else {
|
|
currentTerm, err = tree.addToken(tk)
|
|
}
|
|
case SymQuestion:
|
|
if selectorTerm, err = parser.parseSelector(scanner, tree, allowVarRef); err == nil {
|
|
currentTerm = selectorTerm
|
|
}
|
|
case SymColon, SymDoubleColon:
|
|
var caseTerm *term
|
|
if selectorTerm != nil {
|
|
if caseTerm, err = parser.parseSelectorCase(scanner, allowVarRef, tk.Sym == SymDoubleColon); err == nil {
|
|
addSelectorCase(selectorTerm, caseTerm)
|
|
currentTerm = caseTerm
|
|
if tk.Sym == SymDoubleColon {
|
|
selectorTerm = nil
|
|
}
|
|
}
|
|
} else {
|
|
currentTerm, err = tree.addToken(tk)
|
|
}
|
|
if tk.IsSymbol(SymColon) {
|
|
// Colon outside a selector term acts like a separator
|
|
firstToken = true
|
|
}
|
|
case SymPlusEqual, SymMinusEqual, SymStarEqual:
|
|
currentTerm, err = parser.expandOpAssign(scanner, tree, tk, allowVarRef)
|
|
default:
|
|
currentTerm, err = tree.addToken(tk)
|
|
}
|
|
|
|
if currentTerm != nil && currentTerm.tk.Sym != SymSelector && currentTerm.parent != nil && currentTerm.parent.tk.Sym != SymSelector {
|
|
selectorTerm = nil
|
|
|
|
}
|
|
// lastSym = tk.Sym
|
|
}
|
|
|
|
if err == nil {
|
|
err = tk.Error()
|
|
}
|
|
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
|
|
// }
|
|
|
|
func (parser *parser) expandOpAssign(scanner * scanner, tree *ast, tk *Token, allowVarRef bool) (t *term, err error) {
|
|
var opSym Symbol
|
|
var opString string
|
|
|
|
if tree.root != nil {
|
|
switch tk.Sym {
|
|
case SymPlusEqual:
|
|
opString = "+"
|
|
opSym = SymPlus
|
|
case SymMinusEqual:
|
|
opString = "-"
|
|
opSym = SymMinus
|
|
case SymStarEqual:
|
|
opString = "*"
|
|
opSym = SymStar
|
|
default:
|
|
err = tk.Errorf("unsopported operator %q", tk.source)
|
|
return
|
|
}
|
|
leftExpr := tree.root.Clone()
|
|
leftExpr.setParent(nil)
|
|
if t, err = tree.addToken(NewToken(tk.row, tk.col, SymEqual, "=")); err == nil {
|
|
t = leftExpr
|
|
if err = tree.addTerm(leftExpr); err == nil {
|
|
if t, err = tree.addToken(NewToken(tk.row, tk.col, opSym, opString)); err == nil {
|
|
|
|
var subTree *ast
|
|
if subTree, err = parser.parseGeneral(scanner, false, allowVarRef, SymSemiColon, SymClosedRound); err == nil {
|
|
if scanner.Previous().IsOneOfA(SymSemiColon, SymClosedRound) {
|
|
if err = scanner.UnreadToken(); err != nil {
|
|
return
|
|
}
|
|
}
|
|
subTree.root.priority = priValue
|
|
err = tree.addTerm(newExprTerm(subTree.root))
|
|
t = subTree.root
|
|
}
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
err = tk.Errorf("left operand of %q must be a variable or a variable expression", tk)
|
|
}
|
|
return
|
|
}
|