Compare commits

...

6 Commits

18 changed files with 1074 additions and 242 deletions
+21 -15
View File
@@ -255,18 +255,25 @@ func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any
return
}
// func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
// var varName string
// var ok bool
func charFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var ord int
// if varName, ok = args[kern.ParamName].(string); !ok {
// return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
// } else {
// ctx.GetParent().DeleteVar(varName)
// result = nil
// }
// return
// }
if n, ok := args[kern.ParamValue].(byte); ok {
ord = int(n)
} else if n, ok := args[kern.ParamValue].(int64); ok {
ord = int(n)
} else if n, ok := args[kern.ParamValue].(int); ok {
ord = n
} else {
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
}
if ord < 0 || ord > 255 {
err = kern.ErrFuncInvalidArg(name, fmt.Sprintf("character code must be in range 0-255, got %d", ord))
} else {
result = string(rune(ord))
}
return
}
//// import
@@ -308,10 +315,9 @@ func ImportBuiltinsFuncs(ctx kern.ExprContext) {
kern.NewFuncParam(kern.ParamValue),
})
// ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
// kern.NewFuncParam(kern.ParamName),
// kern.NewFuncParam(kern.ParamValue),
// })
ctx.RegisterFunc("char", kern.NewGolangFunctor(charFunc), kern.TypeString, []kern.ExprFuncParam{
kern.NewFuncParam(kern.ParamValue),
})
}
func init() {
+102
View File
@@ -0,0 +1,102 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const fileByteIteratorType = "fileByteIterator"
type fileFileByteIterator struct {
fileIterBase
b byte
}
func newFileByteIterator(r *file.Reader, autoClose bool) *fileFileByteIterator {
return &fileFileByteIterator{
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
b: 0}
}
func (it *fileFileByteIterator) TypeName() string {
return fileByteIteratorType
}
func (it *fileFileByteIterator) String() string {
return it.repr(fileByteIteratorType)
}
func (it *fileFileByteIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.b, err = it.reader.ReadByte(); err == nil {
it.increment()
item = it.b
} else if it.autoClose {
it.Clean()
}
return
}
func (it *fileFileByteIterator) Current() (item any, err error) {
item = it.b
return
}
func (it *fileFileByteIterator) Reset() (err error) {
if err = it.reader.Reset(); err == nil {
it.reset()
it.b = 0
}
return
}
func (it *fileFileByteIterator) Clean() (err error) {
if it.reader.Valid() {
if err = it.reader.GetFile().Close(); err == nil {
it.reader = nil
}
}
it.reset()
it.b = 0
return
}
func (it *fileFileByteIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
}
func fileByteIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *file.Reader
var invalidFileHandle any
var autoClose bool
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
if handle != nil {
result = newFileByteIterator(handle, autoClose)
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const fileLineIteratorType = "fileLineIterator"
type fileFileLineIterator struct {
fileIterBase
line string
}
func newFileLineIterator(r *file.Reader, autoClose bool) *fileFileLineIterator {
return &fileFileLineIterator{
fileIterBase: fileIterBase{reader: r, index: -1, count: 0, autoClose: autoClose},
line: "",
}
}
func (it *fileFileLineIterator) TypeName() string {
return fileLineIteratorType
}
func (it *fileFileLineIterator) String() string {
return it.repr(fileLineIteratorType)
}
func (it *fileFileLineIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.line, err = it.reader.ReadString('\n'); err == nil {
it.increment()
item = it.line[0 : len(it.line)-1]
} else if it.autoClose {
it.Clean()
}
return
}
func (it *fileFileLineIterator) Current() (item any, err error) {
if len(it.line) > 0 {
item = it.line[0 : len(it.line)-1]
}
return
}
func (it *fileFileLineIterator) Reset() (err error) {
if err = it.reader.Reset(); err == nil {
it.reset()
it.line = ""
}
return
}
func (it *fileFileLineIterator) Clean() (err error) {
if it.reader != nil {
if err = it.reader.Close(); err == nil {
it.reader = nil
}
}
it.reset()
it.line = ""
return
}
func (it *fileFileLineIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
}
func fileLineIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *file.Reader
var invalidFileHandle any
var autoClose bool
if handle, invalidFileHandle, autoClose, err = initFileHandle(ctx, name, args); err == nil {
if handle != nil {
result = newFileLineIterator(handle, autoClose)
}
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
+23 -103
View File
@@ -6,146 +6,66 @@ package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
const paramHandleOrPath = "handle-or-path"
const fileReadTextIteratorType = "fileReadTextIterator"
type fileReadTextIterator struct {
osReader *osReader
type fileIterBase struct {
reader *file.Reader
index int64
count int64
line string
autoClose bool
}
func newReadTextIterator(r *osReader, autoClose bool) *fileReadTextIterator {
return &fileReadTextIterator{osReader: r, index: -1, autoClose: autoClose}
}
func (it *fileReadTextIterator) TypeName() string {
return fileReadTextIteratorType
}
func (it *fileReadTextIterator) String() string {
if it.osReader != nil && it.osReader.fh != nil {
return fmt.Sprintf("$(%s@%q)", fileReadTextIteratorType, it.osReader.fh.Name())
}
return fmt.Sprintf("$(%s@<nil>)", fileReadTextIteratorType)
}
func (it *fileReadTextIterator) Count() int64 {
func (it *fileIterBase) Count() int64 {
return it.count
}
func (it *fileReadTextIterator) Next() (item any, err error) { // must return io.EOF after the last item
if it.osReader.fh != nil {
if it.line, err = it.osReader.reader.ReadString('\n'); err == nil {
it.index++
it.count++
item = it.line[0 : len(it.line)-1]
} else if it.autoClose {
it.Clean()
}
}
return
}
func (it *fileReadTextIterator) Current() (item any, err error) {
if len(it.line) > 0 {
item = it.line[0 : len(it.line)-1]
}
return
}
func (it *fileReadTextIterator) Index() int64 {
func (it *fileIterBase) Index() int64 {
return it.index
}
func (it *fileReadTextIterator) Reset() (err error) {
if _, err = it.osReader.fh.Seek(0, io.SeekStart); err == nil {
it.index = -1
it.count = 0
it.line = ""
}
return
}
func (it *fileReadTextIterator) HasOperation(name string) bool {
func (it *fileIterBase) HasOperation(name string) bool {
return slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName}, name)
}
func (it *fileReadTextIterator) Clean() (err error) {
if it.osReader.fh != nil {
if err = it.osReader.fh.Close(); err == nil {
it.osReader = nil
}
}
return nil
func (it *fileIterBase) reset() {
it.index = -1
it.count = 0
}
func (it *fileReadTextIterator) CallOperation(name string, args map[string]any) (v any, err error) {
switch name {
case kern.NextName:
v, err = it.Next()
case kern.ResetName:
err = it.Reset()
case kern.CleanName:
err = it.Clean()
case kern.IndexName:
v = int64(it.Index())
case kern.CurrentName:
v, err = it.Current()
case kern.CountName:
v = it.count
default:
err = kern.ErrNoOperation(name)
}
return
func (it *fileIterBase) increment() {
it.index++
it.count++
}
func fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *osReader
var invalidFileHandle any
var ok, autoClose bool
func (it *fileIterBase) repr(typeName string) string {
if it.reader.Valid() {
return fmt.Sprintf("$(%s@%q)", typeName, it.reader.GetName())
}
return fmt.Sprintf("$(%s@<nil>)", typeName)
}
result = nil
if handle, ok = args[paramHandleOrPath].(*osReader); !ok {
func initFileHandle(ctx kern.ExprContext, name string, args map[string]any) (handle *file.Reader, invalidFileHandle any, autoClose bool, err error) {
var ok bool
if handle, ok = args[paramHandleOrPath].(*file.Reader); !ok {
if fileName, ok := args[paramHandleOrPath].(string); ok && len(fileName) > 0 {
var handleAny any
if handleAny, err = openFileFunc(ctx, name, map[string]any{kern.ParamFilepath: fileName}); err != nil {
return
}
if handleAny != nil {
handle = handleAny.(*osReader)
handle = handleAny.(*file.Reader)
autoClose = true
}
} else {
invalidFileHandle = args[paramHandleOrPath]
}
}
if handle != nil {
result = newReadTextIterator(handle, autoClose)
}
if err == nil && (handle == nil || invalidFileHandle != nil) {
err = errInvalidFileHandle(name, invalidFileHandle)
}
return
}
// func ImportOsIterFuncs(ctx ExprContext) {
// ctx.RegisterFunc("fileReadIterator", NewGolangFunctor(fileReadIteratorFunc), TypeIterator, []ExprFuncParam{
// NewFuncParam(paramHandleOrPath),
// })
// }
// func init() {
// RegisterBuiltinModule("os.file", ImportOsIterFuncs, "Operating system file iterator functions")
// }
+42 -72
View File
@@ -5,11 +5,10 @@
package expr
import (
"bufio"
"fmt"
"io"
"os"
"git.portale-stac.it/go-pkg/expr/file"
"git.portale-stac.it/go-pkg/expr/kern"
)
@@ -17,44 +16,6 @@ const (
osLimitCh = "limitCh"
)
type osHandle interface {
getFile() *os.File
}
type osWriter struct {
fh *os.File
writer *bufio.Writer
}
func (h *osWriter) TypeName() string {
return "osWriter"
}
func (h *osWriter) String() string {
return "writer"
}
func (h *osWriter) getFile() *os.File {
return h.fh
}
type osReader struct {
fh *os.File
reader *bufio.Reader
}
func (h *osReader) TypeName() string {
return "osReader"
}
func (h *osReader) String() string {
return "reader"
}
func (h *osReader) getFile() *os.File {
return h.fh
}
func errMissingFilePath(funcName string) error {
return fmt.Errorf("%s(): missing or invalid file path", funcName)
}
@@ -67,24 +28,26 @@ func errInvalidFileHandle(funcName string, v any) error {
}
}
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
}
// var fh *os.File
// if fh, err = os.Open(filePath); err == nil {
// result = file.NewReader(fh)
// }
result, err = file.OpenReader(filePath)
} else {
err = errMissingFilePath(name)
}
return
}
func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
}
// var fh *os.File
// if fh, err = os.Create(filePath); err == nil {
// result = file.NewWriter(fh)
// }
result, err = file.CreateWriter(filePath)
} else {
err = errMissingFilePath(name)
}
@@ -93,10 +56,11 @@ func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (resul
func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
}
// var fh *os.File
// if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil {
// result = file.NewWriter(fh)
// }
result, err = file.AppendWriter(filePath)
} else {
err = errMissingFilePath(name)
}
@@ -104,18 +68,18 @@ func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (res
}
func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var handle file.Handle
var invalidFileHandle any
var ok bool
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if fh := handle.getFile(); fh != nil {
if w, ok := handle.(*osWriter); ok {
err = w.writer.Flush()
if fh := handle.GetFile(); fh != nil {
if w, ok := handle.(*file.Writer); ok {
err = w.Flush()
}
if err == nil {
@@ -131,19 +95,20 @@ func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (resu
}
func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var handle file.Handle
var invalidFileHandle any
var ok bool
if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if w, ok := handle.(*osWriter); ok {
if w, ok := handle.(*file.Writer); ok {
if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...)
// result, err = fmt.Fprint(w.writer, argv...)
result, err = w.Write(argv...)
}
} else {
invalidFileHandle = handle
@@ -157,24 +122,24 @@ func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (
}
func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var handle file.Handle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
if r, ok := handle.(*file.Reader); ok {
var limit byte = '\n'
var v string
if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
limit = s[0]
}
v, err = r.reader.ReadString(limit)
v, err = r.ReadString(limit)
if err == io.EOF {
err = nil
}
@@ -197,19 +162,19 @@ func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (r
}
func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle
var handle file.Handle
var invalidFileHandle any
var ok bool
result = nil
if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
if handle, ok = args[kern.ParamHandle].(file.Handle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[kern.ParamHandle]
}
if handle != nil {
if r, ok := handle.(*osReader); ok {
if r, ok := handle.(*file.Reader); ok {
var b []byte
b, err = io.ReadAll(r.reader)
b, err = r.ReadAll()
result = string(b)
} else {
invalidFileHandle = handle
@@ -253,9 +218,14 @@ func ImportOsFuncs(ctx kern.ExprContext) {
kern.NewFuncParam(kern.ParamHandle),
})
ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
ctx.RegisterFunc("fileLineIterator", kern.NewGolangFunctor(fileLineIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
kern.NewFuncParam(paramHandleOrPath),
})
ctx.RegisterFunc("fileByteIterator", kern.NewGolangFunctor(fileByteIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
kern.NewFuncParam(paramHandleOrPath),
})
}
func init() {
+87 -38
View File
@@ -34,7 +34,7 @@ Expressions calculator
toc::[]
#TODO: Work in progress (last update on 2026/04/21, 6:49 p.m.)#
#TODO: Work in progress (last update on 2026/05/08)#
== Expr
_Expr_ is a GO package that can analyze, interpret and calculate expressions.
@@ -698,9 +698,9 @@ The value on the left side of [blue]`=` must be a variable identifier or an expr
=== Selector operator [blue]`? : ::`
The _selector operator_ is very similar to the _switch/case/default_ statement available in many programming languages.
.Selector literal Syntax
.Selector literal syntax
====
_selector-operator_ = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
*_selector-operator_* = _select-expression_ "*?*" _selector-case_ { "*:*" _selector-case_ } ["*::*" _default-multi-expression_] +
_selector-case_ = [_match-list_] _case-value_ +
_match-list_ = "*[*" _item_ {"*,*" _items_} "*]*" +
_item_ = _expression_ +
@@ -737,19 +737,38 @@ The [blue]`:` symbol (colon) is the separator of the selector-cases. Note that i
`>>>` [blue]`10 ? {"a"} : {"b"}` +
[red]`Eval Error: [1:3] no case catches the value (10) of the selection expression`
==== Triple special case of the selector operator
If the _select-expression_ is a boolean expression, the selector operator can be used as a sort of _if-then-else_ statement. In this case, the first case is evaluated if the _select-expression_ is true, and the second case is evaluated if the _select-expression_ is false. In this special case, the _match-list_ of both cases must be empty.
.Example
`>>>` [blue]`(true) ? {"T"}: {"F"}` +
[green]`T` +
`>>>` [blue]`(2 > 1) ? {"a"} : {"b"}` +
[green]`a` +
`>>>` [blue]`(2 < 1) ? {"a"} : {"b"}` +
[green]`b`
[WARNING]
====
The triple special case of the selector operator is very useful, but it only works with boolean expressions.
.Example of confusion
`>>>` [blue]`int(true) ? {"T"}: {"F"}` +
[green]`F`
====
=== Variable default value [blue]`??`, [blue]`?=`, and [blue]`?!`
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operator can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
The left operand of the first two operators, [blue]`??` and [blue]`?=`, must be a variable. The right operatand can be any expression. They return the value of the variable if this is defined; otherwise they return the value of the right expression.
IMPORTANT: If the left variable is defined, the right expression is not evaluated at all.
The [blue]`??` operator do not change the status of the left variable.
The [blue]`?=` assigns the calculated value of the right expression to the left variable.
The [blue]`?=` assigns the calculated value of the right expression to the variable on the left side.
The third one, [blue]`?!`, is the alternate operator. If the variable on the left size is not defined, it returns [blue]_nil_. Otherwise it returns the result of the expressione on the right side.
IMPORTANT: If the left variable is NOT defined, the right expression is not evaluated at all.
IMPORTANT: If the variable [blue]`?!` is NOT defined, the expression is not evaluated at all.
.Examples
`>>>` [blue]`var ?? (1+2)` +
@@ -836,8 +855,14 @@ The table below shows all supported operators by decreasing priorities.
.2+|*INSERT*| [blue]`+>` | _Infix_ | _Prepend_ | _any_ `+>` _list_ -> _list_
| [blue]`<+` | _Infix_ | _Append_ | _list_ `<+` _any_ -> _list_
.2+|*ASSIGN*| [blue]`=` | _Infix_ | _Assignment_ | _identifier_ `=` _any_ -> _any_
4+| _See also the table of special allocation operators below_
4+| _See also the table of special assignment operators below_
.1+|*BUT*| [blue]`but` | _Infix_ | _But_ | _any_ `but` _any_ -> _any_
.6+|*ITER-OP*| [blue]`digest` | _Infix_ | _Item-digesting_ | _iterable_ `digest` _expr_ -> _any_
| [blue]`filter` | _Infix_ | _Item-filtering_ | _iterable_ `filter` _expr_ -> _list_
| [blue]`groupby` | _Infix_ | _Dict-grouping_ | _iterable_ `groupby` _key-expr_ -> _dict_
| [blue]`join` | _Infix_ | _Item-joining_ | _iterable_ `join` _iterable_ -> _list_
| [blue]`map` | _Infix_ | _Item-mapping_ | _iterable_ `map` _-expr_ -> _list_
4+| _See iterators section for examples_
.1+|*RANGE*| [blue]`:` | _Infix_ | _Index-range_ | _integer_ `:` _integer_ -> _integer-pair_
|===
@@ -921,12 +946,12 @@ _param-name_ = _identifier_
[green]`fib(n):any{}`
`>>>` [gray]_// Required and optional parameters_ +
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? [true] {"s"} :: {""}}` +
`>>>` [blue]`measure = func(value, unit="meter"){ value + " " + unit + (value > 1) ? {"s"} :: {""}}` +
[green]`measure(value, unit="meter"):any{}`
=== _Golang_ function definition
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another document that I'll write, one day, maybe.
Description of how to define Golang functions and how to bind them to _Expr_ are topics covered in another documents that I'll write, one day, maybe.
=== Function calls
To call a function, either Expr or Golang type, it is necessary to specify its name and, at least, its required parameters.
@@ -1010,7 +1035,7 @@ Clone variables are normal local variables. The only diffence will appear when t
.Example
`>>>` [blue]`f = func() { @x = 3; x = 5 }` [gray]_// f() declares two *different* local variables: ``@x`` and ``x``_ +
[green]`f():any{}` +
`>>>` [blue]`f()` [gray]_// The multi-expression (two) in f() is calculated and the last result is returned_ +
`>>>` [blue]`f()` [gray]_// The multi-expression (two expressions) in f() is calculated and the last result is returned_ +
[green]`5` +
`>>>` [blue]`x` [gray]_// The `x` variable was not defined in the main context before the f() invocation. It appears in the main context by cloning the `@x` variable, local to f() after its termnation._ +
[green]`3`
@@ -1048,7 +1073,7 @@ Builtins activation is done by using the [blue]`BUILTIN` operator. All modules e
.Builtin activation syntax
====
*_builtin-activation_* = [blue]`BUILTIN` (_builtin-name_ | _list-of-builtin-names_) +
*_builtin-activation_* = [blue]`BUILTIN` (_builtin-name_ | _list-of-builtin-names_ | **"*"**) +
_builtin-name_ = _string_ +
_list-of-builtin-names_ = **[** _string_ { "**,**" _string_ } **]**
====
@@ -1086,9 +1111,9 @@ The "base" builtin module provides functions for type checking and type conversi
* <<_fract,fract()>>
.Other functions
* <<_char,char()>>
* <<_eval,eval()>>
* <<_set,set()>>
* <<_unset,unset()>>
* <<_var,var()>>
@@ -1100,7 +1125,9 @@ Returns _true_ if the value type of _<expr>_ is boolean, false otherwise.
`>>>` [blue]`isBool(true)` +
[green]`true` +
`>>>` [blue]`isBool(3==2)` +
[green]`true`
[green]`true` +
`>>>` [blue]`isBool(3 + 2)` +
[green]`false`
===== isDict()
Syntax: `isDict(<expr>) -> bool` +
@@ -1178,7 +1205,7 @@ Returns _true_ if the value type of _<expr>_ is fraction or int, false otherwise
===== isString()
Syntax: `isString(<expr>) -> bool` +
Returns a boolean value , false otherwise.
Returns _true_ if the value type of _<expr>_ is string, false otherwise.
.Examples
`>>>` [blue]`isString("ciao")` +
@@ -1190,7 +1217,7 @@ Returns a boolean value , false otherwise.
===== bool()
Syntax: `bool(<expr>) -> bool` +
Returns a _boolean_ value consisent to the value of the expression.
Returns a _boolean_ value consisent with the value of the expression.
.Examples
`>>>` [blue]`bool(1)` +
@@ -1295,6 +1322,16 @@ Returns a _fraction_ value consistent with the value of the expression.
`>>>` [blue]`fract(true)` +
[green]`1:1`
===== char()
Syntax: `char(<intexpr>) -> string` +
Returns the character whose ASCII (soon Unicode too) code point is specified by the integer expression.
.Examples
`>>>` [blue]`char(65)` +
[green]`"A"` +
`>>>` [blue]`char(97)` +
[green]`"a"`
===== eval()
Syntax: `eval(<string-expr>) -> any` +
Computes and returns the value of the [.underline]#string# expression.
@@ -1308,12 +1345,12 @@ Syntax: +
`{4sp}var(<string-expr>, <expr>) -> any` +
`{4sp}var(<string-expr>) -> any`
This function allows you to define variables whose names must include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.
This function allows you to define variables whose names can include special characters. The first form of the function allows you to define a variable with a name specified by the first parameter and assign it the value of the second parameter. The second form only returns the value of the variable with the specified name.
.Examples
`>>>` [blue]`var("$x", 3+9)` +
[green]`12` +
`>>>` [blue]`var("$x")` +
`>>>` [blue]`var("$"+"x")` +
[green]`12` +
`>>>` [blue]`var("gain%", var("$x"))` +
[green]`12` +
@@ -1334,26 +1371,17 @@ It is equivalent to the first form of the var() function, but it is more explici
`>>>` [blue]`var("$x")` +
[green]`100` +
===== unset()
Syntax: +
`{4sp}unset(<string-expr>) -> any`
This function allows you to unset a variable whose name can include special characters. The parameter is the name of the variable to unset.
.Examples
`>>>` [blue]`unset("$x")` +
[green]`nil` +
`>>>` [blue]`var("$x")` +
[red]`Eval Error: var(): unknown variable "$x"`
==== Module "fmt"
#to-do#
===== print()
===== println()
==== Module "import"
Module actiovation: +
Module activation: +
`{4sp}BUILTIN "import"`
===== _import()_
Syntax: +
@@ -1364,6 +1392,8 @@ Loads the multi-expression contained in the specified source and returns its val
===== _importAll()_
==== Module "iterator"
Module activation: +
`{4sp}BUILTIN "iterator"`
===== run()
Syntax: +
@@ -1372,6 +1402,9 @@ Syntax: +
Iterates over the specified iterator and applies the specified operator to the current value of the iterator.
==== Module "math.arith"
Module activation: +
`{4sp}BUILTIN "math.arith"`
Currently, the "math.arith" module provides two functions, add() and mul(), that perform addition and multiplication of an arbitrary number of parameters. More functions will be added in the future.
* <<_add,add()>>
@@ -1421,13 +1454,12 @@ Same as <<_add,add()>> but returns the product of the values of the parameters.
[green]`24`
==== Module "os.file"
Module activation: +
`{4sp}BUILTIN "os.file"`
The "os.file" module provides functions for working with files.
Activation: +
`{4sp}builtin "os.file"`
Currently available functions:
.File related functions
* <<_fileOpen,fileOpen()>>
* <<_fileAppend,fileAppend()>>
* <<_fileCreate,fileCreate()>>
@@ -1436,6 +1468,10 @@ Currently available functions:
* <<_fileReadText,fileReadText()>>
* <<_fileReadTextAll,fileReadTextAll()>>
.Iterator functions for files
* <<_fileByteIterator,fileByteIterator()>>
* <<_fileLineIterator,fileLineIterator()>>
More functions will be added in the future.
---
@@ -1447,6 +1483,10 @@ Syntax: +
Returns a file handle for the specified file path. The file is opened in read-write mode. If the file does not exist, it is created.
===== fileAppend()
Syntax: +
`{4sp}fileAppend(<file-path>) -> any`
Like <<_fileCreate,fileCreate()>> but write operations happen at the end of the file.
===== fileCreate()
Syntax: +
@@ -1455,21 +1495,30 @@ Syntax: +
Creates or truncates the named _<file-path>_. If the file already exists, it is truncated. If the file does not exist, it is created with mode 0o666 (before umask). The associated file descriptor has mode [O_RDWR]. The directory containing the file must already exist.
===== fileClose()
#to-do#
===== fileWriteText()
#to-do#
===== fileReadText()
#to-do#
===== fileReadTextAll()
#to-do#
===== fileByteIterator()
#to-do#
===== fileLineIterator()
#to-do#
==== Module "string"
Module activation: +
`{4sp}BUILTIN "string"`
This module provides functions for working with strings.
Activation: +
`{4sp}builtin "string"`
Currently available functions:
* <<_strJoin,strJoin()>>
+322
View File
@@ -0,0 +1,322 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>file: Go Coverage Report</title>
<style>
body {
background: black;
color: rgb(80, 80, 80);
}
body, pre, #legend span {
font-family: Menlo, monospace;
font-weight: bold;
}
#topbar {
background: black;
position: fixed;
top: 0; left: 0; right: 0;
height: 42px;
border-bottom: 1px solid rgb(80, 80, 80);
}
#content {
margin-top: 50px;
}
#nav, #legend {
float: left;
margin-left: 10px;
}
#legend {
margin-top: 12px;
}
#nav {
margin-top: 10px;
}
#legend span {
margin: 0 5px;
}
.cov0 { color: rgb(192, 0, 0) }
.cov1 { color: rgb(128, 128, 128) }
.cov2 { color: rgb(116, 140, 131) }
.cov3 { color: rgb(104, 152, 134) }
.cov4 { color: rgb(92, 164, 137) }
.cov5 { color: rgb(80, 176, 140) }
.cov6 { color: rgb(68, 188, 143) }
.cov7 { color: rgb(56, 200, 146) }
.cov8 { color: rgb(44, 212, 149) }
.cov9 { color: rgb(32, 224, 152) }
.cov10 { color: rgb(20, 236, 155) }
</style>
</head>
<body>
<div id="topbar">
<div id="nav">
<select id="files">
<option value="file0">git.portale-stac.it/go-pkg/expr/file/file.go (88.9%)</option>
<option value="file1">git.portale-stac.it/go-pkg/expr/file/reader.go (77.8%)</option>
<option value="file2">git.portale-stac.it/go-pkg/expr/file/writer.go (100.0%)</option>
</select>
</div>
<div id="legend">
<span>not tracked</span>
<span class="cov0">not covered</span>
<span class="cov8">covered</span>
</div>
</div>
<div id="content">
<pre class="file" id="file0" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// file.go
package file
import (
"os"
"git.portale-stac.it/go-pkg/expr/kern"
)
type Handle interface {
kern.Typer
GetFile() *os.File
GetName() string
Valid() bool
Close() error
}
type handleBase struct {
fh *os.File
}
func (h *handleBase) GetFile() *os.File <span class="cov0" title="0">{
return h.fh
}</span>
func (h *handleBase) GetName() (name string) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
name = h.fh.Name()
}</span>
<span class="cov8" title="1">return</span>
}
func (h *handleBase) Valid() bool <span class="cov8" title="1">{
return h.fh != nil
}</span>
func (h *handleBase) Close() (err error) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
err = h.fh.Close()
h.fh = nil
}</span>
<span class="cov8" title="1">return</span>
}
</pre>
<pre class="file" id="file1" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import (
"bufio"
"io"
"os"
)
type Reader struct {
// fh *os.File
handleBase
reader *bufio.Reader
}
func NewReader(fh *os.File) *Reader <span class="cov8" title="1">{
return &amp;Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
}</span>
func OpenReader(filePath string) (r *Reader, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.Open(filePath); err == nil </span><span class="cov8" title="1">{
r = NewReader(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) TypeName() string <span class="cov8" title="1">{
return "fileReader"
}</span>
func (h *Reader) String() string <span class="cov8" title="1">{
return "reader"
}</span>
func (h *Reader) Valid() bool <span class="cov8" title="1">{
return h.handleBase.Valid() &amp;&amp; h.reader != nil
}</span>
func (w *Reader) Close() (err error) <span class="cov8" title="1">{
w.reader = nil
err = w.handleBase.Close()
return
}</span>
func (h *Reader) ReadByte() (b byte, err error) <span class="cov8" title="1">{
if h.reader != nil </span><span class="cov8" title="1">{
b, err = h.reader.ReadByte()
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) ReadAll() (p []byte, err error) <span class="cov8" title="1">{
if h.reader != nil </span><span class="cov8" title="1">{
p, err = io.ReadAll(h.reader)
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov8" title="1">return</span>
}
func (h *Reader) ReadString(delim byte) (s string, err error) <span class="cov0" title="0">{
if h.reader != nil </span><span class="cov0" title="0">{
s, err = h.reader.ReadString(delim)
}</span> else<span class="cov0" title="0"> {
err = io.ErrClosedPipe
}</span>
<span class="cov0" title="0">return</span>
}
func (h *Reader) Reset() (err error) <span class="cov8" title="1">{
if h.fh != nil </span><span class="cov8" title="1">{
if _, err = h.fh.Seek(0, 0); err == nil </span><span class="cov8" title="1">{
h.reader.Reset(h.fh)
}</span>
}
<span class="cov8" title="1">return</span>
}
</pre>
<pre class="file" id="file2" style="display: none">// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import (
"bufio"
"fmt"
"os"
)
type Writer struct {
// fh *os.File
handleBase
writer *bufio.Writer
}
func NewWriter(fh *os.File) *Writer <span class="cov8" title="1">{
return &amp;Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
}</span>
func CreateWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.Create(filePath); err == nil </span><span class="cov8" title="1">{
w = NewWriter(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func AppendWriter(filePath string) (w *Writer, err error) <span class="cov8" title="1">{
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil </span><span class="cov8" title="1">{
w = NewWriter(fh)
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) TypeName() string <span class="cov8" title="1">{
return "fileWriter"
}</span>
func (w *Writer) String() string <span class="cov8" title="1">{
return "writer"
}</span>
func (w *Writer) Valid() bool <span class="cov8" title="1">{
return w.handleBase.Valid() &amp;&amp; w.writer != nil
}</span>
func (w *Writer) Close() (err error) <span class="cov8" title="1">{
var err1 error
if w.writer != nil </span><span class="cov8" title="1">{
err1 = w.Flush()
w.writer = nil
}</span>
<span class="cov8" title="1">if err = w.handleBase.Close(); err == nil </span><span class="cov8" title="1">{
err = err1
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Flush() (err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
err = w.writer.Flush()
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Write(args ...any) (n int, err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
n, err = fmt.Fprint(w.writer, args...)
}</span>
<span class="cov8" title="1">return</span>
}
func (w *Writer) Writef(format string, args ...any) (n int, err error) <span class="cov8" title="1">{
if w.writer != nil </span><span class="cov8" title="1">{
n, err = fmt.Fprintf(w.writer, format, args...)
}</span>
<span class="cov8" title="1">return</span>
}
</pre>
</div>
</body>
<script>
(function() {
var files = document.getElementById('files');
var visible;
files.addEventListener('change', onChange, false);
function select(part) {
if (visible)
visible.style.display = 'none';
visible = document.getElementById(part);
if (!visible)
return;
files.value = part;
visible.style.display = 'block';
location.hash = part;
}
function onChange() {
select(files.value);
window.scrollTo(0, 0);
}
if (location.hash != "") {
select(location.hash.substr(1));
}
if (!visible) {
select("file0");
}
})();
</script>
</html>
+46
View File
@@ -0,0 +1,46 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// file.go
package file
import (
"os"
"git.portale-stac.it/go-pkg/expr/kern"
)
type Handle interface {
kern.Typer
GetFile() *os.File
GetName() string
Valid() bool
Close() error
}
type handleBase struct {
fh *os.File
}
func (h *handleBase) GetFile() *os.File {
return h.fh
}
func (h *handleBase) GetName() (name string) {
if h.fh != nil {
name = h.fh.Name()
}
return
}
func (h *handleBase) Valid() bool {
return h.fh != nil
}
func (h *handleBase) Close() (err error) {
if h.fh != nil {
err = h.fh.Close()
h.fh = nil
}
return
}
+83
View File
@@ -0,0 +1,83 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import (
"bufio"
"io"
"os"
)
type Reader struct {
// fh *os.File
handleBase
reader *bufio.Reader
}
func NewReader(fh *os.File) *Reader {
return &Reader{handleBase: handleBase{fh: fh}, reader: bufio.NewReader(fh)}
}
func OpenReader(filePath string) (r *Reader, err error) {
var fh *os.File
if fh, err = os.Open(filePath); err == nil {
r = NewReader(fh)
}
return
}
func (h *Reader) TypeName() string {
return "fileReader"
}
func (h *Reader) String() string {
return "reader"
}
func (h *Reader) Valid() bool {
return h.handleBase.Valid() && h.reader != nil
}
func (w *Reader) Close() (err error) {
w.reader = nil
err = w.handleBase.Close()
return
}
func (h *Reader) ReadByte() (b byte, err error) {
if h.reader != nil {
b, err = h.reader.ReadByte()
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) ReadAll() (p []byte, err error) {
if h.reader != nil {
p, err = io.ReadAll(h.reader)
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) ReadString(delim byte) (s string, err error) {
if h.reader != nil {
s, err = h.reader.ReadString(delim)
} else {
err = io.ErrClosedPipe
}
return
}
func (h *Reader) Reset() (err error) {
if h.fh != nil {
if _, err = h.fh.Seek(0, 0); err == nil {
h.reader.Reset(h.fh)
}
}
return
}
+62
View File
@@ -0,0 +1,62 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// reader.go
package file
import "testing"
func TestOpenReader(t *testing.T) {
r, err := OpenReader("t_reader_test.go")
if err != nil {
t.Fatalf("OpenReader failed: %v", err)
}
defer r.Close()
if !r.Valid() {
t.Fatal("Reader should be valid after opening")
}
if r.TypeName() != "fileReader" {
t.Fatalf("Expected TypeName 'fileReader', got '%s'", r.TypeName())
}
if r.String() != "reader" {
t.Fatalf("Expected String 'reader', got '%s'", r.String())
}
// GetName may return either "t_reader_test.go" or "./t_reader_test.go" depending on the environment
name := r.GetName()
if (name != "t_reader_test.go") && (name != "./t_reader_test.go") {
t.Fatalf("Expected GetName 't_reader_test.go' or './t_reader_test.go', got '%s'", name)
}
// Test reading a byte
b, err := r.ReadByte()
if err != nil {
t.Fatalf("ReadByte failed: %v", err)
}
if b == 0 {
t.Fatal("ReadByte should not return zero byte")
}
err = r.Reset()
if err != nil {
t.Fatalf("Reset failed: %v", err)
}
if s, err := r.ReadString('\n'); err != nil {
t.Fatalf("ReadString failed: %v", err)
} else {
t.Logf("ReadString: %s", s)
}
// Test reading all content
content, err := r.ReadAll()
if err != nil {
t.Fatalf("ReadAll failed: %v", err)
}
if len(content) == 0 {
t.Fatal("ReadAll should return non-empty content")
}
}
+69
View File
@@ -0,0 +1,69 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import "testing"
func TestCreateWriter(t *testing.T) {
w, err := CreateWriter("/tmp/test_writer.txt")
if err != nil {
t.Fatalf("CreateWriter failed: %v", err)
}
defer w.Close()
if !w.Valid() {
t.Fatal("Writer should be valid after creation")
}
if w.TypeName() != "fileWriter" {
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
}
if w.String() != "writer" {
t.Fatalf("Expected String 'writer', got '%s'", w.String())
}
name := w.GetName()
if name != "/tmp/test_writer.txt" {
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
}
if n, err := w.Write("Hello, World!\n"); err != nil {
t.Fatalf("Write failed: %v", err)
} else if n != len("Hello, World!\n") {
t.Fatalf("Expected to write %d bytes, wrote %d", len("Hello, World!\n"), n)
}
if n, err := w.Writef("This is a %s.\n", "test"); err != nil {
t.Fatalf("Writef failed: %v", err)
} else if n != len("This is a test.\n") {
t.Fatalf("Expected to write %d bytes, wrote %d", len("This is a test.\n"), n)
}
}
func TestAppendWriter(t *testing.T) {
w, err := AppendWriter("/tmp/test_writer.txt")
if err != nil {
t.Fatalf("AppendWriter failed: %v", err)
}
defer w.Close()
if !w.Valid() {
t.Fatal("Writer should be valid after opening for append")
}
if w.TypeName() != "fileWriter" {
t.Fatalf("Expected TypeName 'fileWriter', got '%s'", w.TypeName())
}
if w.String() != "writer" {
t.Fatalf("Expected String 'writer', got '%s'", w.String())
}
name := w.GetName()
if name != "/tmp/test_writer.txt" {
t.Fatalf("Expected GetName '/tmp/test_writer.txt', got '%s'", name)
}
}
+82
View File
@@ -0,0 +1,82 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// writer.go
package file
import (
"bufio"
"fmt"
"os"
)
type Writer struct {
// fh *os.File
handleBase
writer *bufio.Writer
}
func NewWriter(fh *os.File) *Writer {
return &Writer{handleBase: handleBase{fh: fh}, writer: bufio.NewWriter(fh)}
}
func CreateWriter(filePath string) (w *Writer, err error) {
var fh *os.File
if fh, err = os.Create(filePath); err == nil {
w = NewWriter(fh)
}
return
}
func AppendWriter(filePath string) (w *Writer, err error) {
var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY, 0644); err == nil {
w = NewWriter(fh)
}
return
}
func (w *Writer) TypeName() string {
return "fileWriter"
}
func (w *Writer) String() string {
return "writer"
}
func (w *Writer) Valid() bool {
return w.handleBase.Valid() && w.writer != nil
}
func (w *Writer) Close() (err error) {
var err1 error
if w.writer != nil {
err1 = w.Flush()
w.writer = nil
}
if err = w.handleBase.Close(); err == nil {
err = err1
}
return
}
func (w *Writer) Flush() (err error) {
if w.writer != nil {
err = w.writer.Flush()
}
return
}
func (w *Writer) Write(args ...any) (n int, err error) {
if w.writer != nil {
n, err = fmt.Fprint(w.writer, args...)
}
return
}
func (w *Writer) Writef(format string, args ...any) (n int, err error) {
if w.writer != nil {
n, err = fmt.Fprintf(w.writer, format, args...)
}
return
}
+10 -2
View File
@@ -12,6 +12,8 @@ import (
"git.portale-stac.it/go-pkg/expr/scan"
)
const iterIterType = "IterIter"
type IterIter struct {
it kern.Iterator
count int64
@@ -22,7 +24,13 @@ type IterIter struct {
}
func NewIterIter(it kern.Iterator, ctx kern.ExprContext, exprs []*scan.Term) (iter kern.Iterator, err error) {
iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
if ctx == nil {
err = fmt.Errorf("context is required for %s", iterIterType)
} else if it == nil {
err = fmt.Errorf("source iterator is required for %s", iterIterType)
} else {
iter = &IterIter{it: it, count: 0, index: -1, ctx: ctx, exprList: exprs, current: nil}
}
return
}
@@ -31,7 +39,7 @@ func (it *IterIter) String() string {
}
func (it *IterIter) TypeName() string {
return "IterIter"
return iterIterType
}
func (it *IterIter) HasOperation(name string) bool {
+4
View File
@@ -84,6 +84,10 @@ func ErrUnknownVar(funcName, varName string) error {
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
}
func ErrFuncInvalidArg(funcName, details string) error {
return fmt.Errorf("%s(): invalid argument -- %s", funcName, details)
}
// --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
+1 -1
View File
@@ -21,7 +21,7 @@ func TestFuncRun(t *testing.T) {
/* 7 */ {`builtin "iterator"; run($(1,2,3), func(){1}, "prrr")`, nil, `paramter "vars" must be a dictionary, passed prrr [string]`},
/* 8 */ {`builtin "iterator"; run($(1,2,3), operator=nil)`, nil, nil},
/* 9 */ {`builtin "iterator"; run($(1,2,3), operatorx=nil)`, nil, `run(): unknown param "operatorx"`},
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileReadIterator("test-file.txt"); run(it)`, nil, nil},
/* 10 */ {`builtin ["os.file", "iterator"]; it = fileLineIterator("test-file.txt"); run(it)`, nil, nil},
}
//t.Setenv("EXPR_PATH", ".")
+9 -6
View File
@@ -27,16 +27,19 @@ func TestFuncOs(t *testing.T) {
/* 13 */ {`builtin "os.file"; handle=fileClose(123)`, nil, `fileClose(): invalid file handle`},
/* 14 */ {`builtin "os.file"; handle=fileOpen("/tmp/dummy"); c=fileReadTextAll(handle); fileClose(handle); c`, "bye-bye", nil},
/* 15 */ {`builtin "os.file"; c=fileReadTextAll(123)`, nil, `fileReadTextAll(): invalid file handle 123 [int64]`},
/* 16 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++`, "uno", nil},
/* 17 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()},
/* 18 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); it.clean`, nil, nil},
/* 19 */ {`builtin "os.file"; it=fileReadIterator("test-file.txt"); string(it)`, `$(fileReadTextIterator@"test-file.txt")`, nil},
/* 16 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it++`, "uno", nil},
/* 17 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it++;it++;it++`, nil, io.EOF.Error()},
/* 18 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); it.clean`, nil, nil},
/* 19 */ {`builtin "os.file"; it=fileLineIterator("test-file.txt"); string(it)`, `$(fileLineIterator@"test-file.txt")`, nil},
/* 20 */ {`builtin "os.file"; handle=fileOpen("/etc/hosts"); fileClose(handle); string(handle)`, `reader`, nil},
/* 21 */ {`builtin "os.file"; handle=fileCreate("/tmp/dummy"); fileClose(handle); string(handle)`, `writer`, nil},
/* 22 */ {`builtin "os.file"; it=fileByteIterator("test-file.txt"); string(it)`, `$(fileByteIterator@"test-file.txt")`, nil},
/* 23 */ {`builtin ["os.file", "string"]; it=fileByteIterator("test-file.txt"); char(it++)`, `u`, nil},
/* 24 */ {`builtin ["os.file", "string"]; it=fileByteIterator("test-file.txt"); it++; it.reset; char(it++)`, `u`, nil},
}
// t.Setenv("EXPR_PATH", ".")
// runTestSuiteSpec(t, section, inputs, 19)
runTestSuite(t, section, inputs)
runTestSuiteSpec(t, section, inputs, 24)
// runTestSuite(t, section, inputs)
}
+3 -2
View File
@@ -92,9 +92,10 @@ func TestNewIterList5(t *testing.T) {
}
func TestNewIterList6(t *testing.T) {
ctx := NewSimpleStore()
list := kern.NewListA("a", "b", "c", "d")
it1, _ := NewIterator(nil, list, nil)
it, _ := NewIterator(nil, it1, nil)
it1, _ := NewIterator(ctx, list, nil)
it, _ := NewIterator(ctx, it1, nil)
if item, err := it.Next(); err != nil {
t.Errorf("error: %v", err)
} else if item != "a" {
+3 -3
View File
@@ -35,9 +35,9 @@ func TestIteratorParser(t *testing.T) {
/* 20 */ {`it=$({1:"one",2:"two",3:"three"}, "default", "value"); it++`, "one", nil},
/* 21 */ {`it=$({1:"one",2:"two",3:"three"}, "desc", "key"); it++`, int64(3), nil},
/* 22 */ {`it=$({1:"one",2:"two",3:"three"}, "asc", "item"); it++`, kern.NewList([]any{int64(1), "one"}), nil},
/* 23 */ {`builtin "os.file"; fileReadIterator("test-file.txt") map ${__}`, kern.NewList([]any{int64(0), int64(1)}), nil},
/* 24 */ {`builtin "os.file"; #(fileReadIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
/* 25 */ {`builtin "os.file"; #(fileReadIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
/* 23 */ {`builtin "os.file"; fileLineIterator("test-file.txt") map ${__}`, kern.NewList([]any{int64(0), int64(1)}), nil},
/* 24 */ {`builtin "os.file"; #(fileLineIterator("test-file.txt") filter (#${_} == 2))`, int64(0), nil},
/* 25 */ {`builtin "os.file"; #(fileLineIterator("test-file.txt") filter (#${_} == 3))`, int64(2), nil},
/* 26 */ {`#($(10) map ${_})`, int64(10), nil},
/* 27 */ {`#($(10,0) map ${_})`, int64(10), nil},
/* 28 */ {`$(10) digest ${_}`, int64(9), nil},