2015-12-06 13:49:35 +02:00
|
|
|
|
/* global TokenType, ViewActivity, TextUtils */
|
|
|
|
|
|
|
|
|
|
function Parser(tokens) {
|
|
|
|
|
this.tokens = tokens;
|
|
|
|
|
this.tokensCount = tokens.length;
|
|
|
|
|
this.position = 0;
|
|
|
|
|
this.lastPosition = 0;
|
|
|
|
|
this.labels = {};
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
Parser.prototype.EOF = new Token("", TokenType.EOF);
|
|
|
|
|
|
|
|
|
|
Parser.prototype.getInstance = function () {
|
|
|
|
|
return this;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.next = function () {
|
|
|
|
|
this.lastPosition = this.position;
|
|
|
|
|
|
|
|
|
|
var terminal = false;
|
|
|
|
|
var counter = 0;
|
|
|
|
|
do {
|
|
|
|
|
try {
|
|
|
|
|
terminal = this.statement();
|
|
|
|
|
} catch (re) {
|
|
|
|
|
console.log("Parser.next() " + re);
|
|
|
|
|
if (this.tokens.length === 0) return;
|
|
|
|
|
}
|
|
|
|
|
// антизацикливание
|
|
|
|
|
counter++;
|
|
|
|
|
if (counter >= 1000) {
|
|
|
|
|
this.position++;
|
|
|
|
|
counter = 0;
|
|
|
|
|
}
|
|
|
|
|
} while (!terminal);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.findFrom = function(from, which, step) {
|
|
|
|
|
var pos = from;
|
|
|
|
|
while (true) {
|
|
|
|
|
pos += step;
|
|
|
|
|
if (pos < 0 || pos >= this.tokensCount) break;
|
|
|
|
|
|
|
|
|
|
var token = this.tokens[pos];
|
|
|
|
|
if (which === token.getType()) return pos;
|
|
|
|
|
}
|
|
|
|
|
// Возвращаем текущее значение во избежание лишних проверок.
|
|
|
|
|
return from;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.statement = function() {
|
|
|
|
|
var token = this.get(0);
|
|
|
|
|
if (this.match(token, TokenType.COMMAND)) return this.command();
|
|
|
|
|
if (this.match(token, TokenType.SCENE)) return this.scene();
|
|
|
|
|
if (this.match(token, TokenType.PLAY)) return this.play();
|
|
|
|
|
// if (this.match(token, TokenType.QUEUE)) return this.queue();
|
|
|
|
|
// if (this.match(token, TokenType.STOP)) return this.stop();
|
|
|
|
|
if (this.match(token, TokenType.SHOW)) return this.show();
|
|
|
|
|
if (this.match(token, TokenType.HIDE)) return this.hide();
|
|
|
|
|
|
|
|
|
|
// if (this.match(token, TokenType.JUMP)) return this.jump();
|
|
|
|
|
// if (this.match(token, TokenType.IF)) return this.ifStatement();
|
|
|
|
|
|
|
|
|
|
/*if (this.lookMatch(1, TokenType.COLON)) {
|
|
|
|
|
// menu:
|
|
|
|
|
if (this.match(token, TokenType.MENU)) return this.menu();
|
|
|
|
|
|
|
|
|
|
// Остаток от меню выбора. Пропускаем до появления ENDMENU.
|
|
|
|
|
if (this.match(token, TokenType.TEXT)) {
|
|
|
|
|
if (this.hasEndMenu) this.position += this.skipMenu();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Остаток от условного выражения. Пропускаем до появления ENDIF.
|
|
|
|
|
if (this.match(token, TokenType.ELSE)) {
|
|
|
|
|
if (this.hasEndIf) this.position += this.skipIf();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
// Текст с именем автора реплики.
|
|
|
|
|
if (this.lookMatch(1, TokenType.TEXT) && this.match(token, TokenType.WORD)) {
|
|
|
|
|
var whoid = token.getText();
|
|
|
|
|
ViewActivity.getInstance().text(whoid, this.consume(TokenType.TEXT).getText());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Обычный текст.
|
|
|
|
|
if (this.match(token, TokenType.TEXT)) {
|
|
|
|
|
ViewActivity.getInstance().text(token.getText());
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.EOF)) {
|
2015-12-07 14:38:58 +02:00
|
|
|
|
ViewActivity.getInstance().finish();
|
2015-12-06 13:49:35 +02:00
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.WINDOW)) {
|
|
|
|
|
if (this.match(TokenType.SHOW))
|
|
|
|
|
return ViewActivity.getInstance().windowShow(this.matchWithEffect());
|
|
|
|
|
else if (this.match(TokenType.HIDE))
|
|
|
|
|
return ViewActivity.getInstance().windowHide(this.matchWithEffect());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (!TextUtils.isEmpty(this.matchWithEffect())) return false;
|
|
|
|
|
|
|
|
|
|
this.position++;
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.command = function() {
|
|
|
|
|
var token = this.get(0);
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.RENPY_PAUSE)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
var pause = this.consumeDouble();
|
|
|
|
|
var hard = false;
|
|
|
|
|
if (!this.lookMatch(0, TokenType.RPAREN)) {
|
|
|
|
|
this.consume(TokenType.WORD); // hard
|
|
|
|
|
this.consume(TokenType.EQ);
|
|
|
|
|
hard = this.consumeBoolean();
|
|
|
|
|
}
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().pause(Math.floor(1000 * pause), hard);
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.RENPY_SAY)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
var whoid = this.consume(TokenType.WORD).getText();
|
|
|
|
|
// TODO: this.consume(TokenType.COMMA)
|
|
|
|
|
var text = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
// TODO: this.consume(TokenType.COMMA)
|
|
|
|
|
this.consume(TokenType.WORD); // interact
|
|
|
|
|
this.consume(TokenType.EQ);
|
|
|
|
|
var interact = this.consumeBoolean();
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().text(whoid, text);
|
|
|
|
|
return interact;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.PERSISTENT_SPRITE_TIME)) {
|
|
|
|
|
this.consume(TokenType.EQ);
|
|
|
|
|
this.consume(TokenType.TEXT);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.PROLOG_TIME) ||
|
|
|
|
|
this.match(token, TokenType.DAY_TIME) ||
|
|
|
|
|
this.match(token, TokenType.SUNSET_TIME) ||
|
|
|
|
|
this.match(token, TokenType.NIGHT_TIME)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.MAKE_NAMES_KNOWN) ||
|
|
|
|
|
this.match(token, TokenType.MAKE_NAMES_UNKNOWN)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
if (token.getType() === TokenType.MAKE_NAMES_KNOWN) {
|
|
|
|
|
ViewActivity.getInstance().makeNamesKnown();
|
|
|
|
|
} else ViewActivity.getInstance().makeNamesUnknown();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if (this.match(token, TokenType.SET_NAME)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
var whoid = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
// TODO: this.consume(TokenType.COMMA)
|
|
|
|
|
var name = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().meet(whoid, name);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Карта
|
|
|
|
|
if (this.match(token, TokenType.DISABLE_ALL_ZONES)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().disableAllZones();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (this.match(token, TokenType.DISABLE_CURRENT_ZONE)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().disableCurrentZone();
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (this.match(token, TokenType.RESET_ZONE)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
var zone = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().resetZone(zone);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (this.match(token, TokenType.SET_ZONE)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
var zone = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
// TODO: this.consume(TokenType.COMMA)
|
|
|
|
|
var label = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().setZone(zone, label);
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
if (this.match(token, TokenType.SHOW_MAP)) {
|
|
|
|
|
this.consume(TokenType.LPAREN);
|
|
|
|
|
this.consume(TokenType.RPAREN);
|
|
|
|
|
ViewActivity.getInstance().showMap();
|
|
|
|
|
return true;
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/*if (this.match(token, TokenType.WORD)) {
|
|
|
|
|
if (this.match(TokenType.EQ)) {
|
|
|
|
|
// variable = expression
|
|
|
|
|
Variables.setVariable(token.getText(), 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());
|
|
|
|
|
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());
|
|
|
|
|
return false;
|
|
|
|
|
}
|
|
|
|
|
}*/
|
|
|
|
|
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.scene = function() {
|
|
|
|
|
var type = "";
|
|
|
|
|
if (this.match(TokenType.BG)) type = "bg";
|
|
|
|
|
else if (this.match(TokenType.CG)) type = "cg";
|
|
|
|
|
else if (this.match(TokenType.ANIM)) type = "anim";
|
|
|
|
|
|
|
|
|
|
var name = this.consume(TokenType.WORD).getText();
|
|
|
|
|
var effect = this.matchWithEffect();
|
|
|
|
|
ViewActivity.getInstance().background(type, name, effect);
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.show = function() {
|
|
|
|
|
var whoid = this.consume(TokenType.WORD).getText();
|
|
|
|
|
var params = ""; // emotion? cloth? distance?
|
|
|
|
|
while (this.lookMatch(0, TokenType.WORD)) {
|
|
|
|
|
var text = this.consume(TokenType.WORD).getText();
|
|
|
|
|
params += text;
|
|
|
|
|
if (text.equals("close") || text.equals("far")) break;
|
|
|
|
|
}
|
|
|
|
|
// Положение (left, cleft, ...)
|
|
|
|
|
var position = "";
|
|
|
|
|
if (this.match(TokenType.AT)) {
|
|
|
|
|
position = this.consume(TokenType.WORD).getText();
|
|
|
|
|
}
|
|
|
|
|
// Псевдоним (для показа одно и того же спрайта в разных местах)
|
|
|
|
|
var alias = "";
|
|
|
|
|
if (this.match(TokenType.AS)) {
|
|
|
|
|
alias = this.consume(TokenType.WORD).getText();
|
|
|
|
|
}
|
|
|
|
|
var effect = this.matchWithEffect();
|
|
|
|
|
ViewActivity.getInstance().sprite(whoid, params, position, alias, effect);
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.hide = function() {
|
|
|
|
|
var whoid = this.consume(TokenType.WORD).getText();
|
|
|
|
|
var effect = this.matchWithEffect();
|
|
|
|
|
ViewActivity.getInstance().hideSprite(whoid, effect);
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.play = function() {
|
|
|
|
|
if (this.match(TokenType.MUSIC)) return this.playMusic();
|
|
|
|
|
if (this.match(TokenType.AMBIENCE)) return this.playAmbience();
|
|
|
|
|
if (this.lookMatch(0, TokenType.SOUND) || this.lookMatch(0, TokenType.SOUNDLOOP)) {
|
|
|
|
|
return this.playSound();
|
|
|
|
|
}
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.playMusic = function() {
|
|
|
|
|
var name = this.consumeMusicName();
|
|
|
|
|
var fade = this.matchFade();
|
|
|
|
|
ViewActivity.getInstance().music(name, fade);
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.playSound = function() {
|
|
|
|
|
var loop = false;
|
|
|
|
|
if (this.match(TokenType.SOUND)) loop = false;
|
|
|
|
|
else if (this.match(TokenType.SOUNDLOOP)) loop = true;
|
|
|
|
|
|
|
|
|
|
var name = this.consumeMusicName();
|
|
|
|
|
var fade = this.matchFade();
|
|
|
|
|
ViewActivity.getInstance().sound(name, loop, fade);
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.playAmbience = function() {
|
|
|
|
|
var name = this.consumeMusicName();
|
|
|
|
|
var fade = this.matchFade();
|
|
|
|
|
return false;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
Parser.prototype.consumeMusicName = function() {
|
|
|
|
|
var name = "";
|
|
|
|
|
if (this.lookMatch(1, TokenType.LBRACKET)) {
|
|
|
|
|
// music_list["music"]
|
|
|
|
|
this.consume(TokenType.WORD);
|
|
|
|
|
this.consume(TokenType.LBRACKET);
|
|
|
|
|
name = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
this.consume(TokenType.RBRACKET);
|
|
|
|
|
} else if (this.lookMatch(0, TokenType.TEXT)) {
|
|
|
|
|
name = this.consume(TokenType.TEXT).getText();
|
|
|
|
|
} else {
|
|
|
|
|
name = this.consume(TokenType.WORD).getText();
|
|
|
|
|
}
|
|
|
|
|
return name;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.matchFade = function() {
|
|
|
|
|
var result = new FadeInfo();
|
|
|
|
|
if (this.match(TokenType.FADEIN)) {
|
|
|
|
|
result.fadeIn = true;
|
|
|
|
|
result.duration = this.consumeDouble();
|
|
|
|
|
} else if (this.match(TokenType.FADEOUT)) {
|
|
|
|
|
result.fadeOut = true;
|
|
|
|
|
result.duration = this.consumeDouble();
|
|
|
|
|
}
|
|
|
|
|
return result;
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.matchWithEffect = function() {
|
|
|
|
|
if (this.match(TokenType.WITH)) {
|
|
|
|
|
return this.consume(TokenType.WORD).getText();
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.consumeDouble = function() {
|
|
|
|
|
return parseFloat(this.consume(TokenType.NUMBER).getText());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.consumeBoolean = function() {
|
|
|
|
|
return "true" === (this.consume(TokenType.NUMBER).getText().toLowerCase());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.matchWithEffect = function() {
|
|
|
|
|
if (this.match(TokenType.WITH)) {
|
|
|
|
|
return this.consume(TokenType.WORD).getText();
|
|
|
|
|
}
|
|
|
|
|
return "";
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.match_1 = function(type) {
|
|
|
|
|
if (this.get(0).getType() !== type) return false;
|
|
|
|
|
this.position++;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
Parser.prototype.match_2 = function(token, type) {
|
|
|
|
|
if (type !== token.getType()) return false;
|
|
|
|
|
this.position++;
|
|
|
|
|
return true;
|
|
|
|
|
};
|
|
|
|
|
Parser.prototype.match = function(arg1, arg2) {
|
|
|
|
|
if (arguments.length === 1) return this.match_1(arg1);
|
|
|
|
|
else return this.match_2(arg1, arg2);
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.consume = function(type) {
|
|
|
|
|
if (this.get(0).getType() !== type) throw "Ожидался токен " + type + ".";
|
|
|
|
|
return this.tokens[this.position++];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.lookMatch = function(pos, type) {
|
|
|
|
|
return (type === this.get(pos).getType());
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
Parser.prototype.get = function(offset) {
|
|
|
|
|
if (this.position + offset >= this.tokensCount) return this.EOF;
|
|
|
|
|
return this.tokens[this.position + offset];
|
|
|
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|