-
Notifications
You must be signed in to change notification settings - Fork 8
/
Copy pathgrammar.jison
117 lines (101 loc) · 3.07 KB
/
grammar.jison
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
%{
function toObject(array, resolveConflicts) {
return array.reduce(function (obj, item) {
var oldValue = obj[item.name];
var newValue = item.value;
if (oldValue && resolveConflicts) {
newValue = resolveConflicts(oldValue, newValue);
}
obj[item.name] = newValue;
return obj;
}, Object.create(null));
}
function intf(name, props, base) {
return {
name: name,
value: {
kind: 'interface',
props: props,
base: base
}
};
}
%}
/* lexical grammar */
%lex
%%
'//'.* /* skip one-line comments */
'/*'[\s\S]*?'*/' /* skip multi-line comments */
\s+ /* skip whitespace */
'interface' return 'INTERFACE'
'enum' return 'ENUM'
'extend' return 'EXTEND'
'"'.*?'"' return 'STRING';
\d+ return 'NUMBER';
'true'|'false' return 'BOOLEAN';
'null' return 'NULL';
[A-Za-z_][\w]* return 'NAME';
[,;:[\]{}|]|'<:' return yytext
<<EOF>> return 'EOF'
/lex
/* operator associations and precedence */
%left ';'
%left ',' '|'
%start program
%ebnf
%% /* language grammar */
program
: def* EOF {
return toObject($$, function (oldValue, newValue) {
// Extend earlier found interface or return new one.
if (oldValue.kind === 'interface' && newValue.kind === 'interface' && newValue.base === null) {
Object.assign(oldValue.props, newValue.props);
return oldValue;
} else {
return newValue;
}
});
}
;
def
: EXTEND INTERFACE NAME '<:' names object -> intf($NAME, $object, $names)
| EXTEND INTERFACE NAME object -> intf($NAME, $object, null)
| INTERFACE NAME '<:' names object -> intf($NAME, $object, $names)
| INTERFACE NAME object -> intf($NAME, $object, [])
| EXTEND ENUM NAME '{' enumBody '}' -> {name: $NAME, value: {kind: 'enum', values: $enumBody}}
| ENUM NAME '{' enumBody '}' -> {name: $NAME, value: {kind: 'enum', values: $enumBody}}
;
names
: names ',' NAME {
$$.push($NAME);
}
| NAME -> [$NAME]
;
enumBody
: enumBody '|' literal {
$$.push($literal);
}
| literal -> [$literal]
;
object
: '{' prop* '}' -> toObject($2)
;
prop
: NAME ':' $type ';'? -> {name: $NAME, value: $type}
;
type
: type '|' type {
if ($$.kind !== 'union') $$ = {kind: 'union', types: [$$]};
$$.types.push($type2);
}
| literal -> {kind: 'literal', value: $literal}
| NAME -> {kind: 'reference', name: $NAME}
| '[' type ']' -> {kind: 'array', base: $type}
| object -> {kind: 'object', items: $object}
;
literal
: STRING -> $STRING.slice(1, -1)
| NUMBER -> Number($NUMBER)
| BOOLEAN -> $BOOLEAN === 'true'
| NULL -> null
;