q/src/expression/Parse.go

166 lines
2.8 KiB
Go

package expression
import (
"math"
"git.akyoto.dev/cli/q/src/token"
)
// Parse generates an expression tree from tokens.
func Parse(tokens []token.Token) *Expression {
var (
cursor *Expression
root *Expression
groupLevel = 0
groupPosition = 0
)
for i, t := range tokens {
if t.Kind == token.GroupStart || t.Kind == token.ArrayStart {
groupLevel++
if groupLevel == 1 {
groupPosition = i + 1
}
continue
}
if t.Kind == token.GroupEnd || t.Kind == token.ArrayEnd {
groupLevel--
if groupLevel != 0 {
continue
}
// Function call or array access
if isComplete(cursor) {
parameters := NewList(tokens[groupPosition:i])
node := New()
node.Token.Position = tokens[groupPosition].Position
switch t.Kind {
case token.GroupEnd:
node.Token.Kind = token.Call
case token.ArrayEnd:
node.Token.Kind = token.Array
}
node.Precedence = precedence(node.Token.Kind)
if cursor.Token.IsOperator() && node.Precedence > cursor.Precedence {
cursor.LastChild().Replace(node)
} else {
if cursor == root {
root = node
}
cursor.Replace(node)
}
for _, param := range parameters {
node.AddChild(param)
}
cursor = node
continue
}
group := Parse(tokens[groupPosition:i])
if group == nil {
continue
}
group.Precedence = math.MaxInt8
if cursor == nil {
cursor = group
root = group
} else {
cursor.AddChild(group)
}
continue
}
if groupLevel > 0 {
continue
}
if t.Kind == token.Identifier || t.Kind == token.Number || t.Kind == token.String || t.Kind == token.Rune {
if cursor != nil {
node := NewLeaf(t)
cursor.AddChild(node)
} else {
cursor = NewLeaf(t)
root = cursor
}
continue
}
if !t.IsOperator() {
continue
}
if cursor == nil {
cursor = NewLeaf(t)
cursor.Precedence = precedence(t.Kind)
root = cursor
continue
}
node := NewLeaf(t)
node.Precedence = precedence(t.Kind)
if cursor.Token.IsOperator() {
oldPrecedence := cursor.Precedence
newPrecedence := node.Precedence
if newPrecedence > oldPrecedence {
if len(cursor.Children) == numOperands(cursor.Token.Kind) {
cursor.LastChild().Replace(node)
} else {
cursor.AddChild(node)
}
} else {
start := cursor
for start != nil {
precedence := start.Precedence
if precedence < newPrecedence {
start.LastChild().Replace(node)
break
}
if precedence == newPrecedence {
if start == root {
root = node
}
start.Replace(node)
break
}
start = start.Parent
}
if start == nil {
root.Replace(node)
root = node
}
}
} else {
node.AddChild(cursor)
root = node
}
cursor = node
}
return root
}