Przeglądaj źródła

parentheses expressions, grammar fixes, CHR function.

Vladislav Folts 12 lat temu
rodzic
commit
3d7e40fe85

+ 0 - 3
src/cast.js

@@ -9,9 +9,6 @@ function implicitCast(from, to){
 	if (from === to)
 		return doNoting;
 
-	//if (from instanceof VarParameter)
-	//	return implicitCast(from.type(), (to instanceof VarParameter) ? to.type() : to);
-	
 	if (from instanceof Type.String){
 		if (to === Type.basic.char){
 			var v = from.asChar();

+ 60 - 60
src/context.js

@@ -23,9 +23,43 @@ function getSymbol(context, id){
 	return s;
 }
 
+function throwTypeMismatch(from, to){
+	throw new Errors.Error("type mismatch: expected '" + to.description() +
+					       "', got '" + from.description() + "'");
+}
+
+function checkTypeMatch(from, to){
+	if (from !== to)
+		throwTypeMismatch(from, to);
+}
+
 function checkImplicitCast(from, to){
-	if (!Cast.implicit(from, to))
-		throw new Errors.Error("type mismatch: expected '" + to.name() + "', got '" + from.name() + "'");
+	var result = Cast.implicit(from, to);
+	if (!result)
+		throwTypeMismatch(from, to);
+	return result;
+}
+
+function promoteTypeInExpression(e, type){
+	var fromType = e.type();
+	if (type == Type.basic.char && fromType instanceof Type.String){
+		var v = fromType.asChar();
+		if (v !== undefined)
+			return new Code.Expression(v, type);
+	}
+    return e;
+}
+
+function promoteExpressionType(context, left, right){
+    var rightType = right.type();
+	if (!left)
+		return right;
+
+    var leftType = left.type();
+	if (rightType === undefined)
+		return right;
+	
+    return checkImplicitCast(rightType, leftType)(context, right);
 }
 
 function checkTypeCast(from, to, msg){
@@ -666,75 +700,34 @@ function writeDerefDesignatorCode(designator, code){
 
 exports.Term = ChainedContext.extend({
 	init: function TermContext(context){
-		ChainedContext.prototype.init.bind(this)(context);
+		ChainedContext.prototype.init.call(this, context);
 		this.__operator = undefined;
-		this.__code = new Code.SimpleGenerator();
-		this.__left = undefined;
-		this.__isConst = true;
-		this.__value = undefined;
-		this.__designator = undefined;
 		this.__expression = undefined;
 	},
-	codeGenerator: function(){return this.__code;},
-	type: function(){return this.parent().type();},
+	type: function(){return this.__expression.type();},
 	setDesignator: function(d){
-		var type = d.type();
-		this.parent().setType(type);
-
 		var value;
 		var info = d.info();
-		if (!(info instanceof Type.Const))
-			this.__isConst = false;
-		else {
+		if (info instanceof Type.Const)
 			value = info.value();
-			this.handleConst(type, info.value(), d.code());
-		}
-		
-		this.__handleExpression(
+		this.handleExpression(
 			new Code.Expression(d.code(), d.type(), d, 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();
 	},
+	handleOperator: function(o){this.__operator = o;},
 	handleConst: function(type, value, code){
-		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;
-		this.__handleExpression(new Code.Expression(
+		this.handleExpression(new Code.Expression(
 			code, type, undefined, value));
 	},
 	handleFactor: function(e){
 		var type = e.type();
 		if (!type)
 			throw new Errors.Error("procedure returning no result cannot be used in an expression");
-		this.setType(type);
-		this.__handleExpression(e);
-		this.__code.write(e.code());
+		this.handleExpression(e);
 	},
 	endParse: function(){this.parent().handleTerm(this.__expression);},
-	__derefDesignator: function(){
-		var designator = this.__designator;
-		if (!designator)
-			return;
-
-		writeDerefDesignatorCode(designator, this.__code);
-		this.__designator = undefined;
-	},
-	__handleExpression: function(e){
-		if (this.__operator)
+	handleExpression: function(e){
+		e = promoteExpressionType(this, this.__expression, e);
+        if (this.__operator)
 			e = this.__expression ? this.__operator(this.__expression, e)
                                   : this.__operator(e);
 		this.__expression = e;
@@ -842,6 +835,7 @@ exports.SimpleExpression = ChainedContext.extend({
 		this.__exp = undefined;
 	},
 	handleTerm: function(e){
+		this.setType(e.type());
 		if (this.__unaryOperator){
 			this.__exp = this.__unaryOperator(e);
 			this.__unaryOperator = undefined;
@@ -884,11 +878,12 @@ exports.Expression = ChainedContext.extend({
             return;
         }
 
-        var leftType = this.__expression.type();
-        var rightType = e.type();
-        var leftCode = this.__expression.code();
         var leftExpression = this.__expression;
-        var rightCode = e.code();
+        var leftType = leftExpression.type();
+        var leftCode = leftExpression.code();
+        var rightExpression = e;
+        var rightType = rightExpression.type();
+        var rightCode = rightExpression.code();
         var code;
 
         if (this.__relation == "IN"){
@@ -911,8 +906,13 @@ exports.Expression = ChainedContext.extend({
                 throw new Errors.Error("type name expected");
             code = leftCode + " instanceof " + rightCode;
         }
-        else
-            checkImplicitCast(rightType, leftType);
+        else{
+            leftExpression = promoteTypeInExpression(leftExpression, rightType);
+            rightExpression = promoteTypeInExpression(rightExpression, leftType);
+            leftCode = leftExpression.code();
+            rightCode = rightExpression.code();
+            checkImplicitCast(rightExpression.type(), leftExpression.type());
+        }
 
         if (this.__relation == "=")
             code = Code.adjustPrecedence(leftExpression, precedence.equal) + " == " + rightCode;
@@ -1384,7 +1384,7 @@ var ProcedureCall = ChainedContext.extend({
 	hasActualParameters: function(){},
 	handleExpression: function(e){this.__procCall.handleArgument(e);},
 	callExpression: function(){return this.__callExpression;},
-	endParse: function(){this.__callExpression = this.__procCall.end(); /*console.log(this.__callExpression.maxPrecedence())*/;}
+	endParse: function(){this.__callExpression = this.__procCall.end();}
 });
 
 exports.StatementProcedureCall = ProcedureCall.extend({

+ 12 - 7
src/grammar.js

@@ -20,6 +20,7 @@ var context = Parser.context;
 var emit = Parser.emit;
 var required = Parser.required;
 
+var identdef = and(ident, optional("*"));
 var selector = or(and(point, ident)
 				// break recursive declaration of expList
 			    , and("[", function(stream, context){return expList(stream, context);}, "]")
@@ -52,7 +53,9 @@ var factor = context(
 				 , optional(function(stream, context){return actualParameters(stream, context);})
 				  )
 			 , Context.ExpressionProcedureCall)
-	 , and("~", function(stream, context){
+	 , and("(", function(stream, context){return expression(stream, context);}
+         , required(")", "no matched ')'"))
+     , and("~", function(stream, context){
 					return factor(stream, context);}) // break recursive declaration of factor
 	 )
 	, Context.Factor);
@@ -81,7 +84,8 @@ var procedureCall = context(and(designator, optional(actualParameters))
 var assignment = context(and(designator, ":=", required(expression, "expression expected"))
 					   , Context.Assignment);
 
-var statement = or(emit(assignment, Context.emitEndStatement)
+var statement = optional(or(
+                   emit(assignment, Context.emitEndStatement)
 				 , emit(procedureCall, Context.emitEndStatement)
 				   // break recursive declaration of ifStatement/caseStatement/whileStatement/repeatStatement
 				 , function(stream, context){return ifStatement(stream, context);}
@@ -89,7 +93,7 @@ var statement = or(emit(assignment, Context.emitEndStatement)
 				 , function(stream, context){return whileStatement(stream, context);}
 				 , function(stream, context){return repeatStatement(stream, context);}
 				 , function(stream, context){return forStatement(stream, context);}
-				 );
+				 ));
 var statementSequence = and(statement, repeat(and(";", statement)));
 
 var ifStatement = and("IF", context(expression, Context.If), "THEN", statementSequence
@@ -114,7 +118,8 @@ var repeatStatement = and("REPEAT", context(statementSequence, Context.Repeat)
 
 var forStatement = context(and("FOR", ident, ":=", expression, "TO", expression
 							 , optional(and("BY", constExpression))
-							 , emit("DO", Context.emitForBegin), statementSequence, "END")
+							 , emit("DO", Context.emitForBegin)
+                             , statementSequence, required("END", "END expected (FOR)"))
 						 , Context.For);
 
 var fieldList = context(and(identList, ":", type), Context.FieldListDeclaration);
@@ -146,7 +151,7 @@ var strucType = or(arrayType, recordType, pointerType, procedureType);
 var typeDeclaration = context(and(ident, "=", strucType), Context.TypeDeclaration);
 
 var procedureHeading = and("PROCEDURE"
-						 , ident
+						 , identdef
 						 , context(optional(formalParameters), Context.FormalParametersProcDecl));
 var procedureDeclaration = context(
 	  and(procedureHeading, ";"
@@ -164,7 +169,7 @@ var declarationSequence = and(optional(and("CONST", repeat(and(constantDeclarati
 var procedureBody = and(declarationSequence
 					  , optional(and("BEGIN", statementSequence))
 					  , optional(context(and("RETURN", expression), Context.Return))
-					  , "END");
+					  , required("END", "END expected (PROCEDURE)"));
 
 var imprt = and(ident, optional(and(":=", ident)));
 var importList = context(and("IMPORT", imprt, repeat(and(",", imprt))),
@@ -173,7 +178,7 @@ var module = context(and("MODULE", ident, ";",
 						 optional(and(importList, ";")),
 						 declarationSequence,
 						 optional(and("BEGIN", statementSequence)),
-						 "END", ident, point),
+						 required("END", "END expected (MODULE)"), ident, point),
 					 Context.ModuleDeclaration);
 
 exports.declarationSequence = declarationSequence;

+ 2 - 0
src/parser.js

@@ -55,6 +55,8 @@ exports.repeat = function(p){
 
 exports.optional = function(p){
 	assert(arguments.length == 1);
+	if (typeof(p) === "string")
+		p = Lexer.literal(p);
 	return function(stream, context){
 		var savePos = stream.pos();
 		if ( !p(stream, context))

+ 25 - 0
src/procedure.js

@@ -357,6 +357,31 @@ exports.predefined = [
             }));
         var symbol = new Type.Symbol(name, type);
         return symbol;
+    }(),
+    function(){
+        var CallGenerator = ProcCallGenerator.extend({
+            init: function ChrProcCallGenerator(context, id, type){
+                ProcCallGenerator.prototype.init.call(this, context, id, type);
+                this.__callExpression = undefined;
+            },
+            prolog: function(){return "";},
+            epilog: function(){return "";},
+            checkArgument: function(pos, e){
+                this.__callExpression = new Code.Expression(e.code(), Type.basic.char);
+                return ProcCallGenerator.prototype.checkArgument.call(this, pos, e);
+            },
+            callExpression: function(){return this.__callExpression;}
+        });
+        var name = "CHR";
+        var type = new Type.Procedure(new ProcType(
+            "predefined procedure " + name,
+            [new Arg(Type.basic.int, false)],
+            Type.basic.char,
+            function(context, id, type){
+                return new CallGenerator(context, id, type);
+            }));
+        var symbol = new Type.Symbol(name, type);
+        return symbol;
     }()
 	];
 

+ 14 - 0
test/expected/chr.js

@@ -0,0 +1,14 @@
+var RTL$ = {
+	assert: function (condition, code){
+        if (!condition)
+            throw new Error("assertion failed"
+                          + ((code !== undefined) ? " with code " + code : ""));
+    }
+};
+var m = function (){
+var ch = 0;
+var i = 0;
+i = 65;
+ch = i;
+RTL$.assert(97 == 97);
+}();

+ 1 - 1
test/expected/errors/syntax.txt

@@ -1 +1 @@
-line 4: syntax error
+line 4: END expected (MODULE)

+ 7 - 0
test/expected/string.js

@@ -29,6 +29,11 @@ var RTL$ = {
         for(i = 0; i < s.length; ++i)
             result[i] = s.charCodeAt(i);
         return result;
+    },
+	assert: function (condition, code){
+        if (!condition)
+            throw new Error("assertion failed"
+                          + ((code !== undefined) ? " with code " + code : ""));
     }
 };
 var m = function (){
@@ -49,4 +54,6 @@ RTL$.assignArrayFromString(a2, s2);
 p1(RTL$.strToArray(s1));
 p1(RTL$.strToArray(s2));
 p2(34);
+RTL$.assert(ch1 == 34);
+RTL$.assert(34 == ch1);
 }();

+ 9 - 0
test/input/chr.ob

@@ -0,0 +1,9 @@
+MODULE m;
+
+VAR ch: CHAR; i: INTEGER;
+
+BEGIN
+    i := 65;
+    ch := CHR(i);
+	ASSERT(CHR(ORD("a")) = "a")
+END m.

+ 4 - 1
test/input/string.ob

@@ -21,5 +21,8 @@ BEGIN
 
 	p1(s1);
 	p1(s2);
-    p2(s1)
+    p2(s1);
+
+    ASSERT(ch1 = s1);
+	ASSERT(s1 = ch1)
 END m.

+ 3 - 6
test/test.js

@@ -3,8 +3,7 @@ TestError.prototype.toString = function(){return this.__s;};
 
 function runImpl(tests, stat, tab){
     for(var t in tests)
-        if (!runTest(t, tests, stat, tab))
-            ++stat.failCount;
+        runTest(t, tests, stat, tab);
 }
 
 function runTest(t, tests, stat, tab){
@@ -12,10 +11,9 @@ function runTest(t, tests, stat, tab){
 	if (typeof r != "function"){
         console.log(tab + t);
         runImpl(r, stat, tab + "\t");
-        return true;
+        return;
     }
 
-    var result = false;
 	var padding = "                           ";
 	var log = t;
 	if (log.length < padding.length)
@@ -27,16 +25,15 @@ function runTest(t, tests, stat, tab){
         ++stat.count;
 		r();
 		log += "OK";
-		result = true;
 	}
 	catch (x){
+        ++stat.failCount;
 		if (x instanceof TestError)
 			log += "Failed\n\t" + tab + x;
 		else
 			log += "Failed\n" + (x.stack ? x.stack : '\t' + tab + x);
 	}
 	console.log(tab + log);
-	return result;
 }
 
 function run(tests){

+ 1 - 1
test/test_compile.cmd

@@ -1,2 +1,2 @@
 SET NODE_PATH=.;%~dp0../src
-"C:\Program Files\nodejs\node.exe" test_compile.js
+"C:\Program Files\nodejs\node.exe" test_compile.js %*

+ 8 - 0
test/test_compile.js

@@ -64,6 +64,14 @@ function makeTests(test, dirs){
     return tests;
 }
 
+if (process.argv.length > 2){
+    var tests = {};
+    var name = process.argv[2];
+    tests[name] = function(){run(name);};
+    Test.run(tests);
+    return;
+}
+
 var okDirs = {input: "input", output: "output", expected: "expected"};
 var errDirs = {};
 var runDirs = {};

+ 22 - 3
test/test_unit.js

@@ -134,8 +134,17 @@ expression: function(){
 	test.expectError("FFX", "undeclared identifier: 'FFX'");
 	//assert(!parse("1 + \"a\""));
 	//assert(parse("\"a\" + \"b\""));
-}
-,identifier: function(){
+},
+"parentheses": function(){
+	var test = setup(Grammar.expression);
+
+	test.parse("(1)");
+	test.parse("(1 + 2)");
+	test.parse("(1 + 2) * 3");
+	test.parse("3 * (1 + 2)");
+	test.expectError("(1  + 2", "no matched ')'");
+},
+identifier: function(){
 	var IdentDeclarationContext = Class.extend({
 		init: function(){this.__ident = undefined;},
 		setIdent: function(id){this.__ident = id;},
@@ -350,6 +359,12 @@ expression: function(){
 	test.expectError("ORD(1.2)", "type mismatch for argument 1: 'REAL' cannot be converted to 'CHAR or BOOLEAN or SET'");
 	test.expectError("ORD(\"abc\")", "type mismatch for argument 1: 'multi-character string' cannot be converted to 'CHAR or BOOLEAN or SET'");
 },
+"CHR": function(){
+	var test = setupWithContext(Grammar.statement, "VAR i: INTEGER; ch: CHAR;");
+
+	test.parse("CHR(i)");
+	//test.expectError("CHR(ch)", "type mismatch for argument 1: 'CHAR' cannot be converted to 'INTEGER'");
+},
 "assignment statement": function(){
 	var test = setupWithContext(
 		  Grammar.statement
@@ -521,6 +536,8 @@ expression: function(){
 				   , "constant expression expected as 'BY' parameter");
 	test.expectError("FOR i := 0 TO 10 BY TRUE DO END"
 				   , "'INTEGER' expression expected as 'BY' parameter, got 'BOOLEAN'");
+	test.expectError("FOR i := 0 TO 10 DO - END"
+				   , "END expected (FOR)");
 },
 "logical operators": function(){
 	var test = setupWithContext(
@@ -613,7 +630,8 @@ expression: function(){
 	test.parse("VAR END");
 	test.parse("VAR i: INTEGER; END");
 	test.parse("VAR a: ARRAY 10 OF INTEGER; END");
-	test.expectError("VAR i: INTEGER;", "not parsed");
+	test.expectError("VAR i: INTEGER;", "END expected (PROCEDURE)");
+    test.expectError("VAR i: INTEGER; i := 1; END", "END expected (PROCEDURE)");
 	test.parse("VAR i: INTEGER; BEGIN i := 1 END");
 	test.parse("VAR b: BOOLEAN; BEGIN b := TRUE END");
 	test.expectError("VAR i: INTEGER; BEGIN j := 1 END", "undeclared identifier: 'j'");
@@ -864,6 +882,7 @@ module: function(){
 	test.parse("MODULE m; END m.");
 	test.expectError("MODULE m; END undeclared.",
 					 "original module name 'm' expected, got 'undeclared'");
+    test.expectError("MODULE m; BEGIN - END m.", "END expected (MODULE)");
 },
 assert: function(){
 	var test = setup(Grammar.statement);