Skip to content

Instantly share code, notes, and snippets.

@PanJarda
Last active April 11, 2019 00:05
Show Gist options
  • Save PanJarda/05a4e369f6341c06d2015e53931a467e to your computer and use it in GitHub Desktop.
Save PanJarda/05a4e369f6341c06d2015e53931a467e to your computer and use it in GitHub Desktop.
"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