Created
August 8, 2017 06:40
-
-
Save lingsamuel/2a44674fb7b99d5161426f36d455f98f to your computer and use it in GitHub Desktop.
Expression Transpiler
This file contains 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
function tokenize(program) { | |
// this function can help you tokenize program | |
// you can use this function in attempt | |
const regex = /\s*(->|[-+*/\(\)\[\]\{\};,]|[A-Za-z]+|[0-9]+)\s*/g; | |
return program.replace(regex, ":$1").substring(1).split(':'); | |
}; | |
let parser; | |
let isNumeric = n => !isNaN(parseFloat(n)) && isFinite(n); | |
let isChar = char => char.length === 1 && char.match(/[a-z]/i); | |
function validVarLetter(char) { | |
return isNumeric(char) || (isChar(char) || char === "_") | |
} | |
function allMatch(str, fn) { | |
let match = true; | |
for (let i = 0; i < str.length; i++) { | |
match = fn(str.charAt(i)) && match; | |
if (match == false) return false | |
} | |
return true | |
} | |
function validVarName(str) { | |
return allMatch(str, validVarLetter) && !isNumeric(str.charAt(0)) | |
} | |
function validNumber(str) { | |
return allMatch(token, isNumeric) | |
} | |
class Node { | |
constructor(name) { | |
this.name = name; | |
} | |
stream() { return this } | |
trans() { return "" } | |
} | |
class ExpNode extends Node { | |
constructor(expression) { | |
super("Exp") | |
this.actual = expression | |
} | |
stream() { | |
} | |
trans() { | |
return this.actual.trans() | |
} | |
} | |
class NumNode extends Node { | |
constructor(value) { | |
super("Num") | |
this.value = value | |
} | |
stream() { | |
return this | |
} | |
trans() { return this.name } | |
} | |
class VarNode extends Node { | |
constructor(name) { | |
super(name) | |
} | |
stream() { | |
return this | |
} | |
trans() { return this.name } | |
} | |
class FnNode extends Node { | |
constructor(expression, paras, lambda) { | |
// actual name or nothing | |
super("Fn") | |
this.exp = expression | |
this.paras = paras | |
this.lambda = lambda | |
} | |
stream() { | |
} | |
trans() { | |
} | |
} | |
class ParasNode extends Node { | |
constructor(list) { | |
super("Paras") | |
this.paras = list | |
} | |
stream() { | |
} | |
trans() { | |
} | |
} | |
class LambdaNode extends Node { | |
constructor(lambdaParas, lambdaStmt) { | |
super("Lambda") | |
this.paras = lambdaParas | |
this.stmt = lambdaStmt | |
} | |
stream() { | |
} | |
trans() { | |
} | |
} | |
class LambdaParasNode extends Node { | |
constructor(list) { | |
super("LParas") | |
this.paras = list; | |
} | |
stream() { | |
} | |
trans() { | |
} | |
} | |
class LambdaStmtNode extends Node { | |
constructor(body) { | |
super("LStmt") | |
this.stmt = body | |
} | |
stream() { | |
return this | |
} | |
trans() { | |
} | |
} | |
class Parser { | |
constructor(tokens) { | |
this.tokens = tokens; | |
this.id = 0; | |
} | |
matchExp() { | |
token = this.now() | |
this.next() | |
if (token == "") { | |
// Should not be empty string | |
return null | |
} | |
if (validNumber(token)) { | |
// in fact, as tokenizer's impl, number are automatically divided. | |
return new NumNode(token) | |
} else if (validVarName(token)) { | |
return new VarNode(token) | |
} else if (token === "{") { | |
return new LambdaNode(token) | |
} else { | |
return null | |
} | |
} | |
checkExp(expNode) { | |
if (expNode instanceof NumNode) { | |
return null | |
} else if (expNode instanceof VarNode) { | |
return this.matchFn(expNode) | |
} else if (expNode instanceof LambdaNode) { | |
return this.matchFn(expNode) | |
} else { | |
return null | |
} | |
} | |
matchFn(expNode) { | |
if (this.now() === "(") { | |
return new FnNode(expNode, this.matchOptionalParas(")", ","), this.matchLambda()) | |
} else if (this.now() === "{") { | |
return new FnNode(expNode, null, this.matchLambda()) | |
} | |
} | |
matchOptionalParas(stop, step) { | |
this.next() // from `(` or step(`,`) to element | |
if (this.now() === stop) { | |
return null // Optional failed, no paras | |
} else { | |
let paras = [] | |
let paraNode = this.matchExp() | |
paras = paras.concat(paraNode) | |
if (this.next() === step) { | |
let append = this.matchOptionalParas(stop, step) | |
paras = paras.concat(append) | |
} else if (this.next() === stop){ | |
this.next() // step over the stop condition | |
return paras | |
} else { | |
return null | |
} | |
} | |
} | |
parse() { | |
let expNode = matchExp() | |
let fnNode = checkExp(expNode) | |
} | |
hasNext() { | |
if (this.id >= this.tokens.length) { | |
return false | |
} else { | |
return true | |
} | |
} | |
now() { | |
if (this.id >= this.tokens.length) { | |
return undefined | |
} | |
return this.tokens[this.id] | |
} | |
next() { | |
this.id++ | |
return this.now() | |
} | |
step(s) { | |
this.id += s | |
return this.now() | |
} | |
rollback() { | |
this.id -= 1 | |
return this.now() | |
} | |
prev() { | |
let i = this.id - 1 | |
return this.tokens[i] | |
} | |
remain() { | |
return this.tokens.slice(this.id) | |
} | |
} | |
function transpile(program, log = false) { | |
let tokens = tokenize(program) | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment