Compare commits

...

17 Commits

Author SHA1 Message Date
camoroso 531cb1c249 Doc: concepts 2024-06-19 09:38:02 +02:00
camoroso d1b468f35b t_list_test.go: new test cases added 2024-06-19 09:24:50 +02:00
camoroso ff9cf80c66 operator-index.go: ConstLastIndex is checked 2024-06-19 09:24:19 +02:00
camoroso d63b18fd76 parser.go: SymColon resets the firstToken flag; that is needed to specify range indeces that can define negative limits 2024-06-19 09:22:40 +02:00
camoroso 019470faf1 operator-range.go: Fixed priority bug; when range only has the left limit, right limit is set to ConstLastIndex constant 2024-06-19 09:20:02 +02:00
camoroso 302430d57d common-params.go: added the constant ConstLastIndex 2024-06-19 09:17:46 +02:00
camoroso 62ef0d699d token.go: new function IsOneOf() 2024-06-19 09:16:19 +02:00
camoroso 866de759dd file-reader.expr: simpler implementation 2024-06-17 14:07:39 +02:00
camoroso b1d6b6de44 refactored dict's item access 2024-06-17 14:06:33 +02:00
camoroso 7e357eea62 changed to comply new builtin-os-file.go's function names 2024-06-17 09:54:20 +02:00
camoroso d6b4c79736 operator-index.go: dict item access by generic keys implemented 2024-06-17 09:05:23 +02:00
camoroso d066344af8 builtin-os-file.go: changed read and write function names; added fileReadTextAll 2024-06-17 06:59:15 +02:00
camoroso f41dba069e plugin.go: support Liteide and VScode to debug executable file name 2024-06-17 06:57:47 +02:00
camoroso 703ecf6829 Added utility function GetLast() to context 2024-06-17 06:56:32 +02:00
camoroso ba479a1b99 Function call moved from operand-func.go to function.go 2024-06-17 06:54:50 +02:00
camoroso 24e6a293b0 common-params.go: ParamName added 2024-06-12 11:16:57 +02:00
camoroso 28f464c4dc plugins.go: If the main program's file name ends with '.debug', plugins will be loaded from file with name ending with '.debug' 2024-06-12 11:15:31 +02:00
22 changed files with 371 additions and 152 deletions
+12 -12
View File
@@ -43,16 +43,16 @@ func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (re
return
}
func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
// func CallExprFunction(parentCtx ExprContext, funcName string, params ...any) (v any, err error) {
// ctx := cloneContext(parentCtx)
// ctx.SetParent(parentCtx)
if err == nil {
if err = checkFunctionCall(ctx, funcName, &params); err == nil {
if v, err = ctx.Call(funcName, params); err == nil {
exportObjects(parentCtx, ctx)
}
}
}
return
}
// if err == nil {
// if err = checkFunctionCall(ctx, funcName, &params); err == nil {
// if v, err = ctx.Call(funcName, params); err == nil {
// exportObjects(parentCtx, ctx)
// }
// }
// }
// return
// }
+52 -16
View File
@@ -68,7 +68,7 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
}
} else {
err = errMissingFilePath("createFile")
err = errMissingFilePath(name)
}
return
}
@@ -80,7 +80,7 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
}
} else {
err = errMissingFilePath("openFile")
err = errMissingFilePath(name)
}
return
}
@@ -92,7 +92,7 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
}
} else {
err = errMissingFilePath("appendFile")
err = errMissingFilePath(name)
}
return
}
@@ -118,13 +118,13 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("closeFileFunc", handle)
err = errInvalidFileHandle(name, handle)
}
result = err == nil
return
}
func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
@@ -142,12 +142,12 @@ func writeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("writeFileFunc", invalidFileHandle)
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
func readFileFunc(ctx ExprContext, name string, args []any) (result any, err error) {
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
@@ -165,23 +165,50 @@ func readFileFunc(ctx ExprContext, name string, args []any) (result any, err err
limit = s[0]
}
if v, err = r.reader.ReadString(limit); err == nil {
if len(v) > 0 && v[len(v)-1] == limit {
v, err = r.reader.ReadString(limit)
if err == io.EOF {
err = nil
}
if len(v) > 0 {
if v[len(v)-1] == limit {
result = v[0 : len(v)-1]
} else {
result = v
}
}
if err == io.EOF {
err = nil
}
} else {
invalidFileHandle = handle
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle("readFileFunc", invalidFileHandle)
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) {
var handle osHandle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil {
invalidFileHandle = args[0]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
var b []byte
b, err = io.ReadAll(r.reader)
result = string(b)
} else {
invalidFileHandle = handle
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
@@ -190,21 +217,30 @@ func ImportOsFuncs(ctx ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{
NewFuncParam(ParamFilepath),
})
ctx.RegisterFunc("fileWrite", NewGolangFunctor(writeFileFunc), TypeInt, []ExprFuncParam{
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
NewFuncParam(TypeHandle),
})
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""),
})
ctx.RegisterFunc("fileRead", NewGolangFunctor(readFileFunc), TypeString, []ExprFuncParam{
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"),
})
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{
NewFuncParam(TypeHandle),
})
}
+6
View File
@@ -15,7 +15,13 @@ const (
ParamStart = "start"
ParamEnd = "end"
ParamValue = "value"
ParamName = "name"
ParamEllipsis = "..."
ParamFilepath = "filepath"
ParamDirpath = "dirpath"
)
// to be moved in its own source file
const (
ConstLastIndex = 0xFFFF_FFFF
)
+6
View File
@@ -41,3 +41,9 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
}
}
}
func exportObjectsToParent(sourceCtx ExprContext) {
if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
exportObjects(parentCtx, sourceCtx)
}
}
+1
View File
@@ -40,6 +40,7 @@ type ExprContext interface {
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetVar(varName string) (value any, exists bool)
GetLast() any
SetVar(varName string, value any)
UnsafeSetVar(varName string, value any)
EnumVars(func(name string) (accept bool)) (varNames []string)
+60 -26
View File
@@ -22,18 +22,45 @@ Expressions calculator
toc::[]
#TODO: Work in progress (last update on 2024/06/02, 08:18 a.m.)#
#TODO: Work in progress (last update on 2024/06/17, 16:31 a.m.)#
== Expr
_Expr_ is a GO package capable of analysing, interpreting and calculating expressions.
=== Concepts and terminology
#TODO#
Expressions are texts containing sequences of operations represented by a syntax very similar to that of most programming languages. _Expr_ package provides these macro functions:
* *_Scanner_* -- Its input is a text. It scans expression text characters to produce a flow of logical symbol and related attributes, aka tokens.
* *_Parser_* -- Parser input is the token flow coming from the scanner. It analyses the token flow verifyng if it complies with the _Expr_ syntax. If that is the case, the Parser generates the Abstract Syntax Tree (AST). This is tree data structure that represents the components of an expressions and how they are related one each other.
* *_Calculator_*. Its input is the AST. It computes the parsed expression contained in the AST and returns the result or an error.
image::expression-diagram.png[]
==== Variables
_Expr_ supports variables. The result of an expression can be stored in a variable and reused in other espressions simply specifying the name of the variable as an operand.
==== Multi-expression
An input text valid for _Expr_ can contain more than an expression. Expressions are separated by [blue]`;` (semicolon). When an input contains two or more expressions it is called _multi-expression_.
_Expr_ parses and computes each expression of a multi-espression, from the left to the right. If all expressions are computed without errors, it only returns the value of the last, the right most.
The result of each expression of a multi-expression is stored in an automatic variable named _last_. In this way, each expression can refer to the result of the previous one without the need to assign that value to a new dedicated variable.
==== Calculation context
All objects, such as variables and functions, created during the calculation of an expression are stored in a memory called _context_.
The expression context is analogous to the stack-frame of other programming languages. When a function is called, a new context is allocated to store local definitions.
Function contexts are created by cloning the calling context. More details on this topic are given later in this document.
_Expr_ creates and keeps a inner _global context_ where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the _main context_. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The createt context can be called _function context_.
Imported functions are registerd in the _global context_. When an expression first calls an imported function, that function is linked to the current context; this can be the _main context_ or a _function context_.
=== `dev-expr` test tool
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provides an important aid for quickly testing of new features during their development.
`dev-expr` is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, `dev-expr` provided an important aid for quickly testing of new features during their development.
`dev-expr` can work as a _REPL_, _**R**ead-**E**xecute-**P**rint-**L**oop_, or it can process expression acquired from files or standard input.
@@ -47,11 +74,10 @@ Here are some examples of execution.
# Type 'exit' or Ctrl+D to quit the program.
[user]$ ./dev-expr
expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.10.0
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.19.0
Type help to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
>>> help
--- REPL commands:
source -- Load a file as input
@@ -61,6 +87,7 @@ expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@por
help -- Show command list
ml -- Enable/Disable multi-line output
mods -- List builtin modules
output -- Enable/Disable printing expression results. Options 'on', 'off', 'status'
--- Command line options:
-b <builtin> Import builtin modules.
@@ -70,9 +97,10 @@ expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@por
-i Force REPL operation when all -e occurences have been processed
-h, --help, help Show this help menu
-m, --modules List all builtin modules
--noout Disable printing of expression results
-p Print prefix form
-t Print tree form <2>
-v, --version Show program version
>>>
----
@@ -83,8 +111,8 @@ expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@por
[source,shell]
----
[user]$ ./dev-expr
expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.10.0
dev-expr -- Expressions calculator v1.10.0(build 14),2024/06/17 (celestino.amoroso@portale-stac.it)
Based on the Expr package v0.19.0
Type help to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
@@ -101,21 +129,21 @@ expr -- Expressions calculator v1.7.1(build 2),2024/05/16 (celestino.amoroso@por
7
-
6
>>> 1+2 but 5|2+0.5 <4>
>>> 4+2 but 5|2+0.5 <4>
3
>>> 1+2; 5|2+0.5 <5>
>>> 4+2; 5|2+0.5 <5>
3
>>>
----
<1> Number bases: 0x = hexadecimal, 0o = octal, 0b = binary.
<2> Fractions: numerator | denominator.
<1> Number bases: 0x = _hexadecimal_, 0o = _octal_, 0b = _binary_.
<2> Fractions: _numerator_ | _denominator_.
<3> Activate multi-line output of fractions.
<4> But operator, see <<_but_operator>>.
<5> Multi-expression: the same result of the previous single expression but this it is obtained with two separated calculations.
== Data types
_Expr_ supports numerical, string, relational, boolean expressions, and mixed-type lists.
_Expr_ has its type system which is a subset of Golang's type system. It supports numerical, string, relational, boolean expressions, and mixed-type lists and maps.
=== Numbers
_Expr_ supports three type of numbers:
@@ -272,25 +300,31 @@ Some arithmetic operators can also be used with strings.
| [blue]`*` | _repeat_ | Make _n_ copy of a string | [blue]`"one" * 2` -> _"oneone"_
|===
The items of strings can be accessed using the dot `.` operator.
The items of strings can be accessed using the square `[]` operator.
.Item access syntax
====
_item_ = _string-expr_ "**.**" _integer-expr_
_item_ = _string-expr_ "**[**" _integer-expr_ "**]**"
====
.Sub string syntax
====
_sub-string_ = _string-expr_ "**[**" _integer-expr_ "**:**" _integer-expr_ "**]**"
====
.String examples
`>>>` [blue]`s="abc"` [gray]_// assign the string to variable s_ +
[green]`abc` +
`>>>` [blue]`s.1` [gray]_// char at position 1 (starting from 0)_ +
[green]`b` +
`>>>` [blue]`s.(-1)` [gray]_// char at position -1, the rightmost one_ +
[green]`c` +
`>>>` [blue]`s="abcd"` [gray]_// assign the string to variable s_ +
[green]`"abcd"` +
`>>>` [blue]`s[1]` [gray]_// char at position 1 (starting from 0)_ +
[green]`"b"` +
`>>>` [blue]`s.[-1]` [gray]_// char at position -1, the rightmost one_ +
[green]`"d"` +
`>>>` [blue]`\#s` [gray]_// number of chars_ +
[gren]`3` +
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
[green]`3`
[gren]`4` +
`>>>` [blue]`#"abc"` [gray]_// number of chars_ +
[green]`3` +
`>>>` [blue]`s[1:3]` [gray]_// chars from position 1 to position 3 excluded_ +
[grean]`"bc"`
=== Booleans
Boolean data type has two values only: [blue]_true_ and [blue]_false_. Relational and boolean expressions result in boolean values.
+99 -33
View File
@@ -535,7 +535,13 @@ pre.rouge .ss {
<ul class="sectlevel1">
<li><a href="#_expr">1. Expr</a>
<ul class="sectlevel2">
<li><a href="#_concepts_and_terminology">1.1. Concepts and terminology</a></li>
<li><a href="#_concepts_and_terminology">1.1. Concepts and terminology</a>
<ul class="sectlevel3">
<li><a href="#_variables">1.1.1. Variables</a></li>
<li><a href="#_multi_expression">1.1.2. Multi-expression</a></li>
<li><a href="#_calculation_context">1.1.3. Calculation context</a></li>
</ul>
</li>
<li><a href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></li>
</ul>
</li>
@@ -554,7 +560,7 @@ pre.rouge .ss {
<li><a href="#_dictionaries">2.5. Dictionaries</a></li>
</ul>
</li>
<li><a href="#_variables">3. Variables</a></li>
<li><a href="#_variables_2">3. Variables</a></li>
<li><a href="#_other_operations">4. Other operations</a>
<ul class="sectlevel2">
<li><a href="#_operator">4.1. <code class="blue">;</code> operator</a></li>
@@ -585,7 +591,7 @@ pre.rouge .ss {
<div class="sectionbody">
<!-- toc disabled -->
<div class="paragraph">
<p><mark>TODO: Work in progress (last update on 2024/06/02, 08:18 a.m.)</mark></p>
<p><mark>TODO: Work in progress (last update on 2024/06/17, 16:31 a.m.)</mark></p>
</div>
</div>
</div>
@@ -598,18 +604,67 @@ pre.rouge .ss {
<div class="sect2">
<h3 id="_concepts_and_terminology"><a class="anchor" href="#_concepts_and_terminology"></a><a class="link" href="#_concepts_and_terminology">1.1. Concepts and terminology</a></h3>
<div class="paragraph">
<p><mark>TODO</mark></p>
<p>Expressions are texts containing sequences of operations represented by a syntax very similar to that of most programming languages. <em>Expr</em> package provides these macro functions:</p>
</div>
<div class="ulist">
<ul>
<li>
<p><strong><em>Scanner</em></strong>&#8201;&#8212;&#8201;Its input is a text. It scans expression text characters to produce a flow of logical symbol and related attributes, aka tokens.</p>
</li>
<li>
<p><strong><em>Parser</em></strong>&#8201;&#8212;&#8201;Parser input is the token flow coming from the scanner. It analyses the token flow verifyng if it complies with the <em>Expr</em> syntax. If that is the case, the Parser generates the Abstract Syntax Tree (AST). This is tree data structure that represents the components of an expressions and how they are related one each other.</p>
</li>
<li>
<p><strong><em>Calculator</em></strong>. Its input is the AST. It computes the parsed expression contained in the AST and returns the result or an error.</p>
</li>
</ul>
</div>
<div class="imageblock">
<div class="content">
<img src="expression-diagram.png" alt="expression diagram">
</div>
</div>
<div class="sect3">
<h4 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">1.1.1. Variables</a></h4>
<div class="paragraph">
<p><em>Expr</em> supports variables. The result of an expression can be stored in a variable and reused in other espressions simply specifying the name of the variable as an operand.</p>
</div>
</div>
<div class="sect3">
<h4 id="_multi_expression"><a class="anchor" href="#_multi_expression"></a><a class="link" href="#_multi_expression">1.1.2. Multi-expression</a></h4>
<div class="paragraph">
<p>An input text valid for <em>Expr</em> can contain more than an expression. Expressions are separated by <code class="blue">;</code> (semicolon). When an input contains two or more expressions it is called <em>multi-expression</em>.</p>
</div>
<div class="paragraph">
<p><em>Expr</em> parses and computes each expression of a multi-espression, from the left to the right. If all expressions are computed without errors, it only returns the value of the last, the right most.</p>
</div>
<div class="paragraph">
<p>The result of each expression of a multi-expression is stored in an automatic variable named <em>last</em>. In this way, each expression can refer to the result of the previous one without the need to assign that value to a new dedicated variable.</p>
</div>
</div>
<div class="sect3">
<h4 id="_calculation_context"><a class="anchor" href="#_calculation_context"></a><a class="link" href="#_calculation_context">1.1.3. Calculation context</a></h4>
<div class="paragraph">
<p>All objects, such as variables and functions, created during the calculation of an expression are stored in a memory called <em>context</em>.</p>
</div>
<div class="paragraph">
<p>The expression context is analogous to the stack-frame of other programming languages. When a function is called, a new context is allocated to store local definitions.</p>
</div>
<div class="paragraph">
<p>Function contexts are created by cloning the calling context. More details on this topic are given later in this document.</p>
</div>
<div class="paragraph">
<p><em>Expr</em> creates and keeps a inner <em>global context</em> where it stores imported functions, either from builtin or plugin modules. To perform calculations, the calling program must provide its own context; this is the <em>main context</em>. All calculations take place in this context. As mentioned eralier, when a function is called, a new context is created by cloning the calling context. The createt context can be called <em>function context</em>.</p>
</div>
<div class="paragraph">
<p>Imported functions are registerd in the <em>global context</em>. When an expression first calls an imported function, that function is linked to the current context; this can be the <em>main context</em> or a <em>function context</em>.</p>
</div>
</div>
</div>
<div class="sect2">
<h3 id="_dev_expr_test_tool"><a class="anchor" href="#_dev_expr_test_tool"></a><a class="link" href="#_dev_expr_test_tool">1.2. <code>dev-expr</code> test tool</a></h3>
<div class="paragraph">
<p><code>dev-expr</code> is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, <code>dev-expr</code> provides an important aid for quickly testing of new features during their development.</p>
<p><code>dev-expr</code> is a simple program that can be used to evaluate expressions interactively. As its name suggests, it was created for testing purpose. In fact, in additon to the automatic verification test suite based on the Go test framework, <code>dev-expr</code> provided an important aid for quickly testing of new features during their development.</p>
</div>
<div class="paragraph">
<p><code>dev-expr</code> can work as a <em>REPL</em>, <em><strong>R</strong>ead-<strong>E</strong>xecute-<strong>P</strong>rint-<strong>L</strong>oop</em>, or it can process expression acquired from files or standard input.</p>
@@ -626,11 +681,10 @@ pre.rouge .ss {
<pre class="rouge highlight"><code data-lang="shell"><span class="c"># Type 'exit' or Ctrl+D to quit the program.</span>
<span class="o">[</span>user]<span class="nv">$ </span>./dev-expr
<span class="nb">expr</span> <span class="nt">--</span> Expressions calculator v1.7.1<span class="o">(</span>build 2<span class="o">)</span>,2024/05/16 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
Based on the Expr package v0.10.0
dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o">(</span>build 14<span class="o">)</span>,2024/06/17 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
Based on the Expr package v0.19.0
Type <span class="nb">help </span>to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
<span class="o">&gt;&gt;&gt;</span> <span class="nb">help</span>
<span class="nt">---</span> REPL commands:
<span class="nb">source</span> <span class="nt">--</span> Load a file as input
@@ -640,6 +694,7 @@ pre.rouge .ss {
<span class="nb">help</span> <span class="nt">--</span> Show <span class="nb">command </span>list
ml <span class="nt">--</span> Enable/Disable multi-line output
mods <span class="nt">--</span> List <span class="nb">builtin </span>modules
output <span class="nt">--</span> Enable/Disable printing expression results. Options <span class="s1">'on'</span>, <span class="s1">'off'</span>, <span class="s1">'status'</span>
<span class="nt">---</span> Command line options:
<span class="nt">-b</span> &lt;<span class="nb">builtin</span><span class="o">&gt;</span> Import <span class="nb">builtin </span>modules.
@@ -649,9 +704,10 @@ pre.rouge .ss {
<span class="nt">-i</span> Force REPL operation when all <span class="nt">-e</span> occurences have been processed
<span class="nt">-h</span>, <span class="nt">--help</span>, <span class="nb">help </span>Show this <span class="nb">help </span>menu
<span class="nt">-m</span>, <span class="nt">--modules</span> List all <span class="nb">builtin </span>modules
<span class="nt">--noout</span> Disable printing of expression results
<span class="nt">-p</span> Print prefix form
<span class="nt">-t</span> Print tree form <i class="conum" data-value="2"></i><b>(2)</b>
<span class="nt">-v</span>, <span class="nt">--version</span> Show program version
<span class="o">&gt;&gt;&gt;</span></code></pre>
</div>
</div>
@@ -671,8 +727,8 @@ pre.rouge .ss {
<div class="title">REPL examples</div>
<div class="content">
<pre class="rouge highlight"><code data-lang="shell"><span class="o">[</span>user]<span class="nv">$ </span>./dev-expr
<span class="nb">expr</span> <span class="nt">--</span> Expressions calculator v1.7.1<span class="o">(</span>build 2<span class="o">)</span>,2024/05/16 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
Based on the Expr package v0.10.0
dev-expr <span class="nt">--</span> Expressions calculator v1.10.0<span class="o">(</span>build 14<span class="o">)</span>,2024/06/17 <span class="o">(</span>celestino.amoroso@portale-stac.it<span class="o">)</span>
Based on the Expr package v0.19.0
Type <span class="nb">help </span>to get the list of available commands
See also https://git.portale-stac.it/go-pkg/expr/src/branch/main/README.adoc
@@ -689,9 +745,9 @@ pre.rouge .ss {
7
-
6
<span class="o">&gt;&gt;&gt;</span> 1+2 but 5|2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
<span class="o">&gt;&gt;&gt;</span> 4+2 but 5|2+0.5 <i class="conum" data-value="4"></i><b>(4)</b>
3
<span class="o">&gt;&gt;&gt;</span> 1+2<span class="p">;</span> 5|2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
<span class="o">&gt;&gt;&gt;</span> 4+2<span class="p">;</span> 5|2+0.5 <i class="conum" data-value="5"></i><b>(5)</b>
3
<span class="o">&gt;&gt;&gt;</span></code></pre>
</div>
@@ -700,11 +756,11 @@ pre.rouge .ss {
<table>
<tr>
<td><i class="conum" data-value="1"></i><b>1</b></td>
<td>Number bases: 0x = hexadecimal, 0o = octal, 0b = binary.</td>
<td>Number bases: 0x = <em>hexadecimal</em>, 0o = <em>octal</em>, 0b = <em>binary</em>.</td>
</tr>
<tr>
<td><i class="conum" data-value="2"></i><b>2</b></td>
<td>Fractions: numerator | denominator.</td>
<td>Fractions: <em>numerator</em> | <em>denominator</em>.</td>
</tr>
<tr>
<td><i class="conum" data-value="3"></i><b>3</b></td>
@@ -727,7 +783,7 @@ pre.rouge .ss {
<h2 id="_data_types"><a class="anchor" href="#_data_types"></a><a class="link" href="#_data_types">2. Data types</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><em>Expr</em> supports numerical, string, relational, boolean expressions, and mixed-type lists.</p>
<p><em>Expr</em> has its type system which is a subset of Golang&#8217;s type system. It supports numerical, string, relational, boolean expressions, and mixed-type lists and maps.</p>
</div>
<div class="sect2">
<h3 id="_numbers"><a class="anchor" href="#_numbers"></a><a class="link" href="#_numbers">2.1. Numbers</a></h3>
@@ -1011,28 +1067,38 @@ pre.rouge .ss {
</tbody>
</table>
<div class="paragraph">
<p>The items of strings can be accessed using the dot <code>.</code> operator.</p>
<p>The items of strings can be accessed using the square <code>[]</code> operator.</p>
</div>
<div class="exampleblock">
<div class="title">Example 4. Item access syntax</div>
<div class="content">
<div class="paragraph">
<p><em>item</em> = <em>string-expr</em> "<strong>.</strong>" <em>integer-expr</em></p>
<p><em>item</em> = <em>string-expr</em> "<strong>[</strong>" <em>integer-expr</em> "<strong>]</strong>"</p>
</div>
</div>
</div>
<div class="exampleblock">
<div class="title">Example 5. Sub string syntax</div>
<div class="content">
<div class="paragraph">
<p><em>sub-string</em> = <em>string-expr</em> "<strong>[</strong>" <em>integer-expr</em> "<strong>:</strong>" <em>integer-expr</em> "<strong>]</strong>"</p>
</div>
</div>
</div>
<div class="paragraph">
<div class="title">String examples</div>
<p><code>&gt;&gt;&gt;</code> <code class="blue">s="abc"</code> <em class="gray">// assign the string to variable s</em><br>
<code class="green">abc</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s.1</code> <em class="gray">// char at position 1 (starting from 0)</em><br>
<code class="green">b</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s.(-1)</code> <em class="gray">// char at position -1, the rightmost one</em><br>
<code class="green">c</code><br>
<p><code>&gt;&gt;&gt;</code> <code class="blue">s="abcd"</code> <em class="gray">// assign the string to variable s</em><br>
<code class="green">"abcd"</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s[1]</code> <em class="gray">// char at position 1 (starting from 0)</em><br>
<code class="green">"b"</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s.[-1]</code> <em class="gray">// char at position -1, the rightmost one</em><br>
<code class="green">"d"</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#s</code> <em class="gray">// number of chars</em><br>
<code class="gren">3</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#"abc"</code> <em class="gray">// number of chars</em><br>
<code class="green">3</code></p>
<code class="gren">4</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">#"abc"</code> <em class="gray">// number of chars</em><br>
<code class="green">3</code><br>
<code>&gt;&gt;&gt;</code> <code class="blue">s[1:3]</code> <em class="gray">// chars from position 1 to position 3 excluded</em><br>
<code class="grean">"bc"</code></p>
</div>
</div>
<div class="sect2">
@@ -1179,7 +1245,7 @@ pre.rouge .ss {
<p><em>Expr</em> supports list of mixed-type values, also specified by normal expressions. Internally, <em>Expr</em>'s lists are Go arrays.</p>
</div>
<div class="exampleblock">
<div class="title">Example 5. List literal syntax</div>
<div class="title">Example 6. List literal syntax</div>
<div class="content">
<div class="paragraph">
<p><strong><em>list</em></strong> = <em>empty-list</em> | <em>non-empty-list</em><br>
@@ -1261,7 +1327,7 @@ pre.rouge .ss {
<p>The items of array can be accessed using the dot <code>.</code> operator.</p>
</div>
<div class="exampleblock">
<div class="title">Example 6. Item access syntax</div>
<div class="title">Example 7. Item access syntax</div>
<div class="content">
<div class="paragraph">
<p><em>item</em> = <em>list-expr</em> "<strong>.</strong>" <em>integer-expr</em></p>
@@ -1297,7 +1363,7 @@ pre.rouge .ss {
<p>Dictionary literals are sequences of pairs separated by comma <code class="blue">,</code> enclosed between brace brackets.</p>
</div>
<div class="exampleblock">
<div class="title">Example 7. Dict literal syntax</div>
<div class="title">Example 8. Dict literal syntax</div>
<div class="content">
<div class="paragraph">
<p><strong><em>dict</em></strong> = <em>empty-dict</em> | <em>non-empty-dict</em><br>
@@ -1357,13 +1423,13 @@ pre.rouge .ss {
</div>
</div>
<div class="sect1">
<h2 id="_variables"><a class="anchor" href="#_variables"></a><a class="link" href="#_variables">3. Variables</a></h2>
<h2 id="_variables_2"><a class="anchor" href="#_variables_2"></a><a class="link" href="#_variables_2">3. Variables</a></h2>
<div class="sectionbody">
<div class="paragraph">
<p><em>Expr</em>, like most programming languages, supports variables. A variable is an identifier with an assigned value. Variables are stored in <em>contexts</em>.</p>
</div>
<div class="exampleblock">
<div class="title">Example 8. Variable literal syntax</div>
<div class="title">Example 9. Variable literal syntax</div>
<div class="content">
<div class="paragraph">
<p><strong><em>variable</em></strong> = <em>identifier</em> "<strong>=</strong>" <em>any-value</em><br>
@@ -1884,7 +1950,7 @@ These operators have a high priority, in particular higher than the operator <co
</div>
<div id="footer">
<div id="footer-text">
Last updated 2024-06-03 06:26:03 +0200
Last updated 2024-06-19 09:33:41 +0200
</div>
</div>
</body>
+39
View File
@@ -199,3 +199,42 @@ func (info *funcInfo) MaxArgs() int {
func (info *funcInfo) Functor() Functor {
return info.functor
}
// ----- Call a function ---
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
if info, exists, owner := GetFuncInfo(ctx, name); exists {
passedCount := len(*varParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
}
for i, p := range info.Params() {
if i >= passedCount {
if !p.IsDefault() {
break
}
*varParams = append(*varParams, p.DefaultValue())
}
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
}
if err == nil && owner != ctx {
ctx.RegisterFuncInfo(info)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunction(parentCtx ExprContext, name string, params []any) (result any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
if err = checkFunctionCall(ctx, name, &params); err == nil {
result, err = ctx.Call(name, params)
exportObjectsToParent(ctx)
}
return
}
+2 -35
View File
@@ -6,7 +6,6 @@ package expr
import (
"errors"
"fmt"
)
// -------- function call term
@@ -22,35 +21,7 @@ func newFuncCallTerm(tk *Token, args []*term) *term {
}
// -------- eval func call
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) {
if info, exists, owner := GetFuncInfo(ctx, name); exists {
passedCount := len(*varParams)
if info.MinArgs() > passedCount {
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
}
for i, p := range info.Params() {
if i >= passedCount {
if !p.IsDefault() {
break
}
*varParams = append(*varParams, p.DefaultValue())
}
}
if err == nil && info.MaxArgs() >= 0 && info.MaxArgs() < len(*varParams) {
err = ErrTooMuchParams(name, info.MaxArgs(), len(*varParams))
}
if err == nil && owner != ctx {
ctx.RegisterFuncInfo(info)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
ctx := cloneContext(parentCtx)
ctx.SetParent(parentCtx)
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) {
name, _ := self.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5)
for i, tree := range self.children {
@@ -62,11 +33,7 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) {
}
if err == nil {
if err = checkFunctionCall(ctx, name, &params); err == nil {
if v, err = ctx.Call(name, params); err == nil {
exportObjects(parentCtx, ctx)
}
}
v, err = CallFunction(ctx, name, params)
}
return
}
+30 -3
View File
@@ -40,11 +40,14 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
v, _ := ((*indexList)[0]).(*intPair)
startIndex = v.a
endIndex = v.b
if endIndex == ConstLastIndex {
endIndex = maxValue
}
if startIndex < 0 && startIndex >= -maxValue {
startIndex = maxValue + startIndex
}
if endIndex < 0 && endIndex >= -maxValue {
endIndex = maxValue + endIndex + 1
endIndex = maxValue + endIndex
}
if startIndex < 0 || startIndex > maxValue {
err = indexTerm.Errorf("range start-index %d is out of bounds", startIndex)
@@ -87,13 +90,14 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = string(unboxedValue[index])
}
case *DictType:
var ok bool
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*unboxedValue)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}
} */
v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default:
err = self.errIncompatibleTypes(leftValue, rightValue)
}
@@ -113,6 +117,29 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
default:
err = self.errIncompatibleTypes(leftValue, rightValue)
}
} else if IsDict(leftValue) {
d := leftValue.(*DictType)
/* var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}*/
v, err = getDictItem(d, indexTerm, indexList, rightValue)
}
return
}
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) {
var ok bool
var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
}
}
return
}
+2 -2
View File
@@ -29,7 +29,7 @@ func newRangeTerm(tk *Token) (inst *term) {
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDot,
priority: priRange,
evalFunc: evalRange,
}
}
@@ -47,7 +47,7 @@ func evalRange(ctx ExprContext, self *term) (v any, err error) {
if leftValue, err = self.children[0].compute(ctx); err != nil {
return
}
rightValue = int64(-1)
rightValue = int64(ConstLastIndex)
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
return
}
+13 -1
View File
@@ -325,6 +325,14 @@ func couldBeACollection(t *term) bool {
return sym == SymList || sym == SymString || sym == SymDict || sym == SymExpression || sym == SymVariable
}
// func areSymbolsOutOfCtx(tk *Token, ctxTerm *term, syms ...Symbol) bool {
// var areOut = false
// if ctxTerm != nil {
// areOut = tk.IsOneOf(syms)
// }
// return areOut
// }
func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef bool, termSymbols ...Symbol) (tree *ast, err error) {
var selectorTerm *term = nil
var currentTerm *term = nil
@@ -332,7 +340,7 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
tree = NewAst()
firstToken := true
// lastSym := SymUnknown
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); tk = scanner.Next() {
for tk = scanner.Next(); err == nil && tk != nil && !tk.IsTerm(termSymbols); /*&& !areSymbolsOutOfCtx(tk, selectorTerm, SymColon, SymDoubleColon)*/ tk = scanner.Next() {
if tk.Sym == SymComment {
continue
}
@@ -438,6 +446,10 @@ func (self *parser) parseGeneral(scanner *scanner, allowForest bool, allowVarRef
} else {
currentTerm, err = tree.addToken2(tk)
}
if tk.IsSymbol(SymColon) {
// Colon outside a selector term acts like a separator
firstToken = true
}
default:
currentTerm, err = tree.addToken2(tk)
}
+14 -1
View File
@@ -6,7 +6,9 @@ package expr
import (
"fmt"
"os"
"plugin"
"strings"
)
var pluginRegister map[string]*plugin.Plugin
@@ -25,6 +27,17 @@ func pluginExists(name string) (exists bool) {
return
}
func makePluginName(name string) (decorated string) {
var template string
if execName, err := os.Executable(); err != nil || strings.Index(execName, "debug") < 0 {
template = "expr-%s-plugin.so"
} else {
template = "expr-%s-plugin.so.debug"
}
decorated = fmt.Sprintf(template, name)
return
}
func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err error) {
var filePath string
var p *plugin.Plugin
@@ -33,7 +46,7 @@ func importPlugin( /*ctx ExprContext,*/ dirList []string, name string) (err erro
var importFunc func(ExprContext)
var ok bool
decoratedName := fmt.Sprintf("expr-%s-plugin.so", name)
decoratedName := makePluginName(name)
if filePath, err = makeFilepath(decoratedName, dirList); err != nil {
return
+7 -1
View File
@@ -7,7 +7,7 @@ package expr
import (
"fmt"
"slices"
// "strings"
// "strings"
)
type SimpleStore struct {
@@ -50,6 +50,7 @@ func (ctx *SimpleStore) Merge(src ExprContext) {
ctx.funcStore[name], _ = src.GetFuncInfo(name)
}
}
/*
func varsCtxToBuilder(sb *strings.Builder, ctx ExprContext, indent int) {
sb.WriteString("vars: {\n")
@@ -165,6 +166,11 @@ func (ctx *SimpleStore) GetVar(varName string) (v any, exists bool) {
return
}
func (ctx *SimpleStore) GetLast() (v any) {
v = ctx.varStore["last"]
return
}
func (ctx *SimpleStore) UnsafeSetVar(varName string, value any) {
// fmt.Printf("[%p] setVar(%v, %v)\n", ctx, varName, value)
ctx.varStore[varName] = value
+4 -4
View File
@@ -16,10 +16,10 @@ func TestFuncOs(t *testing.T) {
/* 2 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle)`, true, nil},
/* 3 */ {`builtin "os.file"; handle=fileOpen("/etc/hostsX")`, nil, errors.New(`open /etc/hostsX: no such file or directory`)},
/* 4 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle)`, true, nil},
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWrite(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileRead(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=fileRead(nil, "-")`, nil, errors.New(`readFileFunc(): invalid file handle`)},
/* 7 */ {`builtin "os.file"; fileWrite(nil, "bye")`, nil, errors.New(`writeFileFunc(): invalid file handle`)},
/* 5 */ {`builtin "os.file"; handle=fileAppend("/tmp/dummy"); fileWriteText(handle, "bye-bye"); fileClose(handle)`, true, nil},
/* 6 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); word=fileReadText(handle, "-"); fileClose(handle);word`, "bye", nil},
/* 7 */ {`builtin "os.file"; word=fileReadText(nil, "-")`, nil, errors.New(`fileReadText(): invalid file handle`)},
/* 7 */ {`builtin "os.file"; fileWriteText(nil, "bye")`, nil, errors.New(`fileWriteText(): invalid file handle`)},
}
// t.Setenv("EXPR_PATH", ".")
+1
View File
@@ -31,6 +31,7 @@ func TestDictParser(t *testing.T) {
/* 8 */ {`D={"a":1, "b":2}; D["a"]=9; D`, map[any]any{"a": 9, "b": 2}, nil},
/* 9 */ {`D={"a":1, "b":2}; D["z"]=9; D`, map[any]any{"z": 9, "a": 1, "b": 2}, nil},
/* 10 */ {`D={"a":1, "b":2}; D[nil]=9`, nil, errors.New(`[1:21] index/key is nil`)},
/* 11 */ {`D={"a":1, "b":2}; D["a"]`, int64(1), nil},
}
succeeded := 0
+1 -1
View File
@@ -14,7 +14,7 @@ func TestExpr(t *testing.T) {
inputs := []inputType{
/* 1 */ {`0?{}`, nil, nil},
/* 2 */ {`fact=func(n){(n)?{1}::{n*fact(n-1)}}; fact(5)`, int64(120), nil},
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileRead(f); fileClose(f); line`, "uno", nil},
/* 3 */ {`builtin "os.file"; f=fileOpen("test-file.txt"); line=fileReadText(f); fileClose(f); line`, "uno", nil},
/* 4 */ {`mynot=func(v){int(v)?{true}::{false}}; mynot(0)`, true, nil},
/* 5 */ {`1 ? {1} : [1+0] {3*(1+1)}`, int64(6), nil},
/* 6 */ {`
+3 -3
View File
@@ -14,9 +14,9 @@ func TestIteratorParser(t *testing.T) {
/* 4 */ {`include "test-resources/iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil},
/* 5 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil},
/* 6 */ {`builtin "math.arith"; include "test-resources/iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil},
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil},
/* 7 */ {`builtin "math.arith"; include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); mul(it)`, int64(12000), nil},
/* 8 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it++; it.index`, int64(0), nil},
/* 10 */ {`include "test-resources/file-reader.expr"; it=$(ds,"test-resources/int.list"); it.clean`, true, nil},
/* 11 */ {`it=$(1,2,3); it++`, int64(1), nil},
}
// inputs1 := []inputType{
+4 -2
View File
@@ -49,8 +49,10 @@ func TestListParser(t *testing.T) {
/* 35 */ {`L=[1,2]; L[5]=9; L`, nil, errors.New(`index 5 out of bounds (0, 1)`)},
/* 36 */ {`L=[1,2]; L[]=9; L`, nil, errors.New(`[1:12] index/key specification expected, got [] [list]`)},
/* 37 */ {`L=[1,2]; L[nil]=9;`, nil, errors.New(`[1:12] index/key is nil`)},
// /* 8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
// /* 9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
/* 38 */ {`[0,1,2,3,4][2:3]`, newListA(int64(2)), nil},
/* 39 */ {`[0,1,2,3,4][3:-1]`, newListA(int64(3)), nil},
/* 40 */ {`[0,1,2,3,4][-3:-1]`, newListA(int64(2), int64(3)), nil},
/* 41 */ {`[0,1,2,3,4][0:]`, newListA(int64(0), int64(1), int64(2), int64(3), int64(4)), nil},
}
// t.Setenv("EXPR_PATH", ".")
+10 -11
View File
@@ -1,23 +1,22 @@
builtin ["os.file", "base"];
readInt=func(fh){
line=fileRead(fh);
line=fileReadText(fh);
line ? [nil] {nil} :: {int(line)}
};
ds={
"init":func(filename){
fh=fileOpen(filename);
fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current };
fh ? [nil] {nil} :: { @current=readInt(fh) };
fh
},
"current":func(){
prev
current
},
"next":func(fh){
current ?
[nil] {current}
:: {@prev=current; @current=readInt(fh) but current}
@current=readInt(fh);
current
},
"clean":func(fh){
fileClose(fh)
@@ -25,9 +24,9 @@ ds={
}
//;f=$(ds, "int.list")
/*
;f++
;f++
;f++
*/
//;f++
//;f++
//;f++
//*/
//;add(f)
+5 -1
View File
@@ -60,7 +60,11 @@ func (tk *Token) IsError() bool {
}
func (tk *Token) IsTerm(termSymbols []Symbol) bool {
return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0)
return tk.IsEos() || tk.IsError() || tk.IsOneOf(termSymbols)
}
func (tk *Token) IsOneOf(termSymbols []Symbol) bool {
return termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0
}
func (tk *Token) IsSymbol(sym Symbol) bool {