From 089af798fcd27ea69edd04739616e60d0088c7c0 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 23 Dec 2022 13:02:12 -0800 Subject: [PATCH 1/9] moved expressions into a subdirectory --- doc/other_modules/jenny/language/commands/declare.md | 2 +- .../jenny/language/{ => expressions}/expressions.md | 0 doc/other_modules/jenny/language/language.md | 11 +++++------ doc/other_modules/jenny/language/lines.md | 2 +- 4 files changed, 7 insertions(+), 8 deletions(-) rename doc/other_modules/jenny/language/{ => expressions}/expressions.md (100%) diff --git a/doc/other_modules/jenny/language/commands/declare.md b/doc/other_modules/jenny/language/commands/declare.md index f05081dff14..428e3fde7c5 100644 --- a/doc/other_modules/jenny/language/commands/declare.md +++ b/doc/other_modules/jenny/language/commands/declare.md @@ -91,4 +91,4 @@ variable, similarly to how you would document public members of a class. ::: -[expression]: ../expressions.md +[expression]: ../expressions/expressions.md diff --git a/doc/other_modules/jenny/language/expressions.md b/doc/other_modules/jenny/language/expressions/expressions.md similarity index 100% rename from doc/other_modules/jenny/language/expressions.md rename to doc/other_modules/jenny/language/expressions/expressions.md diff --git a/doc/other_modules/jenny/language/language.md b/doc/other_modules/jenny/language/language.md index 32f5a90d671..6950ae8cc39 100644 --- a/doc/other_modules/jenny/language/language.md +++ b/doc/other_modules/jenny/language/language.md @@ -5,8 +5,6 @@ **Jenny** implementation, which may not contain all the original features, but may also contain some that were not implemented in the YarnSpinner yet. -[official documentation]: https://docs.yarnspinner.dev/getting-started/writing-in-yarn - ## Yarn files @@ -33,9 +31,6 @@ title: Start === ``` -[commands]: commands/commands.md -[nodes]: nodes.md - ### Comments @@ -78,5 +73,9 @@ Nodes Lines Options Commands -Expressions +Expressions ``` + +[commands]: commands/commands.md +[nodes]: nodes.md +[official documentation]: https://docs.yarnspinner.dev/getting-started/writing-in-yarn diff --git a/doc/other_modules/jenny/language/lines.md b/doc/other_modules/jenny/language/lines.md index f2bcd932552..1e0768bde86 100644 --- a/doc/other_modules/jenny/language/lines.md +++ b/doc/other_modules/jenny/language/lines.md @@ -101,7 +101,7 @@ further processing. Which means that the text of the expression may contain spec (such as `[`, `]`, `{`, `}`, `\`, etc), and they don't need to be escaped. It also means that the expression cannot contain markup, or produce a hashtag, etc. -Read more about expressions in the [Expressions](expressions.md) section. +Read more about expressions in the [Expressions](expressions/expressions.md) section. ## Markup From 42f73edbc50f14163515e5f908854ed4e41547f7 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 23 Dec 2022 18:05:09 -0800 Subject: [PATCH 2/9] variables/operators --- .../jenny/language/expressions/expressions.md | 33 ++++++- .../jenny/language/expressions/operators.md | 70 +++++++++++++++ .../jenny/language/expressions/variables.md | 88 +++++++++++++++++++ .../structure/expressions/literal_test.dart | 42 ++++++++- .../expressions/operators/divide_test.dart | 2 +- .../flame_jenny/jenny/test/test_scenario.dart | 2 +- 6 files changed, 232 insertions(+), 5 deletions(-) create mode 100644 doc/other_modules/jenny/language/expressions/operators.md create mode 100644 doc/other_modules/jenny/language/expressions/variables.md diff --git a/doc/other_modules/jenny/language/expressions/expressions.md b/doc/other_modules/jenny/language/expressions/expressions.md index 17b98dd5ded..ab32fd4cc25 100644 --- a/doc/other_modules/jenny/language/expressions/expressions.md +++ b/doc/other_modules/jenny/language/expressions/expressions.md @@ -1,3 +1,34 @@ # Expressions -TODO +The **expressions** in YarnSpinner provide a way to dynamically change the flow or the content +of the dialogue, based on *variables* or *function* calls. They are used in several places: + +- to insert a dynamic text into a [line]; +- as part of a [command] such as `<>` or `<>`; +- to compute the values of [markup] attributes. + + + + +## Functions + +An expression may also contain function calls, which are indicated by the name of the function, +followed by its arguments in parentheses. The parentheses are required, even when there are no +arguments: + +```yarn +<> +<> +``` + + +```{toctree} +:hidden: + +Variables +Operators +``` + +[command]: ../commands/commands.md +[line]: ../lines.md +[markup]: ../markup.md diff --git a/doc/other_modules/jenny/language/expressions/operators.md b/doc/other_modules/jenny/language/expressions/operators.md new file mode 100644 index 00000000000..04668503363 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/operators.md @@ -0,0 +1,70 @@ +# Operators + +Variables can be combined into more complicated formulas with the help of various operators. These +can be loosely grouped into the following categories: + + +## Operator types + +### Arithmetic + +The **arithmetic** operators, same as in traditional math. These apply to numeric values, except +for `+` which can also be used with strings: + +- `+` (addition); +- `-` (subtraction, or unary minus); +- `*` (multiplication); +- `/` (division) -- division by `0` is not allowed, and will throw a runtime error if it occurs; +- `%` (modulo) -- this operator can apply to either integer or decimal numbers. The right-hand + side of `%` must be a positive number, otherwise a runtime error will be thrown. The result of + `x % y` is always a number in the range `[0; y)`, regardless of the sign of `x`. + + +### Assignment + +The **assignment** operators, which modify the value of a variable: + +- `=` (assign); +- `+=` (increase); +- `-=` (decrease); +- `*=` (multiply); +- `/=` (divide); +- `%=` (reduce modulo). + + +### Logical + +The **logical** operators, which apply to boolean values: + +- `!`, `not` (logical NOT); +- `&&`, `and` (logical AND); +- `||`, `or` (logical OR); +- `^`, `xor` (logical XOR). + + +### Relational + +The **relational**, i.e. operators that compare various values. The first two operators in this +list can be applied to operands of any types, as long as the types are the same. The remaining +four operators can only be used with numbers: + +- `==` (equality); +- `!=` (inequality); +- `<` (less than); +- `<=` (less than or equal); +- `>` (greater than); +- `>=` (greater than or equal). + + +## Precedence + +Just as in mathematics, the operators have precedence ordering among them. This order is as +follows, from highest precedence to lowest: + +- `*`, `/`, `%`; +- `-`, `+`; +- `==`, `!=`, `<`, `<=`, `>=`, `>`; +- `!`; +- `&&`, `^`; +- `||`; +- `=`, `+=`, `-=`, `*=`, `/=`, `%=`. diff --git a/doc/other_modules/jenny/language/expressions/variables.md b/doc/other_modules/jenny/language/expressions/variables.md new file mode 100644 index 00000000000..9cf69ed2b63 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/variables.md @@ -0,0 +1,88 @@ +# Variables + +A **variable** is a place to store some piece of information -- it is the same notion as in any +other programming language. Each variable has a **name**, a **value**, a **type**, and a **scope**. + + +## Name + +The **name** of a variable is how you refer to it in a `.yarn` script. The names of all variables +start with a `$` sign, followed by a letter or an underscore, and then by any number of letters, +digits, or underscores. Thus, the following are all valid variables names: + +``` +$i +$WARNING +$_secret_ +$door10 +$climbed_over_wall_and_avoided_all_guard_patrols +$DoorPassword +``` + +while the following are not: + +``` +$2000_years +$[main] +@today +victory +``` + + +## Type + +Each variable has a certain **type** associated with it. The type of a variable is determined when +the variable is first declared, and never changes afterwards. + +There are three types of variables in YarnSpinner: `string`, `number`, and `bool`. + +- `bool` variables can store either `true` or `false` and nothing else; +- `number` variables may contain either integer or decimal numbers, such as `0`, `42`, `2.5`; +- `string` variables contain arbitrary text, for example `"the most random number is 4"`. + +```yarn +// Creates a variable $money of type number, and gives it initial value of 100 +<> + +// Creates variable $name of type string, the initial value will be "" +<> +``` + + +## Value + +Each variable stores a single **value**. This value can be replaced with another value at any time, +but the type of the new value must be the same. + +Each variable will have an initial value assigned to it when the variable is first created, and +then new values can be assigned with the [\<\\>][set] command. + +```yarn +<> // increases the value of $money by 10 +``` + + +## Scope + +The **scope** of a variable is where exactly it can be accessed. In YarnSpinner, the variables can +be either global or local. + +- The **global** variables are introduced via the [\<\\>][declare] command, and once + created can be accessed anywhere. The names of all global variables are unique. +- The **local** variables are created with the [\<\\>][local] command, and can only be used + within the node where they were created. It is possible to have a local variable with the same + name in different nodes, and they will be considered different variables. + +```yarn +<> + +title: MyNode +--- +<> +=== +``` + + +[declare]: ../commands/declare.md +[local]: ../commands/local.md +[set]: ../commands/set.md diff --git a/packages/flame_jenny/jenny/test/structure/expressions/literal_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/literal_test.dart index 1e126ee713a..3762ed91309 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/literal_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/literal_test.dart @@ -14,6 +14,26 @@ void main() { expect(constZero.value, 0); }); + test('various numeric literals', () async { + await testScenario( + input: r''' + title: Start + --- + Integers\: {5} {0} {-0} {777} {1000000000} + Decimals\: {3.5} {0.0} {-0.0} {16.99} {-7.00000} + // Scientific\: {7e5} {1.6e-2} {2e+100} + // Hexadecimal: {0x100} + === + ''', + testPlan: ''' + line: Integers: 5 0 0 777 1000000000 + line: Decimals: 3.5 0.0 -0.0 16.99 -7.0 + // line: Scientific: 700000.0 0.16 2.0e100 + // line: Hexadecimal: 256 + ''', + ); + }); + testScenario( testName: 'DecimalNumbers.yarn', input: r''' @@ -23,7 +43,6 @@ void main() { title: Start --- - // Expressions <= 1.2>> Success @@ -31,7 +50,6 @@ void main() { // Inline expressions Here's a number: {45.1} - === ''', testPlan: ''' @@ -50,6 +68,26 @@ void main() { test('constEmptyString', () { expect(constEmptyString.value, ''); }); + + test('various string literals', () async { + await testScenario( + input: r''' + title: Start + --- + Double quoted: { "one two three" } + Single quoted: { 'four five six' } + Escapes\: { '12 o\'clock' } + No interpolation: { "Hello, {$world}" } + === + ''', + testPlan: r''' + line: Double quoted: one two three + line: Single quoted: four five six + line: Escapes: 12 o'clock + line: No interpolation: Hello, {$world} + ''', + ); + }); }); group('BoolLiteral', () { diff --git a/packages/flame_jenny/jenny/test/structure/expressions/operators/divide_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/operators/divide_test.dart index 1a475c46e7a..ac02ef68921 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/operators/divide_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/operators/divide_test.dart @@ -40,7 +40,7 @@ void main() { final yarn = YarnProject() ..parse( 'title:A\n---\n' - '{ 4 / 0 }\n' + '{ 4.0 / 0.0 }\n' '===\n', ); final line = yarn.nodes['A']!.lines[0] as DialogueLine; diff --git a/packages/flame_jenny/jenny/test/test_scenario.dart b/packages/flame_jenny/jenny/test/test_scenario.dart index 13061447632..acbd91a7b70 100644 --- a/packages/flame_jenny/jenny/test/test_scenario.dart +++ b/packages/flame_jenny/jenny/test/test_scenario.dart @@ -190,7 +190,7 @@ class _TestPlan extends DialogueView { } void _parse(String input) { - final rxEmpty = RegExp(r'^\s*$'); + final rxEmpty = RegExp(r'^\s*(//.*)?$'); final rxLine = RegExp(r'^line:\s+((\w+):\s+)?(.*)$'); final rxOption = RegExp(r'^option:\s+((\w+):\s+)?(.*?)\s*(\[disabled\])?$'); final rxSelect = RegExp(r'^select:\s+(\d+)$'); From d9ebb59b16a646a49dace97c20c321478f8985eb Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 6 Jan 2023 09:46:24 -0800 Subject: [PATCH 3/9] minor --- doc/other_modules/jenny/language/expressions/expressions.md | 3 ++- doc/other_modules/jenny/language/expressions/variables.md | 6 +++--- 2 files changed, 5 insertions(+), 4 deletions(-) diff --git a/doc/other_modules/jenny/language/expressions/expressions.md b/doc/other_modules/jenny/language/expressions/expressions.md index ab32fd4cc25..fefd0ce92c8 100644 --- a/doc/other_modules/jenny/language/expressions/expressions.md +++ b/doc/other_modules/jenny/language/expressions/expressions.md @@ -1,7 +1,7 @@ # Expressions The **expressions** in YarnSpinner provide a way to dynamically change the flow or the content -of the dialogue, based on *variables* or *function* calls. They are used in several places: +of the dialogue, based on [variables] or *function* calls. They are used in several places: - to insert a dynamic text into a [line]; - as part of a [command] such as `<>` or `<>`; @@ -32,3 +32,4 @@ Operators [command]: ../commands/commands.md [line]: ../lines.md [markup]: ../markup.md +[variables]: variables.md diff --git a/doc/other_modules/jenny/language/expressions/variables.md b/doc/other_modules/jenny/language/expressions/variables.md index 9cf69ed2b63..4dab94b46da 100644 --- a/doc/other_modules/jenny/language/expressions/variables.md +++ b/doc/other_modules/jenny/language/expressions/variables.md @@ -1,7 +1,7 @@ # Variables A **variable** is a place to store some piece of information -- it is the same notion as in any -other programming language. Each variable has a **name**, a **value**, a **type**, and a **scope**. +other programming language. Each variable has a **name**, **value**, **type**, and a **scope**. ## Name @@ -19,7 +19,7 @@ $climbed_over_wall_and_avoided_all_guard_patrols $DoorPassword ``` -while the following are not: +while the following are NOT valid names: ``` $2000_years @@ -32,7 +32,7 @@ victory ## Type Each variable has a certain **type** associated with it. The type of a variable is determined when -the variable is first declared, and never changes afterwards. +the variable is first declared, and it never changes afterwards. There are three types of variables in YarnSpinner: `string`, `number`, and `bool`. From 13d0699a349f69e5ec06ba562aa188641563dce9 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 6 Jan 2023 16:18:38 -0800 Subject: [PATCH 4/9] update docs --- doc/_sphinx/theme/flames.css | 14 ++ .../jenny/language/expressions/expressions.md | 27 ++- .../expressions/functions/functions.md | 12 ++ .../jenny/language/expressions/operators.md | 198 ++++++++++++++---- 4 files changed, 197 insertions(+), 54 deletions(-) create mode 100644 doc/other_modules/jenny/language/expressions/functions/functions.md diff --git a/doc/_sphinx/theme/flames.css b/doc/_sphinx/theme/flames.css index 36f8b94615f..95249c04cb6 100644 --- a/doc/_sphinx/theme/flames.css +++ b/doc/_sphinx/theme/flames.css @@ -815,3 +815,17 @@ pre, div[class*="highlight-"] { h2, h3, h4, h5, h6 { clear: both; } + +table.first-col-align-center tr td:first-child, +table.first-col-align-center tr th:first-child { + text-align: center; +} + +table.docutils td { + border-color: rgba(255, 255, 255, 0.125); + vertical-align: top; +} + +table.docutils th { + color: #9d9175; +} diff --git a/doc/other_modules/jenny/language/expressions/expressions.md b/doc/other_modules/jenny/language/expressions/expressions.md index fefd0ce92c8..16dfacb3dd7 100644 --- a/doc/other_modules/jenny/language/expressions/expressions.md +++ b/doc/other_modules/jenny/language/expressions/expressions.md @@ -1,25 +1,19 @@ # Expressions The **expressions** in YarnSpinner provide a way to dynamically change the flow or the content -of the dialogue, based on [variables] or *function* calls. They are used in several places: +of the dialogue, based on [variables], combined with [operators] or [function] calls. They are +used in several places: - to insert a dynamic text into a [line]; +- to create or update a [variable]; - as part of a [command] such as `<>` or `<>`; - to compute the values of [markup] attributes. - - - -## Functions - -An expression may also contain function calls, which are indicated by the name of the function, -followed by its arguments in parentheses. The parentheses are required, even when there are no -arguments: - -```yarn -<> -<> -``` +An expression always evaluates synchronously, meaning that it cannot wait for user's input, nor +perform an action over time, nor carry out any computationally intensive calculations in a +different thread. If such functionality is really desired, then it can be achieved via a +[user-defined command] that waits for the calculation to succeed and then stores the result into +some global [variable], which can then be accessed from an expression. ```{toctree} @@ -27,9 +21,14 @@ arguments: Variables Operators +Functions ``` [command]: ../commands/commands.md +[function]: functions/functions.md [line]: ../lines.md [markup]: ../markup.md +[operators]: operators.md +[user-defined command]: ../commands/user_defined_commands.md +[variable]: variables.md [variables]: variables.md diff --git a/doc/other_modules/jenny/language/expressions/functions/functions.md b/doc/other_modules/jenny/language/expressions/functions/functions.md new file mode 100644 index 00000000000..43587ed21e1 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/functions.md @@ -0,0 +1,12 @@ +# Functions + +TODO + +An expression may also contain function calls, which are indicated by the name of the function, +followed by its arguments in parentheses. The parentheses are required, even when there are no +arguments: + +```yarn +<> +<> +``` diff --git a/doc/other_modules/jenny/language/expressions/operators.md b/doc/other_modules/jenny/language/expressions/operators.md index 04668503363..9900a1b7a3a 100644 --- a/doc/other_modules/jenny/language/expressions/operators.md +++ b/doc/other_modules/jenny/language/expressions/operators.md @@ -1,65 +1,175 @@ # Operators -Variables can be combined into more complicated formulas with the help of various operators. These -can be loosely grouped into the following categories: +The **operators** are special symbols that perform common mathematical operations. For example, +operator `+` performs summation, and thus we can write `$x + $y` to denote the sum of variables +`$x` and `$y`. There are over 20 different operators in YarnSpinner, which can be loosely grouped +into the following categories: ## Operator types ### Arithmetic -The **arithmetic** operators, same as in traditional math. These apply to numeric values, except -for `+` which can also be used with strings: +The **arithmetic** operators, have the same meaning as in regular math. These apply to numeric +arguments (with the exception of `+` which can also be used with strings): + +```{list-table} +:align: left +:class: first-col-align-center +:header-rows: 1 +:widths: 1 2 9 + +* - operator + - name + - notes +* - `+` + - addition + - +* - `-` + - subtraction + - Also, a unary minus +* - `*` + - multiplication + - +* - `/` + - division + - Division by `0` is not allowed, and will throw a runtime error if it occurs. +* - `%` + - modulo + - This operator can apply to both integer and decimal numbers, and it returns the remainder of + integer division of two numbers. The right-hand side of `%` cannot be zero or a negative number, + otherwise a runtime error will be thrown. The result of `x % y` is always a number in the + range `[0; y)`, regardless of the sign of `x`. +* - `+` + - concatenation + - When applied to strings, the `+` operator simply glues them together. For example, + `"Hello" + "World"` produces string `"HelloWorld"`. +``` -- `+` (addition); -- `-` (subtraction, or unary minus); -- `*` (multiplication); -- `/` (division) -- division by `0` is not allowed, and will throw a runtime error if it occurs; -- `%` (modulo) -- this operator can apply to either integer or decimal numbers. The right-hand - side of `%` must be a positive number, otherwise a runtime error will be thrown. The result of - `x % y` is always a number in the range `[0; y)`, regardless of the sign of `x`. +### Logical -### Assignment - -The **assignment** operators, which modify the value of a variable: - -- `=` (assign); -- `+=` (increase); -- `-=` (decrease); -- `*=` (multiply); -- `/=` (divide); -- `%=` (reduce modulo). - +The **logical** operators apply to boolean values. These operators can be written either in +symbolic or word form -- both forms are equivalent: + +```{list-table} +:align: left +:class: first-col-align-center +:header-rows: 1 +:widths: 1 2 9 + +* - operator + - name + - notes +* - `!`, `not` + - logical NOT + - This is a unary operator that inverts its operand: `!true` is `false`, and `!false` is `true`. +* - `&&`, `and` + - logical AND + - Returns `true` if both of its arguments are `true`. +* - `||`, `or` + - logical OR + - Returns `true` if at least one of its arguments is `true`. +* - `^`, `xor` + - logical XOR + - Returns `true` if the arguments are different, and `false` if they are the same. +``` -### Logical -The **logical** operators, which apply to boolean values: +### Assignment -- `!`, `not` (logical NOT); -- `&&`, `and` (logical AND); -- `||`, `or` (logical OR); -- `^`, `xor` (logical XOR). +The **assignment** operators modify the value of a variable. The left-hand side of such an operator +is the variable that shall be modified, the right-hand side is the expression of the same type as +the variable on the left: + +```{list-table} +:align: left +:class: first-col-align-center +:header-rows: 1 +:widths: 1 2 9 + +* - operator + - name + - notes +* - `=`, `to` + - assign + - `$var = X` stores the value of `X` into the variable `$var` +* - `+=` + - increase + - `$var += X` is equivalent to `$var = $var + X` +* - `-=` + - decrease + - `$var -= X` is equivalent to `$var = $var - X` +* - `*=` + - multiply + - `$var *= X` is equivalent to `$var = $var * X` +* - `/=` + - divide + - `$var /= X` is equivalent to `$var = $var / X` +* - `%=` + - reduce modulo + - `$var %= X` is equivalent to `$var = $var % X` +``` + +Unlike all other operators, the assignment operators do not produce a value. This means they +cannot be used inside a larger expression, for example the following is invalid: `3 + ($x += 7)`. +Instead, the assignment operators are only usable at the top level of commands such as +[\<\\>], [\<\\>], and [\<\\>]. ### Relational -The **relational**, i.e. operators that compare various values. The first two operators in this -list can be applied to operands of any types, as long as the types are the same. The remaining -four operators can only be used with numbers: - -- `==` (equality); -- `!=` (inequality); -- `<` (less than); -- `<=` (less than or equal); -- `>` (greater than); -- `>=` (greater than or equal). +The **relational** operators compare various values. The first two operators in this list can be +applied to operands of any types, as long as the types are the same. The remaining four operators +can only be used with numbers. Regardless of the types of operands, the result of every +relational operator is a boolean value, which can be either assigned to a variable, or used in a +larger expression: + +```{list-table} +:align: left +:class: first-col-align-center +:header-rows: 1 +:widths: 1 3 8 + +* - operator + - name + - notes +* - `==` + - equality + - +* - `!=` + - inequality + - +* - `<` + - less than + - +* - `<=` + - less than or equal + - +* - `>` + - greater than + - +* - `>=` + - greater than or equal + - +``` + +Note that operator chaining is not supported. Thus, for example, `$x == $y == $z` will first +compare variables `$x` and `$y`, then the result of that comparison, which is either `true` or +`false`, will be compared with variable `$z`. Given that such expressions would be highly +confusing to a reader, we recommend against using them. If you need to compare that all three +values `$x`, `$y` and `$z` are the same, then you should use the `&&` operator instead: +`$x == $y && $x == $z`. ## Precedence -Just as in mathematics, the operators have precedence ordering among them. This order is as -follows, from highest precedence to lowest: +Just as in mathematics, the operators have precedence ordering among them, meaning that some +operators will always evaluate before the others. For example, if you write `3 + 4 * 5`, then +the result will be `23` instead of `35` because multiplication has higher precedence than addition +and thus evaluates first. + +The precedence order is as follows, from highest to lowest: - `*`, `/`, `%`; - `-`, `+`; @@ -68,3 +178,11 @@ follows, from highest precedence to lowest: - `&&`, `^`; - `||`; - `=`, `+=`, `-=`, `*=`, `/=`, `%=`. + +You can use parentheses `()` in order to alter the order of evaluation. For example, `(3 + 4) * 5` +is `35` instead of `23`. + + +[\<\\>]: ../commands/declare.md +[\<\\>]: ../commands/local.md +[\<\\>]: ../commands/set.md From 4869acb9abf816a9914b5520cf062d8e7f1dac1e Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Fri, 6 Jan 2023 18:44:32 -0800 Subject: [PATCH 5/9] random, numeric and type-conversion functions --- doc/_sphinx/theme/flames.css | 2 +- .../expressions/functions/functions.md | 37 +++- .../language/expressions/functions/numeric.md | 195 ++++++++++++++++++ .../language/expressions/functions/other.md | 1 + .../language/expressions/functions/random.md | 50 +++++ .../language/expressions/functions/type.md | 43 ++++ .../jenny/runtime/command_storage.md | 6 - .../jenny/runtime/yarn_project.md | 3 + .../jenny/lib/src/command_storage.dart | 11 +- .../structure/expressions/functions/bool.dart | 6 +- .../jenny/lib/src/yarn_project.dart | 5 + .../expressions/functions/bool_test.dart | 8 +- .../expressions/functions/number_test.dart | 5 + .../functions/round_places_test.dart | 2 + .../expressions/functions/round_test.dart | 4 + .../expressions/functions/string_test.dart | 4 + 16 files changed, 358 insertions(+), 24 deletions(-) create mode 100644 doc/other_modules/jenny/language/expressions/functions/numeric.md create mode 100644 doc/other_modules/jenny/language/expressions/functions/other.md create mode 100644 doc/other_modules/jenny/language/expressions/functions/random.md create mode 100644 doc/other_modules/jenny/language/expressions/functions/type.md diff --git a/doc/_sphinx/theme/flames.css b/doc/_sphinx/theme/flames.css index 6dea08af1fb..790825850b3 100644 --- a/doc/_sphinx/theme/flames.css +++ b/doc/_sphinx/theme/flames.css @@ -815,7 +815,7 @@ div.admonition.seealso { --admonition-border-color: #54d452; --admonition-icon: '\f064'; --admonition-icon-color: #acfab6; - --admonition-title-background-color: #285131; + --admonition-title-background-color: #28513140; } div.admonition.admonition-deprecated { diff --git a/doc/other_modules/jenny/language/expressions/functions/functions.md b/doc/other_modules/jenny/language/expressions/functions/functions.md index 43587ed21e1..0bd94fb82a7 100644 --- a/doc/other_modules/jenny/language/expressions/functions/functions.md +++ b/doc/other_modules/jenny/language/expressions/functions/functions.md @@ -1,7 +1,5 @@ # Functions -TODO - An expression may also contain function calls, which are indicated by the name of the function, followed by its arguments in parentheses. The parentheses are required, even when there are no arguments: @@ -10,3 +8,38 @@ arguments: <> <> ``` + +- **Random** + - [`dice(n)`](random.md#dicen) + - [`random()`](random.md#random) + - [`random_range(a, b)`](random.md#random_rangea-b) + +- **Numeric** + - [`ceil(x)`](numeric.md#ceilx) + - [`dec(x)`](numeric.md#decx) + - [`decimal(x)`](numeric.md#decimalx) + - [`floor(x)`](numeric.md#floorx) + - [`inc(x)`](numeric.md#incx) + - [`int(x)`](numeric.md#intx) + - [`round(x)`](numeric.md#roundx) + - [`round_places(x, n)`](numeric.md#round_placesx-n) + +- **Type conversion** + - [`bool(x)`](type.md#boolx) + - [`number(x)`](type.md#numberx) + - [`string(x)`](type.md#stringx) + +- **Other** + - `plural(x, ...)` + - `visit_count(node)` + - `visited(node)` + + +```{toctree} +:hidden: + +Random functions +Numeric functions +Type conversion functions +Miscellaneous functions +``` diff --git a/doc/other_modules/jenny/language/expressions/functions/numeric.md b/doc/other_modules/jenny/language/expressions/functions/numeric.md new file mode 100644 index 00000000000..2668876636e --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/numeric.md @@ -0,0 +1,195 @@ +# Numeric functions + +These functions are used to manipulate numeric values. + + +## `ceil(x)` + +Returns the value `x` rounded up towards positive infinity. In other words, this returns the +smallest integer value greater than or equal to `x`. + +```yarn +title: ceil +--- +{ ceil(0) } // 0 +{ ceil(0.3) } // 1 +{ ceil(5) } // 5 +{ ceil(5.001) } // 6 +{ ceil(5.999) } // 6 +{ ceil(-2.07) } // -2 +=== +``` + +```{seealso} +- [`floor(x)`](#floorx) +- [`int(x)`](#intx) +``` + + +## `dec(x)` + +Returns the value `x` reduced towards the previous integer. Thus, if `x` is already an integer +this returns `x - 1`, but if `x` is not an integer then this returns `floor(x)`. + +```yarn +title: dec +--- +{ dec(0) } // -1 +{ dec(0.3) } // 0 +{ dec(5.0) } // 4 +{ dec(5.001) } // 5 +{ dec(5.999) } // 5 +{ dec(-2.07) } // -3 +=== +``` + +```{seealso} +- [`inc(x)`](#incx) +``` + + +## `decimal(x)` + +Returns a fractional part of `x`. + +If `x` is positive, then the returned value will be between `0` (inclusive) and `1` (exclusive). +If `x` is negative, then the returned value will be between `0` and `-1`. In all cases it should +hold that `x == int(x) + decimal(x)`. + +```yarn +title: decimal +--- +{ decimal(0) } // 0 +{ decimal(0.3) } // 0.3 +{ decimal(5.0) } // 0 +{ decimal(5.001) } // 0.001 +{ decimal(5.999) } // 0.999 +{ decimal(-2.07) } // -0.07 +=== +``` + +```{seealso} +- [`int(x)`](#intx) +``` + + +## `floor(x)` + +Returns the value `x` rounded down towards negative infinity. In other words, this returns the +largest integer value less than or equal to `x`. + +```yarn +title: floor +--- +{ floor(0) } // 0 +{ floor(0.3) } // 0 +{ floor(5) } // 5 +{ floor(5.001) } // 5 +{ floor(5.999) } // 5 +{ floor(-2.07) } // -3 +=== +``` + +```{seealso} +- [`ceil(x)`](#ceilx) +- [`int(x)`](#intx) +``` + + +## `inc(x)` + +Returns the value `x` increased towards the next integer. Thus, if `x` is already an integer +this returns `x + 1`, but if `x` is not an integer then this returns `ceil(x)`. + +```yarn +title: inc +--- +{ inc(0) } // 1 +{ inc(0.3) } // 1 +{ inc(5.0) } // 6 +{ inc(5.001) } // 6 +{ inc(5.999) } // 6 +{ inc(-2.07) } // -2 +=== +``` + +```{seealso} +- [`dec(x)`](#decx) +``` + + +## `int(x)` + +Truncates the fractional part of `x`, rounding it towards zero, and returns just the integer part +of the argument `x`. + +```yarn +title: int +--- +{ int(0) } // 0 +{ int(0.3) } // 0 +{ int(5.0) } // 5 +{ int(5.001) } // 5 +{ int(5.999) } // 5 +{ int(-2.07) } // -2 +=== +``` + +```{seealso} +- [`decimal(x)`](#decimalx) +- [`round(x)`](#roundx) +``` + + +## `round(x)` + +Rounds the value `x` towards a nearest integer. + +The values that end with `.5` are rounded up if `x` is positive, and down if `x` is negative. + +```yarn +title: round +--- +{ round(0) } // 0 +{ round(0.3) } // 0 +{ round(5.0) } // 5 +{ round(5.001) } // 5 +{ round(5.5) } // 6 +{ round(5.999) } // 6 +{ round(-2.07) } // -2 +{ round(-2.5) } // -3 +=== +``` + +```{seealso} +- [`round_places(x, n)`](#round_placesx-n) +``` + +## `round_places(x, n)` + +Rounds the value `x` to `n` decimal places. + +The value `x` can be either positive, negative, or zero, but it must be an integer. Rounding to +`0` decimal places is equivalent to the regular `round(x)` function. If `n` is positive, then the +function will attempt to keep that many digits after the decimal point in `x`. If `n` is negative, +then `round_places()` will round `x` to nearest tens, hundreds, thousands, etc: + +```yarn +title: round_places +--- +{ round_places(0, 1) } // 0 +{ round_places(0.3, 1) } // 0.3 +{ round_places(5.001, 1) } // 5.0 +{ round_places(5.001, 2) } // 5.0 +{ round_places(5.001, 3) } // 5.001 +{ round_places(5.5, 1) } // 5.5 +{ round_places(5.999, 1) } // 6.0 +{ round_places(-2.07, 1) } // -2.1 +{ round_places(13, -1) } // 10 +{ round_places(252, -2) } // 200 +=== +``` + +```{seealso} +- [`round(x)`](#roundx) +``` diff --git a/doc/other_modules/jenny/language/expressions/functions/other.md b/doc/other_modules/jenny/language/expressions/functions/other.md new file mode 100644 index 00000000000..20d36dfed2d --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/other.md @@ -0,0 +1 @@ +# Miscellaneous functions diff --git a/doc/other_modules/jenny/language/expressions/functions/random.md b/doc/other_modules/jenny/language/expressions/functions/random.md new file mode 100644 index 00000000000..d1bc072006f --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/random.md @@ -0,0 +1,50 @@ +# Random functions + +These functions produce random results each time they run. + +Internally, each function uses `YarnSpinner.random` random generator, which can be replaced with a +custom generator if you need reproducible draws for debug purposes, or to prevent the player from +getting different results upon reload. + + +## `dice(n)` + +Returns a random integer between `1` and `n`, inclusive. For example, `dice(6)` will return a +random integer from 1 to 6, as if throwing a regular six-sided die. + +The argument `n` must be numeric, and greater or equal than 1. If `n` is a non-integer, then it +will be truncated to an integer value at runtime. Thus, `dice(3.5)` is equivalent to `dice(3)`. + +```yarn +<> +<> +``` + + +## `random()` + +Returns a random floating-point between `0` and `1`. + +This function can be used to implement events with a prescribed probability. For example: + +```yarn +<> + // This happens only with 0.1% probability + You found it! The Holy Grail! +<> +``` + + +## `random_range(a, b)` + +Returns a random integer between `a` and `b` inclusive. + +Both arguments `a` and `b` must be numeric, and they will be truncated to integers upon evaluation. +The value of `a` must be less than or equal to `b`, or otherwise a runtime exception will be thrown. + +The purpose of this function is similar to `dice()`, but it can be used in situations where a +custom range is desired. + +```yarn +<> +``` diff --git a/doc/other_modules/jenny/language/expressions/functions/type.md b/doc/other_modules/jenny/language/expressions/functions/type.md new file mode 100644 index 00000000000..62dda808b14 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/type.md @@ -0,0 +1,43 @@ +# Type conversion functions + + +## `bool(x)` + +Converts its argument into a boolean value. + +- If `x` is already a boolean, then it returns the argument as-is. +- If `x` is numeric, then the result is `false` when `x` is `0`, and `true` for all other values + of `x`. +- If `x` is string, then the function will check whether that string can be found within + `YarnProject.trueValues` or `YarnProject.falseValues` sets. If yes, then it will return the + `true` / `false` value respectively. Otherwise, an error will be thrown. + + +## `number(x)` + +Converts its argument `x` into a numeric value. + +- If `x` is boolean, then it returns `1` for `true` and `0` for `false`. +- If `x` is numeric, then it is returned unmodified. +- If `x` is string, then the function attempts to parse that string as a number. A runtime + exception will be raised if `x` does not have a valid format for a number. The following formats + are recognized: + - integer: `"-3"`, `"214"` + - decimal: `"0.745"`, `"3.14159"`, `".1"`, `"-3."` + - scientific: `"2e5"`, `"3.11e-05"` + - hexadecimal: `"0xDEAD"`, `"0x7F"` + + +## `string(x)` + +Converts its argument `x` into a string value. + +- If `x` is boolean, returns strings `"true"` or `"false"`. +- If `x` is numeric, converts it into a string representation using the standard Dart's + `.toString()` method, which attempts to produce the shortest string that can represent + the number `x`. In particular, + - if `x` is integer-valued, returns its decimal representation without a decimal point; + - if `x` is a double in the range `1e-6` to `1e21`, returns its decimal representation + with a decimal point; + - for all other doubles, returns `x` written in the scientific (exponential) format. +- If `x` is a string, then it is returned as-is. diff --git a/doc/other_modules/jenny/runtime/command_storage.md b/doc/other_modules/jenny/runtime/command_storage.md index 1cd313555ba..74edde1b02c 100644 --- a/doc/other_modules/jenny/runtime/command_storage.md +++ b/doc/other_modules/jenny/runtime/command_storage.md @@ -21,12 +21,6 @@ In order to register a function as a yarn command, the function must satisfy sev considered optional and will default to `false`. -## Properties - -**trueValues**, **falseValues** `Set` -: The strings that can be recognized as `true`/`false` values respectively. - - ## Methods **hasCommand**(`String name`) → `bool` diff --git a/doc/other_modules/jenny/runtime/yarn_project.md b/doc/other_modules/jenny/runtime/yarn_project.md index 445c2567430..263d103748a 100644 --- a/doc/other_modules/jenny/runtime/yarn_project.md +++ b/doc/other_modules/jenny/runtime/yarn_project.md @@ -63,6 +63,9 @@ final yarn = YarnProject() All custom commands must be added before they can be used in the dialogue script. +**trueValues**, **falseValues** `Set` +: The strings that can be recognized as `true`/`false` values respectively. + ## Methods diff --git a/packages/flame_jenny/jenny/lib/src/command_storage.dart b/packages/flame_jenny/jenny/lib/src/command_storage.dart index 1dfe18217e6..e010b9c9ea9 100644 --- a/packages/flame_jenny/jenny/lib/src/command_storage.dart +++ b/packages/flame_jenny/jenny/lib/src/command_storage.dart @@ -1,6 +1,6 @@ import 'dart:async'; -import 'package:jenny/src/errors.dart'; +import 'package:jenny/jenny.dart'; import 'package:jenny/src/parse/ascii.dart'; import 'package:jenny/src/structure/commands/user_defined_command.dart'; import 'package:meta/meta.dart'; @@ -17,11 +17,6 @@ class CommandStorage { final Map _commands; - /// Tokens that represent valid true/false values when converting an argument - /// into a boolean. These sets can be modified by the user. - static Set trueValues = {'true', 'yes', 'on', '+', 'T', '1'}; - static Set falseValues = {'false', 'no', 'off', '-', 'F', '0'}; - /// Returns `true` if command with the given [name] has been registered. bool hasCommand(String name) => _commands.containsKey(name); @@ -146,9 +141,9 @@ class _Cmd { final strValue = stringArguments[i]; switch (_signature[i]) { case _Type.boolean: - if (CommandStorage.falseValues.contains(strValue)) { + if (YarnProject.falseValues.contains(strValue)) { _arguments[i] = false; - } else if (CommandStorage.trueValues.contains(strValue)) { + } else if (YarnProject.trueValues.contains(strValue)) { _arguments[i] = true; } else { throw TypeError( diff --git a/packages/flame_jenny/jenny/lib/src/structure/expressions/functions/bool.dart b/packages/flame_jenny/jenny/lib/src/structure/expressions/functions/bool.dart index 8642f877762..7f2e8492580 100644 --- a/packages/flame_jenny/jenny/lib/src/structure/expressions/functions/bool.dart +++ b/packages/flame_jenny/jenny/lib/src/structure/expressions/functions/bool.dart @@ -34,11 +34,11 @@ class BoolFn extends BoolExpression { return x != 0; } if (x is String) { - final value = x.trim().toLowerCase(); - if (value == 'true') { + final value = x.trim(); + if (YarnProject.trueValues.contains(value)) { return true; } - if (value == 'false') { + if (YarnProject.falseValues.contains(value)) { return false; } throw DialogueError( diff --git a/packages/flame_jenny/jenny/lib/src/yarn_project.dart b/packages/flame_jenny/jenny/lib/src/yarn_project.dart index 7f64c2c9df1..21a8a1a9a76 100644 --- a/packages/flame_jenny/jenny/lib/src/yarn_project.dart +++ b/packages/flame_jenny/jenny/lib/src/yarn_project.dart @@ -40,6 +40,11 @@ class YarnProject { /// Repository for user-defined commands. final CommandStorage commands; + /// Tokens that represent valid true/false values when converting an argument + /// into a boolean. These sets can be modified by the user. + static Set trueValues = {'true', 'yes', 'on', '+', 'T', '1'}; + static Set falseValues = {'false', 'no', 'off', '-', 'F', '0'}; + /// Random number generator used by the dialogue whenever randomization is /// needed. Random random; diff --git a/packages/flame_jenny/jenny/test/structure/expressions/functions/bool_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/functions/bool_test.dart index 1c5463683a0..b737489f1a1 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/functions/bool_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/functions/bool_test.dart @@ -13,8 +13,8 @@ void main() { --- {bool(0)} {bool(1)} {bool(0.005)} {bool(false)} {bool(true)} - {bool("true")} {bool("True")} {bool(" TRUE ")} - {bool("false")} {bool("False")} {bool("FALSE")} {bool("fAlSe")} + {bool("true")} {bool("yes")} {bool(" true ")} + {bool("false")} {bool("off")} {bool("no")} {bool("F")} === ''', testPlan: ''' @@ -44,11 +44,11 @@ void main() { } expectFails(''); - expectFails('T'); + expectFails('t'); expectFails('t r u e'); expectFails('tru'); expectFails('true 1'); - expectFails('1'); + expectFails('1.0'); }); test('too few arguments', () { diff --git a/packages/flame_jenny/jenny/test/structure/expressions/functions/number_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/functions/number_test.dart index 8265d35e302..bdfda6a9055 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/functions/number_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/functions/number_test.dart @@ -19,9 +19,11 @@ void main() { {number("1")} {number("123") - 1} {number("2e2")} + {number("3.11e-05")} {number(" 2e-1 ")} {number("0x100")} {number("-72.001")} + {number(".5")} {number("5.")} === ''', testPlan: ''' @@ -32,9 +34,11 @@ void main() { line: 1 line: 122 line: 200.0 + line: 0.0000311 line: 0.2 line: 256 line: -72.001 + line: 0.5 5.0 ''', ); }); @@ -62,6 +66,7 @@ void main() { expectFails('1 + 2'); expectFails('1.2.3'); expectFails('--8'); + expectFails('2,3'); }); test('too few arguments', () { diff --git a/packages/flame_jenny/jenny/test/structure/expressions/functions/round_places_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/functions/round_places_test.dart index 77203c8b43d..c68d4ac03ff 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/functions/round_places_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/functions/round_places_test.dart @@ -16,6 +16,7 @@ void main() { 7.001 -> {round_places(7.001, 0)}, {round_places(7.001, 2)} 1/7 -> {round_places(1/7, 3)}, {round_places(1/7, 5)} -1/7 -> {round_places(-1/7, 3)}, {round_places(-1/7, 5)} + 274.5 -> {round_places(274.5, -1)}, {round_places(274.5, -2)} === ''', testPlan: ''' @@ -24,6 +25,7 @@ void main() { line: 7.001 -> 7.0, 7.0 line: 1/7 -> 0.143, 0.14286 line: -1/7 -> -0.143, -0.14286 + line: 274.5 -> 270.0, 300.0 ''', ); }); diff --git a/packages/flame_jenny/jenny/test/structure/expressions/functions/round_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/functions/round_test.dart index 14d1b9d1b51..ed9e6aeedfc 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/functions/round_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/functions/round_test.dart @@ -15,7 +15,9 @@ void main() { 2.3 -> {round(2.3)} 2.5 -> {round(2.5)} 2.99 -> {round(2.99)} + 3.5 -> {round(3.5)} -0.3 -> {round(-0.3)} + -0.5 -> {round(-0.5)} === ''', testPlan: ''' @@ -23,7 +25,9 @@ void main() { line: 2.3 -> 2 line: 2.5 -> 3 line: 2.99 -> 3 + line: 3.5 -> 4 line: -0.3 -> 0 + line: -0.5 -> -1 ''', ); }); diff --git a/packages/flame_jenny/jenny/test/structure/expressions/functions/string_test.dart b/packages/flame_jenny/jenny/test/structure/expressions/functions/string_test.dart index 14b08c3b965..41a763e09cc 100644 --- a/packages/flame_jenny/jenny/test/structure/expressions/functions/string_test.dart +++ b/packages/flame_jenny/jenny/test/structure/expressions/functions/string_test.dart @@ -15,6 +15,8 @@ void main() { {string(true)} {string(false)} {string("Jenny")} + {string(12345678900000000000000000)} + {string(0.000000001)} === ''', testPlan: ''' @@ -22,6 +24,8 @@ void main() { line: true line: false line: Jenny + line: 1.23456789e+25 + line: 1e-9 ''', ); }); From c27beaa07f3e5d1f1b13b9c027e1326d11cb5526 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Sat, 7 Jan 2023 10:24:33 -0800 Subject: [PATCH 6/9] wip --- .../expressions/functions/functions.md | 8 ++-- .../language/expressions/functions/misc.md | 45 +++++++++++++++++++ .../language/expressions/functions/other.md | 1 - 3 files changed, 49 insertions(+), 5 deletions(-) create mode 100644 doc/other_modules/jenny/language/expressions/functions/misc.md delete mode 100644 doc/other_modules/jenny/language/expressions/functions/other.md diff --git a/doc/other_modules/jenny/language/expressions/functions/functions.md b/doc/other_modules/jenny/language/expressions/functions/functions.md index 0bd94fb82a7..3d4916f8266 100644 --- a/doc/other_modules/jenny/language/expressions/functions/functions.md +++ b/doc/other_modules/jenny/language/expressions/functions/functions.md @@ -30,9 +30,9 @@ arguments: - [`string(x)`](type.md#stringx) - **Other** - - `plural(x, ...)` - - `visit_count(node)` - - `visited(node)` + - [`plural(x, ...)`](misc.md#pluralx-words) + - [`visit_count(node)`](misc.md#visit_countnode) + - [`visited(node)`](misc.md#visitednode) ```{toctree} @@ -41,5 +41,5 @@ arguments: Random functions Numeric functions Type conversion functions -Miscellaneous functions +Miscellaneous functions ``` diff --git a/doc/other_modules/jenny/language/expressions/functions/misc.md b/doc/other_modules/jenny/language/expressions/functions/misc.md new file mode 100644 index 00000000000..4f769787a03 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/misc.md @@ -0,0 +1,45 @@ +# Miscellaneous functions + + +## plural(x, words...) + +Returns the correct plural form depending on the value of variable `x`. + +This function is locale-dependent, and its implementation and signature changes depending on the +`locale` property in the `YarnProject`. In all cases, the first argument `x` must be numeric, +while all other arguments should be strings. + + +## visit_count(node) + +Returns the number of times that the `node` was visited. + +A node is considered "visited" if the dialogue enters and then exits that node. The node can be +exited either through the normal dialogue flow, or via the [\<\\>] command. However, if a +runtime exception occurs while running the node, then the visit will not count. + +The `node` argument must be a string, and it must contain a valid node name. If a node with the +given name does not exist in the project, an exception will be thrown. + +```{seealso} +- [`visited(node)`](#visitednode) +``` + + +## visited(node) + +Returns `true` if the node with the given title was visited, and `false` otherwise. + +For a node to be considered "visited", the dialogue must enter and then exit the node at least +once. For example, within a node "X" the expression `visited("X")` will return `false` during the +first run of this node, and `true` upon all subsequent runs. + +The `node` argument must be a string, and it must contain a valid node name. If a node with the +given name does not exist in the project, an exception will be thrown. + +```{seealso} +- [`visit_count(node)`](#visit_countnode) +``` + + +[\<\\>]: ../../commands/stop.md diff --git a/doc/other_modules/jenny/language/expressions/functions/other.md b/doc/other_modules/jenny/language/expressions/functions/other.md deleted file mode 100644 index 20d36dfed2d..00000000000 --- a/doc/other_modules/jenny/language/expressions/functions/other.md +++ /dev/null @@ -1 +0,0 @@ -# Miscellaneous functions From 1ee5be6cd315c1fbf4f60467e82ea60492c20570 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Sat, 7 Jan 2023 17:13:10 -0800 Subject: [PATCH 7/9] plural() --- .../language/expressions/functions/misc.md | 70 +++++++++++++++++++ 1 file changed, 70 insertions(+) diff --git a/doc/other_modules/jenny/language/expressions/functions/misc.md b/doc/other_modules/jenny/language/expressions/functions/misc.md index 4f769787a03..83e223486cd 100644 --- a/doc/other_modules/jenny/language/expressions/functions/misc.md +++ b/doc/other_modules/jenny/language/expressions/functions/misc.md @@ -9,6 +9,47 @@ This function is locale-dependent, and its implementation and signature changes `locale` property in the `YarnProject`. In all cases, the first argument `x` must be numeric, while all other arguments should be strings. +The purpose of this function is to form correct plural phrases, according to the rules of the +current language. For example, suppose you need to say `{$n} items`, where `$n` is a variable. If +you simply plug in the value of the variable like that, you'll end up getting phrases like +"23 items", or "1 items" -- which is not what you want. So instead, the `plural()` function can be +used, which will select the correct plural form of the word "item": + +```yarn +I have {plural($n, "% item")}. +``` + +In English locale (`en`), the function `plural()` takes either 1 or 2 `word`s after the numeral +`$x`. The first word is the singular form, and the second is the plural. The second word can be +omitted if the singular form is simple enough that its plural form can be obtained by adding either +`-s` or `-es`. For example: + +```yarn +// Here "foot" is an irregular noun, so its plural form must be specified +// explicitly. At the same time, "inch" is regular, and the function +// plural() will know to add "es" to make its plural form. +The distance is {plural($ft, "% foot", "% feet")} and {plural($in, "% inch")}. +``` + +In locales other than English, the number of plural words can be anywhere from 1 to 3. Usually, +the first word is the singular form, while others are different plurals -- their meaning would +depend on a particular language. For example, in Ukrainian locale (`uk`) the function `plural()` +requires 3 words: the singular form, the "few" plural form, and the "many" plural form: + +```yarn +// Assuming locale == 'uk' +У мене є {plural($coins, "% монета", "% монети", "% монет")}. + +// Produces phrases like this: +// У мене є 21 монета +// У мене є 23 монети +// У мене є 25 монет +``` + +Note that in all examples above the words contain the `%` sign. This is used as a placeholder where +the numeral itself should be placed. It is allowed for some (or all) of the `words` to not contain +the `%` sign. + ## visit_count(node) @@ -21,6 +62,21 @@ runtime exception occurs while running the node, then the visit will not count. The `node` argument must be a string, and it must contain a valid node name. If a node with the given name does not exist in the project, an exception will be thrown. +```yarn +title: LuckyWheel +--- +<> + Clown: Would you like to speen a wheel and get fabulous prizes? + -> I sure do! + <> + -> I don't talk to strangers... + <> +<> + Clown: Sorry kid, we're all out of prizes for now. +<> +=== +``` + ```{seealso} - [`visited(node)`](#visitednode) ``` @@ -37,6 +93,20 @@ first run of this node, and `true` upon all subsequent runs. The `node` argument must be a string, and it must contain a valid node name. If a node with the given name does not exist in the project, an exception will be thrown. +```yarn +title: MerchantDialogue +--- +<> + // This part of the dialogue will run only during the first interaction + // with the merchant. + Merchant: Greetings! My name is Hazeem + Merchant: I offer exquisute wares for the most fastidious customers! + Player: Hi. I'm Bob. I like stuff. +<> +... +=== +``` + ```{seealso} - [`visit_count(node)`](#visit_countnode) ``` From afd7551b6fbb37daefdec9758ad2f0b35b573946 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Sun, 8 Jan 2023 13:45:32 -0800 Subject: [PATCH 8/9] finish functions docs --- .../expressions/functions/functions.md | 24 +++++++++++++------ .../language/expressions/functions/numeric.md | 3 ++- .../language/expressions/functions/type.md | 4 ++++ .../functions/user_defined_functions.md | 3 +++ 4 files changed, 26 insertions(+), 8 deletions(-) create mode 100644 doc/other_modules/jenny/language/expressions/functions/user_defined_functions.md diff --git a/doc/other_modules/jenny/language/expressions/functions/functions.md b/doc/other_modules/jenny/language/expressions/functions/functions.md index 3d4916f8266..71914c598fd 100644 --- a/doc/other_modules/jenny/language/expressions/functions/functions.md +++ b/doc/other_modules/jenny/language/expressions/functions/functions.md @@ -1,20 +1,27 @@ # Functions -An expression may also contain function calls, which are indicated by the name of the function, -followed by its arguments in parentheses. The parentheses are required, even when there are no -arguments: +A **function** in YarnSpinner is the same notion as in any other programming language, or in math: +it takes a certain number of arguments, and then computes and returns the result. A function call +is indicated by the name of the function, followed by its arguments in parentheses. The parentheses +are required, even when there are no arguments: ```yarn <> <> ``` -- **Random** +There are around 20 built-in functions in Jenny, listed below; and it is also possible to add +[user-defined functions] as well. + + +## Built-in functions + +- **Random functions** - [`dice(n)`](random.md#dicen) - [`random()`](random.md#random) - [`random_range(a, b)`](random.md#random_rangea-b) -- **Numeric** +- **Numeric functions** - [`ceil(x)`](numeric.md#ceilx) - [`dec(x)`](numeric.md#decx) - [`decimal(x)`](numeric.md#decimalx) @@ -24,12 +31,12 @@ arguments: - [`round(x)`](numeric.md#roundx) - [`round_places(x, n)`](numeric.md#round_placesx-n) -- **Type conversion** +- **Type conversion functions** - [`bool(x)`](type.md#boolx) - [`number(x)`](type.md#numberx) - [`string(x)`](type.md#stringx) -- **Other** +- **Other functions** - [`plural(x, ...)`](misc.md#pluralx-words) - [`visit_count(node)`](misc.md#visit_countnode) - [`visited(node)`](misc.md#visitednode) @@ -42,4 +49,7 @@ Random functions Numeric functions Type conversion functions Miscellaneous functions +User-defined functions ``` + +[user-defined functions]: user_defined_functions.md diff --git a/doc/other_modules/jenny/language/expressions/functions/numeric.md b/doc/other_modules/jenny/language/expressions/functions/numeric.md index 2668876636e..d5263b9f392 100644 --- a/doc/other_modules/jenny/language/expressions/functions/numeric.md +++ b/doc/other_modules/jenny/language/expressions/functions/numeric.md @@ -1,6 +1,7 @@ # Numeric functions -These functions are used to manipulate numeric values. +These functions are used to manipulate numeric values. Most of them take a single numeric argument +and produce a numeric result. ## `ceil(x)` diff --git a/doc/other_modules/jenny/language/expressions/functions/type.md b/doc/other_modules/jenny/language/expressions/functions/type.md index 62dda808b14..2acf5fd69d8 100644 --- a/doc/other_modules/jenny/language/expressions/functions/type.md +++ b/doc/other_modules/jenny/language/expressions/functions/type.md @@ -1,5 +1,9 @@ # Type conversion functions +These functions convert values of one type into another type, if possible. All of these functions +take a single argument of arbitrary type, and return the result of the type corresponding to the +name of the function. + ## `bool(x)` diff --git a/doc/other_modules/jenny/language/expressions/functions/user_defined_functions.md b/doc/other_modules/jenny/language/expressions/functions/user_defined_functions.md new file mode 100644 index 00000000000..32e3fdb54e9 --- /dev/null +++ b/doc/other_modules/jenny/language/expressions/functions/user_defined_functions.md @@ -0,0 +1,3 @@ +# User-defined functions + +TODO From ca4d56d771644273a79a2e3d3cf92d8688a4cb06 Mon Sep 17 00:00:00 2001 From: Pasha Stetsenko Date: Sun, 8 Jan 2023 13:55:55 -0800 Subject: [PATCH 9/9] markdown lints --- .../jenny/language/expressions/functions/misc.md | 2 +- .../jenny/language/expressions/functions/numeric.md | 1 + .../jenny/language/expressions/functions/type.md | 4 ++-- 3 files changed, 4 insertions(+), 3 deletions(-) diff --git a/doc/other_modules/jenny/language/expressions/functions/misc.md b/doc/other_modules/jenny/language/expressions/functions/misc.md index 83e223486cd..a1fd48cba00 100644 --- a/doc/other_modules/jenny/language/expressions/functions/misc.md +++ b/doc/other_modules/jenny/language/expressions/functions/misc.md @@ -57,7 +57,7 @@ Returns the number of times that the `node` was visited. A node is considered "visited" if the dialogue enters and then exits that node. The node can be exited either through the normal dialogue flow, or via the [\<\\>] command. However, if a -runtime exception occurs while running the node, then the visit will not count. +runtime exception occurs while running the node, then the visit will not count. The `node` argument must be a string, and it must contain a valid node name. If a node with the given name does not exist in the project, an exception will be thrown. diff --git a/doc/other_modules/jenny/language/expressions/functions/numeric.md b/doc/other_modules/jenny/language/expressions/functions/numeric.md index d5263b9f392..6a67b4a7d8b 100644 --- a/doc/other_modules/jenny/language/expressions/functions/numeric.md +++ b/doc/other_modules/jenny/language/expressions/functions/numeric.md @@ -166,6 +166,7 @@ title: round - [`round_places(x, n)`](#round_placesx-n) ``` + ## `round_places(x, n)` Rounds the value `x` to `n` decimal places. diff --git a/doc/other_modules/jenny/language/expressions/functions/type.md b/doc/other_modules/jenny/language/expressions/functions/type.md index 2acf5fd69d8..8ba6f9d9d6e 100644 --- a/doc/other_modules/jenny/language/expressions/functions/type.md +++ b/doc/other_modules/jenny/language/expressions/functions/type.md @@ -12,7 +12,7 @@ Converts its argument into a boolean value. - If `x` is already a boolean, then it returns the argument as-is. - If `x` is numeric, then the result is `false` when `x` is `0`, and `true` for all other values of `x`. -- If `x` is string, then the function will check whether that string can be found within +- If `x` is string, then the function will check whether that string can be found within `YarnProject.trueValues` or `YarnProject.falseValues` sets. If yes, then it will return the `true` / `false` value respectively. Otherwise, an error will be thrown. @@ -44,4 +44,4 @@ Converts its argument `x` into a string value. - if `x` is a double in the range `1e-6` to `1e21`, returns its decimal representation with a decimal point; - for all other doubles, returns `x` written in the scientific (exponential) format. -- If `x` is a string, then it is returned as-is. +- If `x` is a string, then it is returned as-is.