Forráskód Böngészése

js -> eberon transition

Vladislav Folts 10 éve
szülő
commit
8cbaba0280

BIN
bin/compiled.zip


+ 1 - 1
build.py

@@ -127,7 +127,7 @@ def run_tests(bin, unit_test=None, code_test=None):
 def recompile(bin):
     print('recompile oberon sources using "%s"...' % bin)
     compiler = os.path.join(root, 'src', 'oc_nodejs.js')
-    sources = ['ContextConst.ob', 'ContextIdentdef', 'ContextIf', 'ContextProcedure.ob', 'EberonSymbols.ob', 'EberonCast.ob', 
+    sources = ['ContextCase.ob', 'ContextConst.ob', 'ContextIdentdef', 'ContextIf', 'ContextProcedure.ob', 'EberonSymbols.ob', 'EberonCast.ob', 
                'EberonConstructor.ob', 'EberonOperator.ob', 'EberonScope.ob',
                'OberonSymbols.ob', 'Lexer.ob', 'Module.ob']
     

+ 0 - 115
src/context.js

@@ -46,121 +46,6 @@ exports.emitEndStatement = function(context){
     context.codeGenerator().write(";\n");
 };
 
-exports.Case = ChainedContext.extend({
-    init: function CaseContext(context){
-        ChainedContext.prototype.init.call(this, context);
-        this.__type = undefined;
-        this.__firstCase = true;
-        this.__var = this.root().currentScope().generateTempVar("case");
-        this.codeGenerator().write("var " + this.__var + " = ");
-    },
-    caseVar: function(){return this.__var;},
-    handleExpression: function(e){
-        var type = e.type();
-        var gen = this.codeGenerator();
-        if (type instanceof Type.String){
-            var v;
-            if (Type.stringAsChar(type, {set: function(value){v = value;}})){
-                gen.write(v);
-                type = basicTypes.ch;
-            }
-        }
-        if (!Type.isInt(type) && type != basicTypes.ch)
-            throw new Errors.Error(
-                Type.intsDescription() + " 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 (!Cast.areTypesMatch(type, this.__type))
-            throw new Errors.Error(
-                "label must be '" + Type.typeName(this.__type) + "' (the same as case expression), got '"
-                + Type.typeName(type) + "'");
-    }
-});
-
-exports.CaseLabelList = ChainedContext.extend({
-    init: function CaseLabelListContext(context){
-        ChainedContext.prototype.init.call(this, context);
-        this.__glue = "";
-    },
-    handleLabelType: function(type){this.parent().handleLabelType(type);},
-    handleRange: function(from, to){
-        var parent = this.parent();
-        if (!this.__glue)
-            parent.caseLabelBegin();
-
-        var v = parent.parent().caseVar();
-        var cond = to === undefined
-            ? v + " === " + from.value
-            : "(" + v + " >= " + from.value + " && " + v + " <= " + to.value + ")";
-        this.codeGenerator().write(this.__glue + cond);
-        this.__glue = " || ";
-    },
-    endParse: function(){this.parent().caseLabelEnd();}
-});
-
-exports.CaseLabel = ChainedContext.extend({
-    init: function CaseLabelContext(context){
-        ChainedContext.prototype.init.call(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.call(this, context);
-        this.__from = undefined;
-        this.__to = undefined;
-    },
-    codeGenerator: function(){return nullCodeGenerator;}, // 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){
-            if (!Type.stringAsChar(type, {set: function(v){value = v;}}))
-                throw new Errors.Error("single-character string expected");
-            type = basicTypes.ch;
-            value = new ConstValue.Int(value);
-        }
-        this.handleLabel(type, value);
-    },
-    handleIdent: function(id){
-        var s = ContextHierarchy.getSymbol(this.root(), 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.call(this, context);

+ 4 - 3
src/eberon/eberon_context.js

@@ -5,6 +5,7 @@ var Class = require("rtl.js").Class;
 var Code = require("js/Code.js");
 var CodeGenerator = require("js/CodeGenerator.js");
 var Context = require("context.js");
+var ContextCase = require("js/ContextCase.js");
 var ContextConst = require("js/ContextConst.js");
 var ContextDesignator = require("js/ContextDesignator.js");
 var ContextExpression = require("js/ContextExpression.js");
@@ -1174,9 +1175,9 @@ var If = Class.extend.call(ContextIf.Type, {
     }
 });
 
-var CaseLabel = Context.CaseLabel.extend({
+var CaseLabel = Class.extend.call(ContextCase.Label, {
     init: function EberonContext$CaseLabel(context){
-        Context.CaseLabel.prototype.init.call(this, context);
+        ContextCase.Label.call(this, context);
     },
     handleLiteral: function(s){
         if (s == ':'){ // statement sequence is expected now
@@ -1189,7 +1190,7 @@ var CaseLabel = Context.CaseLabel.extend({
     },
     endParse: function(){
         this.root().popScope();
-        Context.CaseLabel.prototype.endParse.call(this);
+        ContextCase.Label.prototype.endParse.call(this);
     }
 });
 

+ 4 - 3
src/grammar.js

@@ -1,6 +1,7 @@
 "use strict";
 
 var Context = require("context.js");
+var ContextCase = require("js/ContextCase.js");
 var ContextDesignator = require("js/ContextDesignator.js");
 var ContextExpression = require("js/ContextExpression.js");
 var ContextIdentdef = require("js/ContextIdentdef.js");
@@ -123,12 +124,12 @@ var ifStatement = and("IF", context(and(expression, required("THEN", "THEN expec
                                     contexts.If));
 
 var label = or(integer, string, ident);
-var labelRange = context(and(label, optional(and("..", label))), Context.CaseRange);
-var caseLabelList = context(and(labelRange, repeat(and(",", labelRange))), Context.CaseLabelList);
+var labelRange = context(and(label, optional(and("..", label))), ContextCase.Range);
+var caseLabelList = context(and(labelRange, repeat(and(",", labelRange))), ContextCase.LabelList);
 var caseParser = optional(context(and(caseLabelList, ":", statementSequence), contexts.CaseLabel));
 var caseStatement = and("CASE", context(and(expression
                       , "OF", caseParser, repeat(and("|", caseParser)), "END")
-                      , Context.Case));
+                      , ContextCase.Type));
 
 var whileStatement = and("WHILE", 
                          context(and(expression, "DO", statementSequence, 

+ 171 - 0
src/ob/ContextCase.ob

@@ -0,0 +1,171 @@
+MODULE ContextCase;
+IMPORT
+    Cast, Chars, CodeGenerator, ConstValue, ContextExpression, ContextHierarchy, 
+    Errors, Expression, String, Types;
+TYPE
+    Type* = RECORD(ContextExpression.ExpressionHandler)
+        PROCEDURE Type(parent: ContextHierarchy.PNode);
+
+        PROCEDURE beginCase();
+        PROCEDURE handleLabelType(type: Types.PType);
+
+        var: STRING;
+        type: Types.PType;
+        firstCaseParsed: BOOLEAN;
+    END;
+
+    Label* = RECORD(ContextHierarchy.Node)
+        PROCEDURE caseLabelBegin();
+        PROCEDURE caseLabelEnd();
+    END;
+    PLabel = POINTER TO Label;
+
+    LabelList* = RECORD(ContextHierarchy.Node)
+        PROCEDURE handleRange(from, to: ConstValue.PInt);
+
+        glue: STRING;
+    END;
+
+    Range* = RECORD(ContextExpression.ExpressionHandler)
+        from, to: ConstValue.PInt;
+    END;
+
+PROCEDURE Type.Type(parent: ContextHierarchy.PNode)
+    | SUPER(parent),
+      var(SELF.root().currentScope().generateTempVar("case"));
+BEGIN
+    SELF.codeGenerator().write("var " + SELF.var + " = ");
+END;
+
+PROCEDURE Type.handleExpression(e: Expression.PType);
+VAR
+    c: CHAR;
+BEGIN
+    type <- e.type();
+    gen <- SELF.codeGenerator();
+    IF (type IS Types.PString) & Types.stringAsChar(type^, c) THEN
+        gen.write(String.fromChar(c));
+        SELF.type := Types.basic.ch;
+    ELSIF Types.isInt(type) OR (type = Types.basic.ch) THEN
+        SELF.type := type;
+    ELSE
+        Errors.raise(Types.intsDescription() 
+                     + " or 'CHAR' expected as CASE expression");
+    END;
+    gen.write(";" + Chars.ln);
+END;
+
+PROCEDURE Type.beginCase();
+BEGIN
+    IF ~SELF.firstCaseParsed THEN
+        SELF.firstCaseParsed := TRUE;
+    ELSE
+        SELF.codeGenerator().write("else ");
+    END;
+END;
+
+PROCEDURE Type.handleLabelType(type: Types.PType);
+BEGIN
+    IF ~Cast.areTypesMatch(type, SELF.type) THEN
+        Errors.raise("label must be '" + SELF.type.description() + "' (the same as case expression), got '"
+                     + type.description() + "'");
+    END;
+END;
+
+PROCEDURE LabelList.handleRange(from, to: ConstValue.PInt);
+VAR
+    cond: STRING;
+BEGIN
+    parent <- SELF.parent()(PLabel);
+    IF LEN(SELF.glue) = 0 THEN
+        parent.caseLabelBegin();
+    END;
+
+    v <- parent.parent()^(Type).var;
+    IF to = NIL THEN
+        cond := v + " === " + String.fromInt(from.value);
+    ELSE
+        cond := "(" + v + " >= " + String.fromInt(from.value)
+              + " && " + v + " <= " + String.fromInt(to.value) + ")";
+    END;
+
+    SELF.codeGenerator().write(SELF.glue + cond);
+    SELF.glue := " || ";
+END;
+
+PROCEDURE LabelList.endParse(): BOOLEAN;
+BEGIN
+    SELF.parent()(PLabel).caseLabelEnd();
+    RETURN TRUE;
+END;
+
+PROCEDURE Label.caseLabelBegin();
+BEGIN
+    SELF.parent()^(Type).beginCase();
+    SELF.codeGenerator().write("if (");
+END;
+
+PROCEDURE Label.caseLabelEnd();
+BEGIN
+    gen <- SELF.codeGenerator();
+    gen.write(")");
+    gen.openScope();
+END;
+
+PROCEDURE Label.endParse(): BOOLEAN;
+BEGIN
+    SELF.codeGenerator().closeScope("");
+    RETURN TRUE;
+END;
+
+PROCEDURE handleLabel(VAR r: Range; type: Types.PType; v: ConstValue.PInt);
+BEGIN
+    r.parent().parent().parent()^(Type).handleLabelType(type);
+    IF r.from = NIL THEN
+        r.from := v;
+    ELSE
+        r.to := v;
+    END;
+END;
+
+PROCEDURE Range.codeGenerator(): CodeGenerator.PIGenerator;
+    RETURN CodeGenerator.nullGenerator;
+END;
+
+PROCEDURE Range.handleExpression(e: Expression.PType);
+VAR
+    c: CHAR;
+BEGIN
+    type <- e.type();
+    IF type IS Types.PString THEN
+        IF ~Types.stringAsChar(type^, c) THEN
+            Errors.raise("single-character string expected");
+        END;
+        handleLabel(SELF, Types.basic.ch, NEW ConstValue.Int(ORD(c)));
+    ELSE
+        handleLabel(SELF, type, e.constValue()(ConstValue.PInt));
+    END;
+END;
+
+PROCEDURE Range.handleIdent(id: STRING);
+BEGIN
+    info <- ContextHierarchy.getSymbol(SELF.root()^, id).info();
+    IF ~(info IS Types.PConst) THEN
+        Errors.raise("'" + id + "' is not a constant");
+    ELSE
+        type <- info.type;
+        IF type IS Types.PString THEN
+            SELF.handleExpression(Expression.makeSimple("", type));
+        ELSE
+            handleLabel(SELF, type, info.value(ConstValue.PInt));
+        END;
+    END;
+END;
+
+PROCEDURE Range.endParse(): BOOLEAN;
+BEGIN
+    SELF.parent()^(LabelList).handleRange(SELF.from, SELF.to);
+    RETURN TRUE;
+END;
+
+END ContextCase.

+ 17 - 19
src/ob/ContextExpression.ob

@@ -61,7 +61,6 @@ TYPE
     PFactor = POINTER TO Factor;
 
     Factor* = RECORD(ExpressionHandler)
-        PROCEDURE handleConst(type: Types.PType; value: ConstValue.PType; code: STRING);
         PROCEDURE handleLogicalNot();
 
         expression: Expression.PType;
@@ -86,9 +85,9 @@ TYPE
     END;
 
     Const = RECORD(ContextHierarchy.Node)
-        PROCEDURE Const(factor: PFactor);
+        PROCEDURE Const(parent: PExpressionHandler);
 
-        factor: PFactor;
+        parentExpression: PExpressionHandler;
     END;
 
     Integer* = RECORD(Const)
@@ -703,19 +702,18 @@ BEGIN
     RETURN TRUE;
 END;
 
-PROCEDURE Factor.handleConst(type: Types.PType; value: ConstValue.PType; code: STRING);
-BEGIN
-    SELF.expression := Expression.make(code, type, NIL, value);
+PROCEDURE expressionFromConst(type: Types.PType; value: ConstValue.PType; code: STRING): Expression.PType;
+    RETURN Expression.make(code, type, NIL, value);
 END;
 
 PROCEDURE Factor.handleLiteral(s: STRING);
 BEGIN
     IF s = "NIL" THEN
-        SELF.handleConst(Types.nil, NIL, "null");
+        SELF.handleExpression(expressionFromConst(Types.nil, NIL, "null"));
     ELSIF s = "TRUE" THEN
-        SELF.handleConst(Types.basic.bool, NEW ConstValue.Int(1), "true");
+        SELF.handleExpression(expressionFromConst(Types.basic.bool, NEW ConstValue.Int(1), "true"));
     ELSIF s = "FALSE" THEN
-        SELF.handleConst(Types.basic.bool, NEW ConstValue.Int(0), "false");
+        SELF.handleExpression(expressionFromConst(Types.basic.bool, NEW ConstValue.Int(0), "false"));
     ELSIF s = "~" THEN
         SELF.handleLogicalNot();
     END;
@@ -867,25 +865,25 @@ PROCEDURE AddOperator.doExpectPlusOperator(): STRING;
     RETURN "numeric type or SET";
 END;
 
-PROCEDURE Const.Const(factor: PFactor)
-    | SUPER(factor),
-      factor(factor);
+PROCEDURE Const.Const(parent: PExpressionHandler)
+    | SUPER(parent),
+      parentExpression(parent);
 END;
 
 PROCEDURE Integer.handleInt(n: INTEGER);
 BEGIN
-    SELF.factor.handleConst(
+    SELF.parentExpression.handleExpression(expressionFromConst(
         Types.basic.integer, 
         NEW ConstValue.Int(n), 
-        String.fromInt(n));
+        String.fromInt(n)));
 END;
 
 PROCEDURE Real.handleReal(r: REAL);
 BEGIN
-    SELF.factor.handleConst(
+    SELF.parentExpression.handleExpression(expressionFromConst(
         Types.basic.real, 
         NEW ConstValue.Real(r), 
-        String.fromReal(r));
+        String.fromReal(r)));
 END;
 
 PROCEDURE escapeString(s: STRING): STRING;
@@ -924,10 +922,10 @@ END;
 
 PROCEDURE Str.handleStr(s: STRING);
 BEGIN
-    SELF.factor.handleConst(
+    SELF.parentExpression.handleExpression(expressionFromConst(
         NEW Types.String(s), 
         NEW ConstValue.String(s), 
-        escapeString(s));
+        escapeString(s)));
 END;
 
 PROCEDURE SetElement.SetElement(parent: PSet)
@@ -984,7 +982,7 @@ PROCEDURE Set.endParse(): BOOLEAN;
 BEGIN
     parent <- SELF.parent()(PFactor);
     IF LEN(SELF.expression) = 0 THEN
-        parent.handleConst(Types.basic.set, NEW ConstValue.Set(SELF.value), String.fromInt(ORD(SELF.value)));
+        parent.handleExpression(expressionFromConst(Types.basic.set, NEW ConstValue.Set(SELF.value), String.fromInt(ORD(SELF.value))));
     ELSE
         code <- SELF.root().language().rtl().makeSet(SELF.expression);
         IF SELF.value # {} THEN

+ 2 - 1
src/oberon/oberon_grammar.js

@@ -3,6 +3,7 @@
 var Cast = require("js/Cast.js");
 var CodeGenerator = require("js/CodeGenerator.js");
 var Context = require("context.js");
+var ContextCase = require("js/ContextCase.js");
 var ContextConst = require("js/ContextConst.js");
 var ContextDesignator = require("js/ContextDesignator.js");
 var ContextExpression = require("js/ContextExpression.js");
@@ -128,7 +129,7 @@ exports.language = {
             For:                Context.For,
             While:              Context.While,
             If:                 ContextIf.Type,
-            CaseLabel:          Context.CaseLabel,
+            CaseLabel:          ContextCase.Label,
             Repeat:             Context.Repeat,
             ModuleDeclaration:  Context.ModuleDeclaration
         },

+ 8 - 3
test/test_unit.js

@@ -628,7 +628,7 @@ return {
     ),
 "CASE statement": testWithContext(
     context(grammar.statement,
-              "CONST ci = 15; cc = \"A\";"
+              "CONST ci = 15; cc = \"A\"; cb = TRUE; cs = \"abc\";"
             + "VAR c1: CHAR; b1: BOOLEAN; i1, i2: INTEGER; byte: BYTE; p: POINTER TO RECORD END;"),
     pass("CASE i1 OF END",
          "CASE i1 OF | END",
@@ -645,7 +645,8 @@ return {
          "CASE i1 OF ci..2: b1 := TRUE END",
          "CASE c1 OF cc..\"Z\": b1 := TRUE END",
          "CASE i1 OF 1, 2, 3: b1 := TRUE | 4..10: b1 := FALSE | 11: c1 := \"A\" END",
-         "CASE i1 OF 1, 2, 5..9: b1 := TRUE END"),
+         "CASE i1 OF 1, 2, 5..9: b1 := TRUE END"
+         ),
     fail(["CASE i1 OF undefined: b1 := TRUE END",
           "undeclared identifier: 'undefined'"],
          ["CASE i1 OF i2: b1 := TRUE END",
@@ -659,7 +660,11 @@ return {
          ["CASE c1 OF \"A\", 1: b1 := TRUE END",
           "label must be 'CHAR' (the same as case expression), got 'INTEGER'"],
          ["CASE c1 OF \"A\"..1: b1 := TRUE END",
-          "label must be 'CHAR' (the same as case expression), got 'INTEGER'"])
+          "label must be 'CHAR' (the same as case expression), got 'INTEGER'"],
+         ["CASE c1 OF cs: b1 := TRUE END", "single-character string expected"],
+         ["CASE ci OF cb: b1 := TRUE END", "label must be 'INTEGER' (the same as case expression), got 'BOOLEAN'"],
+         ["CASE ci OF TRUE: b1 := TRUE END", "not parsed"]
+         )
     ),
 "WHILE statement": testWithContext(
     context(grammar.statement,