diff --git a/doc/function/break_text.txt b/doc/function/break_text.txt index a82bf441..bea4ed70 100644 --- a/doc/function/break_text.txt +++ b/doc/function/break_text.txt @@ -2,20 +2,21 @@ Function: break_text --Usage-- > break_text(some_string, match: regular expression, in_context: regular expression) -> break_rule(match: ..., in_context: ...)(some_string) Break text by only keeping the parts of the input that match the regular expression. The function returns a [[type:list]] of parts. If @in_context@ is given, the context must also match the string where the match is represented as <match>. -This function is available in [[script:rule form]]. -When the @break_text@ is used many times the rule form is more efficient, because the regular expression is only compiled once. +When the @break_text@ is used many times with the same @match@ or @in_context@ values it is more efficient to declare these as default arguments: +> my_break := break_text@(match: "something") +> my_break("input") # called many times +This way the regular expression is only compiled once. --Filter vs. break-- The function @filter_text@ is very similar to @break_text@, instead of returning a list it concatenates the items. -So for example where @break_text@ would return @["a","b","c"]@, @filter_text@ would return @"abc"@. +So for example where @break_text@ returns @["a","b","c"]@, @filter_text@ would return @"abc"@. In fact, @filter_text@ could be implemented as > filter_text := { for part in break_text() do part } @@ -32,10 +33,9 @@ In fact, @filter_text@ could be implemented as > break_text(match: "/", "a/b/c") == ["/","/"] > break_text(match: "[^/]+", "a/b/c") == ["a","b","c"] > -> f := break_rule(match: "xx+") +> f := break_text@(match: "xx+") > f("xyzxxxxyyzz") == ["xxxx"] --See also-- -| [[fun:filter_text|filter_text / filter_rule]] - Keep only the text matching a regular expression. +| [[fun:filter_text]] Keep only the text matching a regular expression. diff --git a/doc/function/contains.txt b/doc/function/contains.txt index 93463605..339de15f 100644 --- a/doc/function/contains.txt +++ b/doc/function/contains.txt @@ -15,5 +15,5 @@ Does one string contain another at any position? > contains("abcdefg", match:"abd") == false --See also-- -| [[fun:match|match / match_rule]] Does a string match a regular expression? -| [[fun:chosen]] Is the given choice selected in a multiple choice value? +| [[fun:match]] Does a string match a regular expression? +| [[fun:chosen]] Is the given choice selected in a multiple choice value? diff --git a/doc/function/expand_keywords.txt b/doc/function/expand_keywords.txt index 648ae2e9..3e254650 100644 --- a/doc/function/expand_keywords.txt +++ b/doc/function/expand_keywords.txt @@ -2,7 +2,6 @@ Function: expand_keywords --Usage-- > expand_keywords(some_tagged_string, default_expand: {...}, combine: {...}) -> expand_keywords_rule(default_expand: {...}, combine: {...})(some_tagged_string) Find [[type:keyword]]s and generate their reminder text. @@ -28,8 +27,6 @@ For example, in the case of magic: > ) is used. This shows reminder text by default based on a [[type:set]] option, and it combined the keyword as @"keyword (reminder)"@. -This function is available in [[script:rule form]]. - --Parameters-- ! Parameter Type Description | @input@ [[type:tagged string]] String to expand keywords in. @@ -41,5 +38,5 @@ Assuming a keyword @"mse"@ exists with reminder text @"Magic Set Editor"@ exists > expand_keywords(default_expand: {true}, combine: { keyword + " = " + reminder }, "mse is cool") > == "mse = Magic Set Editor is cool" > -> f := expand_keywords_rule(default_expand: {true}, combine: { keyword+"="+reminder }) +> f := expand_keywords@(default_expand: {true}, combine: { keyword+"="+reminder }) > f("mse is cool") == "mse = Magic Set Editor is cool" diff --git a/doc/function/filter_text.txt b/doc/function/filter_text.txt index 88c73ffc..ad97e937 100644 --- a/doc/function/filter_text.txt +++ b/doc/function/filter_text.txt @@ -2,14 +2,15 @@ Function: filter_text --Usage-- > filter_text(some_string, match: regular expression, in_context: regular expression) -> filter_rule(match: ..., in_context: ...)(some_string) Filter text by only keeping the parts of the input that match the regular expression. If @in_context@ is given, the context must also match the string where the match is represented as <match>. -This function is available in [[script:rule form]]. -When the @filter_text@ is used many times the rule form is more efficient, because the regular expression is only compiled once. +When the @filter_text@ is used many times with the same @match@ or @in_context@ values it is more efficient to declare these as default arguments: +> my_filter := filter_text@(match: "something") +> my_filter("input") # called many times +This way the regular expression is only compiled once. --Parameters-- ! Parameter Type Description @@ -22,11 +23,9 @@ When the @filter_text@ is used many times the rule form is more efficient, becau > filter_text(match: ".", in_context:"a", "banana") == "nn" > filter_text(match: "[xy]", "xyz") == "xy" > -> f := filter_rule(match: "xx+") +> f := filter_text@(match: "xx+") > f("xyzxxyyzz") == "xx" --See also-- -| [[fun:break_text|break_text / break_rule]] - Break text into parts each matching a regular expression. -| [[fun:replace|replace / replace_rule]] - Replace text matching a regular expression. +| [[fun:break_text]] Break text into parts each matching a regular expression. +| [[fun:replace]] Replace text matching a regular expression. diff --git a/doc/function/format.txt b/doc/function/format.txt index f6897dc0..f5645364 100644 --- a/doc/function/format.txt +++ b/doc/function/format.txt @@ -2,12 +2,9 @@ Function: format --Usage-- > format(some_number, format: format_specification) -> format_rule(format: format_specification)(some_number) Format a number or other string as a string, [[http://www.cplusplus.com/reference/clibrary/cstdio/printf.html|printf]] style. -This function is available in [[script:rule form]]. - --Parameters-- ! Parameter Type Description | @input@ [[type:int]] or [[type:double]] or [[type:string]] @@ -19,5 +16,5 @@ This function is available in [[script:rule form]]. > format(format: "%03d", 13) == "013" > format(format: "%3s", "xy") == " xy" > -> f := format_rule(format: "%03d") +> f := format@(format: "%03d") > f(1) == "001" diff --git a/doc/function/index.txt b/doc/function/index.txt index 1c81b8f7..98c533a4 100644 --- a/doc/function/index.txt +++ b/doc/function/index.txt @@ -9,27 +9,19 @@ These functions are built into the program, other [[type:function]]s can be defi | [[fun:reverse]] Reverse a string, @"aBc" -> "cBa"@. | [[fun:trim]] Remove leading and trailing whitespace from a string, @" abc " -> "abc"@. | [[fun:substring]] Extract a part of a string. -| [[fun:format|format / format_rule]] - Format a number as a string (printf). +| [[fun:format]] Format a number as a string (printf). | [[fun:curly_quotes]] Make quotes curly. -| [[fun:replace|replace / replace_rule]] - Replace text matching a regular expression. -| [[fun:filter_text|filter_text / filter_rule]] - Keep only the text matching a regular expression. -| [[fun:break_text|break_text / break_rule]] - Break text into parts each matching a regular expression. -| [[fun:sort_text|sort_text / sort_rule]] - Sort the letters in a string using a custom order. +| [[fun:replace]] Replace text matching a regular expression. +| [[fun:filter_text]] Keep only the text matching a regular expression. +| [[fun:break_text]] Break text into parts each matching a regular expression. +| [[fun:sort_text]] Sort the letters in a string using a custom order. | [[fun:contains]] Does a string contain another one? -| [[fun:match|match / match_rule]] - Does a string match a regular expression? +| [[fun:match]] Does a string match a regular expression? | [[fun:regex_escape]] Escape a string for use in a regular expression. ! [[type:tagged_string|Tags]] <<< -| [[fun:tag_contents|tag_contents / tag_contents_rule]] - Change the contents of a specific tag. -| [[fun:remove_tag|remove_tag / tag_remove_rule]] - Remove a tag, keep the contents. +| [[fun:tag_contents]] Change the contents of a specific tag. +| [[fun:remove_tag]] Remove a tag, keep the contents. | [[fun:remove_tags]] Remove all tags from tagged text. ! [[type:list|Lists]] <<< @@ -40,8 +32,7 @@ These functions are built into the program, other [[type:function]]s can be defi | [[fun:filter_list]] Filter a list, keeping only elements that match a predicate. ! Keywords <<< -| [[fun:expand_keywords|expand_keywords / expand_keywords_rule]] - Expand the keywords in a piece of text. +| [[fun:expand_keywords]] Expand the keywords in a piece of text. | [[fun:keyword_usage]] What keywords are used on a card, and how often are they used? ! English language <<< diff --git a/doc/function/match.txt b/doc/function/match.txt index 5a959128..481edb0f 100644 --- a/doc/function/match.txt +++ b/doc/function/match.txt @@ -2,12 +2,13 @@ Function: match --Usage-- > match(some_string, match: regular expression) -> match_rule(match:regular expression)(some_string) Does a string match the given [[type:regex|regular expression]]? -This function is available in [[script:rule form]]. -When the match is performed many times the rule form is more efficient, because the regular expression is only compiled once. +When the match is performed many times with the same regular expression it is more efficient to declare that as a default argument: +> my_match := match@(match: "something") +> my_match("input") # called many times +This way the regular expression is only compiled once. --Parameters-- ! Parameter Type Description @@ -20,7 +21,7 @@ When the match is performed many times the rule form is more efficient, because > match("abc", match:"b+") == true > match("abc", match:"$b+^") == false > -> f := match_rule(match: "a+|b+") +> f := match@(match: "a+|b+") > f("xyz") == false > f("aabb") == true diff --git a/doc/function/remove_tag.txt b/doc/function/remove_tag.txt index 24ed9993..88e47acc 100644 --- a/doc/function/remove_tag.txt +++ b/doc/function/remove_tag.txt @@ -2,14 +2,11 @@ Function: remove_tag --Usage-- > remove_tag(tag: some_tag, some_string) -> tag_remove_rule(tag: some_tag)(some_string) Remove a tag and its matching close tag from a [[type:tagged string]], but keep the contents. The tag can be a whole tag, @""@, or a prefix @" remove_tag(tag: "", "bold text") == "bold text" > remove_tag(tag: "bold text") == "bold text" > -> f := tag_remove_rule(tag: "") +> f := remove_tag@(tag: "") > f("text, reminder italic") == "text, reminder italic" --See also-- -| [[fun:tag_contents|tag_contents / tag_contents_rule]] - Change the contents of a specific tag. -| [[fun:to_text]] Remove all tags from tagged text. +| [[fun:tag_contents]] Change the contents of a specific tag. +| [[fun:remove_tags]] Remove all tags from tagged text. +| [[fun:to_text]] Remove all tags from tagged text, and convert it to a [[type:string]]. diff --git a/doc/function/remove_tags.txt b/doc/function/remove_tags.txt index e0d3369b..2024183d 100644 --- a/doc/function/remove_tags.txt +++ b/doc/function/remove_tags.txt @@ -17,5 +17,4 @@ This function differs from [[fun:to_text]], that function also un-escapes < c --See also-- | [[fun:to_text]] Remove all tags from tagged text, and convert it to a [[type:string]]. -| [[fun:remove_tag|remove_tag / tag_remove_rule]] - Remove a single tag type. +| [[fun:remove_tag]] Remove a single tag type. diff --git a/doc/function/replace.txt b/doc/function/replace.txt index 9bf4f79b..d8131251 100644 --- a/doc/function/replace.txt +++ b/doc/function/replace.txt @@ -2,7 +2,6 @@ Function: replace --Usage-- > replace(some_string, match: regular expression, replace: replacement, in_context: regular expression) -> replace_rule(match: ..., replace: ..., in_context: ...)(some_string) Replace all matches of a regular expression with a replacement value. The replacement can either be a string or a function. @@ -11,8 +10,10 @@ The replacement can either be a string or a function. Optionally a context can be given. The replacement is only performed if the string where the match is represented as <match> also matches the context. -This function is available in [[script:rule form]]. -When the replacement is performed many times the rule form is more efficient, because the regular expression is only compiled once. +When the @replace@ is used many times with the same @match@ or @in_context@ values it is more efficient to declare these as default arguments: +> my_replace := replace@(match: "something", replace: "something else") +> my_replace("input") # called many times +This way the regular expression is compiled only once. --Parameters-- ! Parameter Type Description @@ -26,11 +27,11 @@ When the replacement is performed many times the rule form is more efficient, be > replace(match: "a", replace: "e", "banana") == "benene" > replace(match: "a", replace: "e", in_context: "n", "banana") == "benena" > replace(match: "(a|b)x", replace: "[\\0,\\1]", "axabxc") == "[ax,a]a[bx,b]c" -> replace(match: "[ab]", replace: { to_upper(input) }, "banana") == "BAnAnA" +> replace(match: "[ab]", replace: to_upper, "banana") == "BAnAnA" +> replace(match: "([0-9])[*]([0-9])", replace: { _1 * _2 }, "2*2+3*3") == "4+9" > -> f := replace_rule(match: "xx+", replace: "A") +> f := replace@(match: "xx+", replace: "A") > f("xyzxxyyzz") == "xyzAAyyzz" --See also-- -| [[fun:filter_text|filter_text / filter_rule]] - Keep only the text matching a regular expression. +| [[fun:filter_text]] Keep only the text matching a regular expression. diff --git a/doc/function/sort_text.txt b/doc/function/sort_text.txt index a6174c38..432572a2 100644 --- a/doc/function/sort_text.txt +++ b/doc/function/sort_text.txt @@ -2,7 +2,6 @@ Function: sort_text --Usage-- > sort_text(order: order specification, some_text) -> sort_rule(order: order specification)(some_text) Sort a string or filter it by keeping specific characters. @@ -45,8 +44,6 @@ behaves the same as > sort_text(order: "x") because the first @"x"@ already selects all xs from the input. -This function is available in [[script:rule form]]. - --Parameters-- ! Parameter Type Description | @input@ [[type:string]] Text to sort @@ -67,5 +64,5 @@ This function is available in [[script:rule form]]. > sort_text(order: "pattern(./. cycle(wubrg))", "wgw/g") == "g/w" > sort_text(order: "[1234567890]cycle(wubrg)", "21wg") == "21gw" > -> f := sort_rule(order: "[1234567890]cycle(wubrg)") +> f := sort_text@(order: "[1234567890]cycle(wubrg)") > f("21wg") == "21gw" diff --git a/doc/function/tag_contents.txt b/doc/function/tag_contents.txt index a81ef16f..621105e8 100644 --- a/doc/function/tag_contents.txt +++ b/doc/function/tag_contents.txt @@ -2,15 +2,12 @@ Function: tag_contents --Usage-- > tag_contents(tag: some_tag, contents: some_function, some_string) -> tag_contents_rule(tag: some_tag, contents: some_function)(some_string) Apply a function to the contents of a particular tag. The function is called with @input@ set to the old value inside the tag. The tag can be a whole tag, @""@, or a prefix @" tag_contents(tag: " loses 1 life") > == "Pink Elephant loses 1 life" > -> f := tag_contents_rule(tag: "", contents: {""}) +> f := tag_contents@(tag: "", contents: {""}) > f("text, reminder italic") == "text, reminder " --See also-- -| [[fun:remove_tag|remove_tag / tag_remove_rule]] - Remove a tag, keep the contents. +| [[fun:remove_tag]] Remove a tag, keep the contents. diff --git a/doc/script/best_practices.txt b/doc/script/best_practices.txt index 54947305..bbef82b3 100644 --- a/doc/script/best_practices.txt +++ b/doc/script/best_practices.txt @@ -23,12 +23,15 @@ Instead define a function in the init script. >field: > script: flubble_script() ---Use rule form-- -If a function is available in [[script:rule form]] use it where possible. +--Use default arguments-- -Many rules can be chained together using the @+@ operator. +Many functions can be chained together using the @+@ operator. +For these functions [[script:default arguments]] can be given. Have a look at @text_filter@ in the magic game file for an example. +Using default arguments is especially important for functions that work on [[type:regex|regular expressions]], +since it allows MSE to compile the regular expressions just once instead of for every call. + --Don't be afraid to nest-- Don't be afraid to nest complex things like @if@ expressions inside other expressions. For example the blend scripts for magic use: diff --git a/doc/script/default_arguments.txt b/doc/script/default_arguments.txt index d630c8b2..4c0ef2fa 100644 --- a/doc/script/default_arguments.txt +++ b/doc/script/default_arguments.txt @@ -11,21 +11,24 @@ For determining whether the argument is set only explicit arguments count, not e > arg := "something else" > function() == "argument was: default" -//Defaults are evaluated at the time the @@@@ operator is evaluated, so they can be used to simulate static scoping. +Defaults are evaluated at the time the @@@@ operator is evaluated, they will not be overriden later +> default := "old default" +> function := { "argument was: " + arg }@(arg: default) +> default := "new default" +> function() == "argument was: old default" --Rule functions-- -Some functions are available in ''rule form''. - -These rule form functions are functions that create a new [[type:function]] with some defaults filled in. -That new function, the rule, applies some transformation to the input and returns the result. +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. +Programming in a ''rule style'' is still very useful. A rule is like a normal function with all parameters given, except for the @input@. - -Rules are often combined using the + operator, for example: +These rules can then be combined using the @+@ operator, for example: > # First all "a"s are replaced, then all "b"s. -> remove_as_and_bs := replace_rule(match: "a", replace: "") + -> replace_rule(match: "b", replace: "") +> remove_as_and_bs := replace@(match: "a", replace: "") + +> replace@(match: "b", replace: "") > > text_with_as_and_bs := "bla bla bla" > text_without_as_and_bs := remove_as_and_bs(text_with_as_and_bs) diff --git a/doc/script/index.txt b/doc/script/index.txt index 187ad2c8..44cdc00b 100644 --- a/doc/script/index.txt +++ b/doc/script/index.txt @@ -5,9 +5,9 @@ MSE uses a custom scripting language to add complicated behaviour to [[type:fiel --Topics-- * [[script:introduction|Introduction to scripting]] * [[script:Operators]] -* [[script:variables|Variables]] -* [[script:functions|Functions]] -* [[script:default_arguments|Default arguments]] +* [[script:Variables]] +* [[script:Functions]] +* [[script:Default arguments]] * [[script:Control structures]] * [[script:Predefined variables]] * [[script:Best practices]] diff --git a/doc/script/rule_form.txt b/doc/script/rule_form.txt deleted file mode 100644 index ac6f2958..00000000 --- a/doc/script/rule_form.txt +++ /dev/null @@ -1,17 +0,0 @@ -Rule form - -Some functions are available in ''rule form''. - -These rule form functions are functions that create a new [[type:function]]. -That new function, the rule, applies some transformation to the input and returns the result. - -A rule is like a normal function with all parameters given, except for the @input@. - -Rules are often combined using the + operator, for example: -> # First all "a"s are replaced, then all "b"s. -> remove_as_and_bs := replace_rule(match: "a", replace: "") + -> replace_rule(match: "b", replace: "") -> -> text_with_as_and_bs := "bla bla bla" -> text_without_as_and_bs := remove_as_and_bs(text_with_as_and_bs) - diff --git a/doc/type/function.txt b/doc/type/function.txt index d53ef719..cabaeea9 100644 --- a/doc/type/function.txt +++ b/doc/type/function.txt @@ -13,7 +13,7 @@ Functions can be composed using the @+@ operator, evaluating @a + b@ first evalu > example := to_upper + { "result == {input}" } > example("xyz") == "result == XYZ" -Multiple functions can be changed together like this, especially using [[script:rule form]]. +Multiple functions can be changed together like this, this is especially convenient in combination with [[script:default arguments]]. --Example-- > example := { a + b } diff --git a/doc/type/tagged_string.txt b/doc/type/tagged_string.txt index bee04f33..6111068d 100644 --- a/doc/type/tagged_string.txt +++ b/doc/type/tagged_string.txt @@ -48,7 +48,7 @@ This is written as the character with code 1 in files. --Related functions-- The following script functions deal with tags: -| [[fun:tag_contents|tag_contents / tag_contents_rule]] - Change the contents of a specific tag. -| [[fun:remove_tag|remove_tag / tag_remove_rule]] - Remove a tag, keep the contents. +| [[fun:tag_contents]] Change the contents of a specific tag. +| [[fun:remove_tag]] Remove a tag, keep the contents. +| [[fun:remove_tags]] Remove all tags from tagged text. +| [[fun:to_text]] Remove all tags from tagged text, and convert it to a [[type:string]]. diff --git a/src/script/functions/regex.cpp b/src/script/functions/regex.cpp index 432ef2bc..ace1a46c 100644 --- a/src/script/functions/regex.cpp +++ b/src/script/functions/regex.cpp @@ -30,6 +30,7 @@ ScriptRegexP regex_from_script(const ScriptValueP& value) { // is it a regex already? ScriptRegexP regex = dynamic_pointer_cast(value); if (!regex) { + // TODO: introduce some kind of caching? // compile string regex = new_intrusive(); if (!regex->regex.Compile(*value, wxRE_ADVANCED)) { @@ -225,6 +226,7 @@ SCRIPT_FUNCTION_SIMPLIFY_CLOSURE(replace) { // ----------------------------------------------------------------------------- : Rules : regex filter +/* class ScriptFilterRule : public ScriptValue { public: virtual ScriptType type() const { return SCRIPT_FUNCTION; } @@ -259,7 +261,7 @@ ScriptValueP filter_rule(Context& ctx) { SCRIPT_PARAM_DEFAULT_C(String, in_context, String()); // cache - //* + // * const int CACHE_SIZE = 6; struct CacheItem{ String match, in_context; @@ -279,9 +281,9 @@ ScriptValueP filter_rule(Context& ctx) { cache[cache_pos].rule = intrusive_ptr(new ScriptFilterRule); intrusive_ptr& ret = cache[cache_pos].rule; cache_pos = (cache_pos+1) % CACHE_SIZE; - /*/ + / * / intrusive_ptr ret(new ScriptFilterRule); - //*/ + //* / // match if (!ret->regex.Compile(match, wxRE_ADVANCED)) { @@ -301,6 +303,36 @@ SCRIPT_FUNCTION(filter_rule) { } SCRIPT_FUNCTION(filter_text) { return filter_rule(ctx)->eval(ctx); +}*/ + +SCRIPT_FUNCTION_WITH_SIMPLIFY(filter_text) { + SCRIPT_PARAM_C(String, input); + SCRIPT_PARAM_C(ScriptRegexP, match); + SCRIPT_OPTIONAL_PARAM_C_(ScriptRegexP, in_context); + String ret; + // find all matches + while (match->regex.Matches(input)) { + // match, append to result + size_t start, len; + bool ok = match->regex.GetMatch(&start, &len, 0); + assert(ok); + String inside = input.substr(start, len); // the match + String next_input = input.substr(start + len); // everything after the match + if (!in_context || in_context->regex.Matches(input.substr(0,start) + _("") + next_input)) { + // no context or context match + ret += inside; + } + input = next_input; + } + SCRIPT_RETURN(ret); +} +SCRIPT_FUNCTION_SIMPLIFY_CLOSURE(filter_text) { + FOR_EACH(b, closure.bindings) { + if (b.first == SCRIPT_VAR_match || b.first == SCRIPT_VAR_in_context) { + b.second = regex_from_script(b.second); // pre-compile + } + } + return ScriptValueP(); } // ----------------------------------------------------------------------------- : Rules : regex break @@ -452,7 +484,7 @@ void init_script_regex_functions(Context& ctx) { ctx.setVariable(_("break text"), script_break_text); ctx.setVariable(_("match"), script_match); ctx.setVariable(_("replace rule"), new_intrusive1(script_replace)); - ctx.setVariable(_("filter rule"), script_filter_rule); + ctx.setVariable(_("filter rule"), new_intrusive1(script_filter_text)); ctx.setVariable(_("break rule"), new_intrusive1(script_break_text)); ctx.setVariable(_("match rule"), new_intrusive1(script_match)); }