From ba56e7414a0bdf1575f61f10d9f5d80762f909d8 Mon Sep 17 00:00:00 2001 From: Laurence Morgan Date: Sun, 8 Oct 2023 21:45:02 +0100 Subject: [PATCH] docs added for elvis and null coalescing operators --- app/app.go | 2 +- builtins/docs/summaries.go | 4 +- docs/parser/README.md | 4 +- docs/parser/elvis.md | 19 ++++-- docs/parser/logical-and.md | 2 +- docs/parser/logical-or.md | 2 +- docs/parser/null-coalescing.md | 84 ++++++++++++++++++++++++++ docs/user-guide/README.md | 4 +- docs/user-guide/rosetta-stone.md | 4 +- gen/parser/elvis_op_doc.yaml | 18 ++++-- gen/parser/null_coalescing_op_doc.yaml | 62 +++++++++++++++++++ gen/user-guide/rosetta-stone.inc.md | 4 +- lang/expressions/expression.go | 4 +- lang/expressions/symbols/exp.go | 2 +- lang/expressions/symbols/exp_string.go | 8 +-- utils/readline/unicode.go | 3 +- version.svg | 2 +- 17 files changed, 196 insertions(+), 32 deletions(-) create mode 100644 docs/parser/null-coalescing.md create mode 100644 gen/parser/null_coalescing_op_doc.yaml diff --git a/app/app.go b/app/app.go index b358e3bc4..9fbaa07ad 100644 --- a/app/app.go +++ b/app/app.go @@ -15,7 +15,7 @@ const ( version = "%d.%d.%d" Major = 5 Minor = 1 - Revision = 2000 + Revision = 2100 ) // Copyright is the copyright owner string diff --git a/builtins/docs/summaries.go b/builtins/docs/summaries.go index 5470ad139..15a24dd96 100644 --- a/builtins/docs/summaries.go +++ b/builtins/docs/summaries.go @@ -272,7 +272,8 @@ func init() { "parser/equ": "Evaluate a mathematical function (deprecated)", "parser/greater-than-greater-than": "Writes STDIN to disk - appending contents if file already exists", "parser/pipe-append": "Redirects STDOUT to a file and append its contents", - "parser/elvis": "Returns the right operand if the left operand is empty", + "parser/elvis": "Returns the right operand if the left operand is falsy", + "parser/null-coalescing": "Returns the right operand if the left operand is empty / undefined", "parser/pipe-err": "Pipes STDERR from the left hand command to STDIN of the right hand command", "parser/range": "Outputs a ranged subset of data from STDIN", "parser/element": "Outputs an element from a nested structure", @@ -729,6 +730,7 @@ func init() { "parser/fappend": "parser/greater-than-greater-than", "parser/pipe-append": "parser/pipe-append", "parser/elvis": "parser/elvis", + "parser/null-coalescing": "parser/null-coalescing", "parser/pipe-err": "parser/pipe-err", "parser/@[": "parser/range", "parser/[[": "parser/element", diff --git a/docs/parser/README.md b/docs/parser/README.md index ec548fe53..5d926faaa 100644 --- a/docs/parser/README.md +++ b/docs/parser/README.md @@ -56,7 +56,9 @@ files. * [`>>` Append Pipe](../parser/pipe-append.md): Redirects STDOUT to a file and append its contents * [`?:` Elvis Operator](../parser/elvis.md): - Returns the right operand if the left operand is empty + Returns the right operand if the left operand is falsy +* [`??` Null Coalescing Operator](../parser/null-coalescing.md): + Returns the right operand if the left operand is empty / undefined * [`?` STDERR Pipe](../parser/pipe-err.md): Pipes STDERR from the left hand command to STDIN of the right hand command * [`[..range]`](../parser/range.md): diff --git a/docs/parser/elvis.md b/docs/parser/elvis.md index 93317df82..98f04c99c 100644 --- a/docs/parser/elvis.md +++ b/docs/parser/elvis.md @@ -1,16 +1,21 @@ # `?:` Elvis Operator -> Returns the right operand if the left operand is empty +> Returns the right operand if the left operand is falsy ## Description The elvis operator is a little like a conditional where the result of the -operation is the first non-empty value from left to right. +operation is the first non-falsy value from left to right. -An empty value is any of the following: +A falsy value is any of the following: -* An unset / undefined variable -* Any value with a `null` data type +* an unset / undefined variable +* any value with a `null` data type +* a `str` or generic with the value `false`, `null`, `0`, `no`, `off`, `fail`, + `failed`, or `disabled` +* a number (`num`, `float` or `int`) with the value `0` +* an empty object or zero length array +* and, of course, a boolean with the value `false` Other "falsy" values such as numerical values of `0`, boolean `false`, zero length strings and strings containing `"null"` are not considered empty by the @@ -31,7 +36,7 @@ If `$bar` is unset then the value of `$foo` will be **"baz"**. **Multiple elvis operators:** ``` -» $unset_variable ?: null ?: "foobar" +» $unset_variable ?: null ?: false ?: "foobar" foobar ``` @@ -54,6 +59,8 @@ where it says: Overview of the different schedulers (or 'run modes') in Murex * [`&&` And Logical Operator](../parser/logical-and.md): Continues next operation if previous operation passes +* [`??` Null Coalescing Operator](../parser/null-coalescing.md): + Returns the right operand if the left operand is empty / undefined * [`?` STDERR Pipe](../parser/pipe-err.md): Pipes STDERR from the left hand command to STDIN of the right hand command * [`err`](../commands/err.md): diff --git a/docs/parser/logical-and.md b/docs/parser/logical-and.md index 8b94f366d..1fbc9835e 100644 --- a/docs/parser/logical-and.md +++ b/docs/parser/logical-and.md @@ -48,7 +48,7 @@ try { * [Schedulers](../user-guide/schedulers.md): Overview of the different schedulers (or 'run modes') in Murex * [`?:` Elvis Operator](../parser/elvis.md): - Returns the right operand if the left operand is empty + Returns the right operand if the left operand is falsy * [`?` STDERR Pipe](../parser/pipe-err.md): Pipes STDERR from the left hand command to STDIN of the right hand command * [`err`](../commands/err.md): diff --git a/docs/parser/logical-or.md b/docs/parser/logical-or.md index eb7a8291c..85c2fe701 100644 --- a/docs/parser/logical-or.md +++ b/docs/parser/logical-or.md @@ -53,7 +53,7 @@ There is no workaround for `trypipe`. * [`&&` And Logical Operator](../parser/logical-and.md): Continues next operation if previous operation passes * [`?:` Elvis Operator](../parser/elvis.md): - Returns the right operand if the left operand is empty + Returns the right operand if the left operand is falsy * [`?` STDERR Pipe](../parser/pipe-err.md): Pipes STDERR from the left hand command to STDIN of the right hand command * [`err`](../commands/err.md): diff --git a/docs/parser/null-coalescing.md b/docs/parser/null-coalescing.md new file mode 100644 index 000000000..52d1e88ca --- /dev/null +++ b/docs/parser/null-coalescing.md @@ -0,0 +1,84 @@ +# `??` Null Coalescing Operator + +> Returns the right operand if the left operand is empty / undefined + +## Description + +The null coalescing operator is a little like a conditional where the result of the +operation is the first non-empty value from left to right. + +An empty value is any of the following: + +* an unset / undefined variable +* any value with a `null` data type + +Other "falsy" values such as numerical values of `0`, boolean `false`, zero +length strings and strings containing `"null"` are not considered empty by the +null coalescing operator. + + + +## Examples + +**Assign a variable with a default value:** + +``` +» $foo = $bar ?? "baz" +``` + +If `$bar` is unset then the value of `$foo` will be **"baz"**. + +**Multiple operators:** + +``` +» $unset_variable ?? null ?? "foobar" +foobar +``` + +## Detail + +The following extract was taken from [Wikipedia](https://en.wikipedia.org/wiki/Null_coalescing_operator): + +> The null coalescing operator (called the Logical Defined-Or operator in Perl) +> is a binary operator that is part of the syntax for a basic conditional +> expression in several programming languages, including C#, PowerShell as of +> version 7.0.0, Perl as of version 5.10, Swift, and PHP 7.0.0. While its +> behavior differs between implementations, the null coalescing operator +> generally returns the result of its left-most operand if it exists and is not +> null, and otherwise returns the right-most operand. This behavior allows a +> default value to be defined for cases where a more specific value is not +> available. +> +> In contrast to the ternary conditional if operator used as `x ? x : y`, but +> like the binary Elvis operator used as `x ?: y`, the null coalescing operator +> is a binary operator and thus evaluates its operands at most once, which is +> significant if the evaluation of `x` has side-effects. + +## See Also + +* [Pipeline](../user-guide/pipeline.md): + Overview of what a "pipeline" is +* [Schedulers](../user-guide/schedulers.md): + Overview of the different schedulers (or 'run modes') in Murex +* [`&&` And Logical Operator](../parser/logical-and.md): + Continues next operation if previous operation passes +* [`?:` Elvis Operator](../parser/elvis.md): + Returns the right operand if the left operand is falsy +* [`?` STDERR Pipe](../parser/pipe-err.md): + Pipes STDERR from the left hand command to STDIN of the right hand command +* [`err`](../commands/err.md): + Print a line to the STDERR +* [`out`](../commands/out.md): + Print a string to the STDOUT with a trailing new line character +* [`try`](../commands/try.md): + Handles errors inside a block of code +* [`trypipe`](../commands/trypipe.md): + Checks state of each function in a pipeline and exits block on error +* [`||` Or Logical Operator](../parser/logical-or.md): + Continues next operation only if previous operation fails +* [null](../commands/devnull.md): + null function. Similar to /dev/null + +
+ +This document was generated from [gen/parser/null_coalescing_op_doc.yaml](https://github.com/lmorg/murex/blob/master/gen/parser/null_coalescing_op_doc.yaml). \ No newline at end of file diff --git a/docs/user-guide/README.md b/docs/user-guide/README.md index 0e6456978..8fe295fc3 100644 --- a/docs/user-guide/README.md +++ b/docs/user-guide/README.md @@ -94,7 +94,9 @@ The [Language Tour](/tour.md) is a great introduction into the Murex language. * [`>>` Append Pipe](parser/pipe-append.md): Redirects STDOUT to a file and append its contents * [`?:` Elvis Operator](parser/elvis.md): - Returns the right operand if the left operand is empty + Returns the right operand if the left operand is falsy +* [`??` Null Coalescing Operator](parser/null-coalescing.md): + Returns the right operand if the left operand is empty / undefined * [`?` STDERR Pipe](parser/pipe-err.md): Pipes STDERR from the left hand command to STDIN of the right hand command * [`[..range]`](parser/range.md): diff --git a/docs/user-guide/rosetta-stone.md b/docs/user-guide/rosetta-stone.md index bdc110e88..fe8504952 100644 --- a/docs/user-guide/rosetta-stone.md +++ b/docs/user-guide/rosetta-stone.md @@ -88,11 +88,11 @@ if you want to learn more about shell scripting in Murex. ### Variables | Description | Bash | Murex | |---------------|---------------|--------| -| [Printing a variable](../parser/string.md) | `echo "$foobar"` | `out $foobar` [[5]](#footnotes)

`$foobar`

(variables don't need to be quoted in Murex) | +| [Printing a variable](../parser/scalar.md) | `echo "$foobar"` | `out $foobar` [[5]](#footnotes)

`$foobar`

(variables don't need to be quoted in Murex) | | [Assign a local variable](../commands/set.md) | `local foo="bar"` | `$foo = "bar"` [[2]](#footnotes) [[6]](#footnotes)

`out "bar" \| set $foo` | | [Assign a global variable](../commands/global.md) | `foo="bar"` | `$GLOBAL.foo = "bar"` [[6]](#footnotes)

`out "bar" \| global $foo` | | [Assign an environmental variable](../commands/export.md) | `export foo="bar"` | `export foo = "bar"` [[1]](#footnotes) [[2]](#footnotes) [[3]](#footnotes)

`$ENV.foo = "bar"` [[6]](#footnotes)

`out "bar" \| export $foo` [[3]](#footnotes) | -| Assign with a default value | `FOOBAR="${VARIABLE:-default}"` | `$foobar = $variable ?: "default"`

(the elvis operator can be used in any part of expressions and just for assignments) +| [Assign with a default value](../parser/null-coalescing.md) | `FOOBAR="${VARIABLE:-default}"` | `$foobar = $variable ?: "default"`

(the elvis operator can be used in any part of expressions and just for assignments) ### Arrays (eg arrays, lists) diff --git a/gen/parser/elvis_op_doc.yaml b/gen/parser/elvis_op_doc.yaml index 9396a5f3f..e5ffc99f3 100644 --- a/gen/parser/elvis_op_doc.yaml +++ b/gen/parser/elvis_op_doc.yaml @@ -3,15 +3,20 @@ `?:` Elvis Operator CategoryID: parser Summary: >- - Returns the right operand if the left operand is empty + Returns the right operand if the left operand is falsy Description: |- The elvis operator is a little like a conditional where the result of the - operation is the first non-empty value from left to right. + operation is the first non-falsy value from left to right. - An empty value is any of the following: + A falsy value is any of the following: - * An unset / undefined variable - * Any value with a `null` data type + * an unset / undefined variable + * any value with a `null` data type + * a `str` or generic with the value `false`, `null`, `0`, `no`, `off`, `fail`, + `failed`, or `disabled` + * a number (`num`, `float` or `int`) with the value `0` + * an empty object or zero length array + * and, of course, a boolean with the value `false` Other "falsy" values such as numerical values of `0`, boolean `false`, zero length strings and strings containing `"null"` are not considered empty by the @@ -28,7 +33,7 @@ **Multiple elvis operators:** ``` - » $unset_variable ?: null ?: "foobar" + » $unset_variable ?: null ?: false ?: "foobar" foobar ``` Detail: |- @@ -41,6 +46,7 @@ > ?:, is viewed sideways, it resembles an emoticon of Elvis Presley with his > signature hairstyle. Related: + - null-coalescing - pipe-err - pipeline - schedulers diff --git a/gen/parser/null_coalescing_op_doc.yaml b/gen/parser/null_coalescing_op_doc.yaml new file mode 100644 index 000000000..70c98bb05 --- /dev/null +++ b/gen/parser/null_coalescing_op_doc.yaml @@ -0,0 +1,62 @@ +- DocumentID: null-coalescing + Title: >- + `??` Null Coalescing Operator + CategoryID: parser + Summary: >- + Returns the right operand if the left operand is empty / undefined + Description: |- + The null coalescing operator is a little like a conditional where the result of the + operation is the first non-empty value from left to right. + + An empty value is any of the following: + + * an unset / undefined variable + * any value with a `null` data type + + Other "falsy" values such as numerical values of `0`, boolean `false`, zero + length strings and strings containing `"null"` are not considered empty by the + null coalescing operator. + Examples: |- + **Assign a variable with a default value:** + + ``` + » $foo = $bar ?? "baz" + ``` + + If `$bar` is unset then the value of `$foo` will be **"baz"**. + + **Multiple operators:** + + ``` + » $unset_variable ?? null ?? "foobar" + foobar + ``` + Detail: |- + The following extract was taken from [Wikipedia](https://en.wikipedia.org/wiki/Null_coalescing_operator): + + > The null coalescing operator (called the Logical Defined-Or operator in Perl) + > is a binary operator that is part of the syntax for a basic conditional + > expression in several programming languages, including C#, PowerShell as of + > version 7.0.0, Perl as of version 5.10, Swift, and PHP 7.0.0. While its + > behavior differs between implementations, the null coalescing operator + > generally returns the result of its left-most operand if it exists and is not + > null, and otherwise returns the right-most operand. This behavior allows a + > default value to be defined for cases where a more specific value is not + > available. + > + > In contrast to the ternary conditional if operator used as `x ? x : y`, but + > like the binary Elvis operator used as `x ?: y`, the null coalescing operator + > is a binary operator and thus evaluates its operands at most once, which is + > significant if the evaluation of `x` has side-effects. + Related: + - elvis + - pipe-err + - pipeline + - schedulers + - out + - err + - try + - trypipe + - logical-and + - logical-or + - "null" diff --git a/gen/user-guide/rosetta-stone.inc.md b/gen/user-guide/rosetta-stone.inc.md index 70c66bb3e..291d1b2ab 100644 --- a/gen/user-guide/rosetta-stone.inc.md +++ b/gen/user-guide/rosetta-stone.inc.md @@ -84,11 +84,11 @@ if you want to learn more about shell scripting in Murex. ### Variables | Description | Bash | Murex | |---------------|---------------|--------| -| [Printing a variable](../parser/string.md) | `echo "$foobar"` | `out $foobar` [[5]](#footnotes)

`$foobar`

(variables don't need to be quoted in Murex) | +| [Printing a variable](../parser/scalar.md) | `echo "$foobar"` | `out $foobar` [[5]](#footnotes)

`$foobar`

(variables don't need to be quoted in Murex) | | [Assign a local variable](../commands/set.md) | `local foo="bar"` | `$foo = "bar"` [[2]](#footnotes) [[6]](#footnotes)

`out "bar" \| set $foo` | | [Assign a global variable](../commands/global.md) | `foo="bar"` | `$GLOBAL.foo = "bar"` [[6]](#footnotes)

`out "bar" \| global $foo` | | [Assign an environmental variable](../commands/export.md) | `export foo="bar"` | `export foo = "bar"` [[1]](#footnotes) [[2]](#footnotes) [[3]](#footnotes)

`$ENV.foo = "bar"` [[6]](#footnotes)

`out "bar" \| export $foo` [[3]](#footnotes) | -| Assign with a default value | `FOOBAR="${VARIABLE:-default}"` | `$foobar = $variable ?: "default"`

(the elvis operator can be used in any part of expressions and just for assignments) +| [Assign with a default value](../parser/null-coalescing.md) | `FOOBAR="${VARIABLE:-default}"` | `$foobar = $variable ?: "default"`

(the elvis operator can be used in any part of expressions and just for assignments) ### Arrays (eg arrays, lists) diff --git a/lang/expressions/expression.go b/lang/expressions/expression.go index ff7bd2d49..5ce938d1f 100644 --- a/lang/expressions/expression.go +++ b/lang/expressions/expression.go @@ -96,10 +96,10 @@ func executeExpression(tree *ParserT, order symbols.Exp) (err error) { err = expAssignMerge(tree) // 13. Conditional expression (ternary) - case symbols.Elvis: - err = expElvis(tree) case symbols.NullCoalescing: err = expNullCoalescing(tree) + case symbols.Elvis: + err = expElvis(tree) // 12. Logical OR case symbols.LogicalOr: diff --git a/lang/expressions/symbols/exp.go b/lang/expressions/symbols/exp.go index 64912f40d..8cdceec4b 100644 --- a/lang/expressions/symbols/exp.go +++ b/lang/expressions/symbols/exp.go @@ -54,8 +54,8 @@ const ( AssignAndMerge // 13. Conditional expression (ternary) - NullCoalescing Elvis + NullCoalescing // 12. Logical OR LogicalOr diff --git a/lang/expressions/symbols/exp_string.go b/lang/expressions/symbols/exp_string.go index 06eab7c53..525994bd1 100644 --- a/lang/expressions/symbols/exp_string.go +++ b/lang/expressions/symbols/exp_string.go @@ -35,8 +35,8 @@ func _() { _ = x[AssignAndDivide-25] _ = x[AssignAndMultiply-26] _ = x[AssignAndMerge-27] - _ = x[NullCoalescing-28] - _ = x[Elvis-29] + _ = x[Elvis-28] + _ = x[NullCoalescing-29] _ = x[LogicalOr-30] _ = x[LogicalAnd-31] _ = x[EqualTo-32] @@ -58,11 +58,11 @@ func _() { const ( _Exp_name_0 = "Undefined" - _Exp_name_1 = "UnexpectedInvalidHyphenSubExpressionEndObjectEndArrayEndDataValuesBarewordSubExpressionBeginObjectBeginArrayBeginQuoteSingleQuoteDoubleQuoteParenthesisNumberBooleanNullScalarCalculatedOperationsAssignAssignUpdateAssignAndAddAssignAndSubtractAssignAndDivideAssignAndMultiplyAssignAndMergeNullCoalescingElvisLogicalOrLogicalAndEqualToNotEqualToLikeNotLikeRegexpNotRegexpGreaterThanGreaterThanOrEqualLessThanLessThanOrEqualAddSubtractMergeIntoMultiplyDivide" + _Exp_name_1 = "UnexpectedInvalidHyphenSubExpressionEndObjectEndArrayEndDataValuesBarewordSubExpressionBeginObjectBeginArrayBeginQuoteSingleQuoteDoubleQuoteParenthesisNumberBooleanNullScalarCalculatedOperationsAssignAssignUpdateAssignAndAddAssignAndSubtractAssignAndDivideAssignAndMultiplyAssignAndMergeElvisNullCoalescingLogicalOrLogicalAndEqualToNotEqualToLikeNotLikeRegexpNotRegexpGreaterThanGreaterThanOrEqualLessThanLessThanOrEqualAddSubtractMergeIntoMultiplyDivide" ) var ( - _Exp_index_1 = [...]uint16{0, 10, 23, 39, 48, 56, 66, 74, 92, 103, 113, 124, 135, 151, 157, 164, 168, 174, 184, 194, 200, 212, 224, 241, 256, 273, 287, 301, 306, 315, 325, 332, 342, 346, 353, 359, 368, 379, 397, 405, 420, 423, 431, 440, 448, 454} + _Exp_index_1 = [...]uint16{0, 10, 23, 39, 48, 56, 66, 74, 92, 103, 113, 124, 135, 151, 157, 164, 168, 174, 184, 194, 200, 212, 224, 241, 256, 273, 287, 292, 306, 315, 325, 332, 342, 346, 353, 359, 368, 379, 397, 405, 420, 423, 431, 440, 448, 454} ) func (i Exp) String() string { diff --git a/utils/readline/unicode.go b/utils/readline/unicode.go index ede6726b5..246e922c0 100644 --- a/utils/readline/unicode.go +++ b/utils/readline/unicode.go @@ -89,8 +89,7 @@ func (u *UnicodeT) SetCellPos(cPos int) { i := u._offByOne(u.rPos) if i != u.rPos { u.rPos-- - // TODO: this isn't wide character compliant - u.cPos-- + u.cPos -= runewidth.RuneWidth(u.value[u.rPos]) } } diff --git a/version.svg b/version.svg index 1a94314d0..ca4f2a451 100644 --- a/version.svg +++ b/version.svg @@ -1 +1 @@ -version: 5.1.2000version5.1.2000 +version: 5.1.2100version5.1.2100