ソースを参照

Implement the ability to call the main function from inside the code

-Implement tests cases for duplicates functions and variables

-Implement tests cases for main functions calls

-Fix tests cases for function parsing that didn't call push/pop varialbes stack
Lucas de Souza 5 年 前
コミット
91abf11885

+ 4 - 0
js/ast/expressions/functionCall.js

@@ -8,6 +8,10 @@ export class FunctionCall extends Expression {
 		this.actualParameters = actualParameters;
 	}
 
+	get isMainCall () {
+		return this.id === null;
+	}
+
 	get parametersSize () {
 		return this.actualParameters.length;
 	}

+ 3 - 0
js/ast/ivprogParser.js

@@ -1105,6 +1105,9 @@ export class IVProgParser {
   getFunctionName (id) {
     const name = LanguageDefinedFunction.getInternalName(id);
     if (name === null) {
+      if (id === LanguageDefinedFunction.getMainFunctionName()) {
+        return null;
+      }
       return id;
     } else {
       return name;

+ 9 - 1
js/processor/ivprogProcessor.js

@@ -220,7 +220,12 @@ export class IVProgProcessor {
   }
 
   executeFunctionCall (store, cmd) {
-    const func = this.findFunction(cmd.id);
+    let func = null;
+    if(cmd.isMainCall) {
+      func = this.findMainFunction();
+    } else {
+      func = this.findFunction(cmd.id);
+    }
     return this.runFunction(func, cmd.actualParameters, store)
       .then(sto => {
         if(!Types.VOID.isCompatible(func.returnType) && sto.mode !== Modes.RETURN) {
@@ -623,6 +628,9 @@ export class IVProgProcessor {
   }
 
   evaluateFunctionCall (store, exp) {
+    if(exp.isMainCall) {
+      return Promise.reject(new Error(`Main function cannot be used inside an expression`));
+    }
     const func = this.findFunction(exp.id);
     if(Types.VOID.isCompatible(func.returnType)) {
       // TODO: better error message

+ 14 - 2
js/processor/semantic/semanticAnalyser.js

@@ -49,6 +49,10 @@ export class SemanticAnalyser {
     }
   }
 
+  getMainFunction () {
+    return this.ast.functions.find(v => v.isMain);
+  }
+
   findFunction (name) {
     if(name.match(/^\$.+$/)) {
       const fun = LanguageDefinedFunction.getFunction(name);
@@ -142,9 +146,12 @@ export class SemanticAnalyser {
     } else if (expression instanceof Literal) {
       return this.evaluateLiteralType(expression);
     } else if (expression instanceof FunctionCall) {
+      if (expression.isMainCall) {
+        throw new Error("void return used in expression");
+      }
       const fun = this.findFunction(expression.id);
       if (fun.returnType.isCompatible(Types.VOID)) {
-        throw new Error("void return");
+        throw new Error("void return used in expression");
       }
       this.assertParameters(fun, expression.actualParameters);
       return fun.returnType;
@@ -384,7 +391,12 @@ export class SemanticAnalyser {
       }
 
     } else if (cmd instanceof FunctionCall) {
-      const fun = this.findFunction(cmd.id);
+      let fun = null;
+      if (cmd.isMainCall) {
+        fun = this.getMainFunction();
+      } else {
+        fun = this.findFunction(cmd.id);
+      }
       this.assertParameters(fun, cmd.actualParameters);
       return optional;
     } else if (cmd instanceof Return) {

+ 2 - 0
tests/test03.spec.js

@@ -13,7 +13,9 @@ describe('Expressions which ends with ID terminals', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test05.spec.js

@@ -19,7 +19,9 @@ describe('IfThenElse command', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test06.spec.js

@@ -15,7 +15,9 @@ describe('While command', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test07.spec.js

@@ -16,7 +16,9 @@ describe('For command', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test08.spec.js

@@ -18,7 +18,9 @@ describe('Break command inside a loop', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test10.spec.js

@@ -20,7 +20,9 @@ describe('IfThenElseIfThenElse command chain', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test11.spec.js

@@ -16,7 +16,9 @@ describe('DoWhile command', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 2 - 0
tests/test12.spec.js

@@ -17,7 +17,9 @@ describe('SwitchCase command', () => {
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
+        as.pushVariableStack();
         const fun = as.parseFunction.bind(as);
         expect(fun).not.toThrow();
+        as.popVariableStack();
     });
 });

+ 33 - 0
tests/testCallMainFunction.spec.js

@@ -0,0 +1,33 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { SemanticAnalyser } from "./../js/processor/semantic/semanticAnalyser";
+import { LanguageService } from '../js/services/languageService';
+
+describe('A call to the main function', function () {
+
+  let input = `programa {
+      inteiro a = 0
+      funcao inicio () {
+        se (a > 5) {
+          retorne
+        } senao {
+          a = a + 1
+          inicio()
+        }
+      }
+    }`;
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should produce a valid state`, function (done) {
+    const parser = new IVProgParser(input, lexer);
+    const semantic = new SemanticAnalyser(parser.parseTree());
+    const exec = new IVProgProcessor(semantic.analyseTree());
+    exec.interpretAST().then(sto => {
+      expect(sto).toBeTruthy();
+      done();
+    }).catch(err => {
+      done(err);
+    });
+  });
+});

+ 25 - 0
tests/testDuplicateFunctions.spec.js

@@ -0,0 +1,25 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
+
+describe('A code with a duplicate function', function () {
+
+  let input = `programa {
+
+    funcao inicio() {
+      escreva(Matematica.tan(90))
+    }
+
+    funcao vazio inicio() {
+
+    }
+  }`;
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should throw a syntax exception`, function () {
+    const parser = new IVProgParser(input, lexer);
+    const func = parser.parseTree;
+    func.bind(parser);
+    expect(func).toThrow();
+  });
+});

+ 22 - 0
tests/testDuplicateVariable.spec.js

@@ -0,0 +1,22 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
+
+describe('A code with a duplicate variable declarations', function () {
+
+  let input = `programa {
+
+    funcao inicio() {
+      real a = 90.0, a = 0.0
+      escreva(Matematica.tan(a))
+    }
+  }`;
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should throw a syntax exception`, function () {
+    const parser = new IVProgParser(input, lexer);
+    const func = parser.parseTree;
+    func.bind(parser);
+    expect(func).toThrow();
+  });
+});