Quellcode durchsuchen

Fix issue #8: IF expression code generator bug. Preparing to implement IMPORT.

Vladislav Folts vor 12 Jahren
Ursprung
Commit
3c756bf850
7 geänderte Dateien mit 186 neuen und 137 gelöschten Zeilen
  1. 45 25
      src/context.js
  2. 13 10
      src/grammar.js
  3. 110 99
      src/lexer.js
  4. 7 0
      src/type.js
  5. 3 0
      test/expected/if.js
  6. 5 1
      test/input/if.ob
  7. 3 2
      test/test.js

+ 45 - 25
src/context.js

@@ -197,7 +197,9 @@ exports.BaseType = ChainedContext.extend({
     init: function BaseTypeContext(context){
         ChainedContext.prototype.init.bind(this)(context);
     },
-    setIdent: function(id){this.parent().setBaseType(id);}
+    handleSymbol: function(s){
+        this.parent().setBaseType(unwrapType(s.symbol().info()));
+    }
 });
 
 var DesignatorInfo = Class.extend({
@@ -215,6 +217,27 @@ var DesignatorInfo = Class.extend({
     scope: function(){return this.__scope;}
 });
 
+exports.QualifiedIdentificator = ChainedContext.extend({
+    init: function QualifiedIdentificator(context){
+        ChainedContext.prototype.init.bind(this)(context);
+        this.__module = undefined;
+        this.__id = undefined;
+    },
+    setIdent: function(id){
+        this.__id = id;
+    },
+    handleLiteral: function(){
+        var s = getSymbol(this, this.__id);
+        if (!s.isModule())
+            return false; // stop parsing
+        this.__module = s.info();
+    },
+    endParse: function(){
+        var s = getSymbolAndScope(this.__module ? this.__module : this, this.__id);
+        this.parent().handleSymbol(s);
+    }
+});
+
 exports.Designator = ChainedContext.extend({
     init: function DesignatorContext(context){
         ChainedContext.prototype.init.bind(this)(context);
@@ -226,21 +249,20 @@ exports.Designator = ChainedContext.extend({
         this.__derefCode = undefined;
         this.__propCode = undefined;
     },
+    handleSymbol: function(found){
+        this.__scope = found.scope();
+        var s = found.symbol();
+        var info = s.info();
+        if (info instanceof Type.Type || s.isType() || s.isProcedure())
+            this.__currentType = info;
+        else if (s.isVariable() || s.isConst())
+            this.__currentType = info.type();
+        this.__info = info;
+        this.__code.write(s.id());
+    },
     setIdent: function(id){
-        var scope;
         var t = this.__currentType;
-        if ( t === undefined){
-            var found = getSymbolAndScope(this.parent(), id);
-            scope = found.scope();
-            var s = found.symbol();
-            var info = s.info();
-            if (info instanceof Type.Type || s.isType() || s.isProcedure())
-                this.__currentType = info;
-            else if (s.isVariable() || s.isConst())
-                this.__currentType = info.type();
-            this.__info = info;
-        }
-        else if (t instanceof Type.Pointer){
+        if (t instanceof Type.Pointer){
             this.__handleDeref();
             this.__denote(id);
         }
@@ -251,8 +273,7 @@ exports.Designator = ChainedContext.extend({
         else
             throw new Errors.Error("cannot designate '" + t.description() + "'");
 
-        this.__scope = scope;
-        this.__code.write(id);
+        this.__scope = undefined;
     },
     codeGenerator: function(){return this.__code;},
     handleExpression: function(e){this.__indexExpression = e;},
@@ -316,7 +337,7 @@ exports.Designator = ChainedContext.extend({
             throw new Errors.Error("Type '" + t.name() + "' has no '" + id + "' field");
         this.__derefCode = this.__code.result();
         this.__propCode = "\"" + id + "\"";
-        this.__code.write(".");
+        this.__code.write("." + id);
         this.__currentType = fieldType;
     },
     endParse: function(){
@@ -340,7 +361,7 @@ exports.Type = ChainedContext.extend({
     init: function TypeContext(context){
         ChainedContext.prototype.init.bind(this)(context);
     },
-    setIdent: function(id){this.setType(getTypeSymbol(this, id));}
+    handleSymbol: function(s){this.setType(unwrapType(s.symbol().info()));}
 });
 
 exports.FormalType = exports.Type.extend({
@@ -385,8 +406,8 @@ exports.FormalParameters = ChainedContext.extend({
     addArgument: function(name, arg){
         this.__arguments.push(arg);
     },
-    setIdent: function(id){
-        this.__result = getTypeSymbol(this.parent(), id);
+    handleSymbol: function(s){
+        this.__result = unwrapType(s.symbol().info());
     },
     endParse: function(){
         this.__type.define(this.__arguments, this.__result);
@@ -1001,6 +1022,7 @@ exports.Expression = ChainedContext.extend({
     handleLiteral: function(relation){
         this.__relation = relation;
     },
+    codeGenerator: function(){return Code.nullGenerator;},
     endParse: function(){
         var parent = this.parent();
         parent.codeGenerator().write(this.__expression.code());
@@ -1453,9 +1475,7 @@ exports.RecordDecl = ChainedContext.extend({
         gen.write("var " + id + " = ");
     },
     addField: function(name, type) {this.__type.addField(name, type);},
-    setBaseType: function(id){
-        this.__type.setBaseType(getTypeSymbol(this.parent(), id));
-    },
+    setBaseType: function(type){this.__type.setBaseType(type);},
     endParse: function(){
         var type = this.__type;
         var baseType = type.baseType();
@@ -1506,8 +1526,8 @@ exports.TypeCast = ChainedContext.extend({
         ChainedContext.prototype.init.bind(this)(context);
         this.__type = undefined;
     },
-    setIdent: function(id){
-        var s = getSymbol(this.parent(), id);
+    handleSymbol: function(s){
+        s = s.symbol();
         if (!s.isType())
             return; // this is not a type cast, may be procedure call
         this.__type = s.info().type();

+ 13 - 10
src/grammar.js

@@ -22,17 +22,20 @@ var context = Parser.context;
 var emit = Parser.emit;
 var required = Parser.required;
 
+var qualident = context(and(optional(and(ident, ".")), ident),
+						Context.QualifiedIdentificator);
 var identdef = and(ident, optional("*"));
 var selector = or(and(point, ident)
 				// break recursive declaration of expList
 			    , and("[", function(stream, context){return expList(stream, context);}, "]")
 				, "^"
-				, context(and("(", ident, ")"), Context.TypeCast)
+				, context(and("(", qualident, ")"), Context.TypeCast)
 			    );
-var designator = context(and(ident, repeat(selector)), Context.Designator);
-var type = or(function(stream, context){return strucType(stream, context);} // break recursive declaration of strucType
-			, context(ident, Context.Type));
-var identList = and(ident, repeat(and(",", ident)));
+var designator = context(and(qualident, repeat(selector)), Context.Designator);
+var type = or(context(qualident, Context.Type),
+			  function(stream, context){return strucType(stream, context);} // break recursive declaration of strucType
+			 );
+var identList = and(identdef, repeat(and(",", ident)));
 var variableDeclaration = context(and(identList, ":", type), Context.VariableDeclaration);
 
 var integer = or(context(and(digit, repeat(hexDigit), "H", separator), Context.HexInteger)
@@ -132,25 +135,25 @@ var arrayType = and("ARRAY", context(and(
 							  , Context.ArrayDimensions)
 				  , "OF", type), Context.ArrayDecl));
 
-var baseType = context(ident, Context.BaseType);
+var baseType = context(qualident, Context.BaseType);
 var recordType = and("RECORD", context(and(optional(and("(", baseType, ")")), optional(fieldListSequence)
 									 , "END"), Context.RecordDecl));
 
 var pointerType = and("POINTER", "TO", context(type, Context.PointerDecl));
 
-var formalType = context(and(repeat(and("ARRAY", "OF")), ident), Context.FormalType);
+var formalType = context(and(repeat(and("ARRAY", "OF")), qualident), Context.FormalType);
 var fpSection = and(optional(literal("VAR")), ident, repeat(and(",", ident)), ":", formalType);
 var formalParameters = and(
 		  "("
 		, optional(context(and(fpSection, repeat(and(";", fpSection))), Context.ProcParams))
 		, ")"
-		, optional(and(":", ident)));
+		, optional(and(":", qualident)));
 
 var procedureType = and("PROCEDURE"
 					  , context(optional(formalParameters), Context.FormalParameters)
 					    );
 var strucType = or(arrayType, recordType, pointerType, procedureType);
-var typeDeclaration = context(and(ident, "=", strucType), Context.TypeDeclaration);
+var typeDeclaration = context(and(identdef, "=", strucType), Context.TypeDeclaration);
 
 var procedureHeading = and("PROCEDURE"
 						 , identdef
@@ -162,7 +165,7 @@ var procedureDeclaration = context(
 	    , ident)
 	, Context.ProcDecl);
 
-var constantDeclaration = context(and(ident, "=", constExpression), Context.ConstDecl);
+var constantDeclaration = context(and(identdef, "=", constExpression), Context.ConstDecl);
 
 var declarationSequence = and(optional(and("CONST", repeat(and(constantDeclaration, ";"))))
 							, optional(and("TYPE", context(repeat(and(typeDeclaration, ";")), Context.TypeSection)))

+ 110 - 99
src/lexer.js

@@ -1,129 +1,140 @@
+"use strict";
+
 var Errors = require("errors.js");
 
 function isDigit(c) {return c >= '0' && c <= '9';}
 
 exports.digit = function(stream, context){
-	var c = stream.char();
-	if (!isDigit(c))
-		return false;
-	context.handleChar(c);
-	return true;
-}
+    var c = stream.char();
+    if (!isDigit(c))
+        return false;
+    context.handleChar(c);
+    return true;
+};
 
 exports.hexDigit = function(stream, context){
-	var c = stream.char();
-	if (!isDigit(c) && (c < 'A' || c > 'F'))
-		return false;
-	context.handleChar(c);
-	return true;
-}
+    var c = stream.char();
+    if (!isDigit(c) && (c < 'A' || c > 'F'))
+        return false;
+    context.handleChar(c);
+    return true;
+};
 
 exports.point = function(stream, context){
-	if (stream.char() != '.' 
-		|| stream.peekChar() == '.') // not a diapason ".."
-		return false;
-	context.handleLiteral(".");
-	return true;
-}
+    if (stream.char() != '.'
+        || stream.peekChar() == '.') // not a diapason ".."
+        return false;
+    context.handleLiteral(".");
+    return true;
+};
 
 exports.character = function(stream, context){
-	var c = stream.char();
-	if (c == '"')
-		return false;
-	context.handleChar(c);
-	return true;
-}
+    var c = stream.char();
+    if (c == '"')
+        return false;
+    context.handleChar(c);
+    return true;
+};
 
 function string(stream, context){
-	var c = stream.char();
-	if (c != '"')
-		return false;
-
-	var result = "";
-	var parsed = false;
-	stream.read(function(c){
-		if (c == '"'){
-			parsed = true;
-			return false;
-		}
-		result += c;
-		return true;
-	});
-	if (!parsed)
-		throw new Errors.Error("unexpected end of string");
-
-	stream.next(1);
-	context.handleString(result);
-	return true;
+    var c = stream.char();
+    if (c != '"')
+        return false;
+
+    var result = "";
+    var parsed = false;
+    stream.read(function(c){
+        if (c == '"'){
+            parsed = true;
+            return false;
+        }
+        result += c;
+        return true;
+    });
+    if (!parsed)
+        throw new Errors.Error("unexpected end of string");
+
+    stream.next(1);
+    context.handleString(result);
+    return true;
 }
 
 function isLetter(c) { return (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z');}
 
+var reservedWords
+    = ["ARRAY", "IMPORT", "THEN", "BEGIN", "IN", "TO", "BY", "IS",
+       "TRUE", "CASE", "MOD", "TYPE", "CONST", "MODULE", "UNTIL", "DIV",
+       "NIL", "VAR", "DO", "OF", "WHILE", "ELSE", "OR", "ELSIF", "POINTER",
+       "END", "PROCEDURE", "FALSE", "RECORD", "FOR", "REPEAT", "IF", "RETURN"
+      ];
+
 exports.ident = function(stream, context){
-	if (!isLetter(stream.peekChar()))
-		return false;
-	
-	var savePos = stream.pos();
-	var result = "";
-	stream.read(function(c){
-		if (!isLetter(c) && !isDigit(c) /*&& c != '_'*/)
-			return false;
-		result += c;
-		return true;
-	});
-
-	var keywords = ["ARRAY", "END", "VAR", "TYPE", "IF", "CASE", "WHILE", "REPEAT", "FOR", "RETURN", "NIL", "TRUE", "FALSE", "IS"];
-	if (keywords.indexOf(result) != -1){
-		stream.setPos(savePos);
-		return false;
-	}
-	context.setIdent(result);
-	return true;
-}
+    if (!isLetter(stream.peekChar()))
+        return false;
+    
+    var savePos = stream.pos();
+    var result = "";
+    stream.read(function(c){
+        if (!isLetter(c) && !isDigit(c) /*&& c != '_'*/)
+            return false;
+        result += c;
+        return true;
+    });
+
+    if (reservedWords.indexOf(result) != -1){
+        stream.setPos(savePos);
+        return false;
+    }
+    context.setIdent(result);
+    return true;
+};
 
 function skipComment(stream){
-	if (stream.peekStr(2) != "(*")
-		return false;
-
-	stream.next(2);
-	while (stream.peekStr(2) != "*)"){
-		if (stream.eof())
-			throw new Errors.Error("comment was not closed");
-		if (!skipComment(stream))
-			stream.next(1);
-		}
-	stream.next(2);
-	return true;
+    if (stream.peekStr(2) != "(*")
+        return false;
+
+    stream.next(2);
+    while (stream.peekStr(2) != "*)"){
+        if (stream.eof())
+            throw new Errors.Error("comment was not closed");
+        if (!skipComment(stream))
+            stream.next(1);
+        }
+    stream.next(2);
+    return true;
 }
 
+function readSpaces(c){return ' \t\n\r'.indexOf(c) != -1;}
+
 exports.skipSpaces = function(stream, context){
-	if (context.isLexem && context.isLexem())
-		return;
+    if (context.isLexem && context.isLexem())
+        return;
 
-	do
-		stream.read(function(c){return ' \t\n\r'.indexOf(c) != -1;});
-	while (skipComment(stream));
-}
+    do {
+        stream.read(readSpaces);
+    }
+    while (skipComment(stream));
+};
 
 exports.separator = function(stream, context){
-	return !isLetter(stream.peekChar());
-}
+    return !isLetter(stream.peekChar());
+};
 
 exports.literal = function(s){
-	return function(stream, context){
-		if (stream.str(s.length) != s)
-			return false;
-		stream.next(s.length);
-		
-		if ((!context.isLexem || !context.isLexem()) && isLetter(s[s.length - 1])){
-			var next = stream.peekChar();
-			if (isLetter(next) || isDigit(next))
-				return false;
-		}
-		
-		context.handleLiteral(s);
-		return true;
-	}
-}	
+    return function(stream, context){
+        if (stream.str(s.length) != s)
+            return false;
+        stream.next(s.length);
+        
+        if ((!context.isLexem || !context.isLexem()) && isLetter(s[s.length - 1])){
+            var next = stream.peekChar();
+            if (isLetter(next) || isDigit(next))
+                return false;
+        }
+        
+        var result = context.handleLiteral(s);
+        return result === undefined || result;
+    };
+};
 
 exports.string = string;

+ 7 - 0
src/type.js

@@ -165,6 +165,12 @@ exports.Procedure = BasicType.extend({
 	idType: function(){return "procedure";}
 });
 
+var Module = Id.extend({
+	init: function Module(){
+		Id.prototype.init.call(this);
+	}
+});
+
 var Symbol = Class.extend({
 	init: function Symbol(id, info){
 		this.__id = id;
@@ -172,6 +178,7 @@ var Symbol = Class.extend({
 	},
 	id: function(){return this.__id;},
 	info: function(){return this.__info;},
+	isModule: function(){return this.__info instanceof Module;},
 	isVariable: function(){return this.__info instanceof exports.Variable;},
 	isConst: function(){return this.__info instanceof exports.Const;},
 	isType: function(){return this.__info instanceof TypeId;},

+ 3 - 0
test/expected/if.js

@@ -28,4 +28,7 @@ if (b1){
 else {
 	b1 = true;
 }
+if (i1 == 1 || i1 == 2){
+	b1 = true;
+}
 }();

+ 5 - 1
test/input/if.ob

@@ -12,6 +12,10 @@ BEGIN
 		IF b1 THEN i1 := 0; b1 := FALSE END
 	ELSE
 		b1 := TRUE
-	END
+	END;
 	
+	IF (i1 = 1) OR (i1 = 2) THEN
+		b1 := TRUE
+	END;
+
 END m.

+ 3 - 2
test/test.js

@@ -24,7 +24,7 @@ function runTest(t, tests, stat, tab){
 	try {
         ++stat.count;
 		r();
-		log += "OK";
+		//log += "OK";
 	}
 	catch (x){
         ++stat.failCount;
@@ -32,13 +32,14 @@ function runTest(t, tests, stat, tab){
 			log += "Failed\n\t" + tab + x;
 		else
 			log += "Failed\n" + (x.stack ? x.stack : '\t' + tab + x);
+        console.log(tab + log);
 	}
-	console.log(tab + log);
 }
 
 function run(tests){
     var stat = {count: 0, failCount: 0};
 
+    console.log("Running..." );
     var start = Date.now();
     if (typeof process != "undefined" && process.argv.length > 2)
         runTest(process.argv[2], tests, stat, "");