|
@@ -0,0 +1,1551 @@
|
|
|
+var Cast = require("cast.js");
|
|
|
+var Code = require("code.js");
|
|
|
+var Errors = require("errors.js");
|
|
|
+var Lexer = require("lexer.js");
|
|
|
+var Module = require("module.js");
|
|
|
+var Parser = require("parser.js");
|
|
|
+var Procedure = require("procedure.js");
|
|
|
+var ImportRTL = require("rtl.js");
|
|
|
+var Stream = require("stream.js").Stream;
|
|
|
+var Type = require("type.js");
|
|
|
+
|
|
|
+var RTL = ImportRTL.RTL;
|
|
|
+var Class = ImportRTL.Class;
|
|
|
+
|
|
|
+var basicTypes = Type.basic;
|
|
|
+var Symbol = Type.Symbol;
|
|
|
+
|
|
|
+function getSymbol(context, id){
|
|
|
+ var s = context.findSymbol(id);
|
|
|
+ if (!s)
|
|
|
+ throw new Errors.Error("undeclared identifier: '" + id + "'");
|
|
|
+ return s;
|
|
|
+}
|
|
|
+
|
|
|
+function checkTypeCast(from, to, msg){
|
|
|
+ if (from instanceof Type.Pointer)
|
|
|
+ from = from.baseType();
|
|
|
+
|
|
|
+ var t = to.baseType();
|
|
|
+ while (t && t != from)
|
|
|
+ t = t.baseType();
|
|
|
+ if (!t)
|
|
|
+ throw new Errors.Error(msg + ": '" + to.description()
|
|
|
+ + "' is not an extension of '" + from.description() + "'");
|
|
|
+}
|
|
|
+
|
|
|
+var ChainedContext = Class.extend({
|
|
|
+ init: function ChainedContext(parent){this.__parent = parent;},
|
|
|
+ parent: function(){return this.__parent;},
|
|
|
+ codeGenerator: function(){return this.__parent.codeGenerator();},
|
|
|
+ findSymbol: function(id){return this.__parent.findSymbol(id);},
|
|
|
+ addSymbol: function(s){this.__parent.addSymbol(s);},
|
|
|
+ currentScope: function(s){return this.__parent.currentScope();},
|
|
|
+ pushScope: function(){this.__parent.pushScope();},
|
|
|
+ popScope: function(){this.__parent.popScope();},
|
|
|
+ setType: function(type){this.__parent.setType(type);},
|
|
|
+ setDesignator: function(d){this.__parent.setDesignator(d);},
|
|
|
+ handleExpression: function(type, value, designator){this.__parent.handleExpression(type, value, designator);},
|
|
|
+ handleLiteral: function(s){this.__parent.handleLiteral(s);},
|
|
|
+ handleConst: function(type, value){this.__parent.handleConst(type, value);},
|
|
|
+ genTypeName: function(){return this.__parent.genTypeName();},
|
|
|
+ genVarName: function(id){return this.__parent.genVarName(id);},
|
|
|
+ rtl: function(){return this.__parent.rtl();}
|
|
|
+});
|
|
|
+
|
|
|
+exports.Integer = ChainedContext.extend({
|
|
|
+ init: function IntegerContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__result = "";
|
|
|
+ this.__isHex = false;
|
|
|
+ },
|
|
|
+ isLexem: function(){return true;},
|
|
|
+ handleChar: function(c){this.__result += c;},
|
|
|
+ handleLiteral: function(){this.__isHex = true;},
|
|
|
+ toInt: function(s){return parseInt(this.__result);},
|
|
|
+ endParse: function(){
|
|
|
+ var n = this.toInt();
|
|
|
+ this.parent().codeGenerator().write(n.toString());
|
|
|
+ this.parent().handleConst(basicTypes.int, n);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.HexInteger = exports.Integer.extend({
|
|
|
+ init: function HexIntegerContext(context){
|
|
|
+ exports.Integer.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ toInt: function(s){return parseInt(this.__result, 16);}
|
|
|
+});
|
|
|
+
|
|
|
+exports.Real = ChainedContext.extend({
|
|
|
+ init: function RealContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__result = "";
|
|
|
+ },
|
|
|
+ isLexem: function(){return true;},
|
|
|
+ handleChar: function(c){this.__result += c;},
|
|
|
+ handleLiteral: function(s){
|
|
|
+ if (s == "D") // LONGREAL
|
|
|
+ s = "E";
|
|
|
+ this.__result += s;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var n = Number(this.__result);
|
|
|
+ this.parent().codeGenerator().write(n.toString());
|
|
|
+ this.parent().handleConst(basicTypes.real, n);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+function escapeString(s){
|
|
|
+ var result = "\"";
|
|
|
+ for(var i = 0; i < s.length; ++i){
|
|
|
+ var c = s[i];
|
|
|
+ if (c == '"')
|
|
|
+ result += "\\\"";
|
|
|
+ else
|
|
|
+ result += s[i];
|
|
|
+ }
|
|
|
+ return result + "\"";
|
|
|
+}
|
|
|
+
|
|
|
+exports.String = ChainedContext.extend({
|
|
|
+ init: function StringContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__result = "";
|
|
|
+ },
|
|
|
+ handleChar: function(c){this.__result += c;},
|
|
|
+ toStr: function(s){return s;},
|
|
|
+ endParse: function(){
|
|
|
+ var s = this.toStr(this.__result);
|
|
|
+ this.parent().codeGenerator().write(escapeString(s));
|
|
|
+ this.parent().handleConst(new Type.String(s), s);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Char = exports.String.extend({
|
|
|
+ init: function CharContext(context){
|
|
|
+ exports.String.prototype.init.bind(this)(context);
|
|
|
+ this.__result = "";
|
|
|
+ },
|
|
|
+ toStr: function(s){return String.fromCharCode(parseInt(s, 16));}
|
|
|
+});
|
|
|
+
|
|
|
+exports.BaseType = ChainedContext.extend({
|
|
|
+ init: function BaseTypeContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ setIdent: function(id){this.parent().setBaseType(id);}
|
|
|
+});
|
|
|
+
|
|
|
+var DesignatorInfo = Class.extend({
|
|
|
+ init: function(code, refCode, type, info){
|
|
|
+ this.__code = code;
|
|
|
+ this.__refCode = refCode;
|
|
|
+ this.__type = type;
|
|
|
+ this.__info = info;
|
|
|
+ },
|
|
|
+ code: function(){return this.__code;},
|
|
|
+ refCode: function(){return this.__refCode(this.__code);},
|
|
|
+ type: function(){return this.__type;},
|
|
|
+ info: function(){return this.__info;}
|
|
|
+});
|
|
|
+
|
|
|
+exports.Designator = ChainedContext.extend({
|
|
|
+ init: function DesignatorContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__currentType = undefined;
|
|
|
+ this.__info = undefined;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ this.__derefCode = undefined;
|
|
|
+ this.__propCode = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var t = this.__currentType;
|
|
|
+ if ( t === undefined){
|
|
|
+ var s = getSymbol(this.parent(), id);
|
|
|
+ var info = s.info();
|
|
|
+ if (s.isType())
|
|
|
+ this.__currentType = info;
|
|
|
+ else if (s.isVariable() || s.isConst() || s.isProcedure()){
|
|
|
+ this.__currentType = info.type();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ throw new Errors.Error("variable, constant or procedure name expected");
|
|
|
+ this.__info = info;
|
|
|
+ }
|
|
|
+ else if (t instanceof Type.Pointer){
|
|
|
+ this.__handleDeref();
|
|
|
+ this.__denote(id);
|
|
|
+ }
|
|
|
+ else if (!(t instanceof Type.Record)
|
|
|
+ && !(t instanceof Module.Type)
|
|
|
+ && !(t instanceof Module.AnyType))
|
|
|
+ throw new Errors.Error("cannot designate '" + t.description() + "'");
|
|
|
+ else
|
|
|
+ this.__denote(id);
|
|
|
+
|
|
|
+ this.__code.write(id);
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ handleExpression: function(expType, value, designator){
|
|
|
+ if (expType != basicTypes.int)
|
|
|
+ throw new Errors.Error("'INTEGER' expression expected, got '" + expType.name() + "'");
|
|
|
+
|
|
|
+ var type = this.__currentType;
|
|
|
+ if (!(type instanceof Type.Array))
|
|
|
+ throw new Errors.Error("ARRAY expected, got '" + type.name() + "'");
|
|
|
+ if (value !== undefined && value >= type.arraySize())
|
|
|
+ throw new Errors.Error("index out of bounds: maximum possible index is "
|
|
|
+ + (type.arraySize() - 1)
|
|
|
+ + ", got " + value );
|
|
|
+ this.__currentType = type.elementsType();
|
|
|
+ this.__info = new Type.Variable(this.__currentType, false, this.__info.isReadOnly());
|
|
|
+ if (designator)
|
|
|
+ writeDerefDesignatorCode(designator, this.__code);
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ if (s == "]" || s == ","){
|
|
|
+ var indexCode = this.__code.result();
|
|
|
+ this.__propCode = indexCode;
|
|
|
+ this.__code = new Code.SimpleGenerator(this.__derefCode + "[" + indexCode + "]");
|
|
|
+ }
|
|
|
+ if (s == "[" || s == ","){
|
|
|
+ this.__derefCode = this.__code.result();
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ }
|
|
|
+ else if (s == "^")
|
|
|
+ this.__handleDeref();
|
|
|
+ },
|
|
|
+ __handleDeref: function(){
|
|
|
+ if (!(this.__currentType instanceof Type.Pointer))
|
|
|
+ throw new Errors.Error("POINTER TO type expected, got '"
|
|
|
+ + this.__currentType.description() + "'");
|
|
|
+ this.__currentType = this.__currentType.baseType();
|
|
|
+ this.__info = new Type.Variable(this.__currentType, false, false);
|
|
|
+ },
|
|
|
+ handleTypeCast: function(type){
|
|
|
+ if (!(type instanceof Type.Record))
|
|
|
+ throw new Errors.Error(
|
|
|
+ "invalid type cast: RECORD type expected as an argument of type guard, got '"
|
|
|
+ + type.description() + "'");
|
|
|
+
|
|
|
+ checkTypeCast(this.__currentType, type, "invalid type cast");
|
|
|
+
|
|
|
+ var code = this.rtl().genCast(this.__code.result(), type);
|
|
|
+ this.__code = new Code.SimpleGenerator(code);
|
|
|
+
|
|
|
+ if (this.__currentType instanceof Type.Pointer)
|
|
|
+ type = new Type.Pointer(this.genTypeName(), type);
|
|
|
+ this.__currentType = type;
|
|
|
+ },
|
|
|
+ __denote: function(id){
|
|
|
+ var t = this.__currentType;
|
|
|
+ var fieldType = t.findSymbol(id);
|
|
|
+ if (!fieldType)
|
|
|
+ throw new Errors.Error("Type '" + t.name() + "' has no '" + id + "' field");
|
|
|
+ this.__derefCode = this.__code.result();
|
|
|
+ this.__propCode = "\"" + id + "\"";
|
|
|
+ this.__code.write(".");
|
|
|
+ this.__currentType = fieldType;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var code = this.__code.result();
|
|
|
+ this.parent().setDesignator(
|
|
|
+ new DesignatorInfo(code, this.__makeRefCode.bind(this), this.__currentType, this.__info));
|
|
|
+ },
|
|
|
+ __makeRefCode: function(code){
|
|
|
+ if (this.__derefCode)
|
|
|
+ return this.rtl().makeRef(this.__derefCode, this.__propCode);
|
|
|
+ if (!(this.__currentType instanceof Type.Array)
|
|
|
+ && this.__info instanceof Type.Variable && !this.__info.isVar())
|
|
|
+ return "{set: function($v){" + code + " = $v;}, get: function(){return " + code + ";}}";
|
|
|
+ return code;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Type = ChainedContext.extend({
|
|
|
+ init: function TypeContext(context){ChainedContext.prototype.init.bind(this)(context);},
|
|
|
+ setIdent: function(id){
|
|
|
+ var s = this.findSymbol(id);
|
|
|
+ if (!s)
|
|
|
+ throw new Errors.Error("undeclared type: '" + id + "'");
|
|
|
+ if (s instanceof Type.Type)
|
|
|
+ throw new Errors.Error("type name expected");
|
|
|
+ this.setType(s.info());
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.FormalType = exports.Type.extend({
|
|
|
+ init: function FormatlTypeContext(context){
|
|
|
+ exports.Type.prototype.init.bind(this)(context);
|
|
|
+ this.__arrayDimension = 0;
|
|
|
+ },
|
|
|
+ setType: function(type){
|
|
|
+ for(var i = 0; i < this.__arrayDimension; ++i)
|
|
|
+ type = new Type.Array("ARRAY OF " + type.name()
|
|
|
+ , undefined
|
|
|
+ , type);
|
|
|
+ this.parent().setType(type);
|
|
|
+
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ if (s == "ARRAY")
|
|
|
+ ++this.__arrayDimension;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+var ProcArg = Class.extend({
|
|
|
+ init: function(type, isVar){
|
|
|
+ this.type = type;
|
|
|
+ this.isVar = isVar;
|
|
|
+ },
|
|
|
+ description: function(){
|
|
|
+ return (this.isVar ? "VAR " : "") + this.type.description();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.FormalParameters = ChainedContext.extend({
|
|
|
+ init: function FormalParametersContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__arguments = [];
|
|
|
+ this.__result = undefined;
|
|
|
+
|
|
|
+ var parent = this.parent();
|
|
|
+ this.__type = new Procedure.Type(parent.typeName());
|
|
|
+ parent.setType(this.__type);
|
|
|
+ },
|
|
|
+ addArgument: function(name, arg){
|
|
|
+ this.__arguments.push(arg);
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var parent = this.parent();
|
|
|
+ var s = getSymbol(parent, id);
|
|
|
+ if (!s.isType())
|
|
|
+ throw new Errors.Error("type name expected");
|
|
|
+ this.__result = s.info();
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ this.__type.define(this.__arguments, this.__result);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.FormalParametersProcDecl = exports.FormalParameters.extend({
|
|
|
+ init: function FormalParametersProcDeclContext(context){
|
|
|
+ exports.FormalParameters.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ addArgument: function(name, arg){
|
|
|
+ exports.FormalParameters.prototype.addArgument.bind(this)(name, arg);
|
|
|
+ this.parent().addArgument(name, arg);
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ exports.FormalParameters.prototype.endParse.bind(this)();
|
|
|
+ this.parent().endParameters();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.ProcDecl = ChainedContext.extend({
|
|
|
+ init: function ProcDeclContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__name = undefined;
|
|
|
+ this.__firstArgument = true;
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__returnParsed = false;
|
|
|
+ this.__outerScope = this.parent().currentScope();
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ if (this.__name === undefined){ // first call
|
|
|
+ this.__name = id;
|
|
|
+ gen.write("\nfunction " + id + "(");
|
|
|
+ this.parent().pushScope();
|
|
|
+ }
|
|
|
+ else if (this.__name === id){
|
|
|
+ gen.closeScope();
|
|
|
+ this.parent().popScope();
|
|
|
+ }
|
|
|
+ else
|
|
|
+ throw new Errors.Error("mismatched procedure names: '" + this.__name
|
|
|
+ + "' at the begining and '" + id + "' at the end");
|
|
|
+ },
|
|
|
+ typeName: function(){return undefined;},
|
|
|
+ setType: function(type){
|
|
|
+ var procSymbol = new Symbol(this.__name, new Type.Procedure(type));
|
|
|
+ this.__outerScope.addSymbol(procSymbol);
|
|
|
+ this.__type = type;
|
|
|
+ },
|
|
|
+ addArgument: function(name, arg){
|
|
|
+ if (name == this.__name)
|
|
|
+ throw new Errors.Error("argument '" + name + "' has the same name as procedure");
|
|
|
+ var readOnly = !arg.isVar && (arg.type instanceof Type.Array);
|
|
|
+ var s = new Symbol(name, new Type.Variable(arg.type, arg.isVar, readOnly));
|
|
|
+ this.parent().addSymbol(s);
|
|
|
+
|
|
|
+ var code = this.codeGenerator();
|
|
|
+ if (!this.__firstArgument)
|
|
|
+ code.write(", ");
|
|
|
+ else
|
|
|
+ this.__firstArgument = false;
|
|
|
+ code.write(name + "/*" + arg.description() + "*/");
|
|
|
+ },
|
|
|
+ endParameters: function(){
|
|
|
+ var code = this.codeGenerator();
|
|
|
+ code.write(")");
|
|
|
+ code.openScope();
|
|
|
+ },
|
|
|
+ handleReturn: function(type){
|
|
|
+ var result = this.__type.result();
|
|
|
+ if (!result)
|
|
|
+ throw new Errors.Error("unexpected RETURN in PROCEDURE declared with no result type");
|
|
|
+ if (!Cast.implicit(type, result))
|
|
|
+ throw new Errors.Error(
|
|
|
+ "RETURN '" + result.description() + "' expected, got '"
|
|
|
+ + type.description() + "'");
|
|
|
+ this.__returnParsed = true;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var result = this.__type.result();
|
|
|
+ if (result && !this.__returnParsed)
|
|
|
+ throw new Errors.Error("RETURN expected at the end of PROCEDURE declared with '"
|
|
|
+ + result.name() + "' result type");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Return = ChainedContext.extend({
|
|
|
+ init: function ReturnContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ handleExpression: function(type, value, designator){
|
|
|
+ this.__type = type;
|
|
|
+ if (designator)
|
|
|
+ writeDerefDesignatorCode(designator, this.__code);
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var parent = this.parent();
|
|
|
+ parent.codeGenerator().write("return " + this.__code.result() + ";\n");
|
|
|
+ parent.handleReturn(this.__type);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.ProcParams = ChainedContext.extend({
|
|
|
+ init: function ProcParamsContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__isVar = false;
|
|
|
+ this.__argNamesForType = [];
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ if (s == "VAR")
|
|
|
+ this.__isVar = true;
|
|
|
+ },
|
|
|
+ setIdent: function(id){ this.__argNamesForType.push(id);},
|
|
|
+ setType: function(type){
|
|
|
+ var names = this.__argNamesForType;
|
|
|
+ for(var i = 0; i < names.length; ++i){
|
|
|
+ var name = names[i];
|
|
|
+ this.parent().addArgument(name, new Procedure.Arg(type, this.__isVar));
|
|
|
+ }
|
|
|
+ this.__isVar = false;
|
|
|
+ this.__argNamesForType = [];
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.PointerDecl = ChainedContext.extend({
|
|
|
+ init: function PointerDeclContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__base = undefined;
|
|
|
+ this.__name = this.parent().genTypeName();
|
|
|
+ },
|
|
|
+ setType: function(type){
|
|
|
+ if (!(type instanceof Type.ForwardRecord) && !(type instanceof Type.Record))
|
|
|
+ throw new Errors.Error(
|
|
|
+ "RECORD is expected as a POINTER base type, got '" + type.description() + "'");
|
|
|
+ this.__base = type;
|
|
|
+ },
|
|
|
+ findSymbol: function(id){
|
|
|
+ var existing = this.parent().findSymbol(id);
|
|
|
+ if (existing)
|
|
|
+ return existing;
|
|
|
+
|
|
|
+ var resolve = function(){return getSymbol(this.__parent, id).info();};
|
|
|
+ return new Symbol(id, new Type.ForwardRecord(resolve.bind(this)));
|
|
|
+ },
|
|
|
+ genTypeName: function(){
|
|
|
+ return this.__name + "$base";
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var type = new Type.Pointer(this.__name, this.__base);
|
|
|
+ this.parent().setType(type);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.ArrayDecl = ChainedContext.extend({
|
|
|
+ init: function ArrayDeclContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__dimensions = undefined;
|
|
|
+ },
|
|
|
+ handleDimensions: function(dimensions){this.__dimensions = dimensions;},
|
|
|
+ setType: function(type){
|
|
|
+ var initializer = type instanceof Type.Array || type instanceof Type.Record
|
|
|
+ ? "function(){return " + type.initializer() + ";}"
|
|
|
+ : type.initializer();
|
|
|
+ var dimensions = "";
|
|
|
+ for(var i = 0; i < this.__dimensions.length; ++i){
|
|
|
+ var length = this.__dimensions[i];
|
|
|
+ dimensions += (dimensions.length ? ", " : "") + length;
|
|
|
+ var arrayInit = i == this.__dimensions.length - 1
|
|
|
+ ? this.rtl().makeArray(dimensions + ", " + initializer)
|
|
|
+ : undefined;
|
|
|
+ type = new Type.Array("ARRAY OF " + type.name()
|
|
|
+ , arrayInit
|
|
|
+ , type
|
|
|
+ , length);
|
|
|
+ }
|
|
|
+
|
|
|
+ this.__type = type;
|
|
|
+ },
|
|
|
+ endParse: function(){this.parent().setType(this.__type);}
|
|
|
+});
|
|
|
+
|
|
|
+exports.ArrayDimensions = ChainedContext.extend({
|
|
|
+ init: function ArrayDimensionsContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__dimensions = [];
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return Code.nullGenerator;},
|
|
|
+ handleExpression: function(type, value){
|
|
|
+ if (type !== basicTypes.int)
|
|
|
+ throw new Errors.Error("'INTEGER' constant expression expected, got '" + type.name() + "'");
|
|
|
+ if (value === undefined)
|
|
|
+ throw new Errors.Error("constant expression expected as ARRAY size");
|
|
|
+ if (value <= 0)
|
|
|
+ throw new Errors.Error("array size must be greater than 0, got " + value);
|
|
|
+ this.__dimensions.push(value);
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ this.parent().handleDimensions(this.__dimensions);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.AddOperator = ChainedContext.extend({
|
|
|
+ init: function AddOperatorContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ var parent = this.parent();
|
|
|
+ if (s == "+"){
|
|
|
+ if (parent.type() == basicTypes.set){
|
|
|
+ parent.handleBinaryOperator(function(x, y){return x | y;});
|
|
|
+ parent.codeGenerator().write(" | ");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ parent.handleBinaryOperator(function(x, y){return x + y;});
|
|
|
+ parent.codeGenerator().write(" + ");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (s == "-"){
|
|
|
+ if (parent.type() == basicTypes.set){
|
|
|
+ parent.handleBinaryOperator(function(x, y){return x & ~y;});
|
|
|
+ parent.codeGenerator().write(" & ~");
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ parent.handleBinaryOperator(function(x, y){return x - y;});
|
|
|
+ parent.codeGenerator().write(" - ");
|
|
|
+ }
|
|
|
+ }
|
|
|
+ else if (s == "OR"){
|
|
|
+ var type = parent.type();
|
|
|
+ if (type != basicTypes.bool)
|
|
|
+ throw new Errors.Error("BOOLEAN expected as operand of 'OR', got '"
|
|
|
+ + type.name() + "'");
|
|
|
+ parent.handleBinaryOperator(function(x, y){return x || y;});
|
|
|
+ this.codeGenerator().write(" || ");
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.MulOperator = ChainedContext.extend({
|
|
|
+ init: function MulOperatorContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ var parent = this.parent();
|
|
|
+ if (s == "*")
|
|
|
+ if (parent.type() == basicTypes.set)
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x & y;}
|
|
|
+ , code: function(x, y){return x + " & " + y;}
|
|
|
+ });
|
|
|
+ else
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x * y;}
|
|
|
+ , code: function(x, y){return x + " * " + y;}
|
|
|
+ });
|
|
|
+ else if (s == "/")
|
|
|
+ if (parent.type() == basicTypes.set)
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x ^ y;}
|
|
|
+ , code: function(x, y){return x + " ^ " + y;}
|
|
|
+ });
|
|
|
+ else
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x / y;}
|
|
|
+ , code: function(x, y){return x + " / " + y;}
|
|
|
+ });
|
|
|
+ else if (s == "DIV")
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return (x / y) >> 0;}
|
|
|
+ , code: function(x, y){return "(" + x + " / " + y + ") >> 0";}
|
|
|
+ });
|
|
|
+ else if (s == "MOD")
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x % y;}
|
|
|
+ , code: function(x, y){return x + " % " + y;}
|
|
|
+ });
|
|
|
+ else if (s == "&"){
|
|
|
+ var type = parent.type();
|
|
|
+ if (type != basicTypes.bool)
|
|
|
+ throw new Errors.Error("BOOLEAN expected as operand of '&', got '"
|
|
|
+ + type.name() + "'");
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return x && y;}
|
|
|
+ , code: function(x, y){return x + " && " + y;}
|
|
|
+ });
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+function writeDerefDesignatorCode(designator, code){
|
|
|
+ var info = designator.info();
|
|
|
+ if (info instanceof Type.Variable && info.isVar())
|
|
|
+ code.write(".get()");
|
|
|
+}
|
|
|
+
|
|
|
+exports.Term = ChainedContext.extend({
|
|
|
+ init: function TermContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__operator = undefined;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ this.__left = undefined;
|
|
|
+ this.__isConst = true;
|
|
|
+ this.__value = undefined;
|
|
|
+ this.__designator = undefined;
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ type: function(){return this.parent().type();},
|
|
|
+ setDesignator: function(d){
|
|
|
+ var type = d.type();
|
|
|
+ this.parent().setType(type);
|
|
|
+
|
|
|
+ var info = d.info();
|
|
|
+ if (!(info instanceof Type.Const))
|
|
|
+ this.__isConst = false;
|
|
|
+ else
|
|
|
+ this.handleConst(type, info.value());
|
|
|
+
|
|
|
+ this.__code.write(d.code());
|
|
|
+ this.__designator = d;
|
|
|
+ if (this.__operator)
|
|
|
+ this.__derefDesignator();
|
|
|
+ },
|
|
|
+ handleOperator: function(o){
|
|
|
+ this.__derefDesignator();
|
|
|
+ this.__left = this.__operator
|
|
|
+ ? this.__operator.code(this.__left, this.__code.result())
|
|
|
+ : this.__code.result();
|
|
|
+ this.__operator = o;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ handleConst: function(type, value){
|
|
|
+ this.parent().setType(type);
|
|
|
+ if (value === undefined)
|
|
|
+ this.__isConst = false;
|
|
|
+ else if (this.__isConst)
|
|
|
+ this.__value = this.__operator ? this.__operator.eval(this.__value, value)
|
|
|
+ : value;
|
|
|
+ },
|
|
|
+ procCalled: function(type){this.parent().procCalled(type);},
|
|
|
+ endParse: function(){
|
|
|
+ var code = this.__operator ? this.__operator.code(this.__left, this.__code.result())
|
|
|
+ : this.__code.result();
|
|
|
+ this.parent().handleTerm(
|
|
|
+ code
|
|
|
+ , this.__isConst ? this.__value : undefined
|
|
|
+ , this.__operator ? undefined : this.__designator);
|
|
|
+ },
|
|
|
+ __derefDesignator: function(){
|
|
|
+ var designator = this.__designator;
|
|
|
+ if (!designator)
|
|
|
+ return;
|
|
|
+
|
|
|
+ writeDerefDesignatorCode(designator, this.__code);
|
|
|
+ this.__designator = undefined;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Factor = ChainedContext.extend({
|
|
|
+ init: function FactorContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ type: function(){return this.parent().type();},
|
|
|
+ handleLiteral: function(s){
|
|
|
+ var parent = this.parent();
|
|
|
+ if (s == "NIL"){
|
|
|
+ parent.handleConst(Type.nil, undefined);
|
|
|
+ this.codeGenerator().write("null");
|
|
|
+ }
|
|
|
+ else if (s == "TRUE"){
|
|
|
+ parent.handleConst(basicTypes.bool, true);
|
|
|
+ this.codeGenerator().write("true");
|
|
|
+ }
|
|
|
+ else if (s == "FALSE"){
|
|
|
+ parent.handleConst(basicTypes.bool, false);
|
|
|
+ this.codeGenerator().write("false");
|
|
|
+ }
|
|
|
+ else if (s == "~"){
|
|
|
+ parent.setType(basicTypes.bool);
|
|
|
+ parent.handleOperator({
|
|
|
+ eval: function(x, y){return !y;}
|
|
|
+ , code: function(x, y){return "!" + y;}
|
|
|
+ });
|
|
|
+ }
|
|
|
+ },
|
|
|
+ procCalled: function(type){this.parent().procCalled(type);}
|
|
|
+});
|
|
|
+
|
|
|
+exports.Set = ChainedContext.extend({
|
|
|
+ init: function SetContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__value = 0;
|
|
|
+ this.__expr = "";
|
|
|
+ },
|
|
|
+ handleElement: function(from, fromValue, to, toValue){
|
|
|
+ if (fromValue !== undefined && (!to || toValue !== undefined))
|
|
|
+ if (to)
|
|
|
+ for(var i = fromValue; i <= toValue; ++i)
|
|
|
+ this.__value |= 1 << i;
|
|
|
+ else
|
|
|
+ this.__value |= 1 << fromValue;
|
|
|
+ else{
|
|
|
+ if (this.__expr.length)
|
|
|
+ this.__expr += ", ";
|
|
|
+ if (to)
|
|
|
+ this.__expr += "[" + from + ", " + to + "]";
|
|
|
+ else
|
|
|
+ this.__expr += from;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ if (!this.__expr.length){
|
|
|
+ gen.write(this.__value.toString());
|
|
|
+ this.parent().handleConst(basicTypes.set, this.__value);
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ this.parent().setType(basicTypes.set);
|
|
|
+ gen.write(this.rtl().makeSet(this.__expr));
|
|
|
+ if (this.__value)
|
|
|
+ gen.write(" | " + this.__value);
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.SetElement = ChainedContext.extend({
|
|
|
+ init: function SetElementContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__from = undefined;
|
|
|
+ this.__fromValue = undefined;
|
|
|
+ this.__to = undefined;
|
|
|
+ this.__toValue = undefined;
|
|
|
+ this.__expr = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__expr;},
|
|
|
+ handleExpression: function(type, value){
|
|
|
+ if (!this.__from)
|
|
|
+ {
|
|
|
+ this.__from = this.__expr.result();
|
|
|
+ this.__fromValue = value;
|
|
|
+ this.__expr = new Code.SimpleGenerator();
|
|
|
+ }
|
|
|
+ else{
|
|
|
+ this.__to = this.__expr.result();
|
|
|
+ this.__toValue = value;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ this.parent().handleElement(this.__from, this.__fromValue, this.__to, this.__toValue);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+function constValueCode(value){
|
|
|
+ if (typeof value == "string")
|
|
|
+ return escapeString(value);
|
|
|
+ return value.toString();
|
|
|
+}
|
|
|
+
|
|
|
+exports.SimpleExpression = ChainedContext.extend({
|
|
|
+ init: function SimpleExpressionContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ //this.__type = undefined;
|
|
|
+ this.__binaryOperator = undefined;
|
|
|
+ this.__unaryMinus = false;
|
|
|
+ this.__unaryPlus = false;
|
|
|
+ this.__isConst = true;
|
|
|
+ this.__constValue = undefined;
|
|
|
+ this.__designator = undefined;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ handleTerm: function(code, value, designator){
|
|
|
+ if (value !== undefined)
|
|
|
+ this.__handleConst(value);
|
|
|
+ else
|
|
|
+ this.__isConst = false;
|
|
|
+ this.codeGenerator().write(code);
|
|
|
+ this.__designator = designator;
|
|
|
+ this.__derefDesignator();
|
|
|
+ },
|
|
|
+ handleLiteral: function(s){
|
|
|
+ if (s == "-")
|
|
|
+ this.__unaryMinus = true;
|
|
|
+ else if (s == "+")
|
|
|
+ this.__unaryPlus = true;
|
|
|
+ },
|
|
|
+ type: function(){return this.parent().type();},
|
|
|
+ constValue: function(){return this.__isConst ? this.__constValue : undefined;},
|
|
|
+ handleBinaryOperator: function(o){
|
|
|
+ this.__binaryOperator = o;
|
|
|
+ this.__derefDesignator();
|
|
|
+ },
|
|
|
+ handleUnaryOperator: function(o){
|
|
|
+ this.__unary_operator = o;
|
|
|
+ },
|
|
|
+ procCalled: function(type){this.parent().procCalled(type);},
|
|
|
+ endParse: function(){
|
|
|
+ var parent = this.parent();
|
|
|
+ var code = parent.codeGenerator();
|
|
|
+ if (this.__unaryMinus)
|
|
|
+ if (this.type() == basicTypes.set){
|
|
|
+ if (this.__isConst)
|
|
|
+ this.__constValue = ~this.__constValue;
|
|
|
+ else
|
|
|
+ code.write('~');
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (this.__isConst)
|
|
|
+ this.__constValue = -this.__constValue;
|
|
|
+ else
|
|
|
+ code.write('-');
|
|
|
+ }
|
|
|
+
|
|
|
+ code.write(this.__isConst ? constValueCode(this.__constValue) : this.__code.result());
|
|
|
+ parent.handleSimpleExpression(this.constValue(), this.__designator);
|
|
|
+ },
|
|
|
+ __handleConst: function(value){
|
|
|
+ if (!this.__isConst)
|
|
|
+ return;
|
|
|
+
|
|
|
+ if (this.__unary_operator){
|
|
|
+ value = this.__unary_operator(value);
|
|
|
+ this.__unary_operator = undefined;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (!this.__binaryOperator)
|
|
|
+ this.__constValue = value;
|
|
|
+ else
|
|
|
+ this.__constValue = this.__binaryOperator(this.__constValue, value);
|
|
|
+ },
|
|
|
+ __derefDesignator: function(){
|
|
|
+ if (!this.__designator)
|
|
|
+ return;
|
|
|
+ if (!this.__binaryOperator && !this.__unary_operator
|
|
|
+ && !this.__unaryMinus && !this.__unaryPlus)
|
|
|
+ return;
|
|
|
+
|
|
|
+ writeDerefDesignatorCode(this.__designator, this.__code);
|
|
|
+ this.__designator = undefined;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Expression = ChainedContext.extend({
|
|
|
+ init: function ExpressionContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__leftParsed = false;
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__relation = undefined;
|
|
|
+ this.__value = undefined;
|
|
|
+ this.__designator = undefined;
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ setType: function(type){
|
|
|
+ if (this.__relation == "IS"){
|
|
|
+ if (!(type instanceof Type.Record))
|
|
|
+ throw new Errors.Error("RECORD type expected after 'IS'");
|
|
|
+
|
|
|
+ checkTypeCast(this.__type, type, "invalid type test");
|
|
|
+ }
|
|
|
+ else if (type === undefined || this.__type === undefined)
|
|
|
+ this.__type = type;
|
|
|
+ else if (type !== this.__type)
|
|
|
+ throw new Errors.Error("type mismatch: expected '" + this.__type.name()
|
|
|
+ + "', got '" + type.name() + "'");
|
|
|
+ },
|
|
|
+ type: function(){return this.__type;},
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ handleSimpleExpression: function(value, designator){
|
|
|
+ if (!this.__leftParsed){
|
|
|
+ this.__leftParsed = true;
|
|
|
+ this.__value = value;
|
|
|
+ this.__designator = designator;
|
|
|
+ }
|
|
|
+ else {
|
|
|
+ if (this.__relation == "IS"){
|
|
|
+ if (!designator || !(designator.info() instanceof Type.Type))
|
|
|
+ throw new Errors.Error("type name expected");
|
|
|
+ }
|
|
|
+ this.__type = basicTypes.bool;
|
|
|
+ this.__value = undefined;
|
|
|
+ this.__designator = undefined;
|
|
|
+ }
|
|
|
+ },
|
|
|
+ procCalled: function(type){
|
|
|
+ if (!type)
|
|
|
+ throw new Errors.Error("procedure returning no result cannot be used in an expression");
|
|
|
+ this.__type = type;
|
|
|
+ this.__designator = undefined;
|
|
|
+ },
|
|
|
+ handleLiteral: function(relation){
|
|
|
+ if (relation == "IS")
|
|
|
+ if (!(this.__type instanceof Type.Pointer))
|
|
|
+ throw new Errors.Error("POINTER to type expected before 'IS'");
|
|
|
+ else
|
|
|
+ this.codeGenerator().write(" instanceof ");
|
|
|
+ else if (relation == "IN"){
|
|
|
+ if (this.__type != basicTypes.int)
|
|
|
+ throw new Errors.Error("'INTEGER' expected as an element of SET, got '" + this.__type.name() + "'");
|
|
|
+ this.__type = basicTypes.set;
|
|
|
+ this.__code = new Code.SimpleGenerator("1 << " + this.__code.result() + " & ");
|
|
|
+ }
|
|
|
+ else if (relation == "=")
|
|
|
+ this.__code = new Code.SimpleGenerator(this.__code.result() + " == ");
|
|
|
+ else if (relation == "#")
|
|
|
+ this.__code = new Code.SimpleGenerator(this.__code.result() + " != ");
|
|
|
+ else if (relation == "<=" || relation == ">=")
|
|
|
+ this.__code.write(", ");
|
|
|
+
|
|
|
+ this.__relation = relation;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var parent = this.parent();
|
|
|
+ var code = parent.codeGenerator();
|
|
|
+ if (this.__relation == "<=")
|
|
|
+ code.write(this.rtl().setInclL(this.__code.result()));
|
|
|
+ else if (this.__relation == ">=")
|
|
|
+ code.write(this.rtl().setInclR(this.__code.result()));
|
|
|
+ else
|
|
|
+ code.write(this.__code.result());
|
|
|
+ parent.handleExpression(this.__type, this.__value, this.__designator);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+function handleIfExpression(type){
|
|
|
+ if (type !== basicTypes.bool)
|
|
|
+ throw new Errors.Error("'BOOLEAN' expression expected, got '" + type.name() + "'");
|
|
|
+}
|
|
|
+
|
|
|
+function endIfParse(){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write(")");
|
|
|
+ gen.openScope();
|
|
|
+}
|
|
|
+
|
|
|
+exports.If = ChainedContext.extend({
|
|
|
+ init: function IfContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.codeGenerator().write("if (");
|
|
|
+ },
|
|
|
+ handleExpression: handleIfExpression,
|
|
|
+ endParse: endIfParse
|
|
|
+});
|
|
|
+
|
|
|
+exports.ElseIf = ChainedContext.extend({
|
|
|
+ init: function ElseIfContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.closeScope();
|
|
|
+ gen.write("else if (");
|
|
|
+ },
|
|
|
+ handleExpression: handleIfExpression,
|
|
|
+ endParse: endIfParse
|
|
|
+});
|
|
|
+
|
|
|
+exports.Else = ChainedContext.extend({
|
|
|
+ init: function ElseContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.closeScope();
|
|
|
+ gen.write("else ");
|
|
|
+ gen.openScope();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.emitEndStatement = function(context){
|
|
|
+ context.codeGenerator().write(";\n");
|
|
|
+};
|
|
|
+
|
|
|
+exports.emitIfEnd = function(context){
|
|
|
+ context.codeGenerator().closeScope();
|
|
|
+};
|
|
|
+
|
|
|
+exports.Case = ChainedContext.extend({
|
|
|
+ init: function CaseContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__firstCase = true;
|
|
|
+ this.genVarName("$c");
|
|
|
+ this.codeGenerator().write("$c = ");
|
|
|
+ },
|
|
|
+ handleExpression: function(type){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ if (type instanceof Type.String){
|
|
|
+ var v = type.asChar();
|
|
|
+ if (v !== undefined){
|
|
|
+ gen.write(v);
|
|
|
+ type = basicTypes.char;
|
|
|
+ }
|
|
|
+ }
|
|
|
+ if (type != basicTypes.int && type != basicTypes.char)
|
|
|
+ throw new Errors.Error("'INTEGER' or 'CHAR' expected as CASE expression");
|
|
|
+ this.__type = type;
|
|
|
+ gen.write(";\n");
|
|
|
+ },
|
|
|
+ beginCase: function(){
|
|
|
+ if (this.__firstCase)
|
|
|
+ this.__firstCase = false;
|
|
|
+ else
|
|
|
+ this.codeGenerator().write("else ");
|
|
|
+ },
|
|
|
+ handleLabelType: function(type){
|
|
|
+ if (type !== this.__type)
|
|
|
+ throw new Errors.Error(
|
|
|
+ "label must be '" + this.__type.name() + "' (the same as case expression), got '"
|
|
|
+ + type.name() + "'");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.CaseLabelList = ChainedContext.extend({
|
|
|
+ init: function CaseLabelListContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__glue = "";
|
|
|
+ },
|
|
|
+ handleLabelType: function(type){this.parent().handleLabelType(type);},
|
|
|
+ handleRange: function(from, to){
|
|
|
+ if (!this.__glue)
|
|
|
+ this.parent().caseLabelBegin();
|
|
|
+
|
|
|
+ var cond = to === undefined
|
|
|
+ ? "$c === " + from
|
|
|
+ : "($c >= " + from + " && $c <= " + to + ")";
|
|
|
+ this.codeGenerator().write(this.__glue + cond);
|
|
|
+ this.__glue = " || ";
|
|
|
+ },
|
|
|
+ endParse: function(){this.parent().caseLabelEnd();}
|
|
|
+});
|
|
|
+
|
|
|
+exports.CaseLabel = ChainedContext.extend({
|
|
|
+ init: function CaseLabelContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ caseLabelBegin: function(){
|
|
|
+ this.parent().beginCase();
|
|
|
+ this.codeGenerator().write("if (");
|
|
|
+ },
|
|
|
+ caseLabelEnd: function(){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write(")");
|
|
|
+ gen.openScope();
|
|
|
+ },
|
|
|
+ handleLabelType: function(type){this.parent().handleLabelType(type);},
|
|
|
+ handleRange: function(from, to){this.parent().handleRange(from, to);},
|
|
|
+ endParse: function(){this.codeGenerator().closeScope();}
|
|
|
+});
|
|
|
+
|
|
|
+exports.CaseRange = ChainedContext.extend({
|
|
|
+ init: function CaseRangeContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__from = undefined;
|
|
|
+ this.__to = undefined;
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return Code.nullGenerator;}, // suppress any output
|
|
|
+ handleLabel: function(type, v){
|
|
|
+ this.parent().handleLabelType(type);
|
|
|
+ if (this.__from === undefined )
|
|
|
+ this.__from = v;
|
|
|
+ else
|
|
|
+ this.__to = v;
|
|
|
+ },
|
|
|
+ handleConst: function(type, value){
|
|
|
+ if (type instanceof Type.String){
|
|
|
+ value = type.asChar();
|
|
|
+ if (value === undefined)
|
|
|
+ throw new Errors.Error("single-character string expected");
|
|
|
+ type = basicTypes.char;
|
|
|
+ }
|
|
|
+ this.handleLabel(type, value);
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var s = getSymbol(this.parent(), id);
|
|
|
+ if (!s.isConst())
|
|
|
+ throw new Errors.Error("'" + id + "' is not a constant");
|
|
|
+
|
|
|
+ var type = s.info().type();
|
|
|
+ if (type instanceof Type.String)
|
|
|
+ this.handleConst(type, undefined);
|
|
|
+ else
|
|
|
+ this.handleLabel(type, s.info().value());
|
|
|
+ },
|
|
|
+ endParse: function(){this.parent().handleRange(this.__from, this.__to);}
|
|
|
+});
|
|
|
+
|
|
|
+exports.While = ChainedContext.extend({
|
|
|
+ init: function WhileContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write("while (true)");
|
|
|
+ gen.openScope();
|
|
|
+ gen.write("if (");
|
|
|
+ },
|
|
|
+ handleExpression: handleIfExpression,
|
|
|
+ endParse: function(){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write(")");
|
|
|
+ gen.openScope();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.emitWhileEnd = function(context){
|
|
|
+ var gen = context.codeGenerator();
|
|
|
+ gen.closeScope(" else break;\n");
|
|
|
+ gen.closeScope();
|
|
|
+};
|
|
|
+
|
|
|
+exports.Repeat = ChainedContext.extend({
|
|
|
+ init: function RepeatContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var gen = context.codeGenerator();
|
|
|
+ gen.write("do ");
|
|
|
+ gen.openScope();
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.Until = ChainedContext.extend({
|
|
|
+ init: function UntilContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var gen = context.codeGenerator();
|
|
|
+ gen.closeScope(" while (");
|
|
|
+ },
|
|
|
+ handleExpression: handleIfExpression,
|
|
|
+ endParse: function(){this.codeGenerator().write(");\n");}
|
|
|
+});
|
|
|
+
|
|
|
+exports.For = ChainedContext.extend({
|
|
|
+ init: function ForContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__var = undefined;
|
|
|
+ this.__initExprParsed = false;
|
|
|
+ this.__toExpr = new Code.SimpleGenerator();
|
|
|
+ this.__toParsed = false;
|
|
|
+ this.__by_parsed = false;
|
|
|
+ this.__by = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var s = getSymbol(this.parent(), id);
|
|
|
+ if (!s.isVariable())
|
|
|
+ throw new Errors.Error("'" + s.id() + "' is not a variable");
|
|
|
+ if (s.info().type() !== basicTypes.int)
|
|
|
+ throw new Errors.Error(
|
|
|
+ "'" + s.id() + "' is a 'BOOLEAN' variable, 'FOR' control variable must be 'INTEGER'");
|
|
|
+ this.codeGenerator().write("for (" + id + " = ");
|
|
|
+ this.__var = id;
|
|
|
+ },
|
|
|
+ handleExpression: function(type, value){
|
|
|
+ if (type !== basicTypes.int)
|
|
|
+ throw new Errors.Error(
|
|
|
+ !this.__initExprParsed
|
|
|
+ ? "'INTEGER' expression expected to assign '" + this.__var
|
|
|
+ + "', got '" + type.name() + "'"
|
|
|
+ : !this.__toParsed
|
|
|
+ ? "'INTEGER' expression expected as 'TO' parameter, got '" + type.name() + "'"
|
|
|
+ : "'INTEGER' expression expected as 'BY' parameter, got '" + type.name() + "'"
|
|
|
+ );
|
|
|
+ if (!this.__initExprParsed)
|
|
|
+ this.__initExprParsed = true;
|
|
|
+ else if (!this.__toParsed)
|
|
|
+ this.__toParsed = true;
|
|
|
+ else if ( value === undefined )
|
|
|
+ throw new Errors.Error("constant expression expected as 'BY' parameter");
|
|
|
+ else
|
|
|
+ this.__by = value;
|
|
|
+ },
|
|
|
+ codeGenerator: function(){
|
|
|
+ if (this.__initExprParsed && !this.__toParsed)
|
|
|
+ return this.__toExpr;
|
|
|
+ if (this.__toParsed && !this.__by_parsed)
|
|
|
+ return Code.nullGenerator; // suppress output for BY expression
|
|
|
+
|
|
|
+ return this.parent().codeGenerator();
|
|
|
+ },
|
|
|
+ handleBegin: function(){
|
|
|
+ this.__by_parsed = true;
|
|
|
+
|
|
|
+ var relation = this.__by < 0 ? " >= " : " <= ";
|
|
|
+ var step = this.__by === undefined
|
|
|
+ ? "++" + this.__var
|
|
|
+ : this.__var + (this.__by < 0
|
|
|
+ ? " -= " + -this.__by
|
|
|
+ : " += " + this.__by);
|
|
|
+ var s = "; " + this.__var + relation + this.__toExpr.result() + "; " + step + ")";
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write(s);
|
|
|
+ gen.openScope();
|
|
|
+ },
|
|
|
+ endParse: function(){this.codeGenerator().closeScope();}
|
|
|
+});
|
|
|
+
|
|
|
+exports.emitForBegin = function(context){context.handleBegin();};
|
|
|
+
|
|
|
+exports.Assignment = ChainedContext.extend({
|
|
|
+ init: function AssignmentContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__designator = undefined;
|
|
|
+ this.__leftOp = undefined;
|
|
|
+ this.__type = undefined;
|
|
|
+ },
|
|
|
+ setDesignator: function(d){
|
|
|
+ this.__designator = d;
|
|
|
+ },
|
|
|
+ handleLiteral: function(){
|
|
|
+ var d = this.__designator;
|
|
|
+ var d_info = d.info();
|
|
|
+ if (!(d_info instanceof Type.Variable) || d_info.isReadOnly())
|
|
|
+ throw new Errors.Error("cannot assign to " + d_info.idType());
|
|
|
+ this.__leftOp = d.code();
|
|
|
+ this.__type = d.type();
|
|
|
+ this.codeGenerator().write(this.__leftOp + (d_info.isVar() ? ".set(" : " = "));
|
|
|
+ },
|
|
|
+ handleExpression: function(type, value, designator){
|
|
|
+ if (!Cast.implicit(type, this.__type))
|
|
|
+ throw new Errors.Error("type mismatch: '" + this.__leftOp
|
|
|
+ + "' is '" + this.__type.description()
|
|
|
+ + "' and cannot be assigned to '" + type.description() + "' expression");
|
|
|
+ if (designator)
|
|
|
+ writeDerefDesignatorCode(designator, this.codeGenerator());
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ if (this.__designator.info().isVar())
|
|
|
+ this.codeGenerator().write(")");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.ConstDecl = ChainedContext.extend({
|
|
|
+ init: function ConstDeclContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__id = undefined;
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__value = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ this.__id = id;
|
|
|
+ this.codeGenerator().write("var " + id + " = ");
|
|
|
+ },
|
|
|
+ handleExpression: function(type, value){
|
|
|
+ if (value === undefined)
|
|
|
+ throw new Errors.Error("constant expression expected");
|
|
|
+ this.__type = type;
|
|
|
+ this.__value = value;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var c = new Type.Const(this.__type, this.__value);
|
|
|
+ this.addSymbol(new Symbol(this.__id, c));
|
|
|
+ this.codeGenerator().write(";\n");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.VariableDeclaration = ChainedContext.extend({
|
|
|
+ init: function VariableDeclarationContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__idents = [];
|
|
|
+ this.__type = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id) {this.__idents.push(id);},
|
|
|
+ setType: function(type) {this.__type = type;},
|
|
|
+ typeName: function(){return undefined;},
|
|
|
+ endParse: function(){
|
|
|
+ var v = new Type.Variable(this.__type);
|
|
|
+ var idents = this.__idents;
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ for(var i = 0; i < idents.length; ++i)
|
|
|
+ {
|
|
|
+ var varName = idents[i];
|
|
|
+ this.addSymbol(new Symbol(varName, v));
|
|
|
+ var t = v.type();
|
|
|
+ gen.write("var " + varName + " = " + t.initializer() + ";");
|
|
|
+ }
|
|
|
+
|
|
|
+ gen.write("\n");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.FieldListDeclaration = ChainedContext.extend({
|
|
|
+ init: function FieldListDeclarationContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__idents = [];
|
|
|
+ this.__type = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id) {this.__idents.push(id);},
|
|
|
+ setType: function(type) {this.__type = type;},
|
|
|
+ endParse: function(){
|
|
|
+ var idents = this.__idents;
|
|
|
+ for(var i = 0; i < idents.length; ++i){
|
|
|
+ var fieldName = idents[i];
|
|
|
+ var fieldType = this.__type;
|
|
|
+ this.parent().addField(fieldName, fieldType);
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+function assertProcType(type){
|
|
|
+ if (!(type instanceof Procedure.Type) && !(type instanceof Module.AnyType))
|
|
|
+ throw new Errors.Error("PROCEDURE expected, got '" + type.name() + "'");
|
|
|
+}
|
|
|
+
|
|
|
+exports.ActualParameters = ChainedContext.extend({
|
|
|
+ init: function ActualParametersContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.parent().hasActualParameters();
|
|
|
+ },
|
|
|
+});
|
|
|
+
|
|
|
+exports.ProcedureCall = ChainedContext.extend({
|
|
|
+ init: function ProcedureCallContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__procCall = undefined;
|
|
|
+ this.__code = undefined;
|
|
|
+ },
|
|
|
+ setDesignator: function(d){
|
|
|
+ var type = d.type();
|
|
|
+ assertProcType(type);
|
|
|
+ this.__type = type;
|
|
|
+ this.__procCall = type.callGenerator(new Code.SimpleGenerator(), d.code());
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ },
|
|
|
+ codeGenerator: function(){
|
|
|
+ return this.__code ? this.__code : this.parent().codeGenerator();
|
|
|
+ },
|
|
|
+ type: function(){return this.__type;},
|
|
|
+ setType: function(){},
|
|
|
+ hasActualParameters: function(){},
|
|
|
+ handleExpression: function(type, value, designator){
|
|
|
+ var code = this.__code.result();
|
|
|
+ this.__code = new Code.SimpleGenerator();
|
|
|
+ this.__procCall.handleArgument(type, designator, code);
|
|
|
+ },
|
|
|
+ endParse: function(){this.parent().codeGenerator().write(this.__procCall.end());}
|
|
|
+});
|
|
|
+
|
|
|
+exports.ExpressionProcedureCall = exports.ProcedureCall.extend({
|
|
|
+ init: function ExpressionProcedureCallContext(context){
|
|
|
+ exports.ProcedureCall.prototype.init.bind(this)(context);
|
|
|
+ this.__designator = undefined;
|
|
|
+ this.__hasActualParameters = false;
|
|
|
+ },
|
|
|
+ setDesignator: function(d){
|
|
|
+ this.__designator = d;
|
|
|
+ },
|
|
|
+ hasActualParameters: function(){
|
|
|
+ exports.ProcedureCall.prototype.setDesignator.bind(this)(this.__designator);
|
|
|
+ this.__hasActualParameters = true;
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ if (this.__hasActualParameters){
|
|
|
+ exports.ProcedureCall.prototype.endParse.bind(this)();
|
|
|
+ this.parent().procCalled(this.__type.result());
|
|
|
+ }
|
|
|
+ else
|
|
|
+ this.parent().setDesignator(this.__designator);
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.RecordDecl = ChainedContext.extend({
|
|
|
+ init: function RecordDeclContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ var id = this.genTypeName();
|
|
|
+ this.__type = new Type.Record(id);
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write("var " + id + " = ");
|
|
|
+ },
|
|
|
+ addField: function(name, type) {this.__type.addField(name, type);},
|
|
|
+ setBaseType: function(id){
|
|
|
+ var s = getSymbol(this.parent(), id);
|
|
|
+ if (!s.isType())
|
|
|
+ throw new Errors.Error("type name expected");
|
|
|
+ this.__type.setBaseType(s.info());
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ var type = this.__type;
|
|
|
+ var baseType = type.baseType();
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ gen.write((baseType ? baseType.name() : this.rtl().baseClass()) + ".extend(");
|
|
|
+ gen.openScope();
|
|
|
+ gen.write("init: function " + type.name() + "()");
|
|
|
+ gen.openScope();
|
|
|
+ if (baseType)
|
|
|
+ gen.write(baseType.name() + ".prototype.init.bind(this)();\n");
|
|
|
+ var ownFields = type.ownFields();
|
|
|
+ for(var f in ownFields)
|
|
|
+ gen.write("this." + f + " = " + ownFields[f].initializer() + ";\n");
|
|
|
+
|
|
|
+ this.parent().setType(type);
|
|
|
+ gen.closeScope();
|
|
|
+ gen.closeScope(");\n");
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.TypeDeclaration = ChainedContext.extend({
|
|
|
+ init: function TypeDeclarationContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__id = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){this.__ident = id;},
|
|
|
+ setType: function(type){
|
|
|
+ this.addSymbol(new Symbol(this.__ident, type));
|
|
|
+ },
|
|
|
+ typeName: function(){return this.__ident;},
|
|
|
+ genTypeName: function(){return this.__ident;},
|
|
|
+ type: function(){return this.parent().type();}
|
|
|
+});
|
|
|
+
|
|
|
+exports.TypeCast = ChainedContext.extend({
|
|
|
+ init: function TypeCastContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__type = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var s = getSymbol(this.parent(), id);
|
|
|
+ if (!s.isType())
|
|
|
+ return; // this is not a type cast, may be procedure call
|
|
|
+ this.__type = s.info();
|
|
|
+ },
|
|
|
+ endParse: function(){
|
|
|
+ if (this.__type === undefined)
|
|
|
+ return false;
|
|
|
+ this.parent().handleTypeCast(this.__type);
|
|
|
+ return true;
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+exports.ModuleDeclaration = ChainedContext.extend({
|
|
|
+ init: function ModuleDeclarationContext(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ this.__name = undefined;
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ var gen = this.codeGenerator();
|
|
|
+ if (this.__name === undefined ) {
|
|
|
+ this.__name = id;
|
|
|
+ this.addSymbol(new Symbol(id, Type.module));
|
|
|
+ gen.write("var " + id + " = function " + "(){\n");
|
|
|
+ }
|
|
|
+ else if (id === this.__name)
|
|
|
+ gen.write("}();");
|
|
|
+ else
|
|
|
+ throw new Errors.Error("original module name '" + this.__name + "' expected, got '" + id + "'" );
|
|
|
+ }
|
|
|
+});
|
|
|
+
|
|
|
+var ModuleImport = ChainedContext.extend({
|
|
|
+ init: function ModuleImport(context){
|
|
|
+ ChainedContext.prototype.init.bind(this)(context);
|
|
|
+ },
|
|
|
+ setIdent: function(id){
|
|
|
+ if (id == "JS"){
|
|
|
+ this.rtl().supportJS();
|
|
|
+ this.addSymbol(new Symbol("JS", new Module.JS()));
|
|
|
+ }
|
|
|
+ }
|
|
|
+});
|
|
|
+exports.ModuleImport = ModuleImport;
|
|
|
+
|
|
|
+var Scope = Class.extend({
|
|
|
+ init: function Scope(){
|
|
|
+ var symbols = {};
|
|
|
+ for(var t in basicTypes){
|
|
|
+ var type = basicTypes[t];
|
|
|
+ symbols[type.name()] = new Symbol(type.name(), type);
|
|
|
+ }
|
|
|
+ symbols["LONGREAL"] = new Symbol("LONGREAL", basicTypes.real);
|
|
|
+
|
|
|
+ var predefined = Procedure.predefined;
|
|
|
+ for(var i = 0; i < predefined.length; ++i){
|
|
|
+ var s = predefined[i];
|
|
|
+ symbols[s.id()] = s;
|
|
|
+ }
|
|
|
+
|
|
|
+ this.__symbols = symbols;
|
|
|
+ },
|
|
|
+ addSymbol: function(symbol){
|
|
|
+ var id = symbol.id();
|
|
|
+ if (this.findSymbol(id))
|
|
|
+ throw new Errors.Error( "'" + id + "' already declared");
|
|
|
+ this.__symbols[id] = symbol;
|
|
|
+ },
|
|
|
+ findSymbol: function(ident){return this.__symbols[ident];}
|
|
|
+});
|
|
|
+
|
|
|
+exports.Context = Class.extend({
|
|
|
+ init: function Context(){
|
|
|
+ this.__code = new Code.Generator();
|
|
|
+ this.__designator = undefined;
|
|
|
+ this.__type = undefined;
|
|
|
+ this.__scopes = [new Scope()];
|
|
|
+ this.__gen = 0;
|
|
|
+ this.__vars = [];
|
|
|
+ this.__rtl = new RTL();
|
|
|
+ },
|
|
|
+ setDesignator: function(d){this.__designator = d;},
|
|
|
+ //designator: function(id){return this.__designator;},
|
|
|
+ type: function(){return this.__type;},
|
|
|
+ genTypeName: function(){
|
|
|
+ ++this.__gen;
|
|
|
+ return "anonymous$" + this.__gen;
|
|
|
+ },
|
|
|
+ genVarName: function(id){
|
|
|
+ if (this.__vars.indexOf(id) === -1) {
|
|
|
+ this.__code.write("var " + id + ";\n");
|
|
|
+ this.__vars.push(id);
|
|
|
+ }
|
|
|
+ },
|
|
|
+ addSymbol: function(symbol){this.currentScope().addSymbol(symbol);},
|
|
|
+ findSymbol: function(ident){
|
|
|
+ for(var i = this.__scopes.length; i--;){
|
|
|
+ var s = this.__scopes[i].findSymbol(ident);
|
|
|
+ if (s)
|
|
|
+ return s;
|
|
|
+ }
|
|
|
+ return undefined;
|
|
|
+ },
|
|
|
+ currentScope: function(){return this.__scopes[this.__scopes.length - 1];},
|
|
|
+ pushScope: function(){this.__scopes.push(new Scope());},
|
|
|
+ popScope: function(){this.__scopes.pop();},
|
|
|
+ handleExpression: function(){},
|
|
|
+ handleLiteral: function(){},
|
|
|
+ getResult: function(){
|
|
|
+ return this.__rtl.generate() + this.__code.getResult();
|
|
|
+ },
|
|
|
+ codeGenerator: function(){return this.__code;},
|
|
|
+ rtl: function(){
|
|
|
+ return this.__rtl;
|
|
|
+ }
|
|
|
+});
|