-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathparser.js
146 lines (141 loc) · 4.17 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
"use strict";
let parseProgram = require("./parse_functions/program.js");
module.exports = class Parser{
constructor( tokens, error, verbose, directory ) {
this.tokens = tokens;
this.index = 0;
this.error = error;
this.verbose = verbose;
this.lastToken = null;
this.directory = directory;
this.labels = {};
}
get next() {
return this.tokens[this.index];
}
get empty() {
return this.tokens.length <= this.index;
}
get tokensLeft() {
return this.tokens.length - this.index;
}
get( i ) {
return this.tokens[i];
}
pop() {
let popped = this.tokens[this.index++];
this.lastToken = popped;
return popped;
}
getFile(filename){
return this.directory + filename;
}
insertLabel(label){
// Get the tokens out of our hash
let labelTokens = this.labels[label.text];
if(!labelTokens) {
this.error.generic( `Label '${label.text}' is not defined`, label.line, label.column, true);
return false;
}
// Insert tokens right where we are
this.tokens.splice(this.index, 0, ...labelTokens);
return true;
}
storeLabel(label, tokens){
// If label already exists, we overwrite
this.labels[label.text] = tokens;
}
log( message ) {
if(this.verbose) {
console.log(message);
}
}
matchLog( message ) {
this.log(message + ` at line ${this.next.line} col ${this.next.column}`);
}
match( type ){
if( this.empty ) {
this.error.parse(`Expected ${type}, got end of program`, this.lastToken);
}
else if( type === undefined || type === this.next.type) {
let msg = `Matched "${type}"`;
if(this.next.text){
msg += ` with text "${this.next.text}"`;
}
this.log( msg );
this.log(`Tokens remaining: ${this.tokensLeft - 1}`);
return this.pop();
}
else {
this.error.expected(type, this.pop());
}
}
// Returns true if the next token has type "type", or a type in "type" if
// type is an array, false otherwise
at( type ) {
return this.atAhead( type, 0 );
}
atAhead ( type, i ) {
if( this.empty ) {
return false;
}
if(Array.isArray( type )) {
for(let j = 0; j < type.length; ++j) {
if( type[j] === this.get(this.index + i).type ) {
return true;
}
}
return false;
}
return type === this.get(this.index + i).type;
}
atSequential( type ){
for(let i = 0; i < type.length; ++i) {
// If we don't have enough tokens or the token we're at doesn't
// match, no sale.
if(this.tokensLeft <= i || !this.atAhead( type[i], i )) {
return false;
}
}
return true;
}
atBlock() {
return this.atSequential(["newline", "indent"]);
}
atArgs() {
return this.at("openParen") || this.atBlock() || this.atExp();
}
atLabel() {
return this.atSequential(["openCurly", "bareword", "closeCurly"]);
}
atExp() {
return this.at([...this.lits,
"include",
"openSquare",
"openCurly",
"id",
"this",
"dot",
"hash",
"openParen",
"prefixop",
"minus",
...this.builtins,
"bareword"]);
}
get lits() {
return ["stringlit", "intlit", "floatlit", "boollit"];
}
get builtins() {
return ['script', 'style'];
}
get controlTypes() {
return ['if', 'for', 'while'];
}
get binAssignOps() {
return ["plus", "minus", "multop", "boolop"];
}
init() {
return parseProgram( this );
}
};