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");