Commit 7afe1ee1 authored by twanvl's avatar twanvl

Be more strict about type conversion:

  * numbers are no longer auto converted to booleans, use to_boolean or != 0
  * booleans are no longer auto converted to numbers, use to_int
  * strings will soon no longer be auto converted to numbers, use to_int

Added version information ("since 0.3.7") to documentation
parent 18bbf484
......@@ -157,7 +157,7 @@ init script:
# Number of colors in a card_color
card_color_color_count := {
chosen(choice:"white") + chosen(choice:"blue") + chosen(choice:"black") + chosen(choice:"red") + chosen(choice:"green") + chosen(choice:"artifact")
for each choice in ["white","blue","black","red","green","artifact"] do to_int(chosen())
}
# Clean up color field
card_color_filter := {
......@@ -1883,7 +1883,18 @@ pack type:
name: Common
amount: 11
filter: card.rarity == "common"
pack type:
name: Additional rare
card type:
name: Rare
amount: 1
filter: card.rarity == "rare"
pack type:
name: Additional common
card type:
name: Rare
amount: 1
filter: card.rarity == "rare"
......
Function: assert
DOC_MSE_VERSION: since 0.3.7
--Usage--
> assert(condition)
......@@ -12,5 +14,5 @@ Note: @assert@ is a special built-in keyword, so that the error message can incl
| @input@ [[type:boolean]] Condition to check.
--Examples--
> assert(1 + 1 == 2) == nil # nothing happens
> assert(1 * 1 == 2) == nil # An error message is shown
> assert(1 + 1 == 2) # nothing happens
> assert(1 * 1 == 2) # An error message is shown
......@@ -2,6 +2,13 @@ Script functions by category
These functions are built into the program, other [[type:function]]s can be defined using the scripting language.
! Type conversion <<<
| [[fun:to_string]] Convert any value to a [[type:string]]
| [[fun:to_int]] Convert any value to a [[type:int]]
| [[fun:to_real]] Convert any value to a [[type:double]]
| [[fun:to_boolean]] Convert any value to a [[type:boolean]]
| [[fun:to_color]] Convert any value to a [[type:color]]
! Text manipulation <<<
| [[fun:to_upper]] Convert a string to upper case, @"aBc" -> "ABC"@.
| [[fun:to_lower]] Convert a string to lower case, @"aBc" -> "abc"@.
......
Function: to_boolean
DOC_MSE_VERSION: since 0.3.7
--Usage--
> to_boolean(any value)
Convert any value to a [[type:boolean]] representation.
Normally numbers are not converted to booleans automatically, with the to_boolean function @0@ is converted to @false@ while any other number is converted to @true@.
--Parameters--
! Parameter Type Description
| @input@ ''any type'' Value to convert to a boolean.
--Examples--
> to_boolean(true) == true
> to_boolean("true") == true
> to_boolean(1) == true
> to_boolean(0) == false
Function: to_color
DOC_MSE_VERSION: since 0.3.7
--Usage--
> to_color(any value)
Convert any value to a [[type:color]].
--Parameters--
! Parameter Type Description
| @input@ ''any type'' Value to convert to a color
--Examples--
> to_color("red") == rgb(255,0,0)
Function: to_int
DOC_MSE_VERSION: since 0.3.7
--Usage--
> to_int(any value)
Convert any value to a [[type:int]].
* Real numbers are rounded towards zero when converted to integer numbers.
* The boolean value @true@ becomes @1@, while @false@ is converted to @0@.
* For colors the grayscale value between @0@ and @255@ is returned.
--Parameters--
! Parameter Type Description
| @input@ ''any type'' Value to convert to an integer number
--Examples--
> to_int(1.5) == "1"
> to_int("15") == "15"
> to_int(true) == 1
--See also--
| [[fun:to_real]] Convert any value to a [[type:double]]
Function: to_real
DOC_MSE_VERSION: since 0.3.7
--Usage--
> to_real(any value)
Convert any value to a [[type:double]].
--Parameters--
! Parameter Type Description
| @input@ ''any type'' Value to convert to a real number
--Examples--
> to_real(1) == 1.0
> to_real("1.5") == "1.5"
--See also--
| [[fun:to_int]] Convert any value to a [[type:int]]
Function: to_string
DOC_MSE_VERSION: since 0.3.7
--Usage--
> to_string(any value)
Convert any value to a [[type:string]] representation.
The @to_string@ function should not be confused with @to_text@,
the former converts things like number to string, while the latter removes tags from a [[type:tagged string]].
--Parameters--
! Parameter Type Description
| @input@ ''any type'' Value to convert to a string
| @format@ ''optional'' Formating to apply.
--Examples--
> to_string(to_color("blue")) == "rgb(0,0,255)"
> to_string(10 + 20) == "30"
> to_string(10 + 20, format: ".3f") == "30.000"
> to_string(10 + 20, format: "x") == "1e" # hexadecimal notation
Default arguments
DOC_MSE_VERSION: since 0.3.7
It is possible to declare default arguments for functions using the @@@@ operator.
> function := { "argument was: " + arg }@(arg:"default")
If this function is called without the @arg@ argument, then the default value @"default"@ is used instead.
......@@ -19,6 +21,8 @@ Defaults are evaluated at the time the @@@@ operator is evaluated, they will not
--Rule functions--
DOC_MSE_VERSION: until 0.3.6
In earlier versions of MSE some functions were available in a special ''rule form''.
A call to for example @replace_rule(match:"abc",replace:"xyz")@ is equivalent to @replace@@(match:"abc",replace:"xyz")@ .
For backwards compatability these functions are still available, but they should not be used for new templates.
......
......@@ -16,6 +16,7 @@ See also:
* [[fun:index|Built in functions]]
--Syntax index--
| @#comment@ Comments ignored by the parser
| @123@ [[type:int|A literal number]]
| @"stuff"@ [[type:string|A literal string]]
| @[a,b,c]@ [[type:list|A literal list]]
......
......@@ -22,3 +22,5 @@ The operators @or@, @and@ and @xor@ combine two booleans:
| @true@ @false@ @true@ @false@ @true@
| @true@ @true@ @true@ @true@ @false@
--See also--
| [[fun:to_boolean]] Convert a value to a boolean
......@@ -27,3 +27,6 @@ For example:
| @rgba(0,0,0,0)@ transparent <div style="border:1px solid black; width:30px;height:15px;text-align:center;">over</div>
| @rgba(255,0,0,128)@ transparent red <div style="border:1px solid black; background:rgb(255,128,128);color:rgb(128,0,0);width:30px;height:15px;text-align:center;">over</div>
| @rgba(0,0,255,192)@ transparent blue <div style="border:1px solid black; background:rgb(64,64,255);color:rgb(0,0,192);width:30px;height:15px;text-align:center;">over</div>
--See also--
| [[fun:to_color]] Convert any value to a color
......@@ -12,4 +12,5 @@ Conversion from integer to real numbers happens automatically in scripting.
> 123.1 + 456 * -1
--See also--
* [[type:int]]
| [[type:int]] Integer numbers
| [[fun:to_real]] Convert a value to a real number
......@@ -10,4 +10,5 @@ In many cases negative numbers don't make sense, but the program never complains
> 123 + 456 * -1
--See also--
* [[type:double]]
| [[type:double]] Number type that can contain fractional values.
| [[fun:to_int]] Convert a value to an integer number
......@@ -25,10 +25,17 @@ Sections between curly braces are interpreted as script code, that is concatenta
> "ab{1 + 1}c" == "ab2c"
This can be nested arbitrarily.
The @+@ operator concatenates strings. Numbers and most other values are automatically converted to strings when needed.
The @+@ operator concatenates strings. Numbers and most other values are automatically converted to strings when needed. This conversion can be forced with the [[fun:to_string]] function.
Using the @[]@ or @.@ operator characters in a string can be selected. 0 is the first character:
> "xyz"[0] == "x"
> "xyz".0 == "x" # same thing
> "xyz".1 == "y"
> "xyz".2 == "z"
It is an error to select characters outside the string
> "xyz".10 # error
\ No newline at end of file
> "xyz".10 # error
--See also--
| [[type:tagged string]] A string containg tags.
| [[fun:to_string]] Convert any value to a [[type:string]]
......@@ -649,6 +649,7 @@ KeywordParamValue::operator String() const {
KeywordParamValue::operator int() const { return *to_script(value); } // a bit of a hack
KeywordParamValue::operator double() const { return *to_script(value); }
KeywordParamValue::operator bool() const { return *to_script(value); }
KeywordParamValue::operator AColor() const { return *to_script(value); }
int KeywordParamValue::itemCount() const { return to_script(value)->itemCount(); }
......
......@@ -176,6 +176,7 @@ class KeywordParamValue : public ScriptValue {
virtual String typeName() const;
virtual operator String() const;
virtual operator int() const;
virtual operator bool() const;
virtual operator double() const;
virtual operator AColor() const;
virtual int itemCount() const;
......
This diff is collapsed.
......@@ -63,7 +63,7 @@ ScriptValueP Context::eval(const Script& script, bool useScope) {
}
// Conditional jump
case I_JUMP_IF_NOT: {
int condition = *stack.back();
bool condition = *stack.back();
stack.pop_back();
if (!condition) {
instr = &script.instructions[i.data];
......@@ -322,6 +322,11 @@ class ScriptCompose : public ScriptValue {
a = to_script((int)*a OP (int)*b); \
break
// operator on bools
#define OPERATOR_B(OP) \
a = to_script((bool)*a OP (bool)*b); \
break
// operator on doubles or ints
#define OPERATOR_DI(OP) \
if (at == SCRIPT_DOUBLE || bt == SCRIPT_DOUBLE) { \
......@@ -392,9 +397,9 @@ void instrBinary (BinaryInstructionType i, ScriptValueP& a, const ScriptValueP&
a = to_script((int)*a % (int)*b);
}
break;
case I_AND: OPERATOR_I(&&);
case I_OR: OPERATOR_I(||);
case I_XOR: a = to_script((bool)*a != (bool)*b); break;
case I_AND: OPERATOR_B(&&);
case I_OR: OPERATOR_B(||);
case I_XOR: OPERATOR_B(!=);
case I_EQ: a = to_script( equal(a,b)); break;
case I_NEQ: a = to_script(!equal(a,b)); break;
case I_LT: OPERATOR_DI(<);
......
......@@ -78,6 +78,7 @@ class ScriptMissingVariable : public ScriptValue {
virtual operator String() const { return wxEmptyString; }
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
private:
String name; ///< Name of the variable
......
......@@ -37,6 +37,75 @@ SCRIPT_FUNCTION(warning) {
return script_nil;
}
// ----------------------------------------------------------------------------- : Conversion
/// Format the input variable based on a printf like style specification
String format_input(const String& format, Context& ctx) {
String fmt = _("%") + replace_all(format, _("%"), _(""));
// determine type expected by format string
if (format.find_first_of(_("DdIiOoXx")) != String::npos) {
SCRIPT_PARAM_C(int, input);
return String::Format(fmt, input);
} else if (format.find_first_of(_("EeFfGg")) != String::npos) {
SCRIPT_PARAM_C(double, input);
return String::Format(fmt, input);
} else if (format.find_first_of(_("Ss")) != String::npos) {
SCRIPT_PARAM_C(String, input);
return format_string(fmt, input);
} else {
throw ScriptError(_ERROR_1_("unsupported format", format));
}
}
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);
}
}
SCRIPT_FUNCTION(to_int) {
ScriptValueP input = ctx.getVariable(SCRIPT_VAR_input);
ScriptType t = input->type();
int result;
if (t == SCRIPT_BOOL) {
result = (bool)*input ? 1 : 0;
} else if (t == SCRIPT_COLOR) {
AColor c = (AColor)*input;
result = (c.Red() + c.Blue() + c.Green()) / 3;
} else {
result = (int)*input;
}
SCRIPT_RETURN(result);
}
SCRIPT_FUNCTION(to_real) {
SCRIPT_PARAM_C(double, input);
SCRIPT_RETURN(input);
}
SCRIPT_FUNCTION(to_boolean) {
ScriptValueP input = ctx.getVariable(SCRIPT_VAR_input);
ScriptType t = input->type();
bool result;
if (t == SCRIPT_INT) {
result = (int)*input != 0;
} else {
result = (bool)*input;
}
SCRIPT_RETURN(result);
}
SCRIPT_FUNCTION(to_color) {
SCRIPT_PARAM_C(AColor, input);
SCRIPT_RETURN(input);
}
// ----------------------------------------------------------------------------- : String stuff
// convert a string to upper case
......@@ -95,20 +164,7 @@ SCRIPT_FUNCTION(contains) {
SCRIPT_FUNCTION(format) {
SCRIPT_PARAM_C(String, format);
String fmt = _("%") + replace_all(format, _("%"), _(""));
// determine type expected by format string
if (format.find_first_of(_("DdIiOoXx")) != String::npos) {
SCRIPT_PARAM_C(int, input);
SCRIPT_RETURN(String::Format(fmt, input));
} else if (format.find_first_of(_("EeFfGg")) != String::npos) {
SCRIPT_PARAM_C(double, input);
SCRIPT_RETURN(String::Format(fmt, input));
} else if (format.find_first_of(_("Ss")) != String::npos) {
SCRIPT_PARAM_C(String, input);
SCRIPT_RETURN(format_string(fmt, input));
} else {
throw ScriptError(_ERROR_1_("unsupported format", format));
}
SCRIPT_RETURN(format_input(format,ctx));
}
SCRIPT_FUNCTION(curly_quotes) {
......@@ -394,6 +450,12 @@ SCRIPT_FUNCTION(rule) {
void init_script_basic_functions(Context& ctx) {
// debugging
ctx.setVariable(_("trace"), script_trace);
// conversion
ctx.setVariable(_("to string"), script_to_string);
ctx.setVariable(_("to int"), script_to_int);
ctx.setVariable(_("to real"), script_to_real);
ctx.setVariable(_("to boolean"), script_to_boolean);
ctx.setVariable(_("to color"), script_to_color);
// string
ctx.setVariable(_("to upper"), script_to_upper);
ctx.setVariable(_("to lower"), script_to_lower);
......
......@@ -419,4 +419,5 @@ void init_script_export_functions(Context& ctx) {
ctx.setVariable(_("copy file"), script_copy_file);
ctx.setVariable(_("write text file"), script_write_text_file);
ctx.setVariable(_("write image file"), script_write_image_file);
//ctx.setVariable(_("write set file"), script_write_set_file);//TODO
}
......@@ -29,6 +29,11 @@ template <> inline GeneratedImageP from_script<GeneratedImageP>(const ScriptValu
return image_from_script(value);
}
SCRIPT_FUNCTION(to_image) {
SCRIPT_PARAM_C(GeneratedImageP, input);
return input;
}
// ----------------------------------------------------------------------------- : Image functions
SCRIPT_FUNCTION(linear_blend) {
......@@ -181,6 +186,7 @@ SCRIPT_FUNCTION(built_in_image) {
// ----------------------------------------------------------------------------- : Init
void init_script_image_functions(Context& ctx) {
ctx.setVariable(_("to image"), script_to_image);
ctx.setVariable(_("linear blend"), script_linear_blend);
ctx.setVariable(_("masked blend"), script_masked_blend);
ctx.setVariable(_("combine blend"), script_combine_blend);
......
......@@ -23,7 +23,7 @@ DECLARE_TYPEOF_COLLECTION(ScriptParseError);
void store(const ScriptValueP& val, String& var) { var = val->toString(); }
void store(const ScriptValueP& val, int& var) { var = *val; }
void store(const ScriptValueP& val, double& var) { var = *val; }
void store(const ScriptValueP& val, bool& var) { var = static_cast<int>(*val); }
void store(const ScriptValueP& val, bool& var) { var = *val; }
void store(const ScriptValueP& val, Color& var) { var = (AColor)*val; }
void store(const ScriptValueP& val, AColor& var) { var = *val; }
void store(const ScriptValueP& val, Defaultable<String>& var) { var.assign(*val); }
......
......@@ -60,6 +60,7 @@ class ScriptDelayedError : public ScriptValue {
virtual operator String() const;
virtual operator double() const;
virtual operator int() const;
virtual operator bool() const;
virtual operator AColor() const;
virtual int itemCount() const;
virtual CompareWhat compareAs(String&, void const*&) const;
......@@ -243,6 +244,7 @@ class ScriptObject : public ScriptValue {
virtual operator String() const { ScriptValueP d = getDefault(); return d ? *d : ScriptValue::operator String(); }
virtual operator double() const { ScriptValueP d = getDefault(); return d ? *d : ScriptValue::operator double(); }
virtual operator int() const { ScriptValueP d = getDefault(); return d ? *d : ScriptValue::operator int(); }
virtual operator bool() const { ScriptValueP d = getDefault(); return d ? *d : ScriptValue::operator bool(); }
virtual operator AColor() const { ScriptValueP d = getDefault(); return d ? *d : ScriptValue::operator AColor(); }
virtual ScriptValueP getMember(const String& name) const {
GetMember gm(name);
......
......@@ -20,6 +20,7 @@ DECLARE_TYPEOF_COLLECTION(pair<Variable COMMA ScriptValueP>);
ScriptValue::operator String() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("string" ))); }
ScriptValue::operator int() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("integer" ))); }
ScriptValue::operator bool() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("boolean" ))); }
ScriptValue::operator double() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("double" ))); }
ScriptValue::operator AColor() const { throw ScriptError(_ERROR_2_("can't convert", typeName(), _TYPE_("color" ))); }
ScriptValueP ScriptValue::eval(Context&) const { return delayError(_ERROR_2_("can't convert", typeName(), _TYPE_("function"))); }
......@@ -43,6 +44,8 @@ bool equal(const ScriptValueP& a, const ScriptValueP& b) {
ScriptType at = a->type(), bt = b->type();
if (at == bt && at == SCRIPT_INT) {
return (int)*a == (int)*b;
} else if (at == bt && at == SCRIPT_BOOL) {
return (bool)*a == (bool)*b;
} else if ((at == SCRIPT_INT || at == SCRIPT_DOUBLE) &&
(bt == SCRIPT_INT || bt == SCRIPT_DOUBLE)) {
return (double)*a == (double)*b;
......@@ -81,6 +84,7 @@ String ScriptDelayedError::typeName() const { throw error; }
ScriptDelayedError::operator String() const { throw error; }
ScriptDelayedError::operator double() const { throw error; }
ScriptDelayedError::operator int() const { throw error; }
ScriptDelayedError::operator bool() const { throw error; }
ScriptDelayedError::operator AColor() const { throw error; }
int ScriptDelayedError::itemCount() const { throw error; }
CompareWhat ScriptDelayedError::compareAs(String&, void const*&) const { throw error; }
......@@ -171,10 +175,11 @@ ScriptValueP to_script(int v) {
class ScriptBool : public ScriptValue {
public:
ScriptBool(bool v) : value(v) {}
virtual ScriptType type() const { return SCRIPT_INT; }
virtual ScriptType type() const { return SCRIPT_BOOL; }
virtual String typeName() const { return _TYPE_("boolean"); }
virtual operator String() const { return value ? _("true") : _("false"); }
virtual operator int() const { return value; }
// bools don't autoconvert to int
virtual operator bool() const { return value; }
private:
bool value;
};
......@@ -227,12 +232,17 @@ class ScriptString : public ScriptValue {
long l;
if (value.ToLong(&l)) {
return l;
} else if (value == _("yes") || value == _("true")) {
} else {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("integer")));
}
}
virtual operator bool() const {
if (value == _("yes") || value == _("true")) {
return true;
} else if (value == _("no") || value == _("false") || value.empty()) {
return false;
} else {
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("integer")));
throw ScriptError(_ERROR_3_("can't convert value", value, typeName(), _TYPE_("boolean")));
}
}
virtual operator AColor() const {
......@@ -270,7 +280,7 @@ class ScriptAColor : public ScriptValue {
virtual ScriptType type() const { return SCRIPT_COLOR; }
virtual String typeName() const { return _TYPE_("color"); }
virtual operator AColor() const { return value; }
virtual operator int() const { return (value.Red() + value.Blue() + value.Green()) / 3; }
// colors don't auto convert to int, use to_int to force
virtual operator String() const {
return format_acolor(value);
}
......@@ -296,6 +306,7 @@ class ScriptNil : public ScriptValue {
virtual operator String() const { return wxEmptyString; }
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
};
......
......@@ -22,6 +22,7 @@ DECLARE_POINTER_TYPE(ScriptValue);
enum ScriptType
{ SCRIPT_NIL
, SCRIPT_INT
, SCRIPT_BOOL
, SCRIPT_DOUBLE
, SCRIPT_STRING
, SCRIPT_COLOR
......@@ -62,7 +63,7 @@ class ScriptValue : public IntrusivePtrBaseWithDelete {
/// Convert this value to an integer
virtual operator int() const;
/// Convert this value to a boolean
inline operator bool() const { return (int)*this; }
virtual operator bool() const;
/// Convert this value to a color
virtual operator AColor() const;
......
......@@ -161,6 +161,12 @@ function autoformat__handle(&$i, $prefix, $first, $fail_same = false) {
} elseif (preg_match("@^//@", $line)) {
// ignore
// version information block
} elseif (preg_match("@^DOC_MSE_VERSION:@", $line)) {
$line = preg_replace("@^[A-Z_]+:\s*@",'',$line);
$text .= "<div class=\"version-note\">" . $line . "</div>\n";
$state = '';
// Just text
} else if ($fail_same && $autoformat__lines[$i-1]{$len-1} != ' ' && $text != '') {
// consecutive * and # lines are different items
......
......@@ -4,6 +4,12 @@
global $built_in_functions;
$built_in_functions = array(
// conversion
'to_string' =>'',
'to_int' =>'',
'to_real' =>'',
'to_boolean' =>'',
'to_color' =>'',
// text
'to_upper' =>'',
'to_lower' =>'',
......
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