-
Notifications
You must be signed in to change notification settings - Fork 0
/
parser.js
152 lines (127 loc) · 4.48 KB
/
parser.js
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
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
'use strict'
// Written Docs for this tutorial step can be found here:
// https://github.com/SAP/chevrotain/blob/master/docs/tutorial/step2_parsing.md
// Tutorial Step 2:
// Adding a Parser (grammar only, only reads the input without any actions).
// Using the Token Vocabulary defined in the previous step.
const selectLexer = require('./lexer')
const Parser = require('chevrotain').Parser
const tokenVocabulary = selectLexer.tokenVocabulary
// individual imports, prefer ES6 imports if supported in your runtime/transpiler...
const Select = tokenVocabulary.Select
const From = tokenVocabulary.From
const Where = tokenVocabulary.Where
const OrderBy = tokenVocabulary.OrderBy
const Asc = tokenVocabulary.Asc
const Desc = tokenVocabulary.Desc
const Limit = tokenVocabulary.Limit
const Identifier = tokenVocabulary.Identifier
const Integer = tokenVocabulary.Integer
const GreaterThan = tokenVocabulary.GreaterThan
const GreaterThanEqual = tokenVocabulary.GreaterThanEqual
const LessThan = tokenVocabulary.LessThan
const LessThanEqual = tokenVocabulary.LessThanEqual
const Comma = tokenVocabulary.Comma
const Equal = tokenVocabulary.Equal
const NotEqual = tokenVocabulary.NotEqual
// ----------------- parser -----------------
class SelectParser extends Parser {
// A config object as a constructor argument is normally not needed.
// Our tutorial scenario requires a dynamic configuration to support step3 without duplicating code.
constructor(config) {
super(tokenVocabulary, config)
// for conciseness
const $ = this
$.RULE('selectStatement', () => {
$.SUBRULE($.selectClause)
$.SUBRULE($.fromClause)
$.OPTION(() => {
$.SUBRULE($.whereClause)
})
$.OPTION1(() => {
$.SUBRULE($.orderByClause)
})
$.OPTION2(() => {
$.SUBRULE($.limitClause)
})
})
$.RULE('selectClause', () => {
$.CONSUME(Select)
$.AT_LEAST_ONE_SEP({
SEP: Comma,
DEF: () => {
$.CONSUME(Identifier)
},
})
})
$.RULE('fromClause', () => {
$.CONSUME(From)
$.CONSUME(Identifier)
})
$.RULE('whereClause', () => {
$.CONSUME(Where)
$.SUBRULE($.expression)
})
$.RULE('orderByClause', () => {
$.CONSUME(OrderBy)
$.CONSUME(Identifier)
$.OPTION(() => {
$.SUBRULE($.orderByExpression)
})
})
$.RULE('limitClause', () => {
$.CONSUME(Limit)
$.CONSUME(Integer)
})
// The "rhs" and "lhs" (Right/Left Hand Side) labels will provide easy
// to use names during CST Visitor (step 3a).
$.RULE('expression', () => {
$.SUBRULE($.atomicExpression, { LABEL: 'lhs' })
$.SUBRULE($.relationalOperator)
$.SUBRULE2($.atomicExpression, { LABEL: 'rhs' }) // note the '2' suffix to distinguish
// from the 'SUBRULE(atomicExpression)'
// 2 lines above.
})
$.RULE('atomicExpression', () => {
$.OR([
{ ALT: () => $.CONSUME(Integer) },
{ ALT: () => $.CONSUME(Identifier) },
])
})
$.RULE('orderByExpression', () => {
$.OR([{ ALT: () => $.CONSUME(Asc) }, { ALT: () => $.CONSUME(Desc) }])
})
$.RULE('relationalOperator', () => {
$.OR([
{ ALT: () => $.CONSUME(GreaterThanEqual) },
{ ALT: () => $.CONSUME(LessThanEqual) },
{ ALT: () => $.CONSUME(GreaterThan) },
{ ALT: () => $.CONSUME(LessThan) },
{ ALT: () => $.CONSUME(Equal) },
{ ALT: () => $.CONSUME(NotEqual) },
])
})
// very important to call this after all the rules have been defined.
// otherwise the parser may not work correctly as it will lack information
// derived during the self analysis phase.
this.performSelfAnalysis()
}
}
// We only ever need one as the parser internal state is reset for each new input.
const parserInstance = new SelectParser()
module.exports = {
parserInstance: parserInstance,
SelectParser: SelectParser,
parse: function(inputText) {
const lexResult = selectLexer.lex(inputText)
// ".input" is a setter which will reset the parser's internal's state.
parserInstance.input = lexResult.tokens
// No semantic actions so this won't return anything yet.
parserInstance.selectStatement()
if (parserInstance.errors.length > 0) {
throw Error(
'Parsing errors detected!\n' + parserInstance.errors[0].message
)
}
},
}