view parser.js @ 331:61f5b794d939

Breaking change: method call syntax now always uses the syntactic receiver as the actual receiver. This makes its behavior different from function call syntax, but solves some problems with methods being shadowed by local variables and the like.
author Michael Pavone <pavone@retrodev.com>
date Sat, 28 Mar 2015 14:21:04 -0700
parents f73ebc146af9
children
line wrap: on
line source


function op(left, op, right)
{
	this.left = left;
	if (op instanceof Array) {
		op = op[0];
	}
	this.op = op;
	this.right = right;
}
op.prototype.valueOf = function(indent) {
	return this.left.valueOf(indent) + ' ' + this.op.valueOf(indent) + ' ' + this.right.valueOf(indent);
};

function symbol(name, symbols)
{
	this.name = name;
	this.symbols = symbols;
	this.dirty = false;
}
symbol.prototype.cleanName = function() {
	return this.name[0] == ':' ? this.name.substr(1) : this.name;
}
symbol.prototype.valueOf = function() {
	return this.name;
};

function intlit(val, bits, unsigned)
{
	if (!bits) {
		bits = 32;
	}
	if (unsigned === undefined) {
		unsigned = false;
	}
	this.unsigned = unsigned;
	this.bits = bits;
	this.val = val;
}
intlit.prototype.valueOf = function() {
	var val = '' + this.val;
	if (this.bits != 32 || this.unsigned) {
		val += this.unsigned ? 'u' : 'i'
		val += this.bits;
	}
	return val;
};

function floatlit(val, bits)
{
	this.val = val;
	this.bits = bits;
}
floatlit.prototype.valueOf = function() {
	return '' + val;
};

function strlit(val)
{
	this.val = val;
}
strlit.prototype.valueOf = function() {
	return '"'+this.val+'"';
};

function listlit(val)
{
	this.val = val;
}
listlit.prototype.valueOf = function(indent) {
	if (indent === undefined) {
		indent = '';
	}
	var nextindent = indent + '\t';
	var val = '['
	for (var i = 0; i < this.val.length; i++) {
		val += '\n' + nextindent + this.val[i].valueOf(nextindent);
	}
	if (this.val.length) {
		val += '\n' + indent;
	}
	val += ']';
	return val;
};

function arraylit(val)
{
	this.val = val;
}
arraylit.prototype.valueOf = function(indent) {
	if (indent === undefined) {
		indent = '';
	}
	var nextindent = indent + '\t';
	var val = '#['
	for (var i = 0; i < this.val.length; i++) {
		val += '\n' + nextindent + this.val[i].valueOf(nextindent);
	}
	if (this.val.length) {
		val += '\n' + indent;
	}
	val += ']';
	return val;
};

function funcall(name, args)
{
	this.name = name;
	this.args = args;
	this.receiver = null;
	this.dirty = false;
}
funcall.prototype.valueOf = function(indent) {
	var parts = this.name.split(':');
	var val = '';
	if (this.receiver) {
		val += this.receiver.valueOf(indent);
	}
	var curarg = 0;
	for (var i = 0; i < parts.length; i++) {
		if (val) {
			val += ' ';
		}
		if (parts[i]) {
			val += parts[i] + ': ';
		}
		if (curarg < this.args.length) {
			val += this.args[curarg++].valueOf(indent);
		}
	}
	while (curarg < this.args.length) {
		val += ' ' + this.args[curarg++].valueOf(indent);
	}
	return val;
};

function object(messages)
{
	this.messages = messages;
	this.name = null;
}
object.prototype.valueOf = function(indent) {
	if (indent === undefined) {
		indent = '';
	}
	var nextindent = indent + '\t';
	var val = '#{';
	for (var i = 0; i < this.messages.length; i++) {
		val += '\n' + nextindent +  this.messages[i].valueOf(nextindent);
	}
	if (val.length > 2) {
		val += '\n' + indent;
	}
	val += '}';
	return val;
};

function lambda(args, expressions)
{
	this.args = args ? args : [];
	this.expressions = expressions;
}
lambda.prototype.valueOf = function(indent) {
	if (indent === undefined) {
		indent = '';
	}
	var nextindent = indent + '\t';
	var val = '';
	for (var i = 0; i < this.args.length; i++) {
		val += this.args[i].valueOf(indent) + ' ';
	}
	val += '{';
	for (var i = 0; i < this.expressions.length; i++) {
		val += '\n' + nextindent + this.expressions[i].valueOf(nextindent);
	}
	if (this.expressions.length) {
		val += '\n' + indent;
	}
	val += '}';
	return val;
};

function assignment(sym, expr)
{
	this.symbol = sym;
	this.expression = expr;
}
assignment.prototype.valueOf = function(indent) {
	return this.symbol.valueOf(indent) + ' <- ' + this.expression.valueOf(indent);
};

function isLambda(node)
{
	return node instanceof lambda;
}

var grammar =
'start = ws module:(object / lambda) ws { return module; };' +
'ws = ([ \\t\\n\\r] / "//" [^\\n]* "\\n" / "/*" ([^*] / "*" ! "/")* "*/")*;' +
'hws = ([ \\t] / "/*" ([^*] / "*" ! "/")* "*/" )*;' +
'expr = e:(funcall / methcall / assignment / opexpr) ws { return e; };' +
'opexpr = left:compareop pieces:(hws ("&&" / "||") ws compareop)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
'compareop = left:maybecons pieces:(hws ("<=" / ">=" / "<" / ">" / "=" / "!=") ws maybecons)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
'maybecons = consop / addsub;' +
'consop = left:addsub hws "|" ws right:maybecons { return new op(left, "|", right); };'+
'addsub = left:muldiv pieces:(hws ((("xor"/"and"/"or") ! [a-zA-Z_!?@0-9])/("+"/"-"/".")) ws muldiv)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
'muldiv = left:primlitsym pieces:(hws ("*"/"/"/"%") ws primlitsym)* { if (pieces.length) { var cur = new op(left, pieces[0][1], pieces[0][3]); for (var i = 1; i < pieces.length; i++) { cur = new op(cur, pieces[i][1], pieces[i][3]); } return cur; } else { return left; } };'+
'primlitsym = hws val:(float / hex / binary / int / string / symbol / object / array / list / lambda / "(" ws expr:expr hws ")" { return expr; }) { return val; };' +
'symbol = chars:[a-zA-Z_!?@]+ trailing:(":"? [a-zA-Z_!?@0-9])* ! ":" { for (var i = 0; i < trailing.length; i++) { trailing[i] = trailing[i].join(""); } return new symbol(chars.join("") + trailing.join("")); };' +
'float = digits:[0-9]+ "." decimals:[0-9]+ size:("f" ("32" / "64"))? { var bits = size ? parseInt(size[1], 10) : 64; return new floatlit(parseFloat(digits.join("") + "." + decimals.join("")), bits); };' +
'binary = "0b" digits:[01]+ { return new intlit(parseInt(digits.join(""), 2)); };' +
'hex = "0x" digits:[0-9a-fA-F]+ size:([iu] ("8" / "16" / "32" / "64"))? { var bits = size ? parseInt(size[1], 10) : 0; return new intlit(parseInt(digits.join(""), 16), bits, size[0] == "u"); };' +
'int = sign:"-"? digits:[0-9]+ size:([iu] ("8" / "16" / "32" / "64"))? { var bits = size ? parseInt(size[1], 10) : 0; return new intlit(parseInt(sign + digits.join(""), 10), bits, size[0] == "u"); };' +
'string = "\\"" text:(strpart/escape)* "\\"" { return new strlit(text.join("")); };' +
'strpart = text:[^\\"\\\\]+ { return text.join(""); };' +
'escape = "\\\\" char:[nt\\"r\\\\] { if (char == "n") { return "\\n"; } if (char == "r") { return "\\r"; } if (char == "t") { return "\\t"; } return char; };' +
'object = "#{" ws messages:(assignment / funexpr)* "}" { return new object(messages); };' +
'array = "#[" ws els:expr* "]" { return new arraylit(els); };' +
'list = "[" ws els:expr* "]" { return new listlit(els); };' +
'opsym = name:("&&" / "||" / "<=" / ">=" / "<" / ">" / "=" / "!=" / "+" / "-" / "." / "*" / "/" / "%" / "|") { return new symbol(name); };' +
'assignment = ws sym:(opsym / symbol) hws "<-" expr:expr ws { return new assignment(sym, expr); }' +
'lambda = args:((& ":") argname+  )? "{" ws exprs:expr* "}" { return new lambda(args[1], exprs); };' +
'argname = init:":"? chars:[a-zA-Z_!?@]+ trailing:[a-zA-Z_!?@0-9]* hws { return new symbol(init + chars.join("") + trailing.join("")); };' +
'funexpr = f: funcall ws { return f; };' +
'funcall = hws parts: funcallpart+ { var fun = ""; var args = []; for (var i = 0; i < parts.length; i++) { fun += parts[i].name; args = args.concat(parts[i].args); } return new funcall(fun, args); };' +
'funcallpart = fun:funpart args:opexpr* hws { return { name: fun, args: args}; };' +
'funpart = chars:[a-zA-Z_!?@]+ middle:[a-zA-Z_!?@0-9]* ":" & [ \\t\\n\\r] { return chars.join("") + middle.join("") + ":"; };' +
'methcall = receiver:opexpr hws info:methcallrest { info.receiver = receiver; return info; };' +
'methcallrest = funcall / unarymeth;' +
'unarymeth = name:symbol { return new funcall(name.name, []); };';
var parser = PEG.buildParser(grammar);