if (typeof yasp == 'undefined') yasp = { };
(function () {
/**
* parses the code
* @constructor
*/
yasp.Parser = function () {
this.nodes = [ ];
this.input = "";
};
/**
* This function does all the parsing magic
* @param assembler
* @param input
* @returns {*}
*/
yasp.Parser.prototype.pass = function (assembler, input) {
this.nodes = [ ];
this.input = input;
var iterator = new yasp.TokenIterator(assembler, this.input);
iterator.iterate((function() {
var type;
switch (type = iterator.current().getType()) {
case yasp.TokenType.COMMAND:
this.parseCommand(iterator);
break;
case yasp.TokenType.LABEL:
this.parseLabel(iterator);
break;
case yasp.TokenType.DIRECTIVE:
this.parseDirective(iterator);
break;
default:
iterator.riseSyntaxError("Expecting command, directive or label, got " + type + " instead.");
}
}).bind(this));
// generate symbol table
if (assembler.jobs.indexOf("symbols") != -1 && assembler.getFatalErrorCount() == 0) {
// labels are already generated in the Analyser
// defines are generated in Analyser
// usedRegisters & instructions
var registers = { };
assembler.symbols.instructions = { };
assembler.symbols.usedRegisters = { };
for (var i = 0; i < this.nodes.length; i++) {
var node = this.nodes[i];
if (node.type == yasp.AstNodeTypes.NODE_COMMAND) {
var name = (node.params.command.name instanceof Array) ? node.params.command.name[0] : node.params.command.name;
if (!!assembler.symbols.instructions[name]) {
assembler.symbols.instructions[name]++;
} else {
assembler.symbols.instructions[name] = 1;
}
// params
for (var j = 0; j < node.params.params.length; j++) {
var param = node.params.params[j];
var typ = param.getType();
var paramName = param.text.toUpperCase();
if (typ == yasp.TokenType.BYTE_REGISTER || typ == yasp.TokenType.WORD_REGISTER) {
if (!!assembler.symbols.usedRegisters[paramName]) {
assembler.symbols.usedRegisters[paramName]++;
} else {
assembler.symbols.usedRegisters[paramName] = 1;
}
}
}
}
}
}
// set AST
if (assembler.jobs.indexOf("ast") != -1) {
assembler.ast = JSON.parse(JSON.stringify(this.nodes));
}
return this.nodes;
};
/**
* This method parses a command (like MOV, PUSH, ...) => generates AST for these commands. if it does not know the command it raises a syntax error.
* If the command + parameter definition is not unique parseCommand takes the first hit, so keep this in mind.
* @param iterator
* @param opt If this is true, no error is rised if the current token is no command. Default is false.
*/
yasp.Parser.prototype.parseCommand = function (iterator, opt) {
if (typeof opt == 'undefined') opt = false;
var type;
if ((type = iterator.current().getType()) == yasp.TokenType.COMMAND) {
var name = iterator.current().text.toUpperCase();
var command = null;
var params = null;
var commandToken = iterator.current();
var possibleCommands = [ ];
iterator.next();
if (iterator.is(":")) {
iterator.riseSyntaxError("Command cannot be a label.");
}
for (var i = 0; i < yasp.commands.length; i++) {
var commandName;
if (yasp.commands[i].name instanceof Array) {
commandName = yasp.commands[i].name[0];
for (var j = 0; j < yasp.commands[i].name.length; j++) {
if (yasp.commands[i].name[j].toUpperCase() == name) {
commandName = yasp.commands[i].name[j];
break;
}
}
} else {
commandName = yasp.commands[i].name;
}
if (commandName.toUpperCase() == name) {
command = yasp.commands[i];
var oldPos = iterator.pos;
var itsMe = true;
var paramPos = 0;
var start = true;
params = [ ];
while (!(iterator.is('\n') || !iterator.hasNext()) && itsMe) {
if (paramPos >= command.params.length) {
// too many parameters
itsMe = false;
break;
}
if (!start) {
iterator.match(",");
}
var cur = iterator.current();
params.push(cur);
var paramType = command.params[paramPos].type;
var param = yasp.ParamType[paramType.toLowerCase()];
if (!!param) {
itsMe = param.check(cur, iterator.assembler);
} else {
iterator.riseSyntaxError("Internal error (unknown paramter type " + paramType + ")");
}
iterator.optNext();
start = false;
paramPos++;
if (!!cur && cur.getType() == 'delimiter') break;
}
if (!itsMe || paramPos != command.params.length) {
// nope, its not me
// but could it still be?
var typ = !!cur ? cur.getType() : "";
if ((paramPos < command.params.length || (typ == 'delimiter' && paramPos - 1 < command.params.length)) && (itsMe || typ == 'delimiter')) {
possibleCommands.push(command);
}
iterator.pos = oldPos;
command = null;
} else {
break;
}
}
}
if (!command) {
// build parameters
var parameters = "";
var start = true;
var last = null;
var parameterArray = [ ];
while (!iterator.is('\n') && iterator.hasNext()) {
if (!start) {
if (iterator.is(',')) {
iterator.match(",");
} else {
iterator.optNext();
}
parameters += ", ";
}
var cur = iterator.current();
parameterArray.push(cur);
var typ = cur.getType();
if (!!last && typ == yasp.TokenType.NUMBER ) {
var num = +cur.text;
if (num >= Math.pow(2, 8) && last.getType() == yasp.TokenType.BYTE_REGISTER) typ += "[too big for byte register]";
if (num >= Math.pow(2, 16) && last.getType() == yasp.TokenType.WORD_REGISTER) typ += "[too big for word register]";
}
// unknown label?
if (typ == yasp.TokenType.LABEL && !iterator.assembler.getLabel(cur.text)) {
typ += "[unknown label address]"; // shit
}
parameters += typ;
last = cur;
iterator.optNext();
start = false;
}
if (possibleCommands.length > 0) {
var node = new yasp.AstNode(yasp.AstNodeTypes.NODE_UNKNOWNCOMMAND, commandToken, {
possibleCommands: possibleCommands,
params: parameterArray
});
this.nodes.push(node);
if (iterator.is(',')) iterator.next(); // skip ","
}
iterator.riseSyntaxError("Unknown command " + name + "(" + parameters + ")", possibleCommands.length > 0 ? "minor" : "error");
} else {
// build AST
var node = new yasp.AstNode(yasp.AstNodeTypes.NODE_COMMAND, commandToken, {
command: command,
params: params
});
this.nodes.push(node);
}
} else if (!opt) {
iterator.riseSyntaxError("Expecting command, got " + type + " instead");
}
};
/**
* This method parses a label (myLabel: ) => only syntactical correctness
* @param iterator
* @param opt If this is true, no error is rised if the current token is no command. Default is false.
*/
yasp.Parser.prototype.parseLabel = function (iterator, opt) {
if (typeof opt == 'undefined') opt = false;
var type;
if ((type = iterator.current().getType()) == yasp.TokenType.LABEL) {
var current = iterator.current();
iterator.next();
iterator.match(":");
var node = new yasp.AstNode(yasp.AstNodeTypes.NODE_LABEL, current, {
label: iterator.assembler.getLabel(current.text)
});
this.nodes.push(node);
if (iterator.current().getType() == yasp.TokenType.DIRECTIVE) {
this.parseDirective(iterator, true)
} else {
this.parseCommand(iterator, true); // optionally there can be a command
}
} else if (!opt) {
iterator.riseSyntaxError("Expecting label, got " + type + " instead");
}
};
/**
* This method parses ALL THE directives!
* @param iterator
*/
yasp.Parser.prototype.parseDirective = function(iterator, opt) {
if (typeof opt == 'undefined') opt = false;
switch(iterator.current().text.toUpperCase()) {
case "DEFINE":
// skip DEFINE
iterator.next();
var from = iterator.current().text.toUpperCase();
var fromToken = iterator.current();
iterator.next();
var to = iterator.current().text.toUpperCase();
iterator.optNext();
break;
case "ORG":
var cur = iterator.current();
iterator.next();
if (iterator.current().getType() != yasp.TokenType.NUMBER) {
iterator.riseSyntaxError("Invalid parameter for ORG - can only be numeric :(");
}
this.nodes.push(new yasp.AstNode(yasp.AstNodeTypes.NODE_ORG, cur, {
len: +iterator.current().text
}));
iterator.optNext();
break;
case "STRING":
var cur = iterator.current();
do {
iterator.next();
if (iterator.current().getType() != yasp.TokenType.STRING) iterator.riseSyntaxError("STRING expects string value");
this.nodes.push(new yasp.AstNode(yasp.AstNodeTypes.NODE_DUMP, cur, {
data: new String(iterator.current().text)
}));
iterator.optNext();
} while (iterator.is(','));
break;
case "DB":
var cur = iterator.current();
do {
iterator.next();
var val = +iterator.current().text;
if (isNaN(val)) iterator.riseSyntaxError("Expecting number.");
if (val < 0 || val >= Math.pow(2, 8)) iterator.riseSyntaxError("Invalid number [0,2^8]");
this.nodes.push(new yasp.AstNode(yasp.AstNodeTypes.NODE_DUMP, cur, {
data: val,
len: 8
}));
iterator.optNext();
} while (iterator.is(','));
break;
case "DA":
var cur = iterator.current();
do {
iterator.next();
var val = iterator.current().text;
var label;
if (!(label = iterator.assembler.getLabel(val))) {
iterator.riseSyntaxError("Unknown label "+label);
}
this.nodes.push(new yasp.AstNode(yasp.AstNodeTypes.NODE_DUMP, cur, {
data: new String(val),
len: 16
}));
iterator.optNext();
} while (iterator.is(','));
break;
case "DW":
var cur = iterator.current();
do {
iterator.next();
var val = +iterator.current().text;
if (isNaN(val)) iterator.riseSyntaxError("Expecting number.");
if (val < 0 || val >= Math.pow(2, 16)) iterator.riseSyntaxError("Invalid number [0,2^16]");
this.nodes.push(new yasp.AstNode(yasp.AstNodeTypes.NODE_DUMP, cur, {
data: val,
len: 16
}));
iterator.optNext();
} while (iterator.is(','));
break;
case "END":
while(iterator.hasNext()) {
iterator.next();
}
break;
default:
if (!opt) iterator.riseSyntaxError("Expecting directive, got "+iterator.current().getType() + " instead");
}
};
})();