Commit ffa3fab1 authored by twanvl's avatar twanvl

added 'string mode' to script parser; added Keyword and related classes

parent f0bcff4e
...@@ -8,6 +8,7 @@ ...@@ -8,6 +8,7 @@
#include <data/game.hpp> #include <data/game.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/keyword.hpp>
#include <data/statistics.hpp> #include <data/statistics.hpp>
#include <util/io/package_manager.hpp> #include <util/io/package_manager.hpp>
#include <script/script.hpp> #include <script/script.hpp>
...@@ -52,9 +53,9 @@ IMPLEMENT_REFLECTION(Game) { ...@@ -52,9 +53,9 @@ IMPLEMENT_REFLECTION(Game) {
REFLECT(card_fields); REFLECT(card_fields);
REFLECT(statistics_dimensions); REFLECT(statistics_dimensions);
REFLECT(statistics_categories); REFLECT(statistics_categories);
// REFLECT_N("keyword parameter type", keyword_params); REFLECT(keyword_parameter_types);
// REFLECT_N("keyword separator type", keyword_separators); REFLECT(keyword_modes);
// REFLECT(keywords); REFLECT(keywords);
// REFLECT(word_lists); // REFLECT(word_lists);
} }
......
...@@ -19,6 +19,9 @@ DECLARE_POINTER_TYPE(Field); ...@@ -19,6 +19,9 @@ DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Game); DECLARE_POINTER_TYPE(Game);
DECLARE_POINTER_TYPE(StatsDimension); DECLARE_POINTER_TYPE(StatsDimension);
DECLARE_POINTER_TYPE(StatsCategory); DECLARE_POINTER_TYPE(StatsCategory);
DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordMode);
DECLARE_POINTER_TYPE(Keyword);
// ----------------------------------------------------------------------------- : Game // ----------------------------------------------------------------------------- : Game
...@@ -38,6 +41,10 @@ class Game : public Packaged { ...@@ -38,6 +41,10 @@ class Game : public Packaged {
vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions vector<StatsDimensionP> statistics_dimensions; ///< (Additional) statistics dimensions
vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories vector<StatsCategoryP> statistics_categories; ///< (Additional) statistics categories
vector<KeywordParamP> keyword_parameter_types;///< Types of keyword parameters
vector<KeywordModeP> keyword_modes; ///< Modes of keywords
vector<KeywordP> keywords; ///< Keywords for use in text
vector<Dependency> dependent_scripts_cards; ///< scripts that depend on the card list vector<Dependency> dependent_scripts_cards; ///< scripts that depend on the card list
vector<Dependency> dependent_scripts_keywords; ///< scripts that depend on the keywords vector<Dependency> dependent_scripts_keywords; ///< scripts that depend on the keywords
bool dependencies_initialized; ///< are the script dependencies comming from this game all initialized? bool dependencies_initialized; ///< are the script dependencies comming from this game all initialized?
......
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
// ----------------------------------------------------------------------------- : Includes
#include <data/keyword.hpp>
// ----------------------------------------------------------------------------- : Reflection
IMPLEMENT_REFLECTION(KeywordParam) {
REFLECT(name);
REFLECT(description);
REFLECT(match);
REFLECT(in_reminder);
}
IMPLEMENT_REFLECTION(KeywordMode) {
REFLECT(name);
REFLECT(description);
}
IMPLEMENT_REFLECTION(KeywordExpansion) {
REFLECT(before);
REFLECT(after);
REFLECT(reminder);
}
IMPLEMENT_REFLECTION(Keyword) {
REFLECT(keyword);
REFLECT(expansions);
REFLECT(rules);
REFLECT(mode);
}
// ----------------------------------------------------------------------------- : Using keywords
//+----------------------------------------------------------------------------+
//| Description: Magic Set Editor - Program to make Magic (tm) cards |
//| Copyright: (C) 2001 - 2006 Twan van Laarhoven |
//| License: GNU General Public License 2 or later (see file COPYING) |
//+----------------------------------------------------------------------------+
#ifndef HEADER_DATA_KEYWORD
#define HEADER_DATA_KEYWORD
// ----------------------------------------------------------------------------- : Includes
#include <util/prec.hpp>
#include <script/scriptable.hpp>
#include <wx/regex.h>
DECLARE_POINTER_TYPE(KeywordParam);
DECLARE_POINTER_TYPE(KeywordExpansion);
DECLARE_POINTER_TYPE(KeywordMode);
// ----------------------------------------------------------------------------- : Keyword components
/// Parameter type of keywords
class KeywordParam {
public:
String name; ///< Name of the parameter type
String description; ///< Description of the type
String match; ///< Uncompiled regex
wxRegEx matchRe; ///< Regular expression to match
OptionalScript in_reminder; ///< Transformation of the value for showing in the reminder text
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Keyword mode
/// Information on when and how to use a keyword
class KeywordMode {
String name; ///< Name of the mode
String description; ///< Description of the type
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Keyword expansion
/// A way to use a keyword
class KeywordExpansion {
public:
String before; ///< Components before the keyword: parameters and separators (tagged string)
String after; ///< Components after the keyword: parameters and separators
vector<KeywordParamP> parameters; ///< The types of parameters
wxRegEx splitter; ///< Regular expression to split/match the components, automatically generated
OptionalScript reminder; ///< Reminder text of the keyword
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Keyword
/// A keyword for a set or a game
class Keyword {
public:
String keyword; ///< The keyword
vector<KeywordExpansionP> expansions; ///< Expansions, i.e. ways to use this keyword
String rules; ///< Rules/explanation
String mode; ///< Mode of use, can be used by scripts (only gives the name)
DECLARE_REFLECTION();
};
// ----------------------------------------------------------------------------- : Using keywords
/// Expand/update all keywords in the given string
String expand_keywords(const String& text);
// ----------------------------------------------------------------------------- : EOF
#endif
...@@ -10,6 +10,7 @@ ...@@ -10,6 +10,7 @@
#include <data/game.hpp> #include <data/game.hpp>
#include <data/stylesheet.hpp> #include <data/stylesheet.hpp>
#include <data/card.hpp> #include <data/card.hpp>
#include <data/keyword.hpp>
#include <data/field.hpp> #include <data/field.hpp>
#include <data/field/text.hpp> // for 0.2.7 fix #include <data/field/text.hpp> // for 0.2.7 fix
#include <script/value.hpp> #include <script/value.hpp>
...@@ -116,6 +117,7 @@ IMPLEMENT_REFLECTION(Set) { ...@@ -116,6 +117,7 @@ IMPLEMENT_REFLECTION(Set) {
REFLECT_N("styling", styling_data); REFLECT_N("styling", styling_data);
} }
REFLECT(cards); REFLECT(cards);
REFLECT(keywords);
} }
reflect_set_info_get_member(tag,data); reflect_set_info_get_member(tag,data);
REFLECT(apprentice_code); REFLECT(apprentice_code);
......
...@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(StyleSheet); ...@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(StyleSheet);
DECLARE_POINTER_TYPE(Styling); DECLARE_POINTER_TYPE(Styling);
DECLARE_POINTER_TYPE(Field); DECLARE_POINTER_TYPE(Field);
DECLARE_POINTER_TYPE(Value); DECLARE_POINTER_TYPE(Value);
DECLARE_POINTER_TYPE(Keyword);
class ScriptManager; class ScriptManager;
class Context; class Context;
...@@ -38,22 +39,18 @@ class Set : public Packaged { ...@@ -38,22 +39,18 @@ class Set : public Packaged {
Set(const StyleSheetP& stylesheet); Set(const StyleSheetP& stylesheet);
~Set(); ~Set();
/// The game this set uses GameP game; ///< The game this set uses
GameP game; StyleSheetP stylesheet; ///< The default stylesheet
/// The default stylesheet
StyleSheetP stylesheet;
/// The values on the fields of the set /// The values on the fields of the set
/** The indices should correspond to the set_fields in the Game */ /** The indices should correspond to the set_fields in the Game */
IndexMap<FieldP, ValueP> data; IndexMap<FieldP, ValueP> data;
/// Extra values for specitic stylesheets, indexed by stylesheet name /// Extra values for specitic stylesheets, indexed by stylesheet name
DECLARE_POINTER_TYPE(Styling); DECLARE_POINTER_TYPE(Styling);
map<String, StylingP> styling_data; map<String, StylingP> styling_data;
/// The cards in the set vector<CardP> cards; ///< The cards in the set
vector<CardP> cards; vector<KeywordP> keywords; ///< Additional keywords used in this set
/// Code to use for apprentice (Magic only) String apprentice_code; ///< Code to use for apprentice (Magic only)
String apprentice_code; ActionStack actions; ///< Actions performed on this set and the cards in it
/// Actions performed on this set and the cards in it
ActionStack actions;
/// A context for performing scripts /// A context for performing scripts
/** Should only be used from the main thread! */ /** Should only be used from the main thread! */
......
...@@ -837,6 +837,12 @@ ...@@ -837,6 +837,12 @@
<File <File
RelativePath=".\data\game.hpp"> RelativePath=".\data\game.hpp">
</File> </File>
<File
RelativePath=".\data\keyword.cpp">
</File>
<File
RelativePath=".\data\keyword.hpp">
</File>
<File <File
RelativePath=".\data\set.cpp"> RelativePath=".\data\set.cpp">
</File> </File>
......
...@@ -291,6 +291,14 @@ SCRIPT_FUNCTION(substring) { ...@@ -291,6 +291,14 @@ SCRIPT_FUNCTION(substring) {
} }
} }
// does a string contain a substring?
SCRIPT_FUNCTION(contains) {
SCRIPT_PARAM(String, input);
SCRIPT_PARAM(String, match);
SCRIPT_RETURN(input.find(match) != String::npos);
}
// ----------------------------------------------------------------------------- : Vector stuff // ----------------------------------------------------------------------------- : Vector stuff
/// position of some element in a vector /// position of some element in a vector
...@@ -322,6 +330,7 @@ void init_script_functions(Context& ctx) { ...@@ -322,6 +330,7 @@ void init_script_functions(Context& ctx) {
ctx.setVariable(_("to lower"), script_to_lower); ctx.setVariable(_("to lower"), script_to_lower);
ctx.setVariable(_("to title"), script_to_title); ctx.setVariable(_("to title"), script_to_title);
ctx.setVariable(_("substring"), script_substring); ctx.setVariable(_("substring"), script_substring);
ctx.setVariable(_("contains"), script_contains);
ctx.setVariable(_("position"), script_position_of); ctx.setVariable(_("position"), script_position_of);
ctx.setVariable(_("number of items"), script_number_of_items); ctx.setVariable(_("number of items"), script_number_of_items);
} }
......
...@@ -44,11 +44,16 @@ struct Token { ...@@ -44,11 +44,16 @@ struct Token {
inline operator != (const String& s) const { return type == TOK_STRING || value != s; } inline operator != (const String& s) const { return type == TOK_STRING || value != s; }
}; };
enum OpenBrace
{ BRACE_STRING // "
, BRACE_STRING_MODE // fake brace for string mode
, BRACE_PAREN // (, [, {
};
/// Iterator over a string, one token at a time /// Iterator over a string, one token at a time
class TokenIterator { class TokenIterator {
public: public:
TokenIterator(const String& str); TokenIterator(const String& str, bool string_mode);
/// Peek at the next token, doesn't move to the one after that /// Peek at the next token, doesn't move to the one after that
/** Can peek further forward by using higher values of offset. /** Can peek further forward by using higher values of offset.
...@@ -65,9 +70,9 @@ class TokenIterator { ...@@ -65,9 +70,9 @@ class TokenIterator {
private: private:
String input; String input;
size_t pos; size_t pos;
vector<Token> buffer; ///< buffer of unread tokens, front() = current vector<Token> buffer; ///< buffer of unread tokens, front() = current
stack<bool> open_braces; ///< braces we entered, true if the brace was from a smart string escape stack<OpenBrace> open_braces; ///< braces/quotes we entered from script mode
bool newline; ///< Did we just pass a newline? bool newline; ///< Did we just pass a newline?
// more input? // more input?
struct MoreInput { struct MoreInput {
String input; String input;
...@@ -96,11 +101,17 @@ bool isLongOper(const String& s) { return s==_(":=") || s==_("==") || s==_("!=") ...@@ -96,11 +101,17 @@ bool isLongOper(const String& s) { return s==_(":=") || s==_("==") || s==_("!=")
// ----------------------------------------------------------------------------- : Tokenizing // ----------------------------------------------------------------------------- : Tokenizing
TokenIterator::TokenIterator(const String& str) TokenIterator::TokenIterator(const String& str, bool string_mode)
: input(str) : input(str)
, pos(0) , pos(0)
, newline(false) , newline(false)
{} {
if (string_mode) {
open_braces.push(BRACE_STRING_MODE);
putBack();//dummy
readStringToken();
}
}
const Token& TokenIterator::peek(size_t offset) { const Token& TokenIterator::peek(size_t offset) {
// read the next token until we have enough // read the next token until we have enough
...@@ -190,16 +201,16 @@ void TokenIterator::readToken() { ...@@ -190,16 +201,16 @@ void TokenIterator::readToken() {
} }
} else if (c==_('"')) { } else if (c==_('"')) {
// string // string
open_braces.push(BRACE_STRING);
readStringToken(); readStringToken();
} else if (c == _('}') && !open_braces.empty() && open_braces.top()) { } else if (c == _('}') && !open_braces.empty() && open_braces.top() != BRACE_PAREN) {
// closing smart string, resume to string parsing // closing smart string, resume to string parsing
// "a{e}b" --> "a" "{ e }" "b" // "a{e}b" --> "a" "{ e }" "b"
open_braces.pop();
addToken(TOK_RPAREN, _("}\"")); addToken(TOK_RPAREN, _("}\""));
readStringToken(); readStringToken();
} else if (isLparen(c)) { } else if (isLparen(c)) {
// paranthesis/brace // paranthesis/brace
open_braces.push(false); open_braces.push(BRACE_PAREN);
addToken(TOK_LPAREN, String(1,c)); addToken(TOK_LPAREN, String(1,c));
} else if (isRparen(c)) { } else if (isRparen(c)) {
// paranthesis/brace // paranthesis/brace
...@@ -216,17 +227,26 @@ void TokenIterator::readToken() { ...@@ -216,17 +227,26 @@ void TokenIterator::readToken() {
void TokenIterator::readStringToken() { void TokenIterator::readStringToken() {
String str; String str;
while (true) { while (true) {
if (pos >= input.size()) throw ScriptParseError(_("Unexpected end of input in string constant")); if (pos >= input.size()) {
Char c = input[pos++]; //% input.GetChar(pos++); if (!open_braces.empty() && open_braces.top() == BRACE_STRING_MODE) {
// in string mode: end of input = end of string
addToken(TOK_STRING, str);
return;
} else {
throw ScriptParseError(_("Unexpected end of input in string constant"));
}
}
Char c = input.GetChar(pos++);
// parse the string constant // parse the string constant
if (c == _('"')) { if (c == _('"') && !open_braces.empty() && open_braces.top() == BRACE_STRING) {
// end of string // end of string
addToken(TOK_STRING, str); addToken(TOK_STRING, str);
open_braces.pop();
return; return;
} else if (c == _('\\')) { } else if (c == _('\\')) {
// escape // escape
if (pos >= input.size()) throw ScriptParseError(_("Unexpected end of input in string constant")); if (pos >= input.size()) throw ScriptParseError(_("Unexpected end of input in string constant"));
c = input[pos++]; c = input.GetChar(pos++);
if (c == _('n')) str += _('\n'); if (c == _('n')) str += _('\n');
if (c == _('<')) str += _('\1'); // escape for < if (c == _('<')) str += _('\1'); // escape for <
else str += c; // \ or { or " else str += c; // \ or { or "
...@@ -234,7 +254,6 @@ void TokenIterator::readStringToken() { ...@@ -234,7 +254,6 @@ void TokenIterator::readStringToken() {
// smart string // smart string
// "a{e}b" --> "a" "{ e }" "b" // "a{e}b" --> "a" "{ e }" "b"
addToken(TOK_STRING, str); addToken(TOK_STRING, str);
open_braces.push(true);
addToken(TOK_LPAREN, _("\"{")); addToken(TOK_LPAREN, _("\"{"));
return; return;
} else { } else {
...@@ -280,8 +299,8 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec); ...@@ -280,8 +299,8 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec);
*/ */
void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith = I_NOP, int closeWithData = 0); void parseOper(TokenIterator& input, Script& script, Precedence minPrec, InstructionType closeWith = I_NOP, int closeWithData = 0);
ScriptP parse(const String& s) { ScriptP parse(const String& s, bool string_mode) {
TokenIterator input(s); TokenIterator input(s, string_mode);
ScriptP script(new Script); ScriptP script(new Script);
parseOper(input, *script, PREC_ALL, I_RET); parseOper(input, *script, PREC_ALL, I_RET);
if (input.peek() != TOK_EOF) { if (input.peek() != TOK_EOF) {
...@@ -500,9 +519,23 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc ...@@ -500,9 +519,23 @@ void parseOper(TokenIterator& input, Script& script, Precedence minPrec, Instruc
} }
} else if (minPrec <= PREC_STRING && token==_("\"{")) { } else if (minPrec <= PREC_STRING && token==_("\"{")) {
// for smart strings: "x" {{ e }} "y" // for smart strings: "x" {{ e }} "y"
parseOper(input, script, PREC_ALL, I_BINARY, I_ADD); // e // optimize: "" + e -> e
Instruction i = script.getInstructions().back();
if (i.instr == I_PUSH_CONST && String(*script.getConstants()[i.data]).empty()) {
script.getInstructions().pop_back();
parseOper(input, script, PREC_ALL); // e
} else {
parseOper(input, script, PREC_ALL, I_BINARY, I_ADD); // e
}
expectToken(input, _("}\"")); expectToken(input, _("}\""));
parseOper(input, script, PREC_NONE, I_BINARY, I_ADD); // y parseOper(input, script, PREC_NONE); // y
// optimize: e + "" -> e
i = script.getInstructions().back();
if (i.instr == I_PUSH_CONST && String(*script.getConstants()[i.data]).empty()) {
script.getInstructions().pop_back();
} else {
script.addInstruction(I_BINARY, I_ADD);
}
} else if (minPrec <= PREC_NEWLINE && token.newline) { } else if (minPrec <= PREC_NEWLINE && token.newline) {
// newline functions as ; // newline functions as ;
// only if we don't match another token! // only if we don't match another token!
......
...@@ -14,8 +14,11 @@ ...@@ -14,8 +14,11 @@
// ----------------------------------------------------------------------------- : Parser // ----------------------------------------------------------------------------- : Parser
// Parse a String to a Script /// Parse a String to a Script
ScriptP parse(const String& s); /** If string_mode then s is interpreted as a string,
* escaping to script mode can be done with {}
*/
ScriptP parse(const String& s, bool string_mode = false);
// ----------------------------------------------------------------------------- : EOF // ----------------------------------------------------------------------------- : EOF
#endif #endif
...@@ -134,6 +134,8 @@ class Script : public ScriptValue { ...@@ -134,6 +134,8 @@ class Script : public ScriptValue {
/// Get access to the vector of instructions /// Get access to the vector of instructions
inline vector<Instruction>& getInstructions() { return instructions; } inline vector<Instruction>& getInstructions() { return instructions; }
/// Get access to the vector of constants
inline vector<ScriptValueP>& getConstants() { return constants; }
/// Output the instructions in a human readable format /// Output the instructions in a human readable format
String dumpScript() const; String dumpScript() const;
......
...@@ -38,9 +38,9 @@ ScriptValueP OptionalScript::invoke(Context& ctx, bool open_scope) const { ...@@ -38,9 +38,9 @@ ScriptValueP OptionalScript::invoke(Context& ctx, bool open_scope) const {
} }
} }
void OptionalScript::parse(Reader& reader) { void OptionalScript::parse(Reader& reader, bool string_mode) {
try { try {
script = ::parse(unparsed); script = ::parse(unparsed, string_mode);
} catch (const ParseError& e) { } catch (const ParseError& e) {
reader.warning(e.what()); reader.warning(e.what());
} }
......
...@@ -77,7 +77,7 @@ class OptionalScript { ...@@ -77,7 +77,7 @@ class OptionalScript {
ScriptP script; ///< The script, may be null if there is no script ScriptP script; ///< The script, may be null if there is no script
String unparsed; ///< Unparsed script, for writing back to a file String unparsed; ///< Unparsed script, for writing back to a file
// parse the unparsed string, while reading // parse the unparsed string, while reading
void parse(Reader&); void parse(Reader&, bool string_mode = false);
DECLARE_REFLECTION(); DECLARE_REFLECTION();
template <typename T> friend class Scriptable; template <typename T> friend class Scriptable;
}; };
...@@ -126,6 +126,8 @@ void Reader::handle(Scriptable<T>& s) { ...@@ -126,6 +126,8 @@ void Reader::handle(Scriptable<T>& s) {
if (starts_with(s.script.unparsed, _("script:"))) { if (starts_with(s.script.unparsed, _("script:"))) {
s.script.unparsed = s.script.unparsed.substr(7); s.script.unparsed = s.script.unparsed.substr(7);
s.script.parse(*this); s.script.parse(*this);
} else if (s.script.unparsed.find_first_of('{') != String.npos) {
s.script.parse(*this, true);
} else { } else {
handle(s.value); handle(s.value);
} }
......
...@@ -170,6 +170,14 @@ class ScriptString : public ScriptValue { ...@@ -170,6 +170,14 @@ class ScriptString : public ScriptValue {
throw ScriptError(_("Not a number: '") + value + _("'")); throw ScriptError(_("Not a number: '") + value + _("'"));
} }
} }
virtual operator Color() const {
UInt r,g,b;
if (wxSscanf(value.c_str(),_("rgb(%u,%u,%u)"),&r,&g,&b)) {
return Color(r, g, b);
} else {
throw ScriptError(_("Not a color: '") + value + _("'"));
}
}
virtual int itemCount() const { return (int)value.size(); } virtual int itemCount() const { return (int)value.size(); }
virtual ScriptValueP getMember(const String& name) const { virtual ScriptValueP getMember(const String& name) const {
// get member returns characters // get member returns characters
...@@ -198,6 +206,9 @@ class ScriptColor : public ScriptValue { ...@@ -198,6 +206,9 @@ class ScriptColor : public ScriptValue {
virtual ScriptType type() const { return SCRIPT_COLOR; } virtual ScriptType type() const { return SCRIPT_COLOR; }
virtual String typeName() const { return _("color"); } virtual String typeName() const { return _("color"); }
virtual operator Color() const { return value; } virtual operator Color() const { return value; }
virtual operator String() const {
return String::Format(_("rgb(%u,%u,%u)"), value.Red(), value.Green(), value.Blue());
}
private: private:
Color value; Color value;
}; };
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment