diff --git a/public_html/js/Parser.js b/public_html/js/Parser.js index 58a6e6a..3f25d87 100644 --- a/public_html/js/Parser.js +++ b/public_html/js/Parser.js @@ -1,4 +1,4 @@ -/* global TokenType, ViewActivity, TextUtils */ +/* global TokenType, ViewActivity, TextUtils, Variables, Operator */ function Parser(tokens) { this.tokens = tokens; @@ -6,6 +6,11 @@ function Parser(tokens) { this.position = 0; this.lastPosition = 0; this.labels = {}; + /** Оптимизация, чтобы каждый раз не искать endmenu/endif, + * если их попросту нет в необработанном сценарии */ + this.hasEndMenu = false; + this.hasEndIf = false; + Variables.init(); } Parser.prototype.EOF = new Token("", TokenType.EOF); @@ -212,27 +217,27 @@ Parser.prototype.command = function() { return true; } - /*if (this.match(token, TokenType.WORD)) { + if (this.match(token, TokenType.WORD)) { if (this.match(TokenType.EQ)) { // variable = expression - Variables.setVariable(token.getText(), expression().eval()); + Variables.setVariable(token.getText(), this.expression().eval()); return false; } if (this.lookMatch(1, TokenType.EQ) && this.match(TokenType.PLUS)) { // variable += expression this.consume(TokenType.EQ); var varValue = Variables.getVariable(token.getText()); - Variables.setVariable(token.getText(), varValue + expression().eval()); + Variables.setVariable(token.getText(), varValue + this.expression().eval()); return false; } if (this.lookMatch(1, TokenType.EQ) && this.match(TokenType.MINUS)) { // variable -= expression this.consume(TokenType.EQ); var varValue = Variables.getVariable(token.getText()); - Variables.setVariable(token.getText(), varValue - expression().eval()); + Variables.setVariable(token.getText(), varValue - this.expression().eval()); return false; } - }*/ + } return false; }; @@ -312,6 +317,192 @@ Parser.prototype.playAmbience = function() { return false; }; +Parser.prototype.expression = function() { + return this.orTest(); +}; + +Parser.prototype.orTest = function() { + var expression = this.andTest(); + + while (true) { + if (this.match(TokenType.OR)) { + expression = new BinaryExpression(Operator.BOOLEAN_OR, expression, this.andTest()); + continue; + } + break; + } + + return expression; +}; + +Parser.prototype.andTest = function() { + var expression = this.notTest(); + + while (true) { + if (this.match(TokenType.AND)) { + expression = new BinaryExpression(Operator.BOOLEAN_AND, expression, this.notTest()); + continue; + } + break; + } + + return expression; +}; + +Parser.prototype.notTest = function() { + if (this.match(TokenType.NOT)) { + return new ValueExpression( this.notTest().eval() != 0 ? 0 : 1 ); + } + return this.comparison(); +}; + +Parser.prototype.comparison = function() { + var expression = this.additive(); + + while (true) { + if (this.lookMatch(1, TokenType.EQ)) { + if (this.match(TokenType.EQ)) { + // == + this.consume(TokenType.EQ); + expression = new BinaryExpression(Operator.EQUALS, expression, this.additive()); + continue; + } + if (this.match(TokenType.GT)) { + // >= + this.consume(TokenType.EQ); + expression = new BinaryExpression(Operator.GTEQ, expression, this.additive()); + continue; + } + if (this.match(TokenType.LT)) { + // <= + this.consume(TokenType.EQ); + expression = new BinaryExpression(Operator.LTEQ, expression, this.additive()); + continue; + } + if (this.match(TokenType.EXCL)) { + // != + this.consume(TokenType.EQ); + expression = new BinaryExpression(Operator.NOTEQUALS, expression, this.additive()); + continue; + } + } + + if (this.match(TokenType.LT)) { + expression = new BinaryExpression(Operator.LT, expression, this.additive()); + continue; + } + if (this.match(TokenType.GT)) { + expression = new BinaryExpression(Operator.GT, expression, this.additive()); + continue; + } + break; + } + + return expression; +}; + +Parser.prototype.additive = function() { + var expression = this.unary(); + + while (true) { + if (this.match(TokenType.PLUS)) { + expression = new BinaryExpression(Operator.ADD, expression, this.unary()); + continue; + } + if (this.match(TokenType.MINUS)) { + expression = new BinaryExpression(Operator.SUBTRACT, expression, this.unary()); + continue; + } + break; + } + + return expression; +}; + +Parser.prototype.unary = function() { + if (this.match(TokenType.MINUS)) { + return new ValueExpression( -this.primary().eval() ); + } + if (this.match(TokenType.PLUS)) { + return this.primary(); + } + return this.primary(); +}; + +Parser.prototype.primary = function() { + var current = this.get(0); + if (this.match(current, TokenType.NUMBER)) { + return new ValueExpression( parseFloat(current.getText()) ); + } + if (this.match(current, TokenType.WORD)) { + return new VariableExpression(current.getText()); + } + if (this.match(current, TokenType.LPAREN)) { + var expr = this.expression(); + this.match(TokenType.RPAREN); + return expr; + } + throw "Неизвестное выражение"; +}; + +Parser.prototype.preScan = function() { + // Сканируем все метки, для быстрого перехода командой jump. + // А также определяем параметры для оптимизации. + for (var i = 0; i < this.tokensCount - 2; i++) { + var current = this.tokens[i].getType(); + if (current === TokenType.ENDMENU) { + this.hasEndMenu = true; + } else if (current === TokenType.ENDIF) { + this.hasEndIf = true; + } else if ( (current === TokenType.LABEL) && + (this.tokens[i + 2].getType() === TokenType.COLON) ) { + // label word : + var token = this.tokens[i + 1]; + if (token.getType() === TokenType.WORD) { + // Добавляем позицию команды, следующей после метки. + this.labels[token.getText()] = i + 3; + } + i += 2; + } + } +}; + +Parser.prototype.skipMenu = function() { + var pos = 0; + var level = 1; // уровень вложенности меню + while (true) { + // Расчёт уровня меню. + if (this.lookMatch(pos, TokenType.MENU) && this.lookMatch(pos + 1, TokenType.COLON)) { + level++; + pos += 2; + } + if (this.lookMatch(pos, TokenType.ENDMENU)) { + pos++; + level--; + // Завершаем работу по достижению ENDMENU первого уровня. + if (level <= 0) break; + } + if (this.lookMatch(pos, TokenType.EOF)) return 0; + pos++; + } + return pos; +}; + +Parser.prototype.skipIf = function() { + var pos = 0; + var level = 1; + while (true) { + if (this.lookMatch(pos, TokenType.IF)) level++; + else if (this.lookMatch(pos, TokenType.ENDIF)) { + pos++; + level--; + if (level <= 0) break; + } else if (this.lookMatch(pos, TokenType.EOF)) return 0; + pos++; + } + return pos; +}; + Parser.prototype.consumeMusicName = function() { var name = ""; diff --git a/public_html/js/Views.js b/public_html/js/Views.js index 124e67e..e71aabc 100644 --- a/public_html/js/Views.js +++ b/public_html/js/Views.js @@ -2,6 +2,7 @@ function Views(parser) { this.parser = parser; + parser.preScan(); this.windowTag = document.getElementById("window"); this.textAuthorTag = document.getElementById("textAuthor");