-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathparser.js
More file actions
121 lines (102 loc) · 2.54 KB
/
parser.js
File metadata and controls
121 lines (102 loc) · 2.54 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
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
"use strict";
var Tokenizer = require("./tokenizer");
function Parser(tok) {
if (!(tok instanceof Tokenizer))
throw new TypeError("arguments[0] should be instance of Tokenizer");
this.tok = tok;
this.tok.next(); // initialize
this.stack = [
["#root", null] // [type, props, children...]
];
}
Parser.prototype.accept = function(tok) {
if (this.tok.tok == tok) {
this.tok.next();
return true;
}
return false;
};
Parser.prototype.expect = function(tok) {
if (!this.accept(tok)) {
throw new Error("expected " + tok + " found " + this.tok.tok);
}
};
Parser.prototype.peek = function() {
return this.stack[this.stack.length - 1];
};
Parser.prototype.createElement = function(type, props) {
var el = [type, null];
this.peek().push(el); // append as child to parent
this.stack.push(el);
return el;
};
Parser.prototype.root = function() {
return this.stack[0];
};
Parser.prototype.parse = function() {
if (this.accept("<")) {
// let's go!
if (this.accept("/")) {
// closing tag
var endTag = this.tok.val;
this.expect("ident");
var beginTag = this.stack[this.stack.length - 1][0];
if (beginTag !== endTag) {
throw new Error("expected matching closing tag " + beginTag);
}
this.expect(">");
return false; // break while loop
}
var beginTag = this.tok.val;
this.expect("ident");
var el = this.createElement(beginTag, null);
while (this.parseProp());
if (this.accept("/")) {
// self closing tag
this.expect(">");
} else {
this.expect(">");
// children
while (this.parse());
}
this.stack.pop();
return true;
}
var text = this.tok.val;
if (this.accept("text")) {
this.peek().push(text);
return true;
}
return false;
};
Parser.prototype.parseProp = function() {
var name = this.tok.val;
if (this.accept("ident")) {
var el = this.peek();
if (this.accept("=")) {
var val = this.tok.val;
this.expect("string");
var props = el[1] || {};
props[name] = parseLiteral(val);
el[1] = props;
} else {
// implicit boolean
var props = el[1] || {};
props[name] = true;
el[1] = props;
}
return true;
}
return false;
};
function parseLiteral(str) {
if (str.charCodeAt(0) === 39) {
// edge case: single-quoted string literal
return JSON.parse(
'"' + str.substr(1, str.length - 2).replace(/"/g, '\\"') + '"' // slower
);
}
return JSON.parse(str); // faster
}
Parser.default = Parser;
module.exports = Parser;