mirror of
https://github.com/amyinspace/MagicSetEditor2.git
synced 2026-06-10 04:57:00 -04:00
Simplified compilation of 'assert' pseudo function;
Added remove_duplicates flag to sort_list function; Fixed documentation of <size:> tag git-svn-id: svn://svn.code.sf.net/p/magicseteditor/code/trunk@1028 0fc631ac-6414-0410-93d0-97cfa31319b6
This commit is contained in:
@@ -33,7 +33,26 @@ SCRIPT_FUNCTION(trace) {
|
||||
|
||||
SCRIPT_FUNCTION(warning) {
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
handle_warning(input, true);
|
||||
SCRIPT_PARAM_DEFAULT_C(bool, condition, true);
|
||||
if (condition) {
|
||||
handle_warning(input, true);
|
||||
}
|
||||
return script_nil;
|
||||
}
|
||||
SCRIPT_FUNCTION(warning_if_neq) {
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
SCRIPT_PARAM_N(ScriptValueP, SCRIPT_VAR__1, v1);
|
||||
SCRIPT_PARAM_N(ScriptValueP, SCRIPT_VAR__2, v2);
|
||||
if (!equal(v1,v2)) {
|
||||
String s1 = _("?"), s2 = _("?");
|
||||
try {
|
||||
s1 = v1->toCode();
|
||||
} catch (...) {}
|
||||
try {
|
||||
s2 = v2->toCode();
|
||||
} catch (...) {}
|
||||
handle_warning(input + s1 + _(" != ") + s2, true);
|
||||
}
|
||||
return script_nil;
|
||||
}
|
||||
|
||||
@@ -59,13 +78,17 @@ String format_input(const String& format, Context& ctx) {
|
||||
|
||||
SCRIPT_FUNCTION(to_string) {
|
||||
ScriptValueP format = ctx.getVariable(SCRIPT_VAR_format);
|
||||
if (format && format->type() == SCRIPT_STRING) {
|
||||
// format specifier. Be careful, the built in function 'format' has the same name
|
||||
SCRIPT_RETURN(format_input(*format, ctx));
|
||||
} else {
|
||||
// simple conversion
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
SCRIPT_RETURN(input);
|
||||
try {
|
||||
if (format && format->type() == SCRIPT_STRING) {
|
||||
// format specifier. Be careful, the built in function 'format' has the same name
|
||||
SCRIPT_RETURN(format_input(*format, ctx));
|
||||
} else {
|
||||
// simple conversion
|
||||
SCRIPT_PARAM_C(String, input);
|
||||
SCRIPT_RETURN(input);
|
||||
}
|
||||
} catch (const ScriptError& e) {
|
||||
return new_intrusive1<ScriptDelayedError>(e);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -145,6 +168,11 @@ SCRIPT_FUNCTION(to_color) {
|
||||
}
|
||||
}
|
||||
|
||||
SCRIPT_FUNCTION(to_code) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
SCRIPT_RETURN(input->toCode());
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Math
|
||||
|
||||
SCRIPT_FUNCTION(abs) {
|
||||
@@ -344,14 +372,20 @@ int position_in_vector(const ScriptValueP& of, const ScriptValueP& in, const Scr
|
||||
inline bool smart_less_first(const pair<String,ScriptValueP>& a, const pair<String,ScriptValueP>& b) {
|
||||
return smart_less(a.first, b.first);
|
||||
}
|
||||
inline bool smart_equal_first(const pair<String,ScriptValueP>& a, const pair<String,ScriptValueP>& b) {
|
||||
return smart_equal(a.first, b.first);
|
||||
}
|
||||
|
||||
// sort a script list
|
||||
ScriptValueP sort_script(Context& ctx, const ScriptValueP& list, ScriptValue& order_by) {
|
||||
ScriptValueP sort_script(Context& ctx, const ScriptValueP& list, ScriptValue& order_by, bool remove_duplicates) {
|
||||
ScriptType list_t = list->type();
|
||||
if (list_t == SCRIPT_STRING) {
|
||||
// sort a string
|
||||
String s = list->toString();
|
||||
sort(s.begin(), s.end());
|
||||
if (remove_duplicates) {
|
||||
s.erase( unique(s.begin(), s.end()), s.end() );
|
||||
}
|
||||
SCRIPT_RETURN(s);
|
||||
} else {
|
||||
// are we sorting a set?
|
||||
@@ -364,6 +398,10 @@ ScriptValueP sort_script(Context& ctx, const ScriptValueP& list, ScriptValue& or
|
||||
values.push_back(make_pair(order_by.eval(ctx)->toString(), v));
|
||||
}
|
||||
sort(values.begin(), values.end(), smart_less_first);
|
||||
// unique
|
||||
if (remove_duplicates) {
|
||||
values.erase( unique(values.begin(), values.end(), smart_equal_first), values.end() );
|
||||
}
|
||||
// return collection
|
||||
ScriptCustomCollectionP ret(new ScriptCustomCollection());
|
||||
FOR_EACH(v, values) {
|
||||
@@ -442,10 +480,12 @@ SCRIPT_FUNCTION(filter_list) {
|
||||
|
||||
SCRIPT_FUNCTION(sort_list) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
SCRIPT_PARAM_N(ScriptValueP, _("order by"), order_by);
|
||||
return sort_script(ctx, input, *order_by);
|
||||
SCRIPT_PARAM_DEFAULT_N(ScriptValueP, _("order by"), order_by, script_nil);
|
||||
SCRIPT_PARAM_DEFAULT_N(bool, _("remove duplicates"), remove_duplicates, false);
|
||||
return sort_script(ctx, input, *order_by, remove_duplicates);
|
||||
}
|
||||
|
||||
|
||||
SCRIPT_FUNCTION(random_shuffle) {
|
||||
SCRIPT_PARAM_C(ScriptValueP, input);
|
||||
// convert to CustomCollection
|
||||
@@ -571,6 +611,7 @@ void init_script_basic_functions(Context& ctx) {
|
||||
ctx.setVariable(_("to number"), script_to_number);
|
||||
ctx.setVariable(_("to boolean"), script_to_boolean);
|
||||
ctx.setVariable(_("to color"), script_to_color);
|
||||
ctx.setVariable(_("to code"), script_to_code);
|
||||
// math
|
||||
ctx.setVariable(_("abs"), script_abs);
|
||||
ctx.setVariable(_("random_real"), script_random_real);
|
||||
|
||||
+17
-31
@@ -23,6 +23,7 @@ DECLARE_TYPEOF_COLLECTION(Variable);
|
||||
String read_utf8_line(wxInputStream& input, bool eat_bom = true, bool until_eof = false);
|
||||
|
||||
extern ScriptValueP script_warning;
|
||||
extern ScriptValueP script_warning_if_neq;
|
||||
|
||||
// ----------------------------------------------------------------------------- : Tokenizing : class
|
||||
|
||||
@@ -557,45 +558,30 @@ void parseExpr(TokenIterator& input, Script& script, Precedence minPrec) {
|
||||
expectToken(input, _("("));
|
||||
size_t start = input.peek().pos;
|
||||
int line = input.getLineNumber();
|
||||
size_t function_pos = script.getConstants().size();
|
||||
script.addInstruction(I_PUSH_CONST, script_warning);
|
||||
parseOper(input, script, PREC_ALL); // condition
|
||||
size_t end = input.peek().pos;
|
||||
String message = String::Format(_("Assertion failure on line %d:\n expected: "), line) + input.getSourceCode(start,end);
|
||||
expectToken(input, _(")"), &token);
|
||||
if (script.getInstructions().back().instr == I_BINARY && script.getInstructions().back().instr2 == I_EQ) {
|
||||
// compile "assert(x == y)" into "
|
||||
// compile "assert(x == y)" into
|
||||
// warning_if_neq("condition", _1: x, _2: y)
|
||||
message += _("\n found: ");
|
||||
script.getInstructions().pop_back(); // remove ==
|
||||
script.addInstruction(I_DUP, 1); // duplicate X
|
||||
script.addInstruction(I_DUP, 1); // duplicate Y
|
||||
script.addInstruction(I_BINARY, I_EQ); //
|
||||
unsigned jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
|
||||
script.addInstruction(I_PUSH_CONST, script_nil); // push nil
|
||||
unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
|
||||
script.comeFrom(jmpElse); // lbl_else:
|
||||
script.addInstruction(I_PUSH_CONST, script_warning); // push warning
|
||||
script.addInstruction(I_PUSH_CONST, message); // push "condition"
|
||||
script.addInstruction(I_DUP, 3); // duplicate X
|
||||
script.addInstruction(I_BINARY, I_ADD); // add
|
||||
script.addInstruction(I_PUSH_CONST, String(_(" != "))); // push " != "
|
||||
script.addInstruction(I_BINARY, I_ADD); // add
|
||||
script.addInstruction(I_DUP, 2); // duplicate Y
|
||||
script.addInstruction(I_BINARY, I_ADD); // add
|
||||
script.addInstruction(I_CALL, 1); // call
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:)
|
||||
script.comeFrom(jmpEnd); // lbl_end:
|
||||
script.addInstruction(I_BINARY, I_POP); // pop Y_copy
|
||||
script.addInstruction(I_BINARY, I_POP); // pop X_copy
|
||||
} else {
|
||||
// compile into: if condition then nil else warning("condition")
|
||||
unsigned jmpElse = script.addInstruction(I_JUMP_IF_NOT); // jnz lbl_else
|
||||
script.addInstruction(I_PUSH_CONST, script_nil); // push nil
|
||||
unsigned jmpEnd = script.addInstruction(I_JUMP); // jump lbl_end
|
||||
script.comeFrom(jmpElse); // lbl_else:
|
||||
script.addInstruction(I_PUSH_CONST, script_warning); // push warning
|
||||
script.getConstants()[function_pos] = script_warning_if_neq;
|
||||
script.getInstructions().pop_back(); // POP == instruction
|
||||
script.addInstruction(I_PUSH_CONST, message); // push "condition"
|
||||
script.addInstruction(I_CALL, 1); // call
|
||||
script.addInstruction(I_CALL, 3); // call
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR__1); // (_1:)
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR__2); // (_2:)
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:)
|
||||
} else {
|
||||
// compile into: warning("condition", condition: not condition)
|
||||
script.addInstruction(I_UNARY, I_NOT); // not
|
||||
script.addInstruction(I_PUSH_CONST, message); // push "condition"
|
||||
script.addInstruction(I_CALL, 2); // call
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR_condition); // (condition:)
|
||||
script.addInstruction(I_NOP, SCRIPT_VAR_input); // (input:)
|
||||
script.comeFrom(jmpEnd); // lbl_end:
|
||||
}
|
||||
} else {
|
||||
// variable
|
||||
|
||||
@@ -52,6 +52,8 @@ void init_script_variables() {
|
||||
#define VarN(X,name) if (SCRIPT_VAR_##X != string_to_variable(name)) assert(false);
|
||||
#define Var(X) VarN(X,_(#X))
|
||||
Var(input);
|
||||
Var(_1);
|
||||
Var(_2);
|
||||
Var(in);
|
||||
Var(match);
|
||||
Var(replace);
|
||||
@@ -73,6 +75,7 @@ void init_script_variables() {
|
||||
Var(card);
|
||||
Var(styling);
|
||||
Var(value);
|
||||
Var(condition);
|
||||
assert(variables.size() == SCRIPT_VAR_CUSTOM_FIRST);
|
||||
}
|
||||
|
||||
|
||||
@@ -111,6 +111,8 @@ struct Instruction {
|
||||
// for faster lookup from code
|
||||
enum Variable
|
||||
{ SCRIPT_VAR_input
|
||||
, SCRIPT_VAR__1
|
||||
, SCRIPT_VAR__2
|
||||
, SCRIPT_VAR_in
|
||||
, SCRIPT_VAR_match
|
||||
, SCRIPT_VAR_replace
|
||||
@@ -132,6 +134,7 @@ enum Variable
|
||||
, SCRIPT_VAR_card
|
||||
, SCRIPT_VAR_styling
|
||||
, SCRIPT_VAR_value
|
||||
, SCRIPT_VAR_condition
|
||||
, SCRIPT_VAR_CUSTOM_FIRST // other variables start from here
|
||||
, SCRIPT_VAR_CUSTOM_LOTS = 0xFFFFFF // ensure that sizeof(Variable) is large enough
|
||||
};
|
||||
|
||||
@@ -95,6 +95,12 @@ ScriptValueP rangeIterator(int start, int end);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Collections
|
||||
|
||||
class ScriptCollectionBase : public ScriptValue {
|
||||
public:
|
||||
virtual ScriptType type() const { return SCRIPT_COLLECTION; }
|
||||
virtual String toCode() const;
|
||||
};
|
||||
|
||||
// Iterator over a collection
|
||||
template <typename Collection>
|
||||
class ScriptCollectionIterator : public ScriptIterator {
|
||||
@@ -114,10 +120,9 @@ class ScriptCollectionIterator : public ScriptIterator {
|
||||
|
||||
/// Script value containing a collection
|
||||
template <typename Collection>
|
||||
class ScriptCollection : public ScriptValue {
|
||||
class ScriptCollection : public ScriptCollectionBase {
|
||||
public:
|
||||
inline ScriptCollection(const Collection* v) : value(v) {}
|
||||
virtual ScriptType type() const { return SCRIPT_COLLECTION; }
|
||||
virtual String typeName() const { return _TYPE_1_("collection of", type_name(*value->begin())); }
|
||||
virtual ScriptValueP getIndex(int index) const {
|
||||
if (index >= 0 && index < (int)value->size()) {
|
||||
@@ -190,9 +195,8 @@ class ScriptMap : public ScriptValue {
|
||||
// ----------------------------------------------------------------------------- : Collections : from script
|
||||
|
||||
/// Script value containing a custom collection, returned from script functions
|
||||
class ScriptCustomCollection : public ScriptValue {
|
||||
class ScriptCustomCollection : public ScriptCollectionBase {
|
||||
public:
|
||||
virtual ScriptType type() const { return SCRIPT_COLLECTION; }
|
||||
virtual String typeName() const { return _TYPE_("collection"); }
|
||||
virtual ScriptValueP getMember(const String& name) const;
|
||||
virtual ScriptValueP getIndex(int index) const;
|
||||
|
||||
+27
-2
@@ -27,8 +27,9 @@ ScriptValueP ScriptValue::eval(Context&) const { return delay
|
||||
ScriptValueP ScriptValue::next() { throw InternalError(_("Can't convert from ")+typeName()+_(" to iterator")); }
|
||||
ScriptValueP ScriptValue::makeIterator(const ScriptValueP&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); }
|
||||
int ScriptValue::itemCount() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("collection"))); }
|
||||
String ScriptValue::toCode() const { return *this; }
|
||||
CompareWhat ScriptValue::compareAs(String& compare_str, void const*& compare_ptr) const {
|
||||
compare_str = toString();
|
||||
compare_str = toCode();
|
||||
return COMPARE_AS_STRING;
|
||||
}
|
||||
ScriptValueP ScriptValue::getMember(const String& name) const {
|
||||
@@ -322,12 +323,36 @@ class ScriptNil : public ScriptValue {
|
||||
virtual operator double() const { return 0.0; }
|
||||
virtual operator int() const { return 0; }
|
||||
virtual operator bool() const { return false; }
|
||||
virtual ScriptValueP eval(Context&) const { return script_nil; } // nil() == nil
|
||||
virtual ScriptValueP eval(Context& ctx) const {
|
||||
// nil(input) == input
|
||||
return ctx.getVariable(SCRIPT_VAR_input);
|
||||
}
|
||||
};
|
||||
|
||||
/// The preallocated nil value
|
||||
ScriptValueP script_nil(new ScriptNil);
|
||||
|
||||
// ----------------------------------------------------------------------------- : Collection base
|
||||
|
||||
String ScriptCollectionBase::toCode() const {
|
||||
String ret = _("[");
|
||||
bool first = true;
|
||||
#ifdef USE_INTRUSIVE_PTR
|
||||
// we can just turn this into a ScriptValueP
|
||||
// TODO: remove thisP alltogether
|
||||
ScriptValueP it = makeIterator(ScriptValueP(const_cast<ScriptValue*>((ScriptValue*)this)));
|
||||
#else
|
||||
#error "makeIterator needs a ScriptValueP :("
|
||||
#endif
|
||||
while (ScriptValueP v = it->next()) {
|
||||
if (!first) ret += _(",");
|
||||
first = false;
|
||||
ret += v->toCode();
|
||||
}
|
||||
ret += _("]");
|
||||
return ret;
|
||||
}
|
||||
|
||||
// ----------------------------------------------------------------------------- : Custom collection
|
||||
|
||||
// Iterator over a custom collection
|
||||
|
||||
@@ -67,6 +67,9 @@ class ScriptValue : public IntrusivePtrBaseWithDelete {
|
||||
/// Convert this value to a color
|
||||
virtual operator AColor() const;
|
||||
|
||||
/// Script code to generate this value
|
||||
virtual String toCode() const;
|
||||
|
||||
/// Explicit overload to convert to a string
|
||||
/** This is sometimes necessary, because wxString has an int constructor,
|
||||
* which confuses gcc. */
|
||||
|
||||
Reference in New Issue
Block a user