Parcourir la source

Merge branch 'i18nService' of igorfelix/ivprog into master

Lucas de Souza il y a 6 ans
Parent
commit
09c9a3a99c
81 fichiers modifiés avec 1350 ajouts et 394 suppressions
  1. 244 0
      grammar/en/ivprog.g4
  2. 0 0
      grammar/es/ivprog.g4
  3. 6 3
      grammar/index.js
  4. 244 0
      grammar/pt/ivprog.g4
  5. 3 0
      i18n/en/error.json
  6. 9 0
      i18n/en/index.js
  7. 3 0
      i18n/en/message.json
  8. 25 0
      i18n/en/ui.json
  9. 3 0
      i18n/es/error.json
  10. 9 0
      i18n/es/index.js
  11. 3 0
      i18n/es/message.json
  12. 25 0
      i18n/es/ui.json
  13. 1 5
      i18n/i18n-engine.js
  14. 9 0
      i18n/index.js
  15. 0 3
      i18n/pt-br.js
  16. 35 0
      i18n/pt/error.json
  17. 9 0
      i18n/pt/index.js
  18. 3 0
      i18n/pt/message.json
  19. 26 0
      i18n/pt/ui.json
  20. 28 3
      index.html
  21. 0 16
      js/ast/SyntaxError.js
  22. 8 0
      js/ast/error/syntaxError.js
  23. 51 0
      js/ast/error/syntaxErrorFactory.js
  24. 36 37
      js/ast/ivprogParser.js
  25. 14 14
      js/ast/operators.js
  26. 17 15
      js/ast/types.js
  27. 9 7
      js/iassign-integration-functions.js
  28. 1 1
      js/io/domInput.js
  29. 8 2
      js/io/domOutput.js
  30. 32 40
      js/main.js
  31. 138 52
      js/processor/compatibilityTable.js
  32. 1 1
      js/processor/definedFunctions.js
  33. 7 0
      js/processor/error/processorErrorFactory.js
  34. 8 0
      js/processor/error/runtimeError.js
  35. 8 0
      js/processor/error/semanticError.js
  36. 55 57
      js/processor/ivprogProcessor.js
  37. 29 0
      js/services/languageService.js
  38. 52 0
      js/services/localizedStringsService.js
  39. 8 11
      tests/test00.spec.js
  40. 3 7
      tests/test01.spec.js
  41. 2 4
      tests/test02.spec.js
  42. 3 5
      tests/test03.spec.js
  43. 3 5
      tests/test04.spec.js
  44. 4 5
      tests/test05.spec.js
  45. 2 5
      tests/test06.spec.js
  46. 3 5
      tests/test07.spec.js
  47. 2 5
      tests/test08.spec.js
  48. 3 6
      tests/test09.spec.js
  49. 3 5
      tests/test10.spec.js
  50. 3 5
      tests/test11.spec.js
  51. 3 5
      tests/test12.spec.js
  52. 3 2
      tests/test13.spec.js
  53. 4 5
      tests/test14.spec.js
  54. 3 2
      tests/test15.spec.js
  55. 3 2
      tests/test16.spec.js
  56. 3 2
      tests/test17.spec.js
  57. 3 5
      tests/test18.spec.js
  58. 2 3
      tests/test19.spec.js
  59. 3 3
      tests/test20.spec.js
  60. 2 3
      tests/test21.spec.js
  61. 3 2
      tests/test22.spec.js
  62. 3 2
      tests/test23.spec.js
  63. 2 2
      tests/test24.spec.js
  64. 2 3
      tests/test25.spec.js
  65. 2 3
      tests/test26.spec.js
  66. 2 3
      tests/test27.spec.js
  67. 2 3
      tests/test28.spec.js
  68. 2 3
      tests/test29.spec.js
  69. 2 3
      tests/test30.spec.js
  70. 2 3
      tests/test31.spec.js
  71. 2 2
      tests/test33.spec.js
  72. 2 3
      tests/test34.spec.js
  73. 2 2
      tests/test35.spec.js
  74. 2 2
      tests/test37.spec.js
  75. 2 2
      tests/test38.spec.js
  76. 10 0
      tests/test39.spec.js
  77. 30 0
      tests/test40.spec.js
  78. 8 0
      tests/test41.spec.js
  79. 10 0
      tests/test42.spec.js
  80. 32 0
      tests/test43.spec.js
  81. 1 0
      webpack.config.js

+ 244 - 0
grammar/en/ivprog.g4

@@ -0,0 +1,244 @@
+lexer grammar ivprog;
+
+@lexer::members{
+  //Translate to fit your language
+  ivprog.MAIN_FUNCTION_NAME = "start";
+  ivprog.READ_FUNCTION_NAME = "read";
+  ivprog.WRITE_FUNCTION_NAME = "write";
+}
+
+// BEGIN i18n Lexical rules
+RK_PROGRAM
+  : 'program'
+  ;
+
+RK_REAL
+  : 'real'
+  ;
+
+RK_VOID
+  : 'void'
+  ;
+
+RK_BOOLEAN
+  : 'boolean'
+  ;
+
+RK_STRING
+  : 'string'
+  ;
+
+RK_INTEGER
+  : 'integer'
+  ;
+
+RK_CHARACTER
+  : 'character'
+  ;    
+
+RK_SWITCH
+  : 'switch'
+  ;
+
+RK_CASE
+  : 'case'
+  ;
+
+RK_DEFAULT
+  : 'otherwise'
+  ;
+
+RK_CONST
+  : 'const'
+  ;
+
+RK_FUNCTION
+  : 'function'
+  ;
+
+RK_RETURN
+  : 'return'
+  ;  
+
+RK_FOR
+  : 'for'
+  ;
+
+RK_BREAK
+  : 'break'
+  ;
+
+RK_DO
+  : 'do'
+  ;
+
+RK_WHILE
+  : 'while'
+  ;
+
+RK_IF
+  : 'if'
+  ;
+
+RK_ELSE
+  : 'else'
+  ;
+
+RK_FALSE
+  : 'false'
+  ;
+
+RK_TRUE
+  : 'true'
+  ;
+
+fragment RK_LOGICAL_NOT
+  : 'not'
+  ;
+
+fragment RK_LOGICAL_AND
+  : 'AND'
+  ;
+
+fragment RK_LOGICAL_OR
+  : 'OR'
+  ;
+// END i18n Lexical rules
+
+// GAMBIARRA   : '.' |'á'| 'à'| 'ã'|'â'|'é'|'ê'|'í'|'ó'|'ô'|'õ'|'ú'|'ü'|'ç'|'Ä'|'À'|'Ã'|'Â'|'É'|'Ê'|'Ë'|'Ó'|'Ô'|'Õ'|'Ú'|'Ü'|'Ç'|'#'|'$'|'"'|'§'|'?'|'¹'|'²'|'³'|'£'|'¢'|'¬'|'ª'|'º'|'~'|'\''|'`'|'\\'|'@';
+
+OPEN_PARENTHESIS
+  : '('
+  ;
+
+CLOSE_PARENTHESIS
+  : ')'
+  ;
+
+OPEN_BRACE
+  : '['
+  ;
+
+CLOSE_BRACE
+  : ']'
+  ;
+
+OPEN_CURLY
+  : '{'
+  ;
+
+CLOSE_CURLY
+  : '}'
+  ;
+
+COMMA
+  : ','
+  ;
+
+EQUAL
+  : '='
+  ;
+
+SUM_OP
+  : ('+'|'-')
+  ;
+
+MULTI_OP
+  : ('*'|'/'|'%')
+  ;
+
+AND_OPERATOR
+  : RK_LOGICAL_AND
+  ;
+
+OR_OPERATOR
+  : RK_LOGICAL_OR
+  ;
+
+RELATIONAL_OPERATOR
+  : ('>='|'=='|'<='|'>'|'<'|'!=')
+  ;
+
+COLON
+  : ':'
+  ;
+
+NOT_OPERATOR
+  : RK_LOGICAL_NOT
+  ;
+
+ID
+  : [a-zA-Z_] [a-zA-Z0-9_]*
+  ;
+
+// ID_BIBLIOTECA     : ID '.' ID;
+
+INTEGER
+  : [0-9]+
+  | ('0x'|'0X')(HEX_DIGIT)+
+  | ('0b'|'0B')[0-1]+
+  ;
+
+REAL
+  : [0-9]+ '.' [0-9]+
+  | [0-9]+ '.' [0-9]* ExponentPart
+  ;
+
+fragment ExponentPart
+  : [eE] [+-]? [0-9]+
+  ;
+
+STRING
+  : '"' STRING_CHARACTER* '"'
+  ;
+    
+fragment STRING_CHARACTER //String as defined at https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
+  : ~["\\\r\n]
+  | ESC_SEQ
+  ;
+
+CHARACTER //Character as defined at https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
+  : '\'' ( ESC_SEQ | ~['\\\r\n]) '\''
+  ;
+
+WHITESPACE 
+  : ( ' ' | '\t') -> skip
+  ;
+
+fragment SEMICOLON
+  : ';'
+  ;
+
+EOS
+  : [\r\n]+
+  | SEMICOLON
+  ;
+
+fragment HEX_DIGIT
+  : [0-9a-fA-F]
+  ;
+
+fragment OCTAL_DIGIT
+  : [0-7]
+  ;
+
+fragment ESC_SEQ
+  : '\\' ('b'|'t'|'n'|'f'|'r'|'"'|'\''|'\\')
+  | ESC_UNICODE
+  | ESC_OCTAL
+  ;
+
+fragment ESC_OCTAL
+  : '\\' [0-3] OCTAL_DIGIT OCTAL_DIGIT
+  | '\\' OCTAL_DIGIT OCTAL_DIGIT
+  | '\\' OCTAL_DIGIT
+  ;
+
+fragment ESC_UNICODE
+  : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+  ;
+
+COMMENTS
+  : ('//' ~('\n'|'\r')* '\r'? '\n'
+    | '/*' .*? '*/') -> channel(HIDDEN)
+  ;

grammar/pt-br/ivprog.g4 → grammar/es/ivprog.g4


+ 6 - 3
grammar/index.js

@@ -1,4 +1,7 @@
-import * as ptBR from './pt-br/ivprog.g4';
+import * as PT from './pt/ivprog.g4';
+import * as EN from './en/ivprog.g4';
+import * as ES from './es/ivprog.g4';
 
-exports.pt_br = ptBR.ivprog;
-exports.default = ptBR.ivprog;
+exports.pt = PT.ivprog;
+exports.en = EN.ivprog;
+exports.es = ES.ivprog;

+ 244 - 0
grammar/pt/ivprog.g4

@@ -0,0 +1,244 @@
+lexer grammar ivprog;
+
+@lexer::members{
+  //Translate to fit your language
+  ivprog.MAIN_FUNCTION_NAME = "inicio";
+  ivprog.READ_FUNCTION_NAME = "leia";
+  ivprog.WRITE_FUNCTION_NAME = "escreva";
+}
+
+// BEGIN i18n Lexical rules
+RK_PROGRAM
+  : 'programa'
+  ;
+
+RK_REAL
+  : 'real'
+  ;
+
+RK_VOID
+  : 'vazio'
+  ;
+
+RK_BOOLEAN
+  : 'logico'
+  ;
+
+RK_STRING
+  : 'cadeia'
+  ;
+
+RK_INTEGER
+  : 'inteiro'
+  ;
+
+RK_CHARACTER
+  : 'caractere'
+  ;    
+
+RK_SWITCH
+  : 'escolha'
+  ;
+
+RK_CASE
+  : 'caso'
+  ;
+
+RK_DEFAULT
+  : 'contrario'
+  ;
+
+RK_CONST
+  : 'const'
+  ;
+
+RK_FUNCTION
+  : 'funcao'
+  ;
+
+RK_RETURN
+  : 'retorne'
+  ;  
+
+RK_FOR
+  : 'para'
+  ;
+
+RK_BREAK
+  : 'pare'
+  ;
+
+RK_DO
+  : 'faca'
+  ;
+
+RK_WHILE
+  : 'enquanto'
+  ;
+
+RK_IF
+  : 'se'
+  ;
+
+RK_ELSE
+  : 'senao'
+  ;
+
+RK_FALSE
+  : 'falso'
+  ;
+
+RK_TRUE
+  : 'verdadeiro'
+  ;
+
+fragment RK_LOGICAL_NOT
+  : 'nao'
+  ;
+
+fragment RK_LOGICAL_AND
+  : 'E'
+  ;
+
+fragment RK_LOGICAL_OR
+  : 'OU'
+  ;
+// END i18n Lexical rules
+
+// GAMBIARRA   : '.' |'á'| 'à'| 'ã'|'â'|'é'|'ê'|'í'|'ó'|'ô'|'õ'|'ú'|'ü'|'ç'|'Ä'|'À'|'Ã'|'Â'|'É'|'Ê'|'Ë'|'Ó'|'Ô'|'Õ'|'Ú'|'Ü'|'Ç'|'#'|'$'|'"'|'§'|'?'|'¹'|'²'|'³'|'£'|'¢'|'¬'|'ª'|'º'|'~'|'\''|'`'|'\\'|'@';
+
+OPEN_PARENTHESIS
+  : '('
+  ;
+
+CLOSE_PARENTHESIS
+  : ')'
+  ;
+
+OPEN_BRACE
+  : '['
+  ;
+
+CLOSE_BRACE
+  : ']'
+  ;
+
+OPEN_CURLY
+  : '{'
+  ;
+
+CLOSE_CURLY
+  : '}'
+  ;
+
+COMMA
+  : ','
+  ;
+
+EQUAL
+  : '='
+  ;
+
+SUM_OP
+  : ('+'|'-')
+  ;
+
+MULTI_OP
+  : ('*'|'/'|'%')
+  ;
+
+AND_OPERATOR
+  : RK_LOGICAL_AND
+  ;
+
+OR_OPERATOR
+  : RK_LOGICAL_OR
+  ;
+
+RELATIONAL_OPERATOR
+  : ('>='|'=='|'<='|'>'|'<'|'!=')
+  ;
+
+COLON
+  : ':'
+  ;
+
+NOT_OPERATOR
+  : RK_LOGICAL_NOT
+  ;
+
+ID
+  : [a-zA-Z_] [a-zA-Z0-9_]*
+  ;
+
+// ID_BIBLIOTECA     : ID '.' ID;
+
+INTEGER
+  : [0-9]+
+  | ('0x'|'0X')(HEX_DIGIT)+
+  | ('0b'|'0B')[0-1]+
+  ;
+
+REAL
+  : [0-9]+ '.' [0-9]+
+  | [0-9]+ '.' [0-9]* ExponentPart
+  ;
+
+fragment ExponentPart
+  : [eE] [+-]? [0-9]+
+  ;
+
+STRING
+  : '"' STRING_CHARACTER* '"'
+  ;
+    
+fragment STRING_CHARACTER //String as defined at https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
+  : ~["\\\r\n]
+  | ESC_SEQ
+  ;
+
+CHARACTER //Character as defined at https://github.com/antlr/grammars-v4/blob/master/java8/Java8.g4
+  : '\'' ( ESC_SEQ | ~['\\\r\n]) '\''
+  ;
+
+WHITESPACE 
+  : ( ' ' | '\t') -> skip
+  ;
+
+fragment SEMICOLON
+  : ';'
+  ;
+
+EOS
+  : [\r\n]+
+  | SEMICOLON
+  ;
+
+fragment HEX_DIGIT
+  : [0-9a-fA-F]
+  ;
+
+fragment OCTAL_DIGIT
+  : [0-7]
+  ;
+
+fragment ESC_SEQ
+  : '\\' ('b'|'t'|'n'|'f'|'r'|'"'|'\''|'\\')
+  | ESC_UNICODE
+  | ESC_OCTAL
+  ;
+
+fragment ESC_OCTAL
+  : '\\' [0-3] OCTAL_DIGIT OCTAL_DIGIT
+  | '\\' OCTAL_DIGIT OCTAL_DIGIT
+  | '\\' OCTAL_DIGIT
+  ;
+
+fragment ESC_UNICODE
+  : '\\' 'u' HEX_DIGIT HEX_DIGIT HEX_DIGIT HEX_DIGIT
+  ;
+
+COMMENTS
+  : ('//' ~('\n'|'\r')* '\r'? '\n'
+    | '/*' .*? '*/') -> channel(HIDDEN)
+  ;

+ 3 - 0
i18n/en/error.json

@@ -0,0 +1,3 @@
+{
+  
+}

+ 9 - 0
i18n/en/index.js

@@ -0,0 +1,9 @@
+import errors from './error.json';
+import messages from './message.json'
+import ui from './ui.json';
+
+export default {
+ 'message' : messages,
+ 'error': errors,
+ 'ui': ui
+}

+ 3 - 0
i18n/en/message.json

@@ -0,0 +1,3 @@
+{
+  
+}

+ 25 - 0
i18n/en/ui.json

@@ -0,0 +1,25 @@
+{
+  "function": "function",
+  "btn_function": "Function",
+  "start": "start",
+  "void": "void",
+  "integer": "integer",
+  "real": "real",
+  "program": "program",
+  "text": "text",
+  "boolean": "boolean",
+  "true": "true",
+  "false": "false",
+  "variable": "Variable",
+  "command": "Command",
+  "new_parameter": "new_parameter",
+  "new_variable": "new_variable",
+  "new_global": "new_global",
+  "new_function": "new_function",
+  "vector": "vector",
+  "text_comment_start": "Initial comment of function...",
+  "text_comment_main": "This is the main function...",
+  "text_read_var": "Reading data",
+  "text_write_var": "Writing data",
+  "text_comment": "Comment"
+}

+ 3 - 0
i18n/es/error.json

@@ -0,0 +1,3 @@
+{
+  
+}

+ 9 - 0
i18n/es/index.js

@@ -0,0 +1,9 @@
+import errors from './error.json';
+import messages from './message.json'
+import ui from './ui.json';
+
+export default {
+ 'message' : messages,
+ 'error': errors,
+ 'ui': ui
+}

+ 3 - 0
i18n/es/message.json

@@ -0,0 +1,3 @@
+{
+  
+}

+ 25 - 0
i18n/es/ui.json

@@ -0,0 +1,25 @@
+{
+  "function": "function",
+  "btn_function": "Function",
+  "start": "start",
+  "void": "void",
+  "integer": "integer",
+  "real": "real",
+  "program": "program",
+  "text": "text",
+  "boolean": "boolean",
+  "true": "true",
+  "false": "false",
+  "variable": "Variable",
+  "command": "Command",
+  "new_parameter": "new_parameter",
+  "new_variable": "new_variable",
+  "new_global": "new_global",
+  "new_function": "new_function",
+  "vector": "vector",
+  "text_comment_start": "Initial comment of function...",
+  "text_comment_main": "This is the main function...",
+  "text_read_var": "Reading data",
+  "text_write_var": "Writing data",
+  "text_comment": "Comment"
+}

+ 1 - 5
i18n/i18n-engine.js

@@ -26,11 +26,7 @@ i18n.updateLocale = function(new_locale) {
   });
 }
 
-if (iLMparameters.lang) {
-  i18n.locale = iLMparameters.lang;
-} else {
-  i18n.locale = 'en';
-}
+i18n.locale = iLMparameters.lang;
 i18n.db = {};
 
 $.ajaxSetup({

+ 9 - 0
i18n/index.js

@@ -0,0 +1,9 @@
+import EN from './en';
+import PT from './pt';
+import ES from './es';
+
+export default {
+  'en': EN,
+  'pt': PT,
+  'es': ES
+}

+ 0 - 3
i18n/pt-br.js

@@ -1,3 +0,0 @@
-export default {
-  'key': 'value',
-}

+ 35 - 0
i18n/pt/error.json

@@ -0,0 +1,35 @@
+{
+  "token_missing_one": "Erro de sintaxe: Espera-se $0, mas encontrou-se $1 na linha:$2, coluna:$3",
+  "token_missing_two": "Erro de sintaxe: Esperava-se $0 ou $1 mas encontrou-se $2 na liha:$3, coluna: $4",
+  "eos_missing": "Falta uma nova linha ou ; na linha: $0, coluna: $1",
+  "invalid_type": "$0 na linha: $1, coluna: $2 é um tipo inválido. Os tipos válidos são: $3",
+  "invalid_array_dimension": "A dimensão inválida na linha: $0, coluna: $1. Insira um $2 ou identificador válido do mesmo tipo.",
+  "invalid_array_size": "O tamnho do vetor/matriz excede o máximo de 2 na linha $0",
+  "extra_lines": "Nenhum texto é permitido após '}' em 'programa {...}'",
+  "invalid_main_return": "A função $0 deve retornar $1 na linha $2",
+  "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.",
+  "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",
+  "not_implemented": "Erro interno crítico: A função definida pelo sistema $0 não foi implementada.",
+  "function_missing": "A função $0 não foi encontrada",
+  "invalid_parameters_size": "A quantidade de parâmetros fornecidos está incorreta. Esperava-se $0, encontrou-se $1",
+  "invalid_ref": "Você deve fornecer apenas um identificador como parâmetro",
+  "invalid_parameter_type": "O valor fornecido como parâmetro é do tipo $0, mas o tipo esperado é $1",
+  "unknown_command": "Erro interno crítico: Comando desconhecido encontrado",
+  "loop_condition_type": "A condição dos laços de repetição deve ser do tipo lógico",
+  "if_condition_type": "A condição de um comando se...senao deve ser do tipo lógico",
+  "invalid_return_type": "A função $0 deve retornar um tipo $1, ao invés de $1",
+  "unexpected_break_command": "Erro interno crítico: Comando pare encontrado fora do contexto de um laço/escolha..caso",
+  "invalid_dimension": "As dimensões de um vetor/matriz devem ser do tipo inteiro",
+  "void_in_expression": "A função $0 não pode ser utilizada aqui pois seu tipo de retorno é vazio",
+  "invalid_array": "Expressão literal de Vetor/Mariz inválida",
+  "invalid_array_access": "Identificador $0 não se refere a um vetor/matriz válido",
+  "column_outbounds": "Número de colunas $0 é inválido para a matriz $1 que possui $2 colunas",
+  "line_outbounds": "Número de linhas $0 é invaálido para a matriz $1 que possui $2 linhas",
+  "invalid_infix_op": "Não é possível aplicar a operação $0 entre os tipos $1 e $2",
+  "invalid_unary_op": "Não é possível aplicar a operação $0 ao tipo $1",
+  "unknown_op": "Erro interno crítico: Operação $0 desconhecida"
+}

+ 9 - 0
i18n/pt/index.js

@@ -0,0 +1,9 @@
+import errors from './error.json';
+import messages from './message.json'
+import ui from './ui.json';
+
+export default {
+ 'message' : messages,
+ 'error': errors,
+ 'ui': ui
+}

+ 3 - 0
i18n/pt/message.json

@@ -0,0 +1,3 @@
+{
+  
+}

+ 26 - 0
i18n/pt/ui.json

@@ -0,0 +1,26 @@
+{
+  "function": "funcao",
+  "btn_function": "Função",
+  "start": "inicio",
+  "void": "vazio",
+  "integer": "inteiro",
+  "real": "real",
+  "program": "programa",
+  "text": "cadeia",
+  "boolean": "logico",
+  "true": "verdadeiro",
+  "false": "fals0",
+  "variable": "Variável",
+  "command": "Comando",
+  "new_parameter": "novo_parametro",
+  "new_variable": "nova_variavel",
+  "new_global": "nova_global",
+  "new_function": "nova_funcao",
+  "vector": "vetor",
+  "text_comment_start": "Comentário inicial da função...",
+  "text_comment_main": "Esta é a função principal...",
+  "text_read_var": "Leitura de dados",
+  "text_write_var": "Escrita de dados",
+  "text_comment": "Comentário",
+  "join_or": "ou"
+}

+ 28 - 3
index.html

@@ -2,6 +2,25 @@
 <html>
 <head>
   <link rel="stylesheet" type="text/css" href="js/semantic/semantic.min.css">
+  <style>
+    .ivprog-io-output {
+      border: 1px solid gainsboro;
+      width: 80%;
+      height: 360px;
+      overflow-y: scroll;
+      background-color: black;
+      color: white;
+    }
+
+    .ivprog-io-input {
+      width: 80%;
+      margin-top: 10px;
+      border: 1.5px solid green;
+      height: 3rem;
+      color: black;
+      font-size: 12pt;
+    }
+  </style>
   <title></title>
 </head>
 <body>
@@ -27,13 +46,19 @@
         </textarea>
       </div>
       <div class="row">
-          <button class="ui button" id="btn">Generate AST</button>
+          <button class="ui button" id="btn">Run</button>
       </div>
     </div>
     <div class="six wide column">
-      <div style="overflow-y: scroll; height: 20%;">
-          <pre id="json-renderer" class="ui right floated"></pre>
+      <div id="dom-out" class="ivprog-io-output">
+
       </div>
+      <input id="dom-in" type="text" class="ivprog-io-input">
+    </div>
+    <div class="six wide column">
+        <div style="overflow-y: scroll; height: 70%;">
+            <pre id="json-renderer" class="ui right floated"></pre>
+        </div>
     </div>
   </div>
   

+ 0 - 16
js/ast/SyntaxError.js

@@ -1,16 +0,0 @@
-export class SyntaxError extends Error {
-
-  static createError (msg, token) {
-    const val = SyntaxError.getErrorString(msg, token);
-    return new SyntaxError(val);
-  }
-
-  static getErrorString (symbol, token) {
-    return `Syntax error: Expecting '${symbol}' but found '${token.text}' at line:${token.line}, column:${token.column}`;
-  }
-
-  constructor (...msg) {
-    super(...msg);
-    Error.captureStackTrace(this, SyntaxError);
-  }
-}

+ 8 - 0
js/ast/error/syntaxError.js

@@ -0,0 +1,8 @@
+export class SyntaxError extends Error {
+
+  constructor (...msg) {
+    super(...msg);
+    if(Error.captureStackTrace)
+      Error.captureStackTrace(this, SyntaxError);
+  }
+}

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

@@ -0,0 +1,51 @@
+import { LocalizedStrings } from './../../services/localizedStringsService';
+import { SyntaxError } from './syntaxError';
+
+export const SyntaxErrorFactory = Object.freeze({
+  extra_lines: () => new SyntaxError(LocalizedStrings.getError("extra_lines")),
+  token_missing_one: (expected, token) => {
+    const context = [expected, token.text, token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("token_missing_one", context));
+  },
+  token_missing_list: (expectedList, token) => {
+    const line = expectedList.join(LocalizedStrings.getOR());
+    return SyntaxErrorCodes.token_missing_one(line, token);
+  },
+  id_missing: (token) => {
+    const context = [token.text, token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("id_missing", context));
+  },
+  eos_missing: (token) => {
+    const context = [token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError("eos_missing", context));
+  },
+  invalid_array_dimension: (typeName, token) => {
+    const context = [token.line, token.column, typeName];
+    return new SyntaxError(LocalizedStrings.getError("invalid_array_dimension", context));
+  },
+  invalid_array_size: (token) => {
+    const context = [token.line];
+    return new SyntaxError(LocalizedStrings.getError("invalid_array_size", context));
+  },
+  invalid_main_return: (name, typeName, token) => {
+    const context = [name, typeName, token.line];
+    return new SyntaxError(LocalizedStrings.getError("invalid_main_return", context));
+  },
+  invalid_var_declaration: (token) => {
+    const context = [token.line];
+    return new SyntaxError(LocalizedStrings.getError("invalid_var_declaration", context));
+  },
+  invalid_break_command: (cmdName, token) => {
+    const context = [token.line, cmdName];
+    return new SyntaxError(LocalizedStrings.getError("invalid_break_command", context));
+  },
+  invalid_terminal: () => {
+    const context = [token.text, token.line, token.column];
+    return new SyntaxError(LocalizedStrings.getError('invalid_terminal', context));
+  },
+  invalid_type: (list, token) => {
+    const line = list.join(LocalizedStrings.getOR());
+    const context = [token.text, token.line, token.column, line]
+    return new SyntaxError(LocalizedStrings.getError("invalid_type", context));
+  }
+});

+ 36 - 37
js/ast/ivprogParser.js

@@ -3,7 +3,7 @@ import * as Expressions from './expressions/';
 import * as Commands from './commands/';
 import { Types, toInt, toString } from './types';
 import { convertFromString } from './operators';
-import { SyntaxError } from './SyntaxError';
+import { SyntaxErrorFactory } from './error/syntaxErrorFactory';
 import { NAMES } from './../processor/definedFunctions';
 
 export class IVProgParser {
@@ -96,11 +96,11 @@ export class IVProgParser {
       this.pos++;
       this.consumeNewLines();
       if(!this.isEOF()) {
-        throw new Error("No extra characters are allowed after 'program {...}'");
+        throw SyntaxErrorFactory.extra_lines();
       }
       return {global: globalVars, functions: functions};
     } else {
-      throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_PROGRAM], token);
+      throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_PROGRAM], token);
     }
   }
 
@@ -108,7 +108,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.OPEN_CURLY !== token.type){
       if(!attempt)
-        throw SyntaxError.createError('{', token);
+        throw SyntaxErrorFactory.token_missing_one('{', token);
       else
         return false;
     }
@@ -119,7 +119,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.CLOSE_CURLY !== token.type){
       if(!attempt)
-        throw SyntaxError.createError('}', token);
+        throw SyntaxErrorFactory.token_missing_one('}', token);
       else
         return false;
     }
@@ -138,7 +138,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.OPEN_BRACE !== token.type){
       if (!attempt) {
-        throw SyntaxError.createError('[', token);
+        throw SyntaxErrorFactory.token_missing_one('[', token);
       } else {
         return false;
       }
@@ -150,7 +150,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.CLOSE_BRACE !== token.type){
       if (!attempt) {
-        throw SyntaxError.createError(']', token);
+        throw SyntaxErrorFactory.token_missing_one(']', token);
       } else {
         return false;
       }
@@ -162,7 +162,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.OPEN_PARENTHESIS !== token.type){
       if (!attempt) {
-        throw SyntaxError.createError('(', token);
+        throw SyntaxErrorFactory.token_missing_one('(', token);
       } else {
         return false;
       }
@@ -174,7 +174,7 @@ export class IVProgParser {
     const token = this.getToken();
     if(this.lexerClass.CLOSE_PARENTHESIS !== token.type){
       if (!attempt) {
-        throw SyntaxError.createError(')', token);
+        throw SyntaxErrorFactory.token_missing_one(')', token);
       } else {
         return false;
       }
@@ -186,7 +186,7 @@ export class IVProgParser {
     const eosToken = this.getToken();
     if (eosToken.type !== this.lexerClass.EOS) {
       if (!attempt)
-        throw SyntaxError.createError('new line or \';\'', eosToken);
+        throw SyntaxErrorFactory.eos_missing(eosToken);
       else
         return false;
     }
@@ -199,7 +199,7 @@ export class IVProgParser {
       this.pos++;
       return;  
     }
-    throw SyntaxError.createError(';', eosToken);
+    throw SyntaxErrorFactory.token_missing_one(';', eosToken);
   }
 
   parseGlobalVariables () {
@@ -225,7 +225,8 @@ export class IVProgParser {
       const typeString = this.parseType();
       return this.parseDeclaration(typeString);
     } else {
-      throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_CONST] + ' or ' + this.getTypesAsString(), constToken);
+      throw SyntaxErrorFactory.token_missing_list(
+        [this.lexer.literalNames[this.lexerClass.RK_CONST]].concat(this.getTypeArray()), constToken);
     }
 
   }
@@ -272,7 +273,6 @@ export class IVProgParser {
     }
     const commaToken = this.getToken();
     if(commaToken.type === this.lexerClass.COMMA) {
-      console.log("comma found");
       this.pos++;
       this.consumeNewLines();
       return [declaration]
@@ -309,7 +309,7 @@ export class IVProgParser {
       this.pos++;
       return this.parseVariable(dimToken);
     } else {
-      throw SyntaxError.createError('int or ID', dimToken);
+      throw SyntaxErrorFactory.invalid_array_dimension(this.lexer.literalNames[this.lexerClass.RK_INTEGER], dimToken);
     }
   }
 
@@ -342,7 +342,7 @@ export class IVProgParser {
     const beginArray = this.getToken();
     if (this.parsingArrayDimension >= 2) {
       // TODO: better error message
-      throw new Error(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
+      throw SyntaxErrorFactory.token_missing_list(`Array dimensions exceed maximum size of 2 at line ${beginArray.line}`);
     }
     this.pos++;
     this.parsingArrayDimension++;
@@ -402,7 +402,9 @@ export class IVProgParser {
     const func = new Commands.Function(functionID, returnType, formalParams, commandsBlock);
     if (functionID === null && !func.isMain) {
       // TODO: better error message
-      throw new Error(`Function ${this.lexerClass.MAIN_FUNCTION_NAME} must return void (line ${token.line})`);
+      throw SyntaxErrorFactory.invalid_main_return(this.lexerClass.MAIN_FUNCTION_NAME,
+        this.lexer.literalNames[this.lexerClass.RK_VOID],
+        token.line);
     }
     this.popScope();
     return func;
@@ -443,7 +445,7 @@ export class IVProgParser {
   parseID () {
     const token = this.getToken();
     if(token.type !== this.lexerClass.ID) {
-      throw SyntaxError.createError('ID', token);
+      throw SyntaxErrorFactory.id_missing(token);
     } 
     this.pos++;
     if (this.insideScope(IVProgParser.FUNCTION)) {
@@ -477,7 +479,7 @@ export class IVProgParser {
       }
     }
     
-    throw SyntaxError.createError(this.getTypesAsString(), token);
+    throw SyntaxErrorFactory.invalid_type(this.getTypeArray(), token);
   }
 
   parseCommandBlock (optionalCurly = false) {
@@ -516,7 +518,7 @@ export class IVProgParser {
     if (this.isVariableType(token)) {
       if(!this.insideScope(IVProgParser.FUNCTION)) {
         // TODO better error message
-        throw new Error(`Cannot declare variable here (line ${token.line})`);
+        throw SyntaxErrorFactory.invalid_var_declaration(token.line);
       }
       this.pushScope(IVProgParser.BASE);
       const varType = this.parseType();
@@ -536,7 +538,10 @@ export class IVProgParser {
     } else if (token.type === this.lexerClass.RK_BREAK ) {
       if(!this.insideScope(IVProgParser.BREAKABLE)) {
         // TODO better error message
-        throw new Error("Break cannot be used outside of a loop.");
+        throw SyntaxErrorFactory.invalid_break_command(
+          this.lexer.literalNames[this.lexerClass.RK_BREAK],
+          token
+        );
       }
       return this.parseBreak();
     } else if (token.type === this.lexerClass.RK_SWITCH) {
@@ -585,7 +590,7 @@ export class IVProgParser {
     this.consumeNewLines(); //Maybe not...
     const whileToken = this.getToken();
     if (whileToken.type !== this.lexerClass.RK_WHILE) {
-      throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
+      throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_WHILE], whileToken);
     }
     this.pos++;
     this.checkOpenParenthesis();
@@ -629,7 +634,7 @@ export class IVProgParser {
         elseBlock = this.parseIfThenElse();
       } else {
         // TODO better error message
-        throw SyntaxError.createError(`${this.lexer.literalNames[this.lexerClass.RK_IF]} or {`, maybeIf);
+        throw SyntaxErrorFactory.token_missing_list([this.lexer.literalNames[this.lexerClass.RK_IF], '{'], maybeIf);
       }
       return new Commands.IfThenElse(logicalExpression, cmdBlocks, elseBlock);
     }
@@ -677,7 +682,7 @@ export class IVProgParser {
     this.pos++;
     this.checkEOS();
     this.pos++;
-    return (new Commands.Break());
+    return new Commands.Break();
   }
 
   parseReturn () {
@@ -706,7 +711,7 @@ export class IVProgParser {
       this.pos++;
       return funcCall;
     } else {
-      throw SyntaxError.createError("= or (", equalOrParenthesis);
+      throw SyntaxErrorFactory.token_missing_list(['=','('], equalOrParenthesis);
     }
   }
 
@@ -719,7 +724,7 @@ export class IVProgParser {
     const id = this.parseID();
     const equal = this.getToken();
     if (equal.type !== this.lexerClass.EQUAL) {
-      throw SyntaxError.createError('=', equal);
+      throw SyntaxErrorFactory.token_missing_one('=', equal);
     }
     this.pos++
     const exp = this.parseExpressionOR();
@@ -732,7 +737,7 @@ export class IVProgParser {
   parseCases () {
     const token = this.getToken();
     if(token.type !== this.lexerClass.RK_CASE) {
-      throw SyntaxError.createError(this.lexer.literalNames[this.lexerClass.RK_CASE], token);
+      throw SyntaxErrorFactory.token_missing_one(this.lexer.literalNames[this.lexerClass.RK_CASE], token);
     }
     this.pos++;
     const nextToken = this.getToken();
@@ -740,7 +745,7 @@ export class IVProgParser {
       this.pos++;
       const colonToken = this.getToken();
       if (colonToken.type !== this.lexerClass.COLON) {
-        throw SyntaxError.createError(':', colonToken);
+        throw SyntaxErrorFactory.token_missing_one(':', colonToken);
       }
       this.pos++;
       this.consumeNewLines();
@@ -752,7 +757,7 @@ export class IVProgParser {
       const exp = this.parseExpressionOR();
       const colonToken = this.getToken();
       if (colonToken.type !== this.lexerClass.COLON) {
-        throw SyntaxError.createError(':', colonToken);
+        throw SyntaxErrorFactory.token_missing_one(':', colonToken);
       }
       this.pos++;
       this.consumeNewLines();
@@ -888,7 +893,7 @@ export class IVProgParser {
       case this.lexerClass.OPEN_PARENTHESIS:
         return this.parseParenthesisExp();
       default:
-        throw SyntaxError.createError('Terminal', token);
+        throw SyntaxErrorFactory.invalid_terminal(token);
     }
   }
 
@@ -982,14 +987,8 @@ export class IVProgParser {
     return list;
   }
 
-  getTypesAsString () {
+  getTypeArray () {
     const types = this.insideScope(IVProgParser.FUNCTION) ? this.functionTypes : this.variableTypes;
-    return types.map( x => this.lexer.literalNames[x])
-      .reduce((o, n) => {
-        if (o.length <= 0)
-          return n;
-        else
-          return o + ", " + n;
-      }, '');
+    return types.map( x => this.lexer.literalNames[x]);
   }
 }

+ 14 - 14
js/ast/operators.js

@@ -1,18 +1,18 @@
 export const Operators = Object.freeze({
-  ADD: 0,
-  SUB: 1,
-  MULT: 2,
-  DIV: 3,
-  MOD: 4,
-  GT: 5,
-  GE: 6,
-  LT: 7,
-  LE: 8,
-  EQ: 9,
-  NEQ: 10,
-  AND: 11,
-  OR: 12,
-  NOT: 13
+  ADD: {ord: 0, value: "+"},
+  SUB: {ord: 1, value: "-"},
+  MULT: {ord: 2, value: '*'},
+  DIV: {ord: 3, value: '/'},
+  MOD: {ord: 4, value: '%'},
+  GT: {ord: 5, value: '>'},
+  GE: {ord: 6, value: '>='},
+  LT: {ord: 7, value: '<'},
+  LE: {ord: 8, value: '<='},
+  EQ: {ord: 9, value: '=='},
+  NEQ: {ord: 10, value: '!='},
+  AND: {ord: 11, value: 'and'},
+  OR: {ord: 12, value: 'or'},
+  NOT: {ord: 13, value: 'not'}
 });
 
 export function convertFromString (op) {

+ 17 - 15
js/ast/types.js

@@ -1,12 +1,12 @@
 export const Types = Object.freeze({
-  INTEGER: "int",
-  REAL: "real",
-  STRING: "string",
-  BOOLEAN: "bool",
-  VOID: "void",
-  ARRAY: 'array',
-  UNDEFINED: 'undefined',
-  ALL: 'all'
+  INTEGER: {value: "int", ord: 0},
+  REAL: {value: "real", ord: 1},
+  STRING: {value: "string", ord: 2},
+  BOOLEAN: {value: "bool", ord: 3},
+  VOID: {value: "void", ord: 4},
+  ARRAY: {value: 'array', ord: 5},
+  UNDEFINED: {value: 'undefined', ord: 6},
+  ALL: {value: 'all', ord: 7}
 });
 
 export function toInt (str) {
@@ -20,13 +20,15 @@ export function toInt (str) {
 }
 
 export function toString (str) {
-  let value = str.replace("\\b", "\b");
-  value = value.replace("\\t", "\t");
-  value = value.replace("\\n", "\n");
-  value = value.replace("\\r", "\r");
-  value = value.replace("\\\"", "\"");
-  value = value.replace("\\\'", "\'");
-  value = value.replace("\\\\", "\\");
+  let value = str.replace(/^"/, '');
+  value = value.replace(/"$/, '');
+  value = value.replace(/\\b/g, "\b");
+  value = value.replace(/\\t/g, "\t");
+  value = value.replace(/\\n/g, "\n");
+  value = value.replace(/\\r/g, "\r");
+  value = value.replace(/\\\"/g, "\"");
+  value = value.replace(/\\\'/g, "\'");
+  value = value.replace(/\\\\/g, "\\");
   return value;
 }
 

+ 9 - 7
js/iassign-integration-functions.js

@@ -1,20 +1,24 @@
 // Função para ler parâmetros informados pelo iTarefa via URL
 // Apesar de não ser obrigatório, será muito útil para capturar os parâmetros
-function getParameterByName(name) {
+function getParameterByName(name, defaultReturn = null) {
     var match = RegExp('[?&]' + name + '=([^&]*)').exec(window.location.search);
-    return match ? decodeURIComponent(match[1].replace(/\+/g, ' ')) : null;
+    return match ? decodeURIComponent(match[1].replace(/\+/g, ' ')) : defaultReturn;
 }
 
-// Criando um array com os parâmetros informados pelo iTarefa
+// Criando um object com os parâmetros informados pelo iTarefa
 // Observe que para cada parâmetro, é realizada a chamada do método getParameterByName, implementado acima
 var iLMparameters = {
     iLM_PARAM_ServerToGetAnswerURL: getParameterByName("iLM_PARAM_ServerToGetAnswerURL"),
     iLM_PARAM_SendAnswer: getParameterByName("iLM_PARAM_SendAnswer"),
     iLM_PARAM_AssignmentURL: getParameterByName("iLM_PARAM_AssignmentURL"),
     iLM_PARAM_Assignment: getParameterByName("iLM_PARAM_Assignment"),
-    lang: getParameterByName("lang")
+    lang: getParameterByName("lang", "en")
 };
 
+// Set the lang parameter to the localStorage for easy access
+// and no dependency to the global scope, avoind future 'strict mode' problems
+localStorage.setItem('ivprog.lang', iLMparameters.lang);
+
 // Função chamada pelo iTarefa quando o professor finaliza a criação da atividade
 // ou quando o aluno finaliza a resolução do exercício
 // O retorno é um JSON com os dados do exercício ou da resolução
@@ -48,9 +52,7 @@ function getiLMContent() {
 
     // O parâmetro "iLM_PARAM_Assignment" fornece o URL do endereço que deve ser
     // requisitado via AJAX para a captura dos dados da atividade
-    $.get(iLMparameters.iLM_PARAM_Assignment, function (d) {
-
+    $.get(iLMparameters.iLM_PARAM_Assignment, function (data) {
         
-
     });
 }

+ 1 - 1
js/io/domInput.js

@@ -29,7 +29,7 @@ export class DOMInput extends Input{
 
   notifyInput (text) {
     this.listeners.forEach(resolve => {
-      resolve(l);
+      resolve(text);
     })
     this.listeners.splice(0, this.listeners.length);
   }

+ 8 - 2
js/io/domOutput.js

@@ -9,7 +9,13 @@ export class DOMOutput extends Output {
   }
 
   sendOutput (text) {
-    const line = $(`<span class='ivprog-io-output> ${text} </span>`);
-    this.el.append(line);
+    text = text.replace("\n", '</br>');
+    text = text.replace(/\t/g,'&#9;');
+    const span = $('<span />').addClass('ivprog-io-output-text').html(text);
+    this.el.append(span);
+  }
+
+  clear () {
+    this.el.empty();
   }
 }

+ 32 - 40
js/main.js

@@ -1,23 +1,12 @@
-import {
-    InputStream,
-    CommonTokenStream
-} from 'antlr4/index';
-import * as Commands from './ast/commands';
 import { IVProgParser } from './ast/ivprogParser';
-import Lexers from '../grammar/';
 import { IVProgProcessor } from './processor/ivprogProcessor';
+import {DOMInput} from './io/domInput';
+import {DOMOutput} from './io/domOutput';
+import { LanguageService } from './services/languageService';
+import { LocalizedStrings } from './services/localizedStringsService';
 
-const lang = 'pt_br';
-
-const ivprogLexer = Lexers[lang];
-
-const input = `programa {
-             
-  funcao inicio() {
-     inteiro a[2] = {1,2}
-  }
-
-}`;
+const ivprogLexer = LanguageService.getCurrentLexer();
+console.log(LocalizedStrings.getUI('start'));
 
 // const lexer = new ivprogLexer(new InputStream(input));
 // const stream = new CommonTokenStream(lexer);
@@ -29,27 +18,30 @@ const input = `programa {
 //     console.log('\n')
 //     i++;
 // }
-const anaSin = new IVProgParser(input, ivprogLexer);
-const proc = new IVProgProcessor(anaSin.parseTree());
-proc.interpretAST().then( sto => {
-  console.log(sto.applyStore('a'));
-}).catch(e => console.log(e));
-// try {
-//   const data = anaSin.parseTree();
-//   console.log(data);
-//   var editor = new JsonEditor('#json-renderer', data);
-//   $('#btn').click( () => {
-//     const input = $('#input').val();
-//     const analiser = new IVProgParser(input, ivprogLexer);
-//     try {
-//       const data = analiser.parseTree();
-//       console.log(data);
-//       editor.load(data);  
-//     } catch (error) {
-//       alert(error);
-//     }
+// const anaSin = new IVProgParser(input, ivprogLexer);
+const editor = new JsonEditor('#json-renderer', {});
+const domIn = new DOMInput('#dom-in');
+const domOut = new DOMOutput('#dom-out');
+// proc.interpretAST().then( sto => {
+//   console.log(sto.applyStore('a'));
+// }).catch(e => console.log(e));
+try {
+  $('#btn').click( () => {
+    const input = $('#input').val();
+    const analiser = new IVProgParser(input, ivprogLexer);
+    try {
+      const data = analiser.parseTree();
+      const proc = new IVProgProcessor(data);
+      proc.registerInput(domIn);
+      domOut.clear();
+      proc.registerOutput(domOut);
+      proc.interpretAST().then(sto => editor.load(sto.store))
+        .catch( e => alert(e));
+    } catch (error) {
+      alert(error);
+    }
     
-//   });
-// } catch(a) {
-//   console.log(a);
-// }
+  });
+} catch(a) {
+  console.log(a);
+}

+ 138 - 52
js/processor/compatibilityTable.js

@@ -1,60 +1,146 @@
 import { Types } from './../ast/types';
 import { Operators } from './../ast/operators';
 
-const InfixCompatibilityTable = Object.freeze([
-  /*Add*/[Types.INTEGER, Types.REAL, Types.STRING],
-  /*Sub*/[Types.INTEGER, Types.REAL],
-  /*Mult*/[Types.INTEGER, Types.REAL],
-  /*Div*/[Types.INTEGER, Types.REAL],
-  /*Mod*/[Types.INTEGER],
-  /*Gt*/[Types.INTEGER, Types.REAL],
-  /*Ge*/[Types.INTEGER, Types.REAL],
-  /*Lt*/[Types.INTEGER, Types.REAL],
-  /*Le*/[Types.INTEGER, Types.REAL],
-  /*Eq*/[Types.INTEGER, Types.REAL, Types.STRING, Types.BOOLEAN],
-  /*Neq*/[Types.INTEGER, Types.REAL, Types.STRING, Types.BOOLEAN],
-  /*And*/[Types.BOOLEAN],
-  /*Or*/[Types.BOOLEAN],
-  /*Not*/null,
-]);
-
-const UnaryCompatibilityTable = Object.freeze([
-  /*Add*/[Types.INTEGER, Types.REAL],
-  /*Sub*/[Types.INTEGER, Types.REAL],
-  /*Mult*/null,
-  /*Div*/null,
-  /*Mod*/null,
-  /*Gt*/null,
-  /*Ge*/null,
-  /*Lt*/null,
-  /*Le*/null,
-  /*Eq*/null,
-  /*Neq*/null,
-  /*And*/null,
-  /*Or*/null,
-  /*Not*/[Types.BOOLEAN],
-]);
-
-export function canApplyUnaryOp (op, a) {
-  const list = UnaryCompatibilityTable[op];
-  if (!!!list) {
-    return false;
-  }
-  const type = list.find(t => t === a.type);
-  if (!!!type) {
-    return false;
-  }
-  return true
+function buildInfixAddTable () {
+  const table = [[], [], [], []];
+
+  table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER;
+  table[Types.INTEGER.ord][Types.REAL.ord] = Types.REAL;
+  table[Types.INTEGER.ord][Types.STRING.ord] = Types.STRING;
+
+  table[Types.REAL.ord][Types.INTEGER.ord] = Types.REAL;
+  table[Types.REAL.ord][Types.REAL.ord] = Types.REAL;
+  table[Types.REAL.ord][Types.STRING.ord] = Types.STRING;
+
+  table[Types.STRING.ord][Types.INTEGER.ord] = Types.STRING;
+  table[Types.STRING.ord][Types.REAL.ord] = Types.STRING;
+  table[Types.STRING.ord][Types.STRING.ord] = Types.STRING;
+  table[Types.STRING.ord][Types.BOOLEAN.ord] = Types.STRING;
+
+  return table;
+}
+
+function buildInfixMultiDivSubTable () {
+  const table = [[], [], [], []];
+
+  table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER;
+  table[Types.INTEGER.ord][Types.REAL.ord] = Types.REAL;
+
+  table[Types.REAL.ord][Types.INTEGER.ord] = Types.REAL;
+  table[Types.REAL.ord][Types.REAL.ord] = Types.REAL;
+
+  return table;
+}
+
+function buildInfixEqualityInequalityTable () {
+  const table = [[], [], [], []];
+
+  table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.BOOLEAN;
+
+  table[Types.REAL.ord][Types.REAL.ord] = Types.BOOLEAN;
+
+  table[Types.BOOLEAN.ord][Types.BOOLEAN.ord] = Types.BOOLEAN;
+
+  table[Types.STRING.ord][Types.STRING.ord] = Types.BOOLEAN;
+
+  return table;
+}
+
+function buildInfixRelationalTable () {
+  const table = [[], [], [], []];
+
+  table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.BOOLEAN;
+
+  table[Types.REAL.ord][Types.REAL.ord] = Types.BOOLEAN;
+
+  table[Types.STRING.ord][Types.STRING.ord] = Types.BOOLEAN;
+
+  return table;
 }
 
-export function canApplyInfixOp (op, a, b) {
-  const list = InfixCompatibilityTable[op];
-  if (!!!list) {
-    return false;
+function buildInfixAndOrTable () {
+  const table = [[], [], [], []];
+
+  table[Types.BOOLEAN.ord][Types.BOOLEAN.ord] = Types.BOOLEAN;
+
+  return table;
+}
+
+function buildInfixModTable () {
+  const table = [[], [], [], []];
+
+  table[Types.INTEGER.ord][Types.INTEGER.ord] = Types.INTEGER;
+
+  return table;
+}
+
+function buildUnarySumSubList () {
+  const list = [];
+
+  list[Types.INTEGER.ord] = Types.INTEGER;
+
+  list[Types.REAL.ord] = Types.REAL;
+
+  return list;
+}
+
+function buildUnaryNegList () {
+  const list = [];
+
+  list[Types.BOOLEAN.ord] = Types.BOOLEAN;
+
+  return list;
+}
+
+function buildInfixCompatibilityTable () {
+  const compatibilityMap = new WeakMap();
+  compatibilityMap.set(Operators.ADD, buildInfixAddTable());
+  compatibilityMap.set(Operators.SUB, buildInfixMultiDivSubTable());
+  compatibilityMap.set(Operators.MULT, buildInfixMultiDivSubTable());
+  compatibilityMap.set(Operators.DIV, buildInfixMultiDivSubTable());
+  compatibilityMap.set(Operators.EQ, buildInfixEqualityInequalityTable());
+  compatibilityMap.set(Operators.NEQ, buildInfixEqualityInequalityTable());
+  compatibilityMap.set(Operators.GE, buildInfixRelationalTable());
+  compatibilityMap.set(Operators.GT, buildInfixRelationalTable());
+  compatibilityMap.set(Operators.LE, buildInfixRelationalTable());
+  compatibilityMap.set(Operators.LT, buildInfixRelationalTable());
+  compatibilityMap.set(Operators.OR, buildInfixAndOrTable());
+  compatibilityMap.set(Operators.AND, buildInfixAndOrTable());
+  compatibilityMap.set(Operators.MOD, buildInfixModTable());
+  return compatibilityMap;
+}
+
+function buildUnaryCompatibilityTable () {
+  const compatibilityMap = new WeakMap();
+  compatibilityMap.set(Operators.ADD, buildUnarySumSubList());
+  compatibilityMap.set(Operators.SUB, buildUnarySumSubList());
+  compatibilityMap.set(Operators.NOT, buildUnaryNegList());
+  return compatibilityMap;
+}
+
+const infixMap = buildInfixCompatibilityTable();
+const unaryMap = buildUnaryCompatibilityTable();
+
+export function resultTypeAfterInfixOp (operator, leftExpressionType, rightExpressionType) {
+  try {
+    return infixMap.get(operator)[leftExpressionType.ord][rightExpressionType.ord];
+  } catch (e) {
+    if (e instanceof TypeError) {
+      return Types.UNDEFINED;
+    } else {
+      throw e;
+    }
   }
-  const type = list.find(t => t === a.type);
-  if (!!!type) {
-    return false;
+}
+
+export function resultTypeAfterUnaryOp (operator, leftExpressionType) {
+  try {
+    return unaryMap.get(operator)[leftExpressionType.ord];
+  } catch (e) {
+    if (e instanceof TypeError) {
+      return Types.UNDEFINED;
+    } else {
+      throw e;
+    } 
   }
-  return type === b.type;
 }

+ 1 - 1
js/processor/definedFunctions.js

@@ -4,7 +4,7 @@ import {Types} from './../ast/types';
 function createOutputFun () {
   const block = new Commands.CommandBlock([], [new Commands.SysCall('$write')]);
   const func = new Commands.Function('$write', Types.VOID,
-    [new Commands.FormalParameter(Types.ALL, 'p1', 0, true)],
+    [new Commands.FormalParameter(Types.ALL, 'p1', 0, false)],
     block);
   return func;
 }

+ 7 - 0
js/processor/error/processorErrorFactory.js

@@ -0,0 +1,7 @@
+import { RuntimeError } from './runtimeError';
+import { SemanticError } from './semanticError';
+import { LocalizedStrings } from './../../services/localizedStringsService';
+
+export const ProcessorErrorFactory  = Object.freeze({
+  
+});

+ 8 - 0
js/processor/error/runtimeError.js

@@ -0,0 +1,8 @@
+export class RuntimeError extends Error {
+
+  constructor (...msg) {
+    super(...msg);
+    if(Error.captureStackTrace)
+      Error.captureStackTrace(this, RuntimeError);
+  }
+}

+ 8 - 0
js/processor/error/semanticError.js

@@ -0,0 +1,8 @@
+export class SemanticError extends Error {
+
+  constructor (...msg) {
+    super(...msg);
+    if(Error.captureStackTrace)
+      Error.captureStackTrace(this, SemanticError);
+  }
+}

+ 55 - 57
js/processor/ivprogProcessor.js

@@ -7,13 +7,13 @@ import { Context } from './context';
 import { Types, toInt } from './../ast/types';
 import { Operators } from './../ast/operators';
 import { NAMES, LanguageDefinedFunction } from './definedFunctions';
-import { canApplyInfixOp, canApplyUnaryOp } from './compatibilityTable';
+import { resultTypeAfterInfixOp, resultTypeAfterUnaryOp } from './compatibilityTable';
 import * as Commands from './../ast/commands/';
 import * as Expressions from './../ast/expressions/';
 
 export class IVProgProcessor {
 
-  constructor(ast) {
+  constructor (ast) {
     this.ast = ast;
     this.globalStore = new Store();
     this.stores = [this.globalStore];
@@ -170,8 +170,6 @@ export class IVProgProcessor {
 
               if(formalParameter.byRef) {
                 const ref = new StoreObjectRef(stoObj.id, callerStore);
-                console.log('it\'s a ref...');
-                console.log(ref);
                 calleeStore.insertStore(formalParameter.id, ref);
               } else {
                 calleeStore.insertStore(formalParameter.id, stoObj);
@@ -186,23 +184,12 @@ export class IVProgProcessor {
   }
 
   executeCommands (store, cmds) {
-    const auxExecCmdFun = (promise, cmd) => promise.then( sto => this.executeCommand(sto, cmd));
-    let breakLoop = false;
-    let $result = Promise.resolve(store);
-    for (let index = 0; index < cmds.length && !breakLoop; index++) {
-      const cmd = cmds[index];
-      $result = auxExecCmdFun($result, cmd);
-      $result.then(sto => {
-        if(sto.mode === Modes.RETURN) {
-          breakLoop = true;
-        } else if (this.checkContext(Context.BREAKABLE && 
-          sto.mode === Modes.BREAK)) {
-            breakLoop = true;
-        }
-        return sto;
-      });
-    }
-    return $result;
+    // helper to partially apply a function, in this case executeCommand
+    const partial = (fun, cmd) => (sto) => fun(sto, cmd);
+    return cmds.reduce((lastCommand, next) => {
+      const nextCommand = partial(this.executeCommand.bind(this), next);
+      return lastCommand.then(nextCommand);
+    }, Promise.resolve(store));
   }
 
   executeCommand (store, cmd) {
@@ -682,17 +669,18 @@ export class IVProgProcessor {
   evaluateUnaryApp (store, unaryApp) {
     const $left = this.evaluateExpression(store, unaryApp.left);
     return $left.then( left => {
-      if (!canApplyUnaryOp(unaryApp.op, left)) {
+      const resultType = resultTypeAfterUnaryOp(unaryApp.op, left.type);
+      if (resultType === Types.UNDEFINED) {
         // TODO: better urgent error message
         return Promise.reject(new Error(`Cannot use this op to ${left.type}`));
       }
-      switch (unaryApp.op) {
-        case Operators.ADD:
-          return new StoreObject(left.type, +left.value);
-        case Operators.SUB:
-          return new StoreObject(left.type, -left.value);
-        case Operators.NOT:
-          return new StoreObject(left.type, !left.value);
+      switch (unaryApp.op.ord) {
+        case Operators.ADD.ord:
+          return new StoreObject(resultType, +left.value);
+        case Operators.SUB.ord:
+          return new StoreObject(resultType, -left.value);
+        case Operators.NOT.ord:
+          return new StoreObject(resultType, !left.value);
         default:
         return Promise.reject(new Error('!!!Critical Invalid UnaryApp '+ unaryApp.op));
       }
@@ -705,37 +693,47 @@ export class IVProgProcessor {
     return Promise.all([$left, $right]).then(values => {
       const left = values[0];
       const right = values[1];
-      if (!canApplyInfixOp(infixApp.op, left, right)) {
+      const resultType = resultTypeAfterInfixOp(infixApp.op, left.type, right.type);
+      if (resultType === Types.UNDEFINED) {
         // TODO: better urgent error message
         return Promise.reject(new Error(`Cannot use this op to ${left.type} and ${right.type}`));
       }
-      switch (infixApp.op) {
-        case Operators.ADD:
-          return new StoreObject(left.type, left.value + right.value);
-        case Operators.SUB:
-          return new StoreObject(left.type, left.value - right.value);
-        case Operators.MULT:
-          return new StoreObject(left.type, left.value * right.value);
-        case Operators.DIV:
-          return new StoreObject(left.type, left.value / right.value);
-        case Operators.MOD:
-          return new StoreObject(left.type, left.value % right.value);
-        case Operators.GT:
-          return new StoreObject(Types.BOOLEAN, left.value > right.value);
-        case Operators.GE:
-          return new StoreObject(Types.BOOLEAN, left.value >= right.value);
-        case Operators.LT:
-          return new StoreObject(Types.BOOLEAN, left.value < right.value);
-        case Operators.LE:
-          return new StoreObject(Types.BOOLEAN, left.value <= right.value);
-        case Operators.EQ:
-          return new StoreObject(Types.BOOLEAN, left.value === right.value);
-        case Operators.NEQ:
-          return new StoreObject(Types.BOOLEAN, left.value !== right.value);
-        case Operators.AND:
-          return new StoreObject(Types.BOOLEAN, left.value && right.value);
-        case Operators.OR:
-          return new StoreObject(Types.BOOLEAN, left.value || right.value);
+      let result = null;
+      switch (infixApp.op.ord) {
+        case Operators.ADD.ord:
+          return new StoreObject(resultType, left.value + right.value);
+        case Operators.SUB.ord:
+          return new StoreObject(resultType, left.value - right.value);
+        case Operators.MULT.ord: {
+          result = left.value * right.value;
+          if (resultType === Types.INTEGER)
+            result = Math.trunc(result);
+          return new StoreObject(resultType, result);
+        }
+        case Operators.DIV.ord: {
+          result = left.value / right.value;
+          if (resultType === Types.INTEGER)
+            result = Math.trunc(result);
+          return new StoreObject(resultType, result);
+        }
+        case Operators.MOD.ord:
+          return new StoreObject(resultType, left.value % right.value);
+        case Operators.GT.ord:
+          return new StoreObject(resultType, left.value > right.value);
+        case Operators.GE.ord:
+          return new StoreObject(resultType, left.value >= right.value);
+        case Operators.LT.ord:
+          return new StoreObject(resultType, left.value < right.value);
+        case Operators.LE.ord:
+          return new StoreObject(resultType, left.value <= right.value);
+        case Operators.EQ.ord:
+          return new StoreObject(resultType, left.value === right.value);
+        case Operators.NEQ.ord:
+          return new StoreObject(resultType, left.value !== right.value);
+        case Operators.AND.ord:
+          return new StoreObject(resultType, left.value && right.value);
+        case Operators.OR.ord:
+          return new StoreObject(resultType, left.value || right.value);
         default:
           return Promise.reject(new Error('!!!Critical Invalid InfixApp '+ infixApp.op));
       }

+ 29 - 0
js/services/languageService.js

@@ -0,0 +1,29 @@
+import Lexers from './../../grammar/';
+
+const DEFAULT_LANG = "pt";
+
+export const LanguageService  = Object.freeze({
+
+  getLang: () => {
+    const lang = localStorage.getItem('ivprog.lang');
+    if (lang === null || lang === undefined) {
+      console.warn("Internal Error. User language information has not been set. Returning default...");
+      return LanguageService.getDefaultLang();
+    }
+    return lang;
+  },
+
+  getDefaultLang: () => {
+    return DEFAULT_LANG;
+  },
+
+  getCurrentLexer: () => {
+    const lexer = Lexers[LanguageService.getLang()];
+    if(lexer === null || lexer === undefined) {
+      return Lexers[DEFAULT_LANG];
+    } else {
+      return lexer;
+    }
+  }
+
+});

+ 52 - 0
js/services/localizedStringsService.js

@@ -0,0 +1,52 @@
+import { LanguageService } from "./languageService";
+import Langs from './../../i18n';
+
+export const StringTypes = Object.freeze({
+  ERROR: "error",
+  MESSAGE: "message",
+  UI: "ui"
+});
+
+export const LocalizedStrings = Object.freeze({
+
+  getString: (id, type) => {
+    let i18nObj = Langs[LanguageService.getLang()];
+    if(!!!i18nObj) {
+      console.warn(`Internal Error. The language set at ivprog.lang is not valid: ${LanguageService.getLang()}`);
+      i18nObj = Langs[LanguageService.getDefaultLang()];
+    }
+    if(!!!i18nObj[type]) {
+      return "{MISSING_I18N_TYPE_IDENTIFIER}";
+    } else if (!!!i18nObj[type][id]) {
+      return "{MISSING_I18N_IDENTIFIER}";
+    } else {
+      return i18nObj[type][id];
+    }
+  },
+
+  getOR: () => LocalizedStrings.getUI('join_or'),
+
+  getError: (id, context = []) => {
+    const text = LocalizedStrings.getString(id, StringTypes.ERROR);
+    return LocalizedStrings.processString(text, context)
+  },
+
+  getMessage: (id, context = []) => {
+    const text = LocalizedStrings.getString(id, StringTypes.MESSAGE);
+    return LocalizedStrings.processString(text, context)
+  },
+
+  getUI: (id, context = []) => {
+    const text = LocalizedStrings.getString(id, StringTypes.UI);
+    return LocalizedStrings.processString(text, context)
+  },
+
+  processString: (text, context) => {
+    for (let i = 0; i < context.length; i++) {
+      const v = context[i];
+      text = text.replace('\$'+i, v);
+    }
+    return text;
+  }
+
+});

+ 8 - 11
tests/test00.spec.js

@@ -1,30 +1,27 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
 import * as Expressions from './../js/ast/expressions/';
 import * as Commands from './../js/ast/commands/';
+import { LanguageService } from './../js/services/languageService';
 import { Types } from './../js/ast/types';
 
 describe("Testing Syntax Analysis for default", () => {
-  var lang = 'pt_br';
+
   var input;
 
-  var asa;
-  var lexer;
+  const asa = {
+    global: [new Commands.Declaration('PI', Types.REAL, new Expressions.IntLiteral(1), true),
+    new Commands.ArrayDeclaration('a', Types.INTEGER, new Expressions.IntLiteral(5), new Expressions.IntLiteral(5), null, true)],
+    functions: []
+  };
+  const lexer  = LanguageService.getCurrentLexer();
 
   it("it should produce a valid AST", () => {
-    lexer  = Lexers[lang];
     input = `programa {
     const real PI = 1
     const inteiro a[5][5]
     }`;
-
-    asa = {
-        global: [new Commands.Declaration('PI', Types.REAL, new Expressions.IntLiteral(1), true),
-        new Commands.ArrayDeclaration('a', Types.INTEGER, new Expressions.IntLiteral(5), new Expressions.IntLiteral(5), null, true)],
-        functions: []
-      };
       const as = new IVProgParser(input, lexer);
       expect(as.parseTree()).toEqual(asa);
   });

+ 3 - 7
tests/test01.spec.js

@@ -1,9 +1,5 @@
-import Lexers from './../grammar/';
-import {
-    IVProgParser
-} from './../js/ast/ivprogParser';
-import {
-    SyntaxError}from './../js/ast/SyntaxError';
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { LanguageService } from './../js/services/languageService';
 
 describe('Illegal newline syntax', () => {
 
@@ -15,7 +11,7 @@ describe('Illegal newline syntax', () => {
       PI = 5.6, c;
   }
   `;
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should result in SyntaxError`, () => {
       const as = new IVProgParser(input, lexer);

+ 2 - 4
tests/test02.spec.js

@@ -1,9 +1,7 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError}from './../js/ast/SyntaxError';
+import { LanguageService } from './../js/services/languageService';
 
 describe('Legal newline syntax', () => {
 
@@ -20,7 +18,7 @@ describe('Legal newline syntax', () => {
     }
   }
   `;
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should not result in SyntaxError`, () => {
       const as = new IVProgParser(input, lexer);

+ 3 - 5
tests/test03.spec.js

@@ -1,17 +1,15 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
+
 
 describe('Expressions which ends with ID terminals', () => {
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 5
tests/test04.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Literal arrays that have more than 2 dimensions', () => {
+
     let input = `programa {
         const inteiro a[1][1] = {
             {
@@ -19,7 +17,7 @@ describe('Literal arrays that have more than 2 dimensions', () => {
             retorna
         }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 4 - 5
tests/test05.spec.js

@@ -1,12 +1,11 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
+
 
 describe('IfThenElse command', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -16,7 +15,7 @@ describe('IfThenElse command', () => {
         a = 0
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 2 - 5
tests/test06.spec.js

@@ -1,10 +1,7 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('While command', () => {
     let input = `funcao inteiro test(real i) {
@@ -14,7 +11,7 @@ describe('While command', () => {
         a = a + 1
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 5
tests/test07.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('For command', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -14,7 +12,7 @@ describe('For command', () => {
          a = a
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 2 - 5
tests/test08.spec.js

@@ -1,10 +1,7 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Break command inside a loop', () => {
     let input = `funcao inteiro test(real i) {
@@ -17,7 +14,7 @@ describe('Break command inside a loop', () => {
         }
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 6
tests/test09.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Break command outside a loop', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -15,8 +13,7 @@ describe('Break command outside a loop', () => {
       }
       pare
     }`;
-    const lexer = Lexers['pt_br'];
-
+    const lexer = LanguageService.getCurrentLexer();
     it(`should result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);
         const fun = as.parseFunction.bind(as);

+ 3 - 5
tests/test10.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('IfThenElseIfThenElse command chain', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -18,7 +16,7 @@ describe('IfThenElseIfThenElse command chain', () => {
         a = -1
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 5
tests/test11.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('DoWhile command', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -14,7 +12,7 @@ describe('DoWhile command', () => {
         a = a + 1
       } enquanto (a > 5)
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 5
tests/test12.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
 
 describe('SwitchCase command', () => {
+
     let input = `funcao inteiro test(real i) {
       escolha (i) {
         caso 1:
@@ -15,7 +13,7 @@ describe('SwitchCase command', () => {
           retorne 4
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 2
tests/test13.spec.js

@@ -1,9 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('A complete program code', () => {
+
     let input = `programa {
       
       const real PI = 5.7e-10
@@ -18,7 +19,7 @@ describe('A complete program code', () => {
         }
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 4 - 5
tests/test14.spec.js

@@ -1,12 +1,11 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
-import {
-    SyntaxError
-} from './../js/ast/SyntaxError';
+import { LanguageService } from '../js/services/languageService';
+
 
 describe('For with no \';\'', () => {
+
     let input = `funcao inteiro test(real i) {
       inteiro a = 5 + i
       a = 5 + G[i][6]
@@ -16,7 +15,7 @@ describe('For with no \';\'', () => {
          a = a
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 2
tests/test15.spec.js

@@ -1,9 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('A start function with no return type', () => {
+
     let input = `programa {
       
       const real PI = 5.5
@@ -23,7 +24,7 @@ describe('A start function with no return type', () => {
         }
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 2
tests/test16.spec.js

@@ -1,9 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Variable declaration inside a function', () => {
+
     let input = `programa {
       
       const real PI = 5.5
@@ -24,7 +25,7 @@ describe('Variable declaration inside a function', () => {
         }
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not result in SyntaxError`, () => {
         const as = new IVProgParser(input, lexer);

+ 3 - 2
tests/test17.spec.js

@@ -1,4 +1,3 @@
-import Lexers from './../grammar/';
 import * as Expressions from './../js/ast/expressions/';
 import * as Commands from './../js/ast/commands/';
 import { Operators } from './../js/ast/operators';
@@ -6,8 +5,10 @@ import {Types} from './../js/ast/types';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Variable declaration inside a function', () => {
+
     let input = `programa {
 
       funcao inicio() {
@@ -15,7 +16,7 @@ describe('Variable declaration inside a function', () => {
         a = a + 1
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     const ast = {
       global: [ ],

+ 3 - 5
tests/test18.spec.js

@@ -1,12 +1,10 @@
-import Lexers from './../grammar/';
-import * as Expressions from './../js/ast/expressions/';
-import * as Commands from './../js/ast/commands/';
-import {Types} from './../js/ast/types';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Call to a function that receives no parameter', () => {
+
     let input = `programa {
 
       funcao inicio() {
@@ -18,7 +16,7 @@ describe('Call to a function that receives no parameter', () => {
         retorne
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not throw an Error`, () => {
         const as = new IVProgParser(input, lexer);

+ 2 - 3
tests/test19.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('Multi(*) operation', function () {
 
@@ -13,7 +12,7 @@ describe('Multi(*) operation', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should have higher priority than Sum(+)`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 3 - 3
tests/test20.spec.js

@@ -1,10 +1,10 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('An Array initialization with expressions', function () {
 
+
   const input = `programa {
 
     funcao inicio() {
@@ -12,7 +12,7 @@ describe('An Array initialization with expressions', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
   const result = [4,15];
 
   it(`should produce a valid state`, function (done) {

+ 2 - 3
tests/test21.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A call to a function that returns a valid type', function () {
 
@@ -17,7 +16,7 @@ describe('A call to a function that returns a valid type', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
   const result = 2;
 
   it(`should produce a valid state`, function (done) {

+ 3 - 2
tests/test22.spec.js

@@ -1,9 +1,10 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('An assignment to a variable', function () {
 
+
   const input = `programa {
 
     funcao inicio() {
@@ -12,7 +13,7 @@ describe('An assignment to a variable', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should respect the variable type`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 3 - 2
tests/test23.spec.js

@@ -1,9 +1,10 @@
-import Lexers from './../grammar/';
 import {
     IVProgParser
 } from './../js/ast/ivprogParser';
+import { LanguageService } from '../js/services/languageService';
 
 describe('Variable initialization with a function call', () => {
+
     let input = `programa {
 
       funcao inicio() {
@@ -14,7 +15,7 @@ describe('Variable initialization with a function call', () => {
         retorne a * 2
       }
     }`;
-    const lexer = Lexers['pt_br'];
+    const lexer = LanguageService.getCurrentLexer();
 
     it(`should not throw an Error`, () => {
         const as = new IVProgParser(input, lexer);

+ 2 - 2
tests/test24.spec.js

@@ -1,6 +1,6 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('Command Do...While', function () {
 
@@ -14,7 +14,7 @@ describe('Command Do...While', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should result in a valid state`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test25.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('Assigning an ID to another variable', function () {
 
@@ -13,7 +12,7 @@ describe('Assigning an ID to another variable', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should result in a valid state`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test26.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('Assigning a line from a matrix to another vector', function () {
 
@@ -13,7 +12,7 @@ describe('Assigning a line from a matrix to another vector', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should result in a valid state`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test27.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A finite while loop', function () {
 
@@ -17,7 +16,7 @@ describe('A finite while loop', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should terminate`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test28.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A break command inside a inner while loop', function () {
 
@@ -19,7 +18,7 @@ describe('A break command inside a inner while loop', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should terminate the inner while only`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test29.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A break command inside a for loop', function () {
 
@@ -15,7 +14,7 @@ describe('A break command inside a for loop', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should terminate it`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test30.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A break command inside a switch..case', function () {
 
@@ -25,7 +24,7 @@ describe('A break command inside a switch..case', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should terminate it`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test31.spec.js

@@ -1,7 +1,6 @@
-import Lexers from './../grammar/';
-import {Types} from './../js/ast/types';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A case without return/break', function () {
 
@@ -24,7 +23,7 @@ describe('A case without return/break', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should fall through`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 2
tests/test33.spec.js

@@ -1,6 +1,6 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A non-const global variable', function () {
 
@@ -24,7 +24,7 @@ describe('A non-const global variable', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should be modifiable inside a function`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 3
tests/test34.spec.js

@@ -1,9 +1,8 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('IfThenElse command ', function () {
-
   let input = `programa {
 
     funcao inicio() {
@@ -18,7 +17,7 @@ describe('IfThenElse command ', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should produce a valid state`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 2
tests/test35.spec.js

@@ -1,6 +1,6 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { LanguageService } from '../js/services/languageService';
 
 describe('A recursive call', function () {
 
@@ -21,7 +21,7 @@ describe('A recursive call', function () {
     }
   }`;
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should produce a valid state`, function (done) {
     const parser = new IVProgParser(input, lexer);

+ 2 - 2
tests/test37.spec.js

@@ -1,7 +1,7 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
 import { InputTest } from './../js/util/inputTest';
+import { LanguageService } from '../js/services/languageService';
 
 describe('The read function', function () {
 
@@ -15,7 +15,7 @@ describe('The read function', function () {
 
   const input = new InputTest(['0xff']);
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should read data from the input and convert it to the appropriate type`, function (done) {
     const parser = new IVProgParser(code, lexer);

+ 2 - 2
tests/test38.spec.js

@@ -1,7 +1,7 @@
-import Lexers from './../grammar/';
 import { IVProgParser } from './../js/ast/ivprogParser';
 import { IVProgProcessor} from './../js/processor/ivprogProcessor'
 import { OutputTest } from './../js/util/outputTest';
+import { LanguageService } from '../js/services/languageService';
 
 describe('The write function', function () {
 
@@ -15,7 +15,7 @@ describe('The write function', function () {
 
   const output = new OutputTest();
 
-  const lexer = Lexers['pt_br'];
+  const lexer = LanguageService.getCurrentLexer();
 
   it(`should print the value passed to it, no matter it's type`, function (done) {
     const parser = new IVProgParser(code, lexer);

+ 10 - 0
tests/test39.spec.js

@@ -0,0 +1,10 @@
+import { LanguageService } from './../js/services/languageService';
+describe('LanguageService.getLang() ', function () {
+  beforeEach(() => {
+    localStorage.setItem('ivprog.lang', 'pt_br');
+  });
+  it("should return the value associated to the key ivprog.lang, inside localStorage", function () {
+    expect(LanguageService.getLang()).toEqual('pt_br');
+  });
+  afterEach(() => localStorage.removeItem('ivprog.lang'));
+});

+ 30 - 0
tests/test40.spec.js

@@ -0,0 +1,30 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { OutputTest } from './../js/util/outputTest';
+import { LanguageService } from '../js/services/languageService';
+
+describe('The LanguageService', function () {
+
+  const code = `program {
+
+    function start() {
+      real a = 8.01
+      write(a)
+    }
+  }`;
+
+  const output = new OutputTest();
+  localStorage.setItem('ivprog.lang', 'en');
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should provide the appropriate lexer`, function (done) {
+    const parser = new IVProgParser(code, lexer);
+    const exec = new IVProgProcessor(parser.parseTree());
+    exec.registerOutput(output);
+    exec.interpretAST().then(sto => {
+      expect(output.list).toEqual(['8.01']);
+      localStorage.removeItem('ivprog.lang');
+      done();
+    }).catch( err => done(err));
+  });
+});

+ 8 - 0
tests/test41.spec.js

@@ -0,0 +1,8 @@
+import { LocalizedStrings } from '../js/services/localizedStringsService';
+
+describe('The LocalizedStrings services', function () {
+
+  it(`should provide the corret message for the type and id given`, function () {
+    expect(LocalizedStrings.getUI('start')).toEqual('inicio');
+  });
+});

+ 10 - 0
tests/test42.spec.js

@@ -0,0 +1,10 @@
+import { resultTypeAfterInfixOp } from './../js/processor/compatibilityTable';
+import { Operators } from './../js/ast/operators';
+import { Types } from '../js/ast/types';
+
+describe("The compatbility table", () => {
+  it("should return the correct type for a given infix operator application", () => {
+    const type = resultTypeAfterInfixOp(Operators.ADD, Types.INTEGER, Types.REAL);
+    expect(type).toEqual(Types.REAL);
+  });
+});

+ 32 - 0
tests/test43.spec.js

@@ -0,0 +1,32 @@
+import { IVProgParser } from './../js/ast/ivprogParser';
+import { IVProgProcessor} from './../js/processor/ivprogProcessor'
+import { InputTest } from './../js/util/inputTest';
+import { LanguageService } from '../js/services/languageService';
+
+describe('The sum of a real and a integer', function () {
+
+  const code = `programa {
+
+    funcao inicio() {
+      real a;
+      leia(a);
+      a = a + 0xff
+    }
+  }`;
+
+  localStorage.setItem('ivprog.lang', 'pt');
+  const input = new InputTest(['5.8']);
+
+  const lexer = LanguageService.getCurrentLexer();
+
+  it(`should result in a real`, function (done) {
+    const parser = new IVProgParser(code, lexer);
+    const exec = new IVProgProcessor(parser.parseTree());
+    exec.registerInput(input);
+    exec.interpretAST().then(sto => {
+      expect(sto.applyStore('a').value).toEqual(5.8 + 0xff);
+      localStorage.removeItem('ivprog.lang');
+      done();
+    }).catch( err => done(err));
+  });
+});

+ 1 - 0
webpack.config.js

@@ -3,6 +3,7 @@ var webpack = require('webpack');
 module.exports = {
     entry: './js/main.js',
     mode: 'development',
+    watch: true,
     output: {
         path: path.resolve(__dirname, 'build'),
         filename: 'ivprog.bundle.js'