ソースを参照

Implement Declarations and Parameters type check (partially)

-Refactor commands & expressions to include line/column information
-Still missing function body semantic check
Lucas de Souza 5 年 前
コミット
685ca77673

+ 1 - 0
i18n/pt/error.json

@@ -10,6 +10,7 @@
   "invalid_var_declaration": "Erro na linha $0. Variáveis só podem ser declarados no corpo principal da função e de preferência nas primeiras linhas.",
   "invalid_break_command": "Erro na linha $0. O comando $1 não pode ser usado fora de uma estrutura de repetição ou 'escolha...caso'",
   "invalid_terminal": "Não é possível utilizar $0 na expressão da linha: $1, coluna: $2. Tente um valor númerico, variável ou chamada de função.",
+  "const_not_init": "Erro na linha: $0, coluna: $1. Uma variável declarada como const deve ser inicializada",
   "id_missing": "Esperava-se um identificador, mas encontrou-se $0 na linha: $1, coluna: $2",
   "main_missing": "A função principal não foi encontrada",
   "invalid_global_var": "Erro crítico: Chamada inválida da função initGlobal fora do contexto BASE",

+ 4 - 1
js/ast/commands/assign.js

@@ -1,6 +1,9 @@
-export class Assign {
+import { Command } from './command';
+
+export class Assign extends Command {
   
   constructor (id, expression) {
+    super();
     this.id = id;
     this.expression = expression;
   }

+ 8 - 1
js/ast/commands/break.js

@@ -1 +1,8 @@
-export class Break { }
+import { Command } from './command';
+
+export class Break extends Command {
+
+  constructor () {
+    super();
+  }
+}

+ 4 - 1
js/ast/commands/case.js

@@ -1,6 +1,9 @@
-export class Case {
+import { Command } from './command';
+
+export class Case extends Command {
 
   constructor (expression) {
+    super();
     this.expression = expression;
     this.commands = [];
   }

+ 14 - 0
js/ast/commands/command.js

@@ -0,0 +1,14 @@
+export class Command {
+
+  constructor () {
+    this._sourceInfo = null;
+  }
+
+  set sourceInfo (sourceInfo) {
+    this._sourceInfo = sourceInfo;
+  }
+
+  get sourceInfo () {
+    return this._sourceInfo;
+  }
+}

+ 9 - 0
js/ast/commands/commandBlock.js

@@ -3,5 +3,14 @@ export class CommandBlock {
 	constructor(variables, commands) {
 		this.variables = variables;
 		this.commands = commands;
+		this._sourceInfo = null;
+	}
+
+	set sourceInfo (sourceInfo) {
+		this._sourceInfo = sourceInfo;
+	}
+
+	get sourceInfo () {
+		return this._sourceInfo;
 	}
 }

+ 5 - 2
js/ast/commands/declaration.js

@@ -1,9 +1,12 @@
-export class Declaration {
+import { Command } from './command';
+
+export class Declaration extends Command {
   
   constructor (id, type, initial, isConst) {
+    super();
     this.id = id;
     this.type = type;
     this.initial = initial;
     this.isConst = isConst;
   }
-}
+}

+ 5 - 1
js/ast/commands/for.js

@@ -1,5 +1,9 @@
-export class For {
+import { Command } from './command';
+
+export class For extends Command {
+
   constructor (assignment, condition, increment, commandBlock) {
+    super();
     this.assignment = assignment;
     this.condition = condition;
     this.increment = increment;

+ 9 - 0
js/ast/commands/formalParameter.js

@@ -5,5 +5,14 @@ export class FormalParameter {
     this.id = id;
     this.dimensions = dimensions;
     this.byRef = byRef;
+    this._sourceInfo = null;
   }
+
+  set sourceInfo (sourceInfo) {
+		this._sourceInfo = sourceInfo;
+	}
+
+	get sourceInfo () {
+		return this._sourceInfo;
+	}
 }

+ 9 - 0
js/ast/commands/function.js

@@ -7,6 +7,7 @@ export class Function {
     this.returnType = returnType;
     this.formalParameters = formalParameters;
     this.commandBlock = commandBlock;
+    this._sourceInfo = null;
   }
 
   get isMain () {
@@ -20,4 +21,12 @@ export class Function {
   get variablesDeclarations () {
     return this.commandBlock.variables;
   }
+
+  set sourceInfo (sourceInfo) {
+		this._sourceInfo = sourceInfo;
+	}
+
+	get sourceInfo () {
+		return this._sourceInfo;
+	}
 }

+ 4 - 1
js/ast/commands/ifThenElse.js

@@ -1,6 +1,9 @@
-export class IfThenElse {
+import { Command } from './command';
+
+export class IfThenElse extends Command {
 
   constructor (condition, ifTrue, ifFalse) {
+    super();
     this.condition = condition;
     this.ifTrue = ifTrue;
     this.ifFalse = ifFalse;

+ 5 - 1
js/ast/commands/return.js

@@ -1,5 +1,9 @@
-export class Return {
+import { Command } from './command';
+
+export class Return extends Command {
+
   constructor(expression) {
+    super();
     this.expression = expression;
   }
   

+ 4 - 1
js/ast/commands/switch.js

@@ -1,6 +1,9 @@
-export class Switch {
+import { Command } from './command';
+
+export class Switch extends Command {
   
   constructor (expression, cases) {
+    super();
     this.expression = expression;
     this.cases = cases;
   }

+ 4 - 1
js/ast/commands/while.js

@@ -1,6 +1,9 @@
-export class While {
+import { Command } from './command';
+
+export class While extends Command {
 
   constructor (expression, commandBlock) {
+    super();
     this.expression = expression;
     this.commandBlock = commandBlock;
   }

+ 4 - 0
js/ast/error/syntaxErrorFactory.js

@@ -47,5 +47,9 @@ export const SyntaxErrorFactory = Object.freeze({
     const line = list.join(LocalizedStrings.getOR());
     const context = [token.text, token.line, token.column, line]
     return new SyntaxError(LocalizedStrings.getError("invalid_type", context));
+  },
+  const_not_init: (token) => {
+    const context = [token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("const_not_init", context));
   }
 });

+ 4 - 1
js/ast/expressions/arrayAccess.js

@@ -1,6 +1,9 @@
-export class ArrayAccess {
+import { Expression } from './expression';
+
+export class ArrayAccess extends Expression {
 	
 	constructor (id, line, column) {
+		super();
 		this.id = id;
 		this.line = line;
 		this.column = column;

+ 1 - 0
js/ast/expressions/boolLiteral.js

@@ -1,5 +1,6 @@
 import { Literal } from './literal';
 import {Types} from './../types';
+
 export class BoolLiteral extends Literal {
   
   constructor(value) {

+ 14 - 0
js/ast/expressions/expression.js

@@ -0,0 +1,14 @@
+export class Expression {
+
+  constructor () {
+    this._sourceInfo = null;
+  }
+
+  set sourceInfo (sourceInfo) {
+		this._sourceInfo = sourceInfo;
+	}
+
+	get sourceInfo () {
+		return this._sourceInfo;
+	}
+}

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

@@ -1,6 +1,9 @@
-export class FunctionCall {
+import { Expression } from './expression';
+
+export class FunctionCall extends Expression {
 
 	constructor (id, actualParameters) {
+		super();
 		this.id = id;
 		this.actualParameters = actualParameters;
 	}

+ 4 - 1
js/ast/expressions/infixApp.js

@@ -1,6 +1,9 @@
-export class InfixApp {
+import { Expression } from './expression';
+
+export class InfixApp extends Expression {
 
   constructor(op, left, right) {
+    super();
     this.op = op;
     this.left = left;
     this.right = right;

+ 1 - 0
js/ast/expressions/intLiteral.js

@@ -1,5 +1,6 @@
 import { Literal } from './literal';
 import {Types} from './../types';
+
 export class IntLiteral extends Literal {
   
   constructor(value) {

+ 4 - 1
js/ast/expressions/literal.js

@@ -1,6 +1,9 @@
-export class Literal {
+import { Expression } from './expression';
+
+export class Literal extends Expression {
   
   constructor (type) {
+    super();
     this.type = type;
   }
 }

+ 1 - 0
js/ast/expressions/realLiteral.js

@@ -1,5 +1,6 @@
 import { Literal } from './literal';
 import {Types} from './../types';
+
 export class RealLiteral extends Literal {
   
   constructor(value) {

+ 1 - 0
js/ast/expressions/stringLiteral.js

@@ -1,5 +1,6 @@
 import { Literal } from './literal';
 import {Types} from './../types';
+
 export class StringLiteral extends Literal {
   
   constructor(value) {

+ 79 - 17
js/ast/ivprogParser.js

@@ -1,6 +1,7 @@
 import { CommonTokenStream, InputStream } from 'antlr4/index';
 import * as Expressions from './expressions/';
 import * as Commands from './commands/';
+import { SourceInfo } from './sourceInfo';
 import { Types, toInt, toString } from './types';
 import { convertFromString } from './operators';
 import { SyntaxErrorFactory } from './error/syntaxErrorFactory';
@@ -204,7 +205,6 @@ export class IVProgParser {
 
   parseGlobalVariables () {
     const decl = this.parseMaybeConst();
-    const eosToken = this.getToken();
     this.checkEOS();
     this.pos++;
     return decl;
@@ -239,7 +239,9 @@ export class IVProgParser {
     let initial = null;
     let dim1 = null;
     let dim2 = null;
+    const idToken = this.getToken();
     const idString = this.parseID();
+    
     // Check for array or vector
     // ID[int/IDi][int/IDj]
     if (this.checkOpenBrace(true)) {
@@ -260,6 +262,9 @@ export class IVProgParser {
     }
 
     const equalsToken = this.getToken();
+    if(isConst && equalsToken.type !== this.lexerClass.EQUAL ) {
+      throw SyntaxErrorFactory.const_not_init(idToken);
+    }
     if(equalsToken.type === this.lexerClass.EQUAL) {
       this.pos++;
       initial = this.parseExpressionOR();
@@ -271,6 +276,7 @@ export class IVProgParser {
     } else {
       declaration = new Commands.Declaration(idString, typeString, initial, isConst);
     }
+    declaration.sourceInfo = SourceInfo.createSourceInfo(idToken);
     const commaToken = this.getToken();
     if(commaToken.type === this.lexerClass.COMMA) {
       this.pos++;
@@ -320,21 +326,33 @@ export class IVProgParser {
   **/
   getIntLiteral (token) {
     const text = token.text;
-    return new Expressions.IntLiteral(toInt(text));
+    const sourceInfo = SourceInfo.createSourceInfo(token);
+    const exp = new Expressions.IntLiteral(toInt(text));
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   getRealLiteral (token) {
-    return new Expressions.RealLiteral(parseFloat(token.text));
+    const sourceInfo = SourceInfo.createSourceInfo(token);
+    const exp = new Expressions.RealLiteral(parseFloat(token.text));
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   getStringLiteral (token) {
     const text = token.text;
-    return new Expressions.StringLiteral(toString(text));
+    const sourceInfo = SourceInfo.createSourceInfo(token);
+    const exp = new Expressions.StringLiteral(toString(text));
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   getBoolLiteral (token) {
     const val = token.type === this.lexerClass.RK_TRUE ? true : false;
-    return new Expressions.BoolLiteral(val);
+    const sourceInfo = SourceInfo.createSourceInfo(token);
+    const exp = new Expressions.BoolLiteral(val);
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   parseArrayLiteral () {
@@ -350,6 +368,7 @@ export class IVProgParser {
     const data = this.parseExpressionList();
     this.consumeNewLines();
     this.checkCloseCurly()
+    const endArray = this.getToken();
     this.pos++;
     this.parsingArrayDimension--;
     if (this.parsingArrayDimension === 0) {
@@ -359,7 +378,10 @@ export class IVProgParser {
       //   throw new Error(`Invalid array at line ${beginArray.line}`);
       // }
     }
-    return new Expressions.ArrayLiteral(data);
+    const sourceInfo = SourceInfo.createSourceInfoFromList(beginArray, endArray);
+    const exp = new Expressions.ArrayLiteral(data);
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   /*
@@ -367,7 +389,10 @@ export class IVProgParser {
   * @returns object with fields type and value
   **/
   parseVariable (token) {
-    return new Expressions.VariableLiteral(token.text);
+    const sourceInfo = SourceInfo.createSourceInfo(token);
+    const exp = new Expressions.VariableLiteral(token.text);
+    exp.sourceInfo = sourceInfo;
+    return exp;
   }
 
   /*
@@ -700,11 +725,14 @@ export class IVProgParser {
     const id = this.parseID();
     const equalOrParenthesis = this.getToken();
     if (equalOrParenthesis.type === this.lexerClass.EQUAL) {
+      const sourceInfo = SourceInfo.createSourceInfo(this.getToken());
       this.pos++
       const exp = this.parseExpressionOR();
       this.checkEOS();
       this.pos++;
-      return (new Commands.Assign(id, exp));
+      const cmd = new Commands.Assign(id, exp);
+      cmd.sourceInfo = sourceInfo;
+      return cmd;
     } else if (equalOrParenthesis.type === this.lexerClass.OPEN_PARENTHESIS) {
       const funcCall = this.parseFunctionCallCommand(id);
       this.checkEOS();
@@ -731,7 +759,10 @@ export class IVProgParser {
     if(!isLast) {
       this.consumeForSemiColon();
     }
-    return new Commands.Assign(id, exp);
+    const sourceInfo = SourceInfo.createSourceInfo(equal);
+    const cmd = new Commands.Assign(id, exp);
+    cmd.sourceInfo = sourceInfo;
+    return cmd;
   }
 
   parseCases () {
@@ -793,11 +824,13 @@ export class IVProgParser {
   parseExpressionOR () {
     let exp1 = this.parseExpressionAND();
     while (this.getToken().type === this.lexerClass.OR_OPERATOR) {
+      const opToken = this.getToken();
       this.pos++;
       const or = convertFromString('or');
       this.consumeNewLines();
       const exp2 = this.parseExpressionAND();
       const finalExp = new Expressions.InfixApp(or, exp1, exp2);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
       exp1 = finalExp
     }
     return exp1;
@@ -806,11 +839,13 @@ export class IVProgParser {
   parseExpressionAND () {
     let exp1 = this.parseExpressionNot();
     while (this.getToken().type === this.lexerClass.AND_OPERATOR) {
+      const opToken = this.getToken();
       this.pos++;
       const and = convertFromString('and');
       this.consumeNewLines();
       const exp2 = this.parseExpressionNot();
       const finalExp = new Expressions.InfixApp(and, exp1, exp2);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
       exp1 = finalExp;
     }
     return exp1;
@@ -819,10 +854,14 @@ export class IVProgParser {
   parseExpressionNot () {
     const maybeNotToken = this.getToken();
     if (maybeNotToken.type === this.lexerClass.NOT_OPERATOR) {
+      const opToken = this.getToken();
       this.pos++;
       const not = convertFromString('not');
       const exp1 = this.parseExpressionRel();
-      return new Expressions.UnaryApp(not, exp1);
+      finalExp = new Expressions.UnaryApp(not, exp1);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(opToken);
+      return finalExp;
+      
     } else {
       return this.parseExpressionRel();
     }
@@ -836,6 +875,7 @@ export class IVProgParser {
       const rel = convertFromString(relToken.text);
       const exp2 = this.parseExpression();
       const finalExp = new Expressions.InfixApp(rel, exp1, exp2);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(relToken);
       exp1 = finalExp;
     }
     return exp1;
@@ -849,6 +889,7 @@ export class IVProgParser {
       const op = convertFromString(sumOpToken.text);
       const factor2 = this.parseFactor();
       const finalExp = new Expressions.InfixApp(op, factor, factor2);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(sumOpToken);
       factor = finalExp;
     }
     return factor;
@@ -862,6 +903,7 @@ export class IVProgParser {
       const op = convertFromString(multOpToken.text);
       const term2 =this.parseTerm();
       const finalExp = new Expressions.InfixApp(op, term, term2);
+      finalExp.sourceInfo = SourceInfo.createSourceInfo(multOpToken);
       term = finalExp;
     }
     return term;
@@ -869,10 +911,14 @@ export class IVProgParser {
 
   parseTerm () {
     const token = this.getToken();
+    let sourceInfo = null;
     switch(token.type) {
       case this.lexerClass.SUM_OP:
         this.pos++;
-        return new Expressions.UnaryApp(convertFromString(token.text), this.parseTerm());
+        sourceInfo = SourceInfo.createSourceInfo(token);
+        const exp = new Expressions.UnaryApp(convertFromString(token.text), this.parseTerm());
+        exp.sourceInfo = sourceInfo;
+        return exp;
       case this.lexerClass.INTEGER:
         this.pos++;
         return this.getIntLiteral(token);
@@ -899,29 +945,36 @@ export class IVProgParser {
 
   parseIDTerm () {
     const id = this.parseID();
-    const last = this.pos;
+    const tokenA = this.getToken(this.pos - 1);
     if(this.checkOpenBrace(true)) {
+      let tokenB = null;
       this.pos++;
       const firstIndex = this.parseExpression();
       let secondIndex = null;
       this.consumeNewLines();
       this.checkCloseBrace();
+      tokenB = this.getToken();
       this.pos++;
       if(this.checkOpenBrace(true)){
         this.pos++;
         secondIndex = this.parseExpression();
         this.consumeNewLines();
         this.checkCloseBrace();
+        tokenB = this.getToken();
         this.pos++;
       }
-
-      return new Expressions.ArrayAccess(id, firstIndex, secondIndex);
+      const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB); 
+      const exp = new Expressions.ArrayAccess(id, firstIndex, secondIndex);
+      exp.sourceInfo = sourceInfo;
+      return exp;
 
     } else if (this.checkOpenParenthesis(true)) {
       return this.parseFunctionCallExpression(id);
     } else {
-      this.pos = last;
-      return new Expressions.VariableLiteral(id);
+      const sourceInfo = SourceInfo.createSourceInfo(tokenA);
+      const exp = new Expressions.VariableLiteral(id);
+      exp.sourceInfo = sourceInfo;
+      return exp;
     }
   }
 
@@ -936,9 +989,14 @@ export class IVProgParser {
   }
 
   parseFunctionCallExpression (id) {
+    const tokenA = this.getToken(this.pos - 1);
     const actualParameters = this.parseActualParameters();
+    const tokenB = this.getToken(this.pos - 1);
     const funcName = this.getFunctionName(id);
-    return new Expressions.FunctionCall(funcName, actualParameters);
+    const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
+    const cmd = new Expressions.FunctionCall(funcName, actualParameters);
+    cmd.sourceInfo = sourceInfo;
+    return cmd;
   }
 
   parseFunctionCallCommand (id) {
@@ -947,12 +1005,16 @@ export class IVProgParser {
 
   parseParenthesisExp () {
     this.checkOpenParenthesis();
+    const tokenA = this.getToken();
     this.pos++;
     this.consumeNewLines();
     const exp = this.parseExpressionOR();
     this.consumeNewLines();
     this.checkCloseParenthesis();
+    const tokenB = this.getToken();
+    const sourceInfo = SourceInfo.createSourceInfoFromList(tokenA, tokenB);
     this.pos++;
+    exp.sourceInfo = sourceInfo;
     return exp;
   }
 

+ 22 - 0
js/ast/sourceInfo.js

@@ -0,0 +1,22 @@
+export class SourceInfo {
+
+  static createSourceInfo (token) {
+    return new SourceInfo(token.line, token.column, token.text.length);
+  }
+
+  static createSourceInfoFromList (tokenA, tokenB) {
+    const line = tokenA.line;
+    const column = tokenA.column;
+    // copied from https://github.com/UNIVALI-LITE/Portugol-Studio/blob/master/core/src/main/java/br/univali/portugol/nucleo/analise/sintatica/Portugol.g
+    // No idea why...
+    const size = tokenB.tokenIndex + 1 - tokenA.tokenIndex
+    return new SourceInfo(line, column, size);
+  }
+
+  constructor (line, column, size) {
+    this.line = line;
+    this.column = column;
+    this.size = size;
+  }
+
+}

+ 250 - 0
js/processor/semantic/semanticAnalyser.js

@@ -0,0 +1,250 @@
+import { ProcessorErrorFactory } from './../error/processorErrorFactory';
+import { LanguageDefinedFunction } from './../definedFunctions';
+import { LanguageService } from './../../services/languageService';
+import { ArrayDeclaration } from '../../ast/commands';
+import { InfixApp, UnaryApp, FunctionCall, IntLiteral, RealLiteral, StringLiteral, BoolLiteral, VariableLiteral, ArrayLiteral } from '../../ast/expressions';
+import { Literal } from '../../ast/expressions/literal';
+import { resultTypeAfterInfixOp, resultTypeAfterUnaryOp } from '../compatibilityTable';
+import { Types } from '../../ast/types';
+
+export class SemanticAnalyser {
+
+  constructor(ast) {
+    this.ast = ast;
+    this.lexerClass = LanguageService.getCurrentLexer();
+    const lexer = new this.lexerClass(null);
+    this.literalNames = lexer.literalNames;
+    this.symbolMap = null;
+  }
+
+  pushMap () {
+    if(this.symbolMap === null) {
+      this.symbolMap = {map:{}, next: null};
+    } else {
+      const n = {map:{}, next: this.symbolMap};
+      this.symbolMap = n;
+    }
+  }
+
+  popMap () {
+    if(this.symbolMap !== null) {
+      this.symbolMap = this.symbolMap.next;
+    }
+  }
+
+  insertSymbol (id, typeInfo) {
+    this.symbolMap.map[id] = typeInfo;
+  }
+
+  findSymbol (id, symMap) {
+    if(!symMap.map[id]) {
+      if(symMap.next) {
+        return this.findSymbol(id, symMap.next);
+      }
+      throw new Error("variable not defined");
+    } else {
+      return symMap.map[id];
+    }
+  }
+
+  findFunction (name) {
+    if(name.match(/^\$.+$/)) {
+      const fun = LanguageDefinedFunction[name];
+      if(!!!fun) {
+        throw new Error("!!!Internal Error. Language defined function not implemented -> " + name + "!!!");
+      }
+      return fun;
+    } else {
+      const val = this.ast.functions.find( v => v.name === name);
+      if (!!!val) {
+        // TODO: better error message;
+        throw new Error(`Function ${name} is not defined.`);
+      }
+      return val;
+    }
+  }
+
+  analyseTree () {
+    const globalVars = this.ast.global;
+    this.pushMap();
+    this.assertDeclarations(globalVars);
+    const functions = this.ast.functions;
+    for (let i = 0; i < functions.length; i++) {
+      const fun = functions[i];
+      this.assertFunction(fun);
+    }
+    return this.ast;
+  }
+
+  assertDeclarations (list) {
+    for (let i = 0; i < list.length; i++) {
+      this.assertDeclaration(list[i]);
+    }
+  }
+
+  assertDeclaration (declaration) {
+    if (declaration instanceof ArrayDeclaration) {
+      if(declaration.initial === null) {
+        this.insertSymbol(declaration.id, {id: declaration.id, lines: declaration.lines, columns: declaration.columns, type: declaration.type, subtype: declaration.subtype});
+        return;
+      }
+      this.evaluateArrayLiteral(declaration.lines, declaration.columns, declaration.subtype, declaration.initial);
+      this.insertSymbol(declaration.id, {id: declaration.id, lines: declaration.lines, columns: declaration.columns, type: declaration.type, subtype: declaration.subtype});
+
+    } else {
+      if(declaration.initial === null) {
+        this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type});
+        return;
+      }
+      const resultType = this.evaluateExpressionType(declaration.initial);
+      if(declaration.type !== resultType) {
+        throw new Error('Invalid type');
+      } else {
+        this.insertSymbol(declaration.id, {id: declaration.id, type: declaration.type})
+      }
+    }
+  }
+
+  evaluateExpressionType (expression) {
+    if(expression instanceof UnaryApp) {
+      const op = expression.op;
+      const resultType = this.evaluateExpressionType(expression.left);
+      return resultTypeAfterUnaryOp(op, resultType);
+    } else if (expression instanceof InfixApp) {
+      const op = expression.op;
+      const resultTypeLeft = this.evaluateExpressionType(expression.left);
+      const resultTypeRight = this.evaluateExpressionType(expression.right);
+      return resultTypeAfterInfixOp(op, resultTypeLeft, resultTypeRight);
+    } else if (expression instanceof Literal) {
+      return this.evaluateLiteralType(expression);
+    } else if (expression instanceof FunctionCall) {
+      const fun = this.findFunction(expression.id);
+      if (fun.returnType === Types.VOID) {
+        throw new Error("void return");
+      }
+      this.assertParameters(fun, expression.actualParameters);
+      return fun.returnType;
+    }
+  }
+
+  evaluateLiteralType (literal) {
+    if(literal instanceof IntLiteral) {
+      return literal.type;
+    } else if (literal instanceof RealLiteral) {
+      return literal.type;
+    } else if (literal instanceof StringLiteral) {
+      return literal.type;
+    } else if (literal instanceof BoolLiteral) {
+      return literal.type;
+    } else if (literal instanceof VariableLiteral) {
+      const typeInfo = this.findSymbol(literal.id, this.symbolMap);
+      if (typeInfo.type === Types.ARRAY) {
+        return typeInfo;
+      }
+      return typeInfo.type;
+    } else {
+      console.warn("Evaluating type only for an array literal...");
+      return Types.UNDEFINED;
+    }
+  }
+
+  evaluateArrayLiteral (lines, columns, subtype, literal) {
+    if (literal instanceof ArrayLiteral) {
+      if (columns === null) {
+        // it's a vector...
+        if (lines !== literal.value.length) {
+          throw new Error("invalid array size");
+        }
+        literal.value.reduce((last, next) => {
+          const eType = this.evaluateExpressionType(next);
+          if (subtype !== eType || eType !== last) {
+            throw new Error("invalid array type");
+          }
+          return eType;
+        });
+        return true;
+      } else {
+        if (columns !== literal.value.length) {
+          throw new Error("invalid array size");
+        }
+        for (let i = 0; i < columns; i++) {
+          const anotherArray = literal.value[i];
+          this.evaluateArrayLiteral(lines, null, subtype, anotherArray)
+        }
+      }
+
+    } else {
+
+      const resultType = this.evaluateExpressionType(literal);
+      if (!resultType.subtype) {
+        throw new Error("initial must be of type array");
+      }
+      if (resultType.subtype !== subtype) {
+        throw new Error("invalid array type");
+      } else if (resultType.lines !== lines) {
+        throw new Error("invalid array size");
+      } else if (resultType.columns !== columns) {
+        throw new Error("invalid array size");
+      }
+      return true;
+
+    }
+  }
+
+  assertFunction (fun) {
+    this.pushMap();
+    this.assertDeclarations(fun.variablesDeclarations);
+    if(fun.returnType === Types.VOID) {
+      this.assertOptionalReturn(fun);
+    } else {
+      this.assertReturn(fun);
+    }
+    this.popMap();
+  }
+
+  assertOptionalReturn (fun) {
+
+  }
+
+  assertParameters (fun, actualParametersList) {
+    if (fun.formalParameters.length !== actualParametersList.length) {
+      throw new Error("wrong number of parameters...");
+    }
+    for (let i = 0; i < actualParametersList.length; i++) {
+      const param = actualParametersList[i];
+      const formalParam = fun.formalParameters[i];
+      if(formalParam.byRef && !(param instanceof VariableLiteral)) {
+        throw new Error("Invalid param type");
+      }
+      const resultType = this.evaluateExpressionType(param);
+      switch (formalParam.dimensions) {
+        case 1: {
+          if (!resultType.subtype) {
+            throw new Error("invalid param type");   
+          } else if (resultType.subtype !== formalParam.type) {
+            throw new Error("invalid param type");   
+          } else if (resultType.lines === null || resultType.columns !== null) {
+            throw new Error("invalid param type");
+          }
+          break;
+        }
+        case 2: {
+          if (!resultType.subtype) {
+            throw new Error("invalid param type");   
+          } else if (resultType.subtype !== formalParam.type) {
+            throw new Error("invalid param type");   
+          } else if (resultType.lines === null || resultType.columns === null) {
+            throw new Error("invalid param type");
+          }
+          break;
+        }
+        default: {
+          if (resultType.subtype || resultType !== formalParam.type) {
+            throw new Error("invalid param type");
+          }
+          break;
+        }
+      }
+    }
+  }
+}

+ 0 - 0
tests/test44.spec.js


+ 6 - 1
webpack.config.js

@@ -3,7 +3,7 @@ var webpack = require('webpack');
 module.exports = {
     entry: './js/main.js',
     mode: 'development',
-    watch: true,
+    watch: false,
     output: {
         path: path.resolve(__dirname, 'build'),
         filename: 'ivprog.bundle.js',
@@ -27,5 +27,10 @@ module.exports = {
     stats: {
         colors: true
     },
+    /*optimization: {
+        splitChunks: {
+            chunks: 'all'
+        }
+    },*/
     devtool: 'source-map'
 };