Browse Source

Implement write as a function with polymorphiv variadic parameter

Lucas de Souza 3 years ago
parent
commit
dc3f2580be

+ 2 - 1
js/ast/commands/formalParameter.js

@@ -1,9 +1,10 @@
 export class FormalParameter {
 
-  constructor (type, id, byRef = false) {
+  constructor (type, id, byRef = false, variadic = false) {
     this.type = type;
     this.id = id;
     this.byRef = byRef;
+    this.variadic = variadic;
     this._sourceInfo = null;
   }
 

+ 12 - 1
js/ast/commands/function.js

@@ -2,6 +2,13 @@ import { Types } from './../../typeSystem/types';
 
 export class Function {
 
+  /**
+   * 
+   * @param {string} name 
+   * @param {import('./../../typeSystem/itype').IType} returnType 
+   * @param {import('./formalParameter').FormalParameter[]} formalParameters 
+   * @param {import('./commandBlock').CommandBlock} commandBlock 
+   */
   constructor(name, returnType, formalParameters, commandBlock) {
     this.name = name;
     this.returnType = returnType;
@@ -28,5 +35,9 @@ export class Function {
 
 	get sourceInfo () {
 		return this._sourceInfo;
-	}
+  }
+  
+  hasVariadic () {
+    return this.formalParameters.some( p => p.variadic);
+  }
 }

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

@@ -3,6 +3,11 @@ import { LanguageDefinedFunction } from '../../processor/definedFunctions';
 
 export class FunctionCall extends Expression {
 
+	/**
+	 * 
+	 * @param {string} id 
+	 * @param {Expression[]} actualParameters 
+	 */
 	constructor (id, actualParameters) {
 		super();
 		this.id = id;

+ 61 - 10
js/io/domConsole.js

@@ -94,6 +94,7 @@ export class DOMConsole {
       this._appendUserInput(text);
       this.input.value = '';
       this.inputSpan.innerHTML = '';
+      this.currentLine = null;
     }
   }
 
@@ -169,19 +170,43 @@ export class DOMConsole {
     this.anyKey = false;
   }
 
-  write (text) {
-    this._appendText(text, DOMConsole.USER);
+  write (text, newLine = false) {
+    this._appendText(text, DOMConsole.USER, newLine);
   }
 
   info (text) {
-    this._appendText(text, DOMConsole.INFO, false);
+    this._appendTextLn(text, DOMConsole.INFO);
   }
 
   err (text) {
-    this._appendText(text, DOMConsole.ERR, false);
+    this._appendTextLn(text, DOMConsole.ERR);
   }
 
-  async _appendText (text, type, filter = true) {
+  async _appendText (text, type, newLine = false) {
+    const write_time = Date.now();
+    this.pending_writes.push(0);
+    await Utils.sleep(5);
+    this.pending_writes.pop();
+    if (this.last_clear >= write_time) {
+      return;
+    }
+    
+    if (this.currentLine == null) {
+      const divClass = this.getClassForType(type);
+      const textDiv = document.createElement('div');
+      textDiv.classList.add(divClass);
+      this.termDiv.insertBefore(textDiv, this.inputDiv);
+      this.currentLine = textDiv
+    }
+    this.currentLine.innerHTML += this.getOutputText(text);
+    if (newLine) {
+      console.debug("append newline");
+      this.currentLine = null;
+    }
+    this.scrollTerm();
+  }
+
+  async _appendTextLn (text, type, filter = true) {
     const write_time = Date.now();
     this.pending_writes.push(0);
     await Utils.sleep(5);
@@ -197,6 +222,7 @@ export class DOMConsole {
     else
       textDiv.innerHTML = `<span>${text}</span>`;
     this.termDiv.insertBefore(textDiv, this.inputDiv);
+    this.currentLine = null;
     this.scrollTerm();
   }
 
@@ -213,6 +239,7 @@ export class DOMConsole {
     textDiv.innerHTML = this.getUserInputText(text);
     textDiv.classList.add(divClass);
     this.termDiv.insertBefore(textDiv, this.inputDiv);
+    this.currentLine = null;
     this.scrollTerm();
   }
 
@@ -291,6 +318,7 @@ export class DOMConsole {
     this.clearBtn = null;
     this.hideBtn = null;
     this.showBtn = null;
+    this.currentLine = null;
     const cNode = this.parent.cloneNode(false);
     this.parent.parentNode.replaceChild(cNode, this.parent);
     if (this.cursorInterval != null) {
@@ -328,12 +356,33 @@ export class DOMConsole {
   }
 
   sendOutput (text) {
-    const output = ""+text;
-    output.split("\n").forEach(t => {
+    console.debug(text);
+    let output = ""+text;
+    if (output.indexOf('\n') !== -1) {
+      console.debug("newline");
+      const outputList = output.split('\n');
+      let i = 0;
+      for ( ; i < outputList.length - 1; i += 1) {
+        console.debug("newline write");
+        let t = outputList[i];
+        t = t.replace(/\t/g,'&#x0020;&#x0020;');
+        t = t.replace(/\s/g,"&#x0020;");
+        if (t.length == 0)
+          t = "&nbsp;"
+        this.write(t, true);
+      }
+      let t = outputList[i];
       t = t.replace(/\t/g,'&#x0020;&#x0020;');
       t = t.replace(/\s/g,"&#x0020;");
-      this.write(t)
-    });
+      if (t.length != 0)
+        this.write(t);
+    } else {
+      console.debug("no newline");
+      output = output.replace(/\t/g,'&#x0020;&#x0020;');
+      output = output.replace(/\s/g,"&#x0020;");
+      this.write(output);
+    }
+    
   }
 
   clearPendingWrites () {
@@ -345,8 +394,9 @@ export class DOMConsole {
     while (this.inputDiv.parentElement.childNodes.length > 1) {
       this.inputDiv.parentElement.removeChild(this.inputDiv.parentElement.firstChild);
     }
-    this.input.value = "";
+    this.input.value = '';
     this.inputSpan.innerHTML = '';
+    this.currentLine = null;
   }
 
   clearBtnClick () {
@@ -379,6 +429,7 @@ export class DOMConsole {
     }
     this.input.value = '';
     this.inputSpan.innerHTML = '';
+    this.currentLine = null;
     this.hideInput();
     this.anyKey = false;
   }

+ 33 - 6
js/io/domOutput.js

@@ -9,18 +9,45 @@ export class DOMOutput extends Output {
       id = selector.substring(1);
     }
     this.el = document.getElementById(id);
+    this.currentLine = null;
+  }
+
+  write (text, newLine = false) {
+    if (this.currentLine == null) {
+      const span = document.createElement('span');
+      span.classList.add('ivprog-io-output-text');
+      this.el.append(span);
+      this.currentLine = span;
+    }
+    this.currentLine.innerHTML += text;
+    if (newLine) {
+      this.currentLine = null;
+    }
   }
 
   sendOutput (text) {
-    text = text.replace("\n", '</br>');
-    text = text.replace(/\t/g,'&#9;');
-    const span = document.createElement('span');
-    span.classList.add('ivprog-io-output-text');
-    span.innerHTML = text;
-    this.el.append(span);
+    let output = '' + text;
+    if (output.indexOf('\n') !== -1) {
+      const outputList = output.split('\n');
+      let last = outputList.pop();
+      outputList.forEach( t => {
+        t = t.replace(/\t/g,'&#x0020;&#x0020;');
+        t = t.replace(/\s/g,"&#x0020;");
+        if (t.length == 0)
+          t = "&nbsp;";
+        this.write(t, true);
+      });
+      last = last.replace(/\t/g, '&#x0020;&#x0020;');
+      if (last.length != 0)
+        this.write(last);
+    } else {
+      output = output.replace(/\t/g, '&#x0020;&#x0020;');
+      this.write(output);
+    }
   }
 
   clear () {
+    this.currentLine = null;
     while(this.el.childNodes.length > 0) {
       this.el.removeChild(this.el.firstChild);
     }

+ 132 - 67
js/processor/ivprogProcessor.js

@@ -152,6 +152,13 @@ export class IVProgProcessor {
     return finalSto;
   }
 
+  /**
+   * 
+   * @param {import('./../ast/commands/formalParameter').FormalParameter[]} formal_params 
+   * @param {Expression[]} effective_params 
+   * @param {Store} caller_store 
+   * @param {Store} callee_store 
+   */
   async associateParameters(
     formal_params,
     effective_params,
@@ -163,90 +170,148 @@ export class IVProgProcessor {
         ? LanguageDefinedFunction.getMainFunctionName()
         : callee_store.name;
 
-    if (formal_params.length != effective_params.length) {
+    const hasVariadic = formal_params.some(p => p.variadic);
+
+    if ((formal_params.length != effective_params.length && !hasVariadic)
+      || formal_params.length > effective_params.length) {
       throw ProcessorErrorFactory.invalid_parameters_size(
         funcName,
         formal_params.length,
         effective_params.length
       );
     }
-    for (let i = 0; i < effective_params.length; i += 1) {
-      const actualParam = effective_params[i];
-      const actualValue = await this.evaluateExpression(
-        caller_store,
-        actualParam
-      );
-      const exp = effective_params[i];
-      let shouldTypeCast = false;
+    for (let i = 0, j = 0; i < formal_params.length && j < effective_params.length; i += 1, j += 1) {
       const formalParameter = formal_params[i];
-      if (!formalParameter.type.isCompatible(actualValue.type)) {
-        if (
-          Config.enable_type_casting &&
-          !formalParameter.byRef &&
-          Store.canImplicitTypeCast(formalParameter.type, actualValue.type)
-        ) {
-          shouldTypeCast = true;
-        } else {
-          throw ProcessorErrorFactory.invalid_parameter_type(
-            funcName,
-            exp.toString()
-          );
-        }
+      if (formalParameter.variadic) {
+       [j, callee_store] = await this.associateVariadicParameter(funcName, formalParameter, j, effective_params, caller_store,
+          callee_store);
+      } else {
+        const actualParam = effective_params[i];
+        callee_store = await this.associateParameter(funcName, formalParameter, actualParam, caller_store,
+          callee_store);
       }
+      
+    }
+    return callee_store;
+  }
+
+  /**
+   * 
+   * @param {string} funcName 
+   * @param {import('./../ast/commands/formalParameter').FormalParameter} formalParameter 
+   * @param {number} index 
+   * @param {Expression[]} effective_params 
+   * @param {Store} caller_store 
+   * @param {Store} callee_store 
+   */
+  async associateVariadicParameter (funcName, formalParameter, index, effective_params, caller_store, callee_store) {
+    let i;
+    let count = 1
+    for (i = index; i < effective_params.length; i += 1) {
+      const actualParam = effective_params[i];
+      callee_store = await this.associateParameter(funcName, formalParameter, actualParam, caller_store,
+        callee_store, count);
+      count += 1;
+    }
+    const variadicCount = new StoreValue(Types.INTEGER, count, undefined, true);
+    callee_store.insertStore(`${formalParameter.id}.0`, variadicCount);
+    return [i - 1, callee_store];
+  }
+
+  /**
+   * 
+   * @param {string} funcName 
+   * @param {import('./../ast/commands/formalParameter').FormalParameter} formalParameter 
+   * @param {Expression} actualParameter 
+   * @param {Store} callerStore 
+   * @param {Store} calleeStore 
+   * @param {number} variadicCount The number of the current value being assigned to the variadic parameter, default 0
+   */
+  async associateParameter (funcName, formalParameter, actualParameter,
+    callerStore,
+    calleeStore,
+    variadicCount = 0
+  ) {
+    const actualValue = await this.evaluateExpression(
+      callerStore,
+      actualParameter
+    );
 
-      if (formalParameter.byRef && !actualValue.inStore()) {
-        throw ProcessorErrorFactory.invalid_ref(funcName, exp.toString());
+    let shouldTypeCast = false;
+    
+    if (!formalParameter.type.isCompatible(actualValue.type)) {
+      if (
+        Config.enable_type_casting &&
+        !formalParameter.byRef &&
+        Store.canImplicitTypeCast(formalParameter.type, actualValue.type)
+      ) {
+        shouldTypeCast = true;
+      } else {
+        throw ProcessorErrorFactory.invalid_parameter_type(
+          funcName,
+          actualParameter.toString()
+        );
       }
+    }
 
-      if (formalParameter.byRef) {
-        const realObj = caller_store.getStoreObject(actualValue.id);
-        let ref = null;
-        if (actualValue instanceof ArrayStoreValue) {
-          // it's a vector or matrix...
-          const values = actualValue.get();
-          const array_type = actualValue.type;
-          const addresses = values.map((v) =>
-            realObj.getLocAddressOf(v.line, v.column)
-          );
-          const columns = actualValue.isVector() ? 0 : actualValue.columns;
-          ref = new ArrayStoreValueRef(
-            array_type,
-            values,
-            addresses,
-            actualValue.lines,
-            columns,
+    if (formalParameter.byRef && !actualValue.inStore()) {
+      throw ProcessorErrorFactory.invalid_ref(funcName, actualParameter.toString());
+    }
+
+    if (formalParameter.byRef) {
+      const realObj = callerStore.getStoreObject(actualValue.id);
+      let ref = null;
+      if (actualValue instanceof ArrayStoreValue) {
+        // it's a vector or matrix...
+        const values = actualValue.get();
+        const array_type = actualValue.type;
+        const addresses = values.map((v) =>
+          realObj.getLocAddressOf(v.line, v.column)
+        );
+        const columns = actualValue.isVector() ? 0 : actualValue.columns;
+        ref = new ArrayStoreValueRef(
+          array_type,
+          values,
+          addresses,
+          actualValue.lines,
+          columns,
+          realObj.id
+        );
+      } else {
+        if (actualValue instanceof StoreValueAddress) {
+          const line = actualValue.line;
+          const column = actualValue.column;
+          ref = new StoreValueRef(
+            actualValue.type,
+            actualValue.get(),
+            realObj.getLocAddressOf(line, column),
             realObj.id
           );
+          ref.setReferenceDimension(realObj.type.dimensions);
         } else {
-          if (actualValue instanceof StoreValueAddress) {
-            const line = actualValue.line;
-            const column = actualValue.column;
-            ref = new StoreValueRef(
-              actualValue.type,
-              actualValue.get(),
-              realObj.getLocAddressOf(line, column),
-              realObj.id
-            );
-            ref.setReferenceDimension(realObj.type.dimensions);
-          } else {
-            ref = new StoreValueRef(
-              actualValue.type,
-              actualValue.get(),
-              realObj.locAddress,
-              realObj.id
-            );
-          }
-        }
-        callee_store.insertStore(formalParameter.id, ref);
-      } else {
-        let realValue = actualValue;
-        if (shouldTypeCast) {
-          realValue = Store.doImplicitCasting(formalParameter.type, realValue);
+          ref = new StoreValueRef(
+            actualValue.type,
+            actualValue.get(),
+            realObj.locAddress,
+            realObj.id
+          );
         }
-        callee_store.insertStore(formalParameter.id, realValue);
       }
+      let varID = formalParameter.id;
+      if (formalParameter.variadic)
+        varID = `${varID}.${variadicCount}`;
+      calleeStore.insertStore(varID, ref);
+    } else {
+      let realValue = actualValue;
+      if (shouldTypeCast) {
+        realValue = Store.doImplicitCasting(formalParameter.type, realValue);
+      }
+      let varID = formalParameter.id;
+      if (formalParameter.variadic)
+        varID = `${varID}.${variadicCount}`;
+      calleeStore.insertStore(varID, realValue);
     }
-    return callee_store;
+    return calleeStore;
   }
 
   /**

+ 7 - 3
js/processor/lib/io.js

@@ -7,14 +7,18 @@ import { StoreValue } from '../store/value/store_value';
 
 export function createOutputFun () {
   const writeFunction = async function (store, _) {
-    const val = store.applyStore('p1');
-    this.output.sendOutput(convertToString(val.get(), val.type));
+    const totalSV = store.applyStore('p1.0');
+    const total = totalSV.get();
+    for (let i = 1; i < total; i += 1) {
+      const val = store.applyStore(`p1.${i}`);
+      this.output.sendOutput(convertToString(val.get(), val.type));
+    }
     store.mode = Modes.RETURN;
     return store;
   }
   const block = new Commands.CommandBlock([], [new Commands.SysCall(writeFunction)]);
   const func = new Commands.Function('$write', Types.VOID,
-    [new Commands.FormalParameter(Types.ALL, 'p1', false)],
+    [new Commands.FormalParameter(Types.ALL, 'p1', false, true)],
     block);
   return func;
 }

+ 98 - 57
js/processor/semantic/semanticAnalyser.js

@@ -2,6 +2,8 @@ import { ProcessorErrorFactory } from './../error/processorErrorFactory';
 import { LanguageDefinedFunction } from './../definedFunctions';
 import { LanguageService } from './../../services/languageService';
 import { ArrayDeclaration, While, For, Switch, Assign, Break, IfThenElse, Return, ArrayIndexAssign } from '../../ast/commands';
+// eslint-disable-next-line @typescript-eslint/no-unused-vars
+import { Expression } from './../../ast/expressions/expression'
 import { InfixApp, UnaryApp, FunctionCall, IntLiteral, RealLiteral, StringLiteral, BoolLiteral, VariableLiteral, ArrayAccess } from '../../ast/expressions';
 import { Literal } from '../../ast/expressions/literal';
 import { resultTypeAfterInfixOp, resultTypeAfterUnaryOp } from '../compatibilityTable';
@@ -530,69 +532,36 @@ export class SemanticAnalyser {
     );
   }
 
+  /**
+   * 
+   * @param {import('./../../ast/commands/function').Function} fun 
+   * @param {Expression[]} actualParametersList 
+   */
   assertParameters (fun, actualParametersList) {
-    if (fun.formalParameters.length !== actualParametersList.length) {
-      throw ProcessorErrorFactory.invalid_parameters_size_full(fun.name, actualParametersList.length, fun.formalParameters.length, null);
+    const parameterList = fun.formalParameters;
+    if ((parameterList.length > actualParametersList.length)
+      || (parameterList.length !== actualParametersList.length && !fun.hasVariadic())) {
+      throw ProcessorErrorFactory.invalid_parameters_size_full(fun.name, actualParametersList.length,
+        fun.formalParameters.length, null);
     }
-    for (let i = 0; i < actualParametersList.length; ++i) {
-      const param = actualParametersList[i];
-      const formalParam = fun.formalParameters[i];
-      // const id = formalParam.id;
-      if(formalParam.byRef) {
-        if(param instanceof VariableLiteral) {
-          const variable = this.findSymbol(param.id, this.symbolMap);
-          if (variable.isConst) {
-            throw ProcessorErrorFactory.invalid_const_ref_full(fun.name, param.toString(), param.sourceInfo);  
-          }
-        } else if (!(param instanceof VariableLiteral || param instanceof ArrayAccess)) {
-          throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, param.toString(), param.sourceInfo);
-        }
-      }
-      const resultType = this.evaluateExpressionType(param);
-      if(resultType instanceof MultiType && formalParam.type instanceof MultiType) {
-        let shared = 0
-        for (let j = 0; j < resultType.types.length; ++j) {
-          const element = resultType.types[j];
-          if(formalParam.type.types.indexOf(element) !== -1) {
-            shared += 1;
-          }
-        }
-        if(shared <= 0) {
-          if(Config.enable_type_casting && !formalParam.byRef) {
-            if(resultType.isCompatible(Types.INTEGER) || resultType.isCompatible(Types.REAL)) {
-              if(formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL)) {
-                continue;
-              }
-            }
-          }
-          throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, param.toString(), param.sourceInfo);
-        }
-      } else if (resultType instanceof MultiType) {
-        if(!resultType.isCompatible(formalParam.type)) {
-          if(Config.enable_type_casting && !formalParam.byRef) {
-            if(resultType.isCompatible(Types.INTEGER) || resultType.isCompatible(Types.REAL)) {
-              if(formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL)) {
-                continue;
-              }
-            }
-          }
-          throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, param.toString(), param.sourceInfo);
-        }
-      } else if(!formalParam.type.isCompatible(resultType)) {
-        if(Config.enable_type_casting && !formalParam.byRef) {
-          if (Store.canImplicitTypeCast(formalParam.type, resultType)) {
-            continue;
-          }
-        }
-        throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, param.toString(), param.sourceInfo);
+    
+    for (let i = 0, j = 0; i < parameterList.length && j < actualParametersList.length; i += 1, j += 1) {
+      const formalParam = parameterList[i];
+      if (formalParam.variadic && i + 1 !== parameterList.length) {
+        throw "A function variadic parameter must be its last parameter!";
+      }
+      if (formalParam.variadic) {
+        j = this.assertVariadicParameter(fun, formalParam, j, actualParametersList);
+      } else {
+        const param = actualParametersList[j];
+        this.assertParameter(fun, formalParam, param);
       }
-
     }
   }
 
   evaluateVectorLiteralType (literal, type) {
     // console.log(literal);
-    for(let i = 0; i < literal.value.length; i+=1) {
+    for (let i = 0; i < literal.value.length; i+=1) {
       const exp = literal.value[i];
       const expType = this.evaluateExpressionType(exp);
       let compatible = false;
@@ -601,8 +570,8 @@ export class SemanticAnalyser {
       } else {
         compatible = type.canAccept(expType, 1);
       }
-      if(!compatible) {
-        if(!Config.enable_type_casting || !Store.canImplicitTypeCast(type.innerType, expType)) {
+      if (!compatible) {
+        if (!Config.enable_type_casting || !Store.canImplicitTypeCast(type.innerType, expType)) {
           const stringInfo = type.stringInfo();
           const info = stringInfo[0];
           const result_string_info = expType.stringInfo();
@@ -613,4 +582,76 @@ export class SemanticAnalyser {
     }
     return type;
   }
+
+  /**
+   * 
+   * @param {import('./../../ast/commands/function').Function} fun 
+   * @param {import('./../../ast/commands/formalParameter').FormalParameter} formalParam 
+   * @param {number} index 
+   * @param {Expression[]} actualParametersList 
+   */
+  assertVariadicParameter (fun, formalParam, index, actualParametersList) {
+    let i;
+    for (i = index; i < actualParametersList.length; i += 1) {
+      this.assertParameter(fun, formalParam, actualParametersList[i]);
+    }
+    return i - 1;
+  }
+
+  /**
+   * 
+   * @param {import('./../../ast/commands/function').Function} fun 
+   * @param {import('./../../ast/commands/formalParameter').FormalParameter} formalParam 
+   * @param {Expression} actualParameter 
+   */
+  assertParameter (fun, formalParam, actualParameter) {
+    // const id = formalParam.id;
+    if (formalParam.byRef) {
+      if (actualParameter instanceof VariableLiteral) {
+        const variable = this.findSymbol(actualParameter.id, this.symbolMap);
+        if (variable.isConst) {
+          throw ProcessorErrorFactory.invalid_const_ref_full(fun.name, actualParameter.toString(), actualParameter.sourceInfo);  
+        }
+      } else if (!(actualParameter instanceof VariableLiteral || actualParameter instanceof ArrayAccess)) {
+        throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, actualParameter.toString(), actualParameter.sourceInfo);
+      }
+    }
+    const resultType = this.evaluateExpressionType(actualParameter);
+    if (resultType instanceof MultiType && formalParam.type instanceof MultiType) {
+      let shared = 0
+      for (let j = 0; j < resultType.types.length; ++j) {
+        const element = resultType.types[j];
+        if (formalParam.type.types.indexOf(element) !== -1) {
+          shared += 1;
+        }
+      }
+      if (shared <= 0) {
+        if (Config.enable_type_casting && !formalParam.byRef) {
+          if (!resultType.isCompatible(Types.INTEGER) && !resultType.isCompatible(Types.REAL)
+            || (formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL))) {
+              throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, actualParameter.toString(),
+                actualParameter.sourceInfo);
+          }
+        }
+        
+      }
+    } else if (resultType instanceof MultiType) {
+      if(!resultType.isCompatible(formalParam.type)) {
+        if (Config.enable_type_casting && !formalParam.byRef) {
+          if (!resultType.isCompatible(Types.INTEGER) && !resultType.isCompatible(Types.REAL)
+            || (formalParam.type.isCompatible(Types.INTEGER) || formalParam.type.isCompatible(Types.REAL))) {
+              throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, actualParameter.toString(),
+                actualParameter.sourceInfo);
+          }
+        } 
+      }
+    } else if (!formalParam.type.isCompatible(resultType)) {
+      if (Config.enable_type_casting && !formalParam.byRef) {
+        if (!Store.canImplicitTypeCast(formalParam.type, resultType)) {
+          throw ProcessorErrorFactory.invalid_parameter_type_full(fun.name, actualParameter.toString(), actualParameter.sourceInfo);
+        }
+      }
+      
+    }
+  }
 }

+ 17 - 5
js/processor/store/store.ts

@@ -1,3 +1,4 @@
+/* eslint-disable @typescript-eslint/no-non-null-assertion */
 import { Modes } from '../modes';
 import { Types } from "../../typeSystem/types";
 import { StoreObject } from './storeObject';
@@ -38,14 +39,14 @@ export class Store {
 
   private store: Map<string, StoreObject>;
   public nextStore?: Store
-  public mode: Symbol;
+  public mode: symbol;
 
   constructor(public name: string) {
     this.store = new Map<string, StoreObject>();
     this.mode = Modes.RUN; 
   }
 
-  extendStore (nextStore: Store) {
+  extendStore (nextStore: Store): void {
     this.nextStore = nextStore;
   }
 
@@ -140,7 +141,7 @@ export class Store {
    * @param {number} line the line address of the vector/matrix
    * @param {number} column the matrix column, which can be undefined
    */
-  updateStoreArray (id:string, sto_value: IStoreValue, line:number, column?:number): Store {
+  updateStoreArray (id: string, sto_value: IStoreValue, line: number, column?: number): Store {
     if (!this.store.has(id)) {
       if (this.nextStore != null) {
         this.nextStore.updateStoreArray(id, sto_value, line, column);
@@ -198,13 +199,13 @@ export class Store {
    * @param id variable id
    * @param stoValue the value to be used as the initial value of id
    */
-  insertStore (id: string, stoValue: IStoreValue) {
+  insertStore (id: string, stoValue: IStoreValue): Store {
     if (this.store.has(id)) {
       // TODO: better error message
       throw new Error(`${id} is already defined`);
     }
     // TODO check for array....
-    let newObj:StoreObject;
+    let newObj: StoreObject;
     if(stoValue instanceof StoreValueRef) {
       newObj = new StoreObjectRef(stoValue);
     } else if (stoValue instanceof ArrayStoreValueRef) {
@@ -252,4 +253,15 @@ export class Store {
   destroy (): void {
     this.store.forEach(sto => sto.destroy(), this);
   }
+
+  isDefined (id: string): boolean {
+    if (!this.store.has(id)) {
+      if (this.nextStore != null) {
+        return this.nextStore.isDefined(id);
+      } else {
+        return false;
+      }
+    }
+    return true;
+  }
 }

+ 32 - 7
js/util/outputTest.js

@@ -5,15 +5,40 @@ export class OutputTest extends Output {
   constructor () {
     super();
     this.list = [];
+    this.currentLine = null;
   }
 
-  sendOutput (text) {
-    const output = ''+text;
-    output.split("\n").forEach(t => {
-      t = t.replace(/\t/g,'  ');
-      t = t.replace(/\s/g,"\u0020");
-      this.list.push(t);
-    },this);
+  write (text, newLine = false) {
+    if (this.currentLine == null) {
+      this.currentLine = this.list.push('') - 1;
+    }
+
+    this.list[this.currentLine] += text;
+    if (newLine) {
+      this.currentLine = null;
+    }
+  }
 
+  sendOutput (text) {
+    let output = '' + text;
+    if (output.indexOf('\n') !== -1) {
+      const outputList = output.split('\n');
+      let last = outputList.pop();
+      outputList.forEach( t => {
+        t = t.replace(/\t/g, '&#x0020;&#x0020;');
+        t = t.replace(/\s/g, "&#x0020;");
+        if (t.length == 0)
+          t = "&nbsp;";
+        this.write(t, true);
+      });
+      last = last.replace(/\t/g, '&#x0020;&#x0020;');
+      last = last.replace(/\s/g, "&#x0020;");
+      if (last.length != 0)
+        this.write(last);
+    } else {
+      output = output.replace(/\t/g, '&#x0020;&#x0020;');
+      output = output.replace(/\s/g, "&#x0020;");
+      this.write(output);
+    }
   }
 }

+ 38 - 11
js/util/testConsole.js

@@ -10,24 +10,36 @@ export class TestConsole {
     this.index = 0;
     this.inputList = inputList;
     this.list = [];
+    this.currentLine = null;
   }
 
-  write (text) {
-    this._appendText(text, DOMConsole.USER);
+  write (text, newLine = false) {
+    this._appendText(text, DOMConsole.USER, newLine);
   }
 
   info (text) {
-    this._appendText(text, DOMConsole.INFO);
+    this._appendTextLn(text, DOMConsole.INFO);
   }
 
   err (text) {
-    this._appendText(text, DOMConsole.ERR);
+    this._appendTextLn(text, DOMConsole.ERR);
   }
 
-  _appendText (text) {
-    this.list.push(text);
+  _appendText (text, type, newLine = false) {
+    if (this.currentLine == null) {
+      this.currentLine = this.list.push('') - 1;
+    }
+
+    this.list[this.currentLine] += text;
+    if (newLine) {
+      this.currentLine = null;
+    }
   }
 
+  _appendTextLn (text) {
+    this.list.push(text);
+    this.currentLine = null;
+  }
 
   getClassForType (type) {
     switch (type) {
@@ -53,10 +65,25 @@ export class TestConsole {
   }
 
   sendOutput (text) {
-    const output = ""+text;
-    output.split("\n").forEach(t => {
-      t = t.replace(/\t/g,'&#9;');
-      this.write(t)
-    });
+    let output = '' + text;
+    if (output.indexOf('\n') !== -1) {
+      const outputList = output.split('\n');
+      let last = outputList.pop();
+      outputList.forEach( t => {
+        t = t.replace(/\t/g, '&#x0020;&#x0020;');
+        t = t.replace(/\s/g, "&#x0020;");
+        if (t.length == 0)
+          t = "&nbsp;";
+        this.write(t, true);
+      });
+      last = last.replace(/\t/g, '&#x0020;&#x0020;');
+      last = last.replace(/\s/g, "&#x0020;");
+      if (last.length != 0)
+        this.write(last);
+    } else {
+      output = output.replace(/\t/g, '&#x0020;&#x0020;');
+      output = output.replace(/\s/g, "&#x0020;");
+      this.write(output);
+    }
   }
 }