compiler.js
1.92 KB
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
var util = require('./util')
var TERMINALS = {',': 1, '/': 2, '(': 3, ')': 4}
module.exports = compile
/**
* Compiler
*
* Grammar:
* Props ::= Prop | Prop "," Props
* Prop ::= Object | Array
* Object ::= NAME | NAME "/" Object
* Array ::= NAME "(" Props ")"
* NAME ::= ? all visible characters ?
*
* Examples:
* a
* a,d,g
* a/b/c
* a(b)
* ob,a(k,z(f,g/d)),d
*/
function compile (text) {
if (!text) return null
return parse(scan(text))
}
function scan (text) {
var i = 0
var len = text.length
var tokens = []
var name = ''
var ch
function maybePushName () {
if (!name) return
tokens.push({tag: '_n', value: name})
name = ''
}
for (; i < len; i++) {
ch = text.charAt(i)
if (TERMINALS[ch]) {
maybePushName()
tokens.push({tag: ch})
} else {
name += ch
}
}
maybePushName()
return tokens
}
function parse (tokens) {
return _buildTree(tokens, {}, [])
}
function _buildTree (tokens, parent, stack) {
var props = {}
var token
var peek
while ((token = tokens.shift())) {
if (token.tag === '_n') {
token.type = 'object'
token.properties = _buildTree(tokens, token, stack)
// exit if in object stack
peek = stack[stack.length - 1]
if (peek && (peek.tag === '/')) {
stack.pop()
_addToken(token, props)
return props
}
} else if (token.tag === ',') {
return props
} else if (token.tag === '(') {
stack.push(token)
parent.type = 'array'
continue
} else if (token.tag === ')') {
stack.pop(token)
return props
} else if (token.tag === '/') {
stack.push(token)
continue
}
_addToken(token, props)
}
return props
}
function _addToken (token, props) {
props[token.value] = {type: token.type}
if (!util.isEmpty(token.properties)) {
props[token.value].properties = token.properties
}
}