Created
July 27, 2013 08:35
-
-
Save moriyoshi/6094257 to your computer and use it in GitHub Desktop.
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
var xaxtsuxo = (function () { | |
var KEYWORDS = { | |
'division': 'DIVISION', | |
'end': 'END', | |
'run': 'RUN', | |
'if': 'IF', | |
'then': 'THEN', | |
'for': 'FOR', | |
'in': 'IN', | |
'or': 'OR', | |
'ana': 'AND', | |
'to': 'TO', | |
'by': 'BY', | |
'on': 'ON', | |
'upon': 'UPON', | |
'at': 'AT', | |
'from': 'FROM', | |
'with': 'WITH', | |
'yields': 'YIELDS', | |
'true': 'true', | |
'false': 'false', | |
'->': 'YIELDS', | |
'+=': 'ADD_ASSIGN', | |
'-=': 'SUB_ASSIGN', | |
'*=': 'MUL_ASSIGN', | |
'/=': 'DIV_ASSIGN', | |
'&&': 'AND_SIGN', | |
'||': 'OR_SIGN', | |
'.': 'EOS', | |
'(': 'LPAREN', | |
')': 'RPAREN', | |
'[': 'LBRACKET', | |
']': 'RBRACKET', | |
'{': 'LBRACE', | |
'}': 'RBRACE', | |
'&': 'BITWISE_AND', | |
'|': 'BITWISE_OR', | |
'^': 'BITWISE_XOR', | |
'!': 'NOT_SIGN', | |
'*': 'MUL_SIGN', | |
'+': 'ADD_SIGN', | |
'/': 'DIV_SIGN', | |
'-': 'SUB_SIGN', | |
';': 'SEMICOLON', | |
':': 'COLON' | |
}; | |
function createTokenizer(s) { | |
var TOKEN_REGEX = /(\r\n|\r|\n)|([ \t]+)|([ぁぃぅぇぉっゃゅょ]+)|(<\?php)|(division|end|if|then|for|in|or|and|not|to|by|on|upon|at|from|with|yields|true|false|->|\+=|-=|\*=|\/=|&&|\|\||[.,:;()[\]{}~&|!^*+\/-])|([a-zA-Z_][a-zA-Z0-9_]*)|((?:[0-9]+(>:\.[0-9]*)?|\.[0-9]+)(?:[eE][+-]?[0-9]+)?)/gi; | |
var line = 0, column = 0, undone = [], eof = false; | |
var retval = function () { | |
if (undone.length > 0) { | |
return undone.shift(); | |
} | |
var m = null; | |
var retval = null; | |
if (!eof) | |
m = TOKEN_REGEX.exec(s); | |
if (!m) { | |
eof = true; | |
return ['EOF', null, line, column]; | |
} | |
if (m[1]) { | |
retval = ['EOL', m[0], line, column]; | |
line++; | |
column = 0; | |
return retval; | |
} else if (m[2]) { | |
retval = ['WS', m[0], line, column]; | |
} else if (m[3]) { | |
retval = ['ぁっぉ', m[0], line, column]; | |
} else if (m[4]) { | |
retval = ['PHP', m[0], line, column]; | |
} else if (m[6]) { | |
retval = ['IDENT', m[0], line, column]; | |
} else if (m[7]) { | |
retval = ['NUMBER', m[0], line, column]; | |
} else if (m[5]) { | |
retval = [KEYWORDS[m[0].toLowerCase()], m[0], line, column]; | |
} | |
column += m[0].length; | |
return retval; | |
}; | |
retval.undo = function (t) { | |
undone.push(t); | |
}; | |
return retval; | |
} | |
function humanFriendlyEnum(list) { | |
if (list.length == 0) { | |
throw 'list is empty'; | |
} else if (list.length == 1) { | |
return list[0]; | |
} else { | |
return list.slice(0, list.length - 1).join(', ') + ' and ' + list[list.length - 1]; | |
} | |
} | |
function parse(_) { | |
var STATEMENTS = { | |
assign: function () { | |
var t; | |
var numberOrVariable = parseExpr(); | |
if ((t = __())[0] != 'TO') { | |
syntaxError(['TO'], t); | |
} | |
var variable = parseVariable(); | |
t = __(); | |
if (t[0] != 'EOL' && t[0] != 'EOS') | |
syntaxError(['EOL', 'EOS'], t); | |
_.undo(t); | |
return { | |
t: 'ASSIGN', | |
c: [ | |
variable, | |
numberOrVariable | |
] | |
}; | |
}, | |
add: function () { | |
var t; | |
var numberOrVariable = parseExpr(); | |
if ((t = __())[0] != 'TO') { | |
syntaxError(['TO'], t); | |
} | |
var variable = parseVariable(); | |
t = __(); | |
if (t[0] != 'EOL' && t[0] != 'EOS') | |
syntaxError(['EOL', 'EOS'], t); | |
_.undo(t); | |
return { | |
t: 'ASSIGN', | |
c: [ | |
variable, | |
{ | |
t: 'ADD', | |
c: [ variable, numberOrVariable ] | |
} | |
] | |
}; | |
}, | |
sub: function () { | |
var t; | |
var numberOrVariable = parseExpr(); | |
if ((t = __())[0] != 'FROM') { | |
syntaxError(['FROM'], t); | |
} | |
var variable = parseVariable(); | |
t = __(); | |
if (t[0] != 'EOL' && t[0] != 'EOS') | |
syntaxError(['EOL', 'EOS'], t); | |
_.undo(t); | |
return { | |
t: 'ASSIGN', | |
c: [ | |
variable, | |
{ | |
t: 'SUB', | |
c: [ variable, numberOrVariable ] | |
} | |
] | |
}; | |
}, | |
multiply: function () { | |
var t; | |
var variable = parseVariable(); | |
if ((t = __())[0] != 'BY') { | |
syntaxError(['BY'], t); | |
} | |
var numberOrVariable = parseExpr(); | |
t = __(); | |
if (t[0] != 'EOL' && t[0] != 'EOS') | |
syntaxError(['EOL', 'EOS'], t); | |
_.undo(t); | |
return { | |
t: 'ASSIGN', | |
c: [ | |
variable, | |
{ | |
t: 'MULTIPLY', | |
c: [ variable, numberOrVariable ] | |
} | |
] | |
}; | |
}, | |
display: function () { | |
var t; | |
var io = null; | |
var numberOrVariable = parseExpr(); | |
t = __(); | |
switch (t[0]) { | |
case 'EOL': | |
case 'EOS': | |
_.undo(t); | |
break; | |
case 'UPON': | |
io = parseIO(); | |
break; | |
default: | |
syntaxError(['EOL', 'EOS', 'UPON'], t); | |
} | |
return { | |
t: 'DISPLAY', | |
c: [ io, numberOrVariable ] | |
}; | |
}, | |
stop: function () { | |
var t = __(); | |
if (t[0] != 'IDENT' && t[1].toLowerCase() != 'run') | |
syntaxError(['RUN'], t); | |
return { t: 'STOP', c: null }; | |
} | |
}; | |
var kw = []; | |
for (var k in STATEMENTS) | |
kw.push(k.toUpperCase()); | |
function __() { | |
var t; | |
do { | |
t = _(); | |
} while (t[0] == 'WS'); | |
return t; | |
} | |
function syntaxError(expecting, t) { | |
throw 'Expecting ' + humanFriendlyEnum(expecting) + ', got ' + t[1] + ' at line ' + (t[2] + 1) + ', column ' + (t[3] + 1); | |
} | |
function parseExpr() { | |
var t = __(); | |
switch (t[0]) { | |
case 'NUMBER': | |
return { | |
t: 'NUMBER', | |
c: t[1] | |
}; | |
case 'IDENT': | |
return { | |
t: 'VARIABLE', | |
c: t[1] | |
}; | |
case 'TRUE': | |
return { | |
t: 'BOOL', | |
c: true | |
}; | |
case 'FALSE': | |
return { | |
t: 'BOOL', | |
c: false | |
}; | |
default: | |
syntaxError(['NUMBER', 'IDENT'], t); | |
} | |
} | |
function parseVariable() { | |
var t = __(); | |
if (t[0] != 'IDENT') | |
syntaxError(['IDENT'], t); | |
return { | |
t: 'VARIABLE', | |
c: t[1] | |
}; | |
} | |
function parseIO() { | |
var t = __(); | |
if (t[0] != 'IDENT') | |
syntaxError(['IDENT'], t); | |
return { | |
t: 'IO', | |
c: t[1] | |
}; | |
} | |
function parseYields(ident) { | |
return { | |
t: 'FUNCTION', | |
c: parseStatements() | |
}; | |
} | |
function parseStatements() { | |
var retval = []; | |
for (;;) { | |
var t = __(); | |
switch (t[0]) { | |
case 'IDENT': | |
var nt = __(); | |
if (nt[0] == 'YIELDS') { | |
retval.push(parseYields(t)); | |
} else { | |
_.undo(nt); | |
var stmt = STATEMENTS[t[1].toLowerCase()]; | |
if (!stmt) { | |
syntaxError(kw.concat(['EOL', 'EOS']), t); | |
} | |
retval.push(stmt()); | |
} | |
case 'EOL': | |
break; | |
case 'EOS': | |
return retval; | |
default: | |
syntaxError(['IDENT', 'EOL', 'EOS'], t); | |
} | |
} | |
} | |
function parseDivision() { | |
var t; | |
t = __(); | |
switch (t[0]) { | |
case 'ぁっぉ': | |
if ((t = __())[0] != 'DIVISION') { | |
syntaxError(['DIVISION'], t); | |
} | |
if ((t = __())[0] != 'EOS') { | |
syntaxError(['EOS'], t); | |
} | |
if ((t = __())[0] != 'EOL') { | |
syntaxError(['EOL'], t); | |
} | |
return { | |
t: 'ぁっぉ', | |
c: parseStatements() | |
}; | |
case 'PHP': | |
return { | |
t: 'ぁっぉ', | |
c: parseStatements() | |
}; | |
default: | |
syntaxError(['ぁっぉ', 'PHP'], t); | |
} | |
} | |
return parseDivision(); | |
} | |
var evaluate = (function () { | |
var x = { | |
'ぁっぉ': function (ctx, n) { | |
for (var i = 0; i < n.c.length; i++) | |
evaluate(ctx, n.c[i]); | |
}, | |
ASSIGN: function (ctx, n) { | |
return (ctx.vars[n.c[0].c] = evaluate(ctx, n.c[1])); | |
}, | |
ADD: function (ctx, n) { | |
return evaluate(ctx, n.c[0]) + evaluate(ctx, n.c[1]); | |
}, | |
MULTIPLY: function (ctx, n) { | |
return evaluate(ctx, n.c[0]) * evaluate(ctx, n.c[1]); | |
}, | |
VARIABLE: function (ctx, n) { | |
var val = ctx.vars[n.c]; | |
if (val === void(0)) | |
throw "variable " + n.c + " is not defined"; | |
return val; | |
}, | |
NUMBER: function (ctx, n) { | |
return eval(n.c); | |
}, | |
DISPLAY: function (ctx, n) { | |
switch ((n.c[0].c || 'console').toLowerCase()) { | |
case 'dialog': | |
alert(evaluate(ctx, n.c[1])); | |
break; | |
case 'console': | |
console.log(evaluate(ctx, n.c[1])); | |
break; | |
} | |
}, | |
STOP: function (ctx, n) {} | |
}; | |
function evaluate(ctx, n) { return x[n.t](ctx, n); } | |
return evaluate; | |
})(); | |
var compileNode = (function () { | |
var stream = []; | |
var x = { | |
'ぁっぉ': function (ctx, n) { | |
var retval = []; | |
for (var i = 0; i < n.c.length; i++) | |
retval.push.apply(retval, compileNode(ctx, n.c[i]).concat([';', '\n'])); | |
return retval; | |
}, | |
ASSIGN: function (ctx, n) { | |
ctx.vars[n.c[0].c] = true; | |
return [n.c[0].c, ' = '].concat(compileNode(ctx, n.c[1])); | |
}, | |
ADD: function (ctx, n) { | |
return ['('].concat(compileNode(ctx, n.c[0])).concat([' + ']).concat(compileNode(ctx, n.c[1])).concat([')']); | |
}, | |
MULTIPLY: function (ctx, n) { | |
return ['('].concat(compileNode(ctx, n.c[0])).concat([' * ']).concat(compileNode(ctx, n.c[1])).concat([')']) | |
}, | |
VARIABLE: function (ctx, n) { | |
var val = ctx.vars[n.c]; | |
if (val === void(0)) | |
throw "variable " + n.c + " is not defined"; | |
return [n.c]; | |
}, | |
NUMBER: function (ctx, n) { | |
return [n.c]; | |
}, | |
DISPLAY: function (ctx, n) { | |
switch ((n.c[0].c || 'console').toLowerCase()) { | |
case 'dialog': | |
return ['alert', '('].concat(compileNode(ctx, n.c[1])).concat([')']); | |
case 'console': | |
return ['console.log', '('].concat(compileNode(ctx, n.c[1])).concat([')']); | |
} | |
}, | |
STOP: function (ctx, n) { return []; } | |
}; | |
function compileNode(ctx, n) { return x[n.t](ctx, n); } | |
return compileNode; | |
})(); | |
return { | |
createTokenizer: createTokenizer, | |
parse: parse, | |
compileNode: compileNode, | |
compile: function compile(script) { | |
return compileNode({ vars: {} }, parse(createTokenizer(script))).join(''); | |
} | |
}; | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment