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 }