Last active
April 11, 2019 00:05
-
-
Save PanJarda/05a4e369f6341c06d2015e53931a467e to your computer and use it in GitHub Desktop.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
"use strict" | |
var Lexer = (function() { | |
var Context = (function() { | |
var SPECIAL_CHARS = { | |
"=": "OP", | |
"-": "OP", | |
"+": "OP", | |
"/": "COMMENT", | |
"*": "OP", | |
"^": "OP", | |
".": "OP", | |
"&": "OP", | |
"!": "OP", | |
"|": "OP", | |
"<": "OP", | |
">": "OP", | |
"?": "OP", | |
":": "OP", | |
"%": "OP", | |
";": "SEP", | |
",": "SEP", | |
"0": "NUM", | |
"1": "NUM", | |
"2": "NUM", | |
"3": "NUM", | |
"4": "NUM", | |
"5": "NUM", | |
"6": "NUM", | |
"7": "NUM", | |
"8": "NUM", | |
"9": "NUM", | |
"(": "BRACKET", | |
")": "BRACKET", | |
"{": "BLOCK", | |
"}": "BLOCK", | |
"[": "ARR", | |
"]": "ARR", | |
"\"": "STRING_LITERAL", | |
"'": "STRING_LITERAL" | |
}; | |
var KEYWORDS = { | |
"click": "click", | |
"submit": "submit", | |
"value": "value", | |
"text": "text" | |
}; | |
var CONTEXTS = { | |
"GLOBAL": Global, | |
"OP": Operator, | |
"SEP": Separator, | |
"NUM": Digit, | |
"BRACKET": Bracket, | |
"BLOCK": Block, | |
"ARR": Arr, | |
"STRING_LITERAL": StringLiteral, | |
"WORD": Word, | |
"COMMENT": Comment | |
}; | |
var OPERATORS = { | |
"+": "plus", | |
"-": "minus", | |
"*": "multiplication", | |
"/": "division", | |
"^": "power", | |
"%": "modulo", | |
"=": "assignment", | |
"!": "negation", | |
"==": "eq", | |
"<=": "lteq", | |
">=": "gteq", | |
"!=": "neq", | |
"===": "eqt", | |
"!==": "neqt", | |
"?": "ternary", | |
":": "assignment", | |
"&&": "AND", | |
"||": "OR", | |
"&": "bitwiseAND", | |
"|": "bitwiseOR", | |
"~": "bitwiseNOT", | |
"^": "XOR", | |
"<<": "leftshift", | |
">>": "rightshift", | |
">>>": "zerofillrightshift", | |
"+=": "addassign", | |
"-=": "minusassing", | |
"*=": "multiassing", | |
"/=": "divassign", | |
"%=": "moduloassign", | |
"**=": "powerassign", | |
"++": "inc", | |
"--": "dec", | |
".": "access" | |
}; | |
function isWS(char) { | |
return /\s/.test(char); | |
} | |
function AbstractCtx(lexer, parentCtx, pos) { | |
this._lexer = lexer; | |
this._parentCtx = parentCtx; | |
this._startPos = pos; | |
} | |
function Global(lexer) { | |
AbstractCtx.call(this, lexer); | |
} | |
Global.prototype.accept = function(char, pos) { | |
if (isWS(char)) { | |
return; | |
} | |
var lexer = this._lexer, | |
ctxName, | |
ctx; | |
if (char in SPECIAL_CHARS) { | |
ctxName = SPECIAL_CHARS[char]; | |
ctx = CONTEXTS[ctxName]; | |
} else { | |
ctx = CONTEXTS["WORD"]; | |
} | |
lexer.context = new ctx(lexer, this, pos); | |
lexer.context.accept(char, pos); | |
}; | |
function Word(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._word = ""; | |
} | |
Word.prototype.accept = function(char, pos) { | |
var word = this._word, | |
lexer = this._lexer, | |
ctx = this._parentCtx; | |
if (isWS(char) || (char in SPECIAL_CHARS && /[^0-9]/.test(char))) { | |
if (KEYWORDS[word]) { | |
lexer.addToken("KEYWORD", word, this._startPos); | |
} else { | |
lexer.addToken("IDENTIFIER", word, this._startPos); | |
} | |
lexer.context = ctx; | |
lexer.context.accept(char, pos); | |
return; | |
} | |
this._word += char; | |
}; | |
function Digit(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._number = ""; | |
} | |
Digit.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx; | |
if (/[0-9.]/.test(char)) { | |
this._number += char; | |
} else { | |
lexer.addToken("NUMBER", this._number, this._startPos); | |
lexer.context = ctx; | |
lexer.context.accept(char, pos); | |
} | |
}; | |
function Separator(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
} | |
Separator.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx; | |
lexer.addToken("SEPARATOR", char, this._startPos); | |
lexer.context = ctx; | |
}; | |
function Operator(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._operator = ""; | |
} | |
Operator.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx; | |
if ((this._operator + char) in OPERATORS) { | |
this._operator += char; | |
} else { | |
lexer.addToken("OPERATOR", this._operator, this._startPos); | |
lexer.context = ctx; | |
lexer.context.accept(char, pos); | |
} | |
}; | |
function Bracket(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
} | |
Bracket.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx, | |
tokenName = (char == "(" ? "LBRACKET" : "RBRACKET"); | |
lexer.addToken(tokenName, char, this._startPos); | |
lexer.context = ctx; | |
}; | |
function Block(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
} | |
Block.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx, | |
tokenName = (char == "{" ? "BLOCKSTART" : "BLOCKEND"); | |
lexer.addToken(tokenName, char, this._startPos); | |
lexer.context = ctx; | |
}; | |
function Arr(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
} | |
Arr.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx, | |
tokenName = (char == "[" ? "ARRSTART" : "ARREND"); | |
lexer.addToken(tokenName, char, this._startPos); | |
lexer.context = ctx; | |
}; | |
function StringLiteral(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._quote = null; | |
this._string = ""; | |
this._escape = false; | |
} | |
StringLiteral.prototype.accept = function(char, pos) { | |
var lexer = this._lexer, | |
ctx = this._parentCtx; | |
if (this._quote == null) { | |
this._quote = char; | |
this._string += char; | |
return; | |
} | |
if (char === "\\") { | |
this._escape = !this._escape; | |
} else { | |
this._escape = false; | |
} | |
this._string += char; | |
if (char === this._quote && !this._escape) { | |
lexer.addToken("STRING_LITERAL", this._string, this._startPos); | |
lexer.context = ctx; | |
} | |
}; | |
function LineComment(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
} | |
LineComment.prototype.accept = function(char, pos) { | |
if (/[\r\n]/.test(char) || /\r\n/.test(char)) { | |
this._lexer.context = this._parentCtx; | |
} | |
}; | |
function MultilineComment(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._star = false; | |
} | |
MultilineComment.prototype.accept = function(char, pos) { | |
if (char == "/" && this._star) { | |
this._lexer.context = this._parentCtx; | |
} else if (char == "*") { | |
this._star = true; | |
} else if (this._star) { | |
this._star = false; | |
} | |
}; | |
function Comment(lexer, parentCtx, pos) { | |
AbstractCtx.call(this, lexer, parentCtx, pos); | |
this._firstRun = true; | |
} | |
Comment.prototype.accept = function(char, pos) { | |
if (this._firstRun) { | |
this._firstRun = false; | |
return; | |
} | |
var lexer = this._lexer, | |
ctx = this._parentCtx; | |
if (char == "/") { | |
lexer.context = new LineComment(lexer, ctx, pos); | |
} else if (char == "*") { | |
lexer.context = new MultilineComment(lexer, ctx, pos); | |
} else { | |
lexer.context = new Operator(lexer, ctx, this._startPos); | |
lexer.context.accept("/", this._startPos); | |
lexer.context.accept(char, pos); | |
} | |
}; | |
return Global; | |
})(); | |
function Lexer(input) { | |
this._input = input; | |
this._position = 0; | |
this._tokens = []; | |
this.context = new Context(this, null, 0); | |
} | |
Lexer.prototype.addToken = function(name, value, position) { | |
this._tokens.push({name: name, value: value, position: position}); | |
}; | |
Lexer.prototype.next = function() { | |
var ch = this._input.charAt(this._position); | |
if (ch !== "") { | |
this.context.accept(ch, this._position); | |
this._position++; | |
return 1; | |
} else { | |
return 0; | |
} | |
}; | |
Lexer.prototype.run = function() { | |
while (this.next()) {} | |
this.context.accept(" ", this._position + 1); | |
}; | |
return Lexer; | |
})(); | |
var input = "click: 1.2; submit: addItem12 . ahoj \ | |
; (1+1)/2 /* ahoj */ ahoj; 'ahoj' + \"ddddd\"; [1,2,3,4]", | |
lexer = new Lexer(input); | |
lexer.run(); | |
console.log(lexer._tokens); | |
var Parser = (function() { | |
function ParserError() { | |
this.message = message; | |
this.name = "ParserError"; | |
} | |
var Node = (function() { | |
var ENTER_EXPR_TOKENS = { | |
"KEYWORD": Keyword, | |
"IDENTIFIER": Identifier, | |
"NUMBER": _Number, | |
"LBRACKET": Subexpression, | |
"ARRSTART": Arr, | |
"OPERATOR": Operator, | |
"STRING_LITERAL": StringLiteral | |
}; | |
function AbstractNode(parser, parentNode) { | |
this._parser = parser; | |
this._parentNode = parentNode; | |
} | |
function Program(parser) { | |
AbstractNode.call(this, parser); | |
this._expressions = []; | |
} | |
Program.prototype.accept = function(token) { | |
if (token.name in ENTER_EXPR_TOKENS) { | |
var expr = new Expression(this._parser, this); | |
this._epxressions.push(expr); | |
parser.context = expr; | |
expr.accept(token); | |
} else { | |
throw new ParserError("Unexpected token " + token.value + " at position " + token.position); | |
} | |
}; | |
function Expression(parser, parent) { | |
AbstractNode.call(this, parser, parent); | |
this._left; | |
this._middle; | |
this._right; | |
this._operator; | |
} | |
Expression.prototype.accept = function(token) { | |
if (token.name == "OPERATOR") { | |
} | |
}; | |
return { | |
Program: Program, | |
}; | |
})(); | |
function Parser(tokens) { | |
this._tokens = tokens; | |
this._ASTRoot = new Node.Program(this); | |
this.context = this._ASTRoot; | |
} | |
Parser.prototype.run = function() { | |
}; | |
})(); | |
// TODO: template literals, regexps, testy | |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment