Skip to content

Instantly share code, notes, and snippets.

@tormaroe
Created December 20, 2013 23:36
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save tormaroe/8063381 to your computer and use it in GitHub Desktop.
Save tormaroe/8063381 to your computer and use it in GitHub Desktop.
XMASLANG, a small programming language I created for my xmas calendar competition 2013.

To run this you need node and the PEG.js module installed.

To create the parser.js file, run:

pegjs grammar.peg parser.js

Now to compile the xmaslang source, run:

node parser.js program.xmas > programm.js

And finally run the program:

node program.js
var parser = require('./parser');
var fs = require('fs');
var indent_level = 0;
var indent = function(s) {
var lines = s.split('\n');
var indentation = "";
for(var i = 0; i < indent_level; i++)
indentation += " ";
var result = lines.reduce(function(acc, line) {
return acc + "\n" + indentation + line;
}, indentation + lines.shift());
indent_level--;
return result;
};
var map_infix_operation = function(op){
switch (op) {
case "=": return "===";
default: return op;
}
};
var compile_expression = function(expr) {
switch (expr.type) {
case 'function_application':
return compile_expression(expr['function']) +
"(" + compile_expression(expr['argument']) + ")";
case 'function_definition':
indent_level++;
return "function(" + compile_expression(expr.parameter) + ") {\n" +
indent("return " + compile_expression(expr.body) + ";") +
"\n}";
case "if":
indent_level++;
return "if_func$(" + compile_expression(expr.condition) + ", \n" +
indent(compile_expression(expr['true']) + ",\n" +
compile_expression(expr['false']) + ")");
case 'range':
return "range_func$(" + compile_expression(expr.stop) + ")";
case 'map':
return compile_expression(expr.range) + ".map(" +
compile_expression(expr.transformation) + ")";
case 'reduce':
indent_level++;
return compile_expression(expr.range) +
".reduce(function(" + compile_expression(expr.accumulator) + ", " + compile_expression(expr.reducer.parameter) + ") {\n" +
indent("return " + compile_expression(expr.reducer.body) + ";") +
"\n}, " +
compile_expression(expr.initial) + ")";
case 'filter':
return compile_expression(expr.range) + ".filter(" +
compile_expression(expr.predicate) + ")";
case 'pipe':
return compile_expression(expr.target) + "(" +
compile_expression(expr.source) + ")";
case 'identifier':
return expr.value.replace('-', '_');
case 'int':
return expr.value;
case 'infix':
return "(" + compile_expression(expr.left) +
" " + map_infix_operation(expr.operation) + " " +
compile_expression(expr.right) + ")";
case 'comment':
return "";
default:
throw "Unknown expression type: " + expr.type + " " + expr;
}
};
var core = "function if_func$(c, t, f) { if(c){ return t } return f; }\n";
core += "function range_func$(stop) { " +
" var range = [];" +
" for(var i=0;i<=stop;i++){ range.push(i); }" +
" return range;}\n";
var compile = function(ast) {
return core + ast.reduce(function(acc, expr) {
return acc + " " + compile_expression(expr);
}, 'console.log(') + ')';
};
var input_file = process.argv[2];
fs.readFile(input_file, function(err, source) {
if(err)
return console.log(err);
var result = parser.parse(source.toString());
console.log(compile(result));
});
start
= program:expression * { return program; }
expression 'expression'
= expr:(integer
/ range
/ map
/ filter
/ reduce
/ pipe
/ infix_operation
/ if_expression
/ function_application
/ function_definition
/ identifier) whitespace? { return expr; }
range
= '#' whitespace? stop:expression {
return { type: 'range', stop: stop };
}
map
= '->' whitespace? range:expression trans:function_definition {
return { type: 'map', range: range, transformation: trans };
}
reduce
= '$' whitespace? range:expression accumulator:identifier
whitespace? initial:expression reducer:function_definition {
return {
type: 'reduce', range: range, accumulator: accumulator,
initial: initial, reducer: reducer
};
}
filter
= '=?' whitespace? range:expression pred:function_definition {
return { type: 'filter', range: range, predicate: pred };
}
pipe
= '|' whitespace? source:expression target:function_definition {
return { type: 'pipe', source:source, target:target };
}
infix_operation
= op:[+\-\*/=\>%] whitespace? left:expression right:expression {
return { type: 'infix', operation: op, left: left, right: right };
}
if_expression
= '?' whitespace? cond:expression t:expression f:expression {
return { type: 'if', condition: cond, 'true': t, 'false': f };
}
function_application 'function application'
= func:(function_definition / identifier)
'[' whitespace? arg:expression ']' {
return {
type: 'function_application',
function: func, argument: arg
};
}
function_definition 'function definition'
= '<' whitespace? param:identifier whitespace? exp:expression '>' {
return { type: 'function_definition', parameter: param, body: exp };
}
identifier 'identifier'
= word:[a-z\-]+ {
return {type: 'identifier', value: word.join('') };
}
integer 'integer'
= digits:[0-9]+ {
return { type: 'int', value: parseInt(digits.join(""), 10) };
}
whitespace
= [ \t\n\r]+
-+
+/|+5++3 1 1 <x +2x>
$->#2<x 1>s 1<i+s i>
+||#5<x ->x<y+$x z 100 <k - z k
>y> > <x $x a 0 <b +a b> > | -
$ ->#10<x#x>x0<y+x$y z0<y+y z>>
$ ->->#30<x+x1> <x%
$->#10<a*a$=?->#<foo?=foo*2
4<bar*+foo bar bar>[-20 15]
100000>[
|->#||1 <x+1x>
<x+1x>
<x |1 <x+1x>>
<r $r p 0 <q +p q> >
]<i+100i> <x>x100>
acc 0 < item + acc item > >
merry-xmas1<b+merry-xmas b>
x>
a-c0<m+m a-c>
<x*113x>
|||*|||1<x 1><x+|1<x 1>x><x*
||1<x 1><x+|1<x 1>x>x>
|||1<x 1><x+|1<x 1>x><x+
||1<x 1><x+||1<x 1><x+
|1<x 1>x>x>x><x+
|||1<x 1><x+|1<x 1>x><x+
||1<x 1><x+||1<x 1><x+
|1<x 1>x>x>x>x><x+|*
|||1<x 1><x+|1<x 1>x><x*
||1<x 1><x+|1<x 1>x>x>
|||1<x 1><x+|1<x 1>x><x+
||1<x 1><x+||1<x 1><x+
|1<x 1>x>x>x><x+|||1<x 1><x+
|1<x 1>x><x+||1<x 1><x+
||1<x 1><x+
|1<x 1>x>x>x>x>x><x+||*
|||1<x 1><x+|1<x 1>x><x*
||1<x 1><x+|1<x 1>x>x>
|||1<x 1><x+|1<x 1>x><x+
||1<x 1><x+||1<x 1><x+
|1<x 1>x>x>x><x+|||1<x 1><x+
|1<x 1>x><x+||1<x 1><x+
||1<x 1><x+|1<x 1>x>x>x>x><x+
|*|||1<x 1><x+|1<x 1>x><x*
||1<x 1><x+|1<x 1>x>x>
|||1<x 1><x+|1<x 1>x><x+
||1<x 1><x+||1<x 1><x+
|1<x 1>x>x>x><x+|||1<x 1><x+
|1<x 1>x><x+||1<x 1><x+
||1<x 1><x+|1<x 1>x>x>x>x>x>x>
||#80 <r =?r <e > e 80>>
<s $s acc 0 <i + a i>>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment