Graphite: Refactor lexer and parser (#17958)
This commit is contained in:
committed by
Torkel Ödegaard
parent
9b468caea4
commit
88a2c6fce7
@@ -898,7 +898,7 @@ const unicodeLetterTable = [
|
||||
195101,
|
||||
];
|
||||
|
||||
const identifierStartTable = [];
|
||||
const identifierStartTable: any[] = [];
|
||||
|
||||
for (let i = 0; i < 128; i++) {
|
||||
identifierStartTable[i] =
|
||||
@@ -922,24 +922,28 @@ for (let i = 0; i < 128; i++) {
|
||||
|
||||
const identifierPartTable = identifierStartTable;
|
||||
|
||||
export function Lexer(this: any, expression) {
|
||||
this.input = expression;
|
||||
this.char = 1;
|
||||
this.from = 1;
|
||||
}
|
||||
export class Lexer {
|
||||
input: any;
|
||||
char: number;
|
||||
from: number;
|
||||
|
||||
Lexer.prototype = {
|
||||
peek: function(i) {
|
||||
constructor(expression: any) {
|
||||
this.input = expression;
|
||||
this.char = 1;
|
||||
this.from = 1;
|
||||
}
|
||||
|
||||
peek(i?: number) {
|
||||
return this.input.charAt(i || 0);
|
||||
},
|
||||
}
|
||||
|
||||
skip: function(i) {
|
||||
skip(i?: number) {
|
||||
i = i || 1;
|
||||
this.char += i;
|
||||
this.input = this.input.slice(i);
|
||||
},
|
||||
}
|
||||
|
||||
tokenize: function() {
|
||||
tokenize() {
|
||||
const list = [];
|
||||
let token = this.next();
|
||||
while (token) {
|
||||
@@ -947,9 +951,9 @@ Lexer.prototype = {
|
||||
token = this.next();
|
||||
}
|
||||
return list;
|
||||
},
|
||||
}
|
||||
|
||||
next: function() {
|
||||
next() {
|
||||
this.from = this.char;
|
||||
|
||||
// Move to the next non-space character.
|
||||
@@ -979,9 +983,9 @@ Lexer.prototype = {
|
||||
|
||||
// No token could be matched, give up.
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
scanTemplateSequence: function() {
|
||||
scanTemplateSequence() {
|
||||
if (this.peek() === '[' && this.peek(1) === '[') {
|
||||
return {
|
||||
type: 'templateStart',
|
||||
@@ -999,7 +1003,7 @@ Lexer.prototype = {
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a JavaScript identifier out of the next sequence of
|
||||
@@ -1007,7 +1011,7 @@ Lexer.prototype = {
|
||||
* to Identifier this method can also produce BooleanLiteral
|
||||
* (true/false) and NullLiteral (null).
|
||||
*/
|
||||
scanIdentifier: function() {
|
||||
scanIdentifier() {
|
||||
let id = '';
|
||||
let index = 0;
|
||||
let type, char;
|
||||
@@ -1020,7 +1024,7 @@ Lexer.prototype = {
|
||||
// Both approach and unicodeLetterTable were borrowed from
|
||||
// Google's Traceur.
|
||||
|
||||
function isUnicodeLetter(code) {
|
||||
function isUnicodeLetter(code: number) {
|
||||
for (let i = 0; i < unicodeLetterTable.length; ) {
|
||||
if (code < unicodeLetterTable[i++]) {
|
||||
return false;
|
||||
@@ -1034,7 +1038,7 @@ Lexer.prototype = {
|
||||
return false;
|
||||
}
|
||||
|
||||
function isHexDigit(str) {
|
||||
function isHexDigit(str: string) {
|
||||
return /^[0-9a-fA-F]$/.test(str);
|
||||
}
|
||||
|
||||
@@ -1157,7 +1161,7 @@ Lexer.prototype = {
|
||||
value: id,
|
||||
pos: this.char,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a numeric literal out of the next sequence of
|
||||
@@ -1168,26 +1172,26 @@ Lexer.prototype = {
|
||||
* This method's implementation was heavily influenced by the
|
||||
* scanNumericLiteral function in the Esprima parser's source code.
|
||||
*/
|
||||
scanNumericLiteral: function(): any {
|
||||
scanNumericLiteral(): any {
|
||||
let index = 0;
|
||||
let value = '';
|
||||
const length = this.input.length;
|
||||
let char = this.peek(index);
|
||||
let bad;
|
||||
|
||||
function isDecimalDigit(str) {
|
||||
function isDecimalDigit(str: string) {
|
||||
return /^[0-9]$/.test(str);
|
||||
}
|
||||
|
||||
function isOctalDigit(str) {
|
||||
function isOctalDigit(str: string) {
|
||||
return /^[0-7]$/.test(str);
|
||||
}
|
||||
|
||||
function isHexDigit(str) {
|
||||
function isHexDigit(str: string) {
|
||||
return /^[0-9a-fA-F]$/.test(str);
|
||||
}
|
||||
|
||||
function isIdentifierStart(ch) {
|
||||
function isIdentifierStart(ch: string) {
|
||||
return ch === '$' || ch === '_' || ch === '\\' || (ch >= 'a' && ch <= 'z') || (ch >= 'A' && ch <= 'Z');
|
||||
}
|
||||
|
||||
@@ -1369,9 +1373,9 @@ Lexer.prototype = {
|
||||
pos: this.char,
|
||||
isMalformed: !isFinite(+value),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
isPunctuator: ch1 => {
|
||||
isPunctuator(ch1: string) {
|
||||
switch (ch1) {
|
||||
case '.':
|
||||
case '(':
|
||||
@@ -1383,9 +1387,9 @@ Lexer.prototype = {
|
||||
}
|
||||
|
||||
return false;
|
||||
},
|
||||
}
|
||||
|
||||
scanPunctuator: function() {
|
||||
scanPunctuator() {
|
||||
const ch1 = this.peek();
|
||||
|
||||
if (this.isPunctuator(ch1)) {
|
||||
@@ -1397,7 +1401,7 @@ Lexer.prototype = {
|
||||
}
|
||||
|
||||
return null;
|
||||
},
|
||||
}
|
||||
|
||||
/*
|
||||
* Extract a string out of the next sequence of characters and/or
|
||||
@@ -1410,7 +1414,7 @@ Lexer.prototype = {
|
||||
* var str = "hello\
|
||||
* world";
|
||||
*/
|
||||
scanStringLiteral: function() {
|
||||
scanStringLiteral() {
|
||||
/*jshint loopfunc:true */
|
||||
const quote = this.peek();
|
||||
|
||||
@@ -1451,5 +1455,5 @@ Lexer.prototype = {
|
||||
quote: quote,
|
||||
pos: this.char,
|
||||
};
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,18 +1,23 @@
|
||||
import { Lexer } from './lexer';
|
||||
|
||||
export function Parser(this: any, expression) {
|
||||
this.expression = expression;
|
||||
this.lexer = new Lexer(expression);
|
||||
this.tokens = this.lexer.tokenize();
|
||||
this.index = 0;
|
||||
}
|
||||
export class Parser {
|
||||
expression: any;
|
||||
lexer: Lexer;
|
||||
tokens: any;
|
||||
index: number;
|
||||
|
||||
Parser.prototype = {
|
||||
getAst: function() {
|
||||
constructor(expression: any) {
|
||||
this.expression = expression;
|
||||
this.lexer = new Lexer(expression);
|
||||
this.tokens = this.lexer.tokenize();
|
||||
this.index = 0;
|
||||
}
|
||||
|
||||
getAst() {
|
||||
return this.start();
|
||||
},
|
||||
}
|
||||
|
||||
start: function() {
|
||||
start() {
|
||||
try {
|
||||
return this.functionCall() || this.metricExpression();
|
||||
} catch (e) {
|
||||
@@ -22,9 +27,9 @@ Parser.prototype = {
|
||||
pos: e.pos,
|
||||
};
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
curlyBraceSegment: function() {
|
||||
curlyBraceSegment() {
|
||||
if (this.match('identifier', '{') || this.match('{')) {
|
||||
let curlySegment = '';
|
||||
|
||||
@@ -51,9 +56,9 @@ Parser.prototype = {
|
||||
} else {
|
||||
return null;
|
||||
}
|
||||
},
|
||||
}
|
||||
|
||||
metricSegment: function() {
|
||||
metricSegment() {
|
||||
const curly = this.curlyBraceSegment();
|
||||
if (curly) {
|
||||
return curly;
|
||||
@@ -97,14 +102,14 @@ Parser.prototype = {
|
||||
|
||||
this.consumeToken();
|
||||
return node;
|
||||
},
|
||||
}
|
||||
|
||||
metricExpression: function() {
|
||||
metricExpression() {
|
||||
if (!this.match('templateStart') && !this.match('identifier') && !this.match('number') && !this.match('{')) {
|
||||
return null;
|
||||
}
|
||||
|
||||
const node = {
|
||||
const node: any = {
|
||||
type: 'metric',
|
||||
segments: [],
|
||||
};
|
||||
@@ -123,9 +128,9 @@ Parser.prototype = {
|
||||
}
|
||||
|
||||
return node;
|
||||
},
|
||||
}
|
||||
|
||||
functionCall: function() {
|
||||
functionCall() {
|
||||
if (!this.match('identifier', '(')) {
|
||||
return null;
|
||||
}
|
||||
@@ -147,9 +152,9 @@ Parser.prototype = {
|
||||
this.consumeToken();
|
||||
|
||||
return node;
|
||||
},
|
||||
}
|
||||
|
||||
boolExpression: function() {
|
||||
boolExpression() {
|
||||
if (!this.match('bool')) {
|
||||
return null;
|
||||
}
|
||||
@@ -158,9 +163,9 @@ Parser.prototype = {
|
||||
type: 'bool',
|
||||
value: this.consumeToken().value === 'true',
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
functionParameters: function() {
|
||||
functionParameters(): any {
|
||||
if (this.match(')') || this.match('')) {
|
||||
return [];
|
||||
}
|
||||
@@ -179,9 +184,9 @@ Parser.prototype = {
|
||||
|
||||
this.consumeToken();
|
||||
return [param].concat(this.functionParameters());
|
||||
},
|
||||
}
|
||||
|
||||
seriesRefExpression: function() {
|
||||
seriesRefExpression() {
|
||||
if (!this.match('identifier')) {
|
||||
return null;
|
||||
}
|
||||
@@ -197,9 +202,9 @@ Parser.prototype = {
|
||||
type: 'series-ref',
|
||||
value: token.value,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
numericLiteral: function() {
|
||||
numericLiteral() {
|
||||
if (!this.match('number')) {
|
||||
return null;
|
||||
}
|
||||
@@ -208,9 +213,9 @@ Parser.prototype = {
|
||||
type: 'number',
|
||||
value: parseFloat(this.consumeToken().value),
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
stringLiteral: function() {
|
||||
stringLiteral() {
|
||||
if (!this.match('string')) {
|
||||
return null;
|
||||
}
|
||||
@@ -224,29 +229,29 @@ Parser.prototype = {
|
||||
type: 'string',
|
||||
value: token.value,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
errorMark: function(text) {
|
||||
errorMark(text: string) {
|
||||
const currentToken = this.tokens[this.index];
|
||||
const type = currentToken ? currentToken.type : 'end of string';
|
||||
throw {
|
||||
message: text + ' instead found ' + type,
|
||||
pos: currentToken ? currentToken.pos : this.lexer.char,
|
||||
};
|
||||
},
|
||||
}
|
||||
|
||||
// returns token value and incre
|
||||
consumeToken: function() {
|
||||
consumeToken() {
|
||||
this.index++;
|
||||
return this.tokens[this.index - 1];
|
||||
},
|
||||
}
|
||||
|
||||
matchToken: function(type, index) {
|
||||
matchToken(type: any, index: number) {
|
||||
const token = this.tokens[this.index + index];
|
||||
return (token === undefined && type === '') || (token && token.type === type);
|
||||
},
|
||||
}
|
||||
|
||||
match: function(token1, token2) {
|
||||
match(token1: any, token2?: any) {
|
||||
return this.matchToken(token1, 0) && (!token2 || this.matchToken(token2, 1));
|
||||
},
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user