-
Notifications
You must be signed in to change notification settings - Fork 2
/
lang.go
121 lines (106 loc) · 3.24 KB
/
lang.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
package opencypher
import (
"fmt"
"github.com/antlr/antlr4/runtime/Go/antlr"
"github.com/cloudprivacylabs/lpg/v2"
"github.com/cloudprivacylabs/opencypher/parser"
)
//go:generate antlr4 -Dlanguage=Go Cypher.g4 -o parser
type errorListener struct {
antlr.DefaultErrorListener
err error
}
type ErrSyntax string
type ErrInvalidExpression string
func (e ErrSyntax) Error() string { return "Syntax error: " + string(e) }
func (e ErrInvalidExpression) Error() string { return "Invalid expression: " + string(e) }
func (lst *errorListener) SyntaxError(recognizer antlr.Recognizer, offendingSymbol interface{}, line, column int, msg string, e antlr.RecognitionException) {
if lst.err == nil {
lst.err = ErrSyntax(fmt.Sprintf("line %d:%d %s ", line, column, msg))
}
}
// GetParser returns a parser that will parse the input string
func GetParser(input string) *parser.CypherParser {
lexer := parser.NewCypherLexer(antlr.NewInputStream(input))
stream := antlr.NewCommonTokenStream(lexer, 0)
p := parser.NewCypherParser(stream)
p.BuildParseTrees = true
return p
}
// GetEvaluatable returns an evaluatable object
func Parse(input string) (Evaluatable, error) {
pr := GetParser(input)
errListener := errorListener{}
pr.AddErrorListener(&errListener)
c := pr.OC_Cypher()
if errListener.err != nil {
return nil, fmt.Errorf("%w, input: %s", errListener.err, input)
}
out := oC_Cypher(c.(*parser.OC_CypherContext))
return out, nil
}
func ParseAndEvaluate(input string, ctx *EvalContext) (Value, error) {
e, err := Parse(input)
if err != nil {
return nil, err
}
return e.Evaluate(ctx)
}
// ParsePatternExpr parses the pattern expression that starts at the
// current node named 'this', and describes a path reaching one or
// more nodes named 'target'. For instance:
//
// (this)-[]->(target)
//
// will return all nodes reachable from the current node by one step.
//
// This expression:
//
// (this)<[a]-()-[]->(target :x)
//
// will start from the current node, go back one nore following an
// edge with label `a`, and then move to a node with label `x`
func ParsePatternExpr(expr string) (PatternPart, error) {
pr := GetParser(expr)
errListener := errorListener{}
pr.AddErrorListener(&errListener)
c := pr.OC_PatternPart()
if errListener.err != nil {
return PatternPart{}, errListener.err
}
out := oC_PatternPart(c.(*parser.OC_PatternPartContext))
return out, nil
}
// FindRelative evaluates a pattern expression starting at the given
// node. It may return zero or more nodes reached from the node
func (p PatternPart) FindRelative(this *lpg.Node) ([]*lpg.Node, error) {
ctx := NewEvalContext(this.GetGraph())
ctx.SetVar("this", RValue{Value: this})
pattern, err := p.getPattern(ctx)
if err != nil {
return nil, err
}
symbols, err := BuildPatternSymbols(ctx, pattern)
if err != nil {
return nil, err
}
resultAccumulator := matchResultAccumulator{
evalCtx: ctx,
result: NewResultSet(),
}
err = pattern.Run(ctx.graph, symbols, &resultAccumulator)
if err != nil {
return nil, err
}
ret := make([]*lpg.Node, 0, len(resultAccumulator.result.Rows))
for _, row := range resultAccumulator.result.Rows {
t, ok := row["target"]
if !ok {
continue
}
if n, ok := t.Get().(*lpg.Node); ok {
ret = append(ret, n)
}
}
return ret, nil
}