Compare commits

..

209 Commits

Author SHA1 Message Date
camoroso f63ff5953e graph.go: conditioned compilation by 'graph' tag 2026-05-03 07:14:30 +02:00
camoroso b9d37a5b4c kern/compare.go: added copyright comment 2026-05-03 07:13:39 +02:00
camoroso 23b8eec74a builtin-base: removed useless function unset(). See UNSET operator 2026-05-03 06:46:51 +02:00
camoroso bb6b6d17ec operator-map.go: return nil on error 2026-05-03 06:30:56 +02:00
camoroso 53acacbadf kern/common-errors.go: little changes to ErrExpectedGot() and ErrInvalidParameterValue() 2026-05-03 06:30:00 +02:00
camoroso 2ebc52891c Iterator operator: automatic temporary variables _index and _count changed with '__' and '_#' respectively. Note that, sinc '#' is not an identifier allowed char, '_#' requires himBHsnotation: -cover 2026-05-02 15:06:12 +02:00
camoroso 3b2ef7927b new operator 'groupby' 2026-05-02 14:53:19 +02:00
camoroso d5ced343c4 kern/compare.go: new function Equal(a,b) that returns true if a and b have the same value 2026-05-02 14:45:05 +02:00
camoroso 3ac8cab275 enhanced ending operator detection 2026-05-02 09:54:24 +02:00
camoroso 6c5e9db34b int-itrator: new iterator over integer ranges 2026-05-01 17:23:06 +02:00
camoroso 78871641d0 t_common_test.go: Error messages also contains the name of the section introduced by special symbol >>> 2026-05-01 17:17:40 +02:00
camoroso dacbec677a iterator interface chenged index and count members from int to tint 64 2026-05-01 17:15:18 +02:00
camoroso 75ed88915d ast.go, pasrse.go: fixed the recover routine 2026-05-01 17:03:44 +02:00
camoroso f2d1f23774 ast.go: added recover from panic in ast.Eval() 2026-05-01 07:25:05 +02:00
camoroso edd90054d7 parser.go: added recover from panic in parser.Parse() 2026-05-01 07:24:41 +02:00
camoroso 610e2df5f5 operator-dot: added support to access dict value by exprssion; example: D.key, D."key", D.expr 2026-04-30 07:06:20 +02:00
camoroso 32c0b45255 kern/dict-type.go: added GetItem() 2026-04-30 07:04:03 +02:00
camoroso 75a3f220df parser: token '()' returns error 2026-04-30 07:03:28 +02:00
camoroso 116b54836f token.go: little change to ErrorExpectedGotStringWithPrefix() 2026-04-30 07:01:02 +02:00
camoroso 8787973de0 util files moved form kern to util package 2026-04-29 11:03:36 +02:00
camoroso 4d910dd069 moved a subset of source file to the kern package 2026-04-27 19:43:37 +02:00
camoroso f100adead3 increased test coverage and splitted some utility functions by OS 2026-04-26 06:16:43 +02:00
camoroso 7d2cf1e687 new operator 'join' 2026-04-25 07:00:30 +02:00
camoroso 49728307f3 t_operator_test.go: added a test for the digest operator 2026-04-25 06:33:35 +02:00
camoroso 20dc502438 new operator 'digest' 2026-04-25 06:25:39 +02:00
camoroso ce7bfc5e3f operator-map/filter.go: added deletion of temporary variables '_index' and '_count' 2026-04-25 06:24:28 +02:00
camoroso 6e98bdd16b new operator 'filter' 2026-04-24 22:42:46 +02:00
camoroso 6ee365bacc new operator 'map' ans general variable access by ${} 2026-04-24 20:11:25 +02:00
camoroso 20d8236325 Iterators: Removed NewAnyIterator(), added NewIterator() 2026-04-24 06:26:00 +02:00
camoroso c39ee7cec0 dict-iterator.go: fixed behaviour of NewDictIterator() with no args 2026-04-23 22:06:23 +02:00
camoroso 9e4252173b Iterator interface now embeds fmt.Stringer 2026-04-23 22:04:41 +02:00
camoroso 2b80ba6789 t_plugin_test.go: TestLoadPluginName() removed due to linkage mismatch 2026-04-23 22:02:41 +02:00
camoroso d7247f97c5 doc: added description of set() and unset() functions defined in the base buiiltin 2026-04-23 19:09:09 +02:00
camoroso 02df7f1c1f Plugin file name extension adapted to the host OS convention (.so on linux, .dll on windows, .dylib on Darwin/MacOS) 2026-04-23 19:06:11 +02:00
camoroso b6b09b2fb1 builtin-os-file: add function fileReadIterator() that creates an iterator over lines of a text file 2026-04-23 19:01:07 +02:00
camoroso 0677180456 builtin-iterator.go/run(): removed default operator (print item) 2026-04-23 18:55:03 +02:00
camoroso b6952444ab builtin-base: added functions set() and unset() 2026-04-21 17:14:51 +02:00
camoroso 6c3a071a02 doc: last change date-time updated 2026-04-21 06:49:58 +02:00
camoroso d7d03b4af8 README: added link to the main Expr documentation hosted on cdn.paas.portale-stac.it 2026-04-21 06:47:15 +02:00
camoroso 49c5cb6a22 doc: progress about builtin functions 2026-04-21 06:45:41 +02:00
camoroso 53d805a832 doc: test commit 2026-04-19 21:27:43 +02:00
camoroso b99ad5def1 Merge branch 'iterator/dict' 2026-04-19 15:18:07 +02:00
camoroso 0d44b8697b doc: more details about iterators over dicts 2026-04-19 15:17:21 +02:00
camoroso 1a7e537921 more tests on iterator over dicts 2026-04-19 15:16:25 +02:00
camoroso 807df0f3a8 Iterator: added support for iterators over dictionaries 2026-04-19 15:08:14 +02:00
camoroso d7dd628fc9 doc: further details about iterator definition 2026-04-19 08:20:40 +02:00
camoroso 1f57ba28dd Doc: progress about builtin modules 2026-04-18 12:21:42 +02:00
camoroso 92bd366380 builtin-base: bool() supports lists and dicts, int() supports fractions 2026-04-18 12:20:05 +02:00
camoroso 7b93c5b4ac doc: begin of iterators description 2026-04-16 10:13:41 +02:00
camoroso 3ba8194ddb Expr.doc, a lot of fixes 2026-04-15 18:17:27 +02:00
camoroso 037565c41e New var() function added to the builtin set 2026-04-15 16:04:12 +02:00
camoroso f8d12b1a93 added TestGoFunction() to test Go functions 2026-04-15 16:03:41 +02:00
camoroso 518d4d8d65 simple-store.go: added function Init() 2026-01-03 09:11:43 +01:00
camoroso d64602cb00 builtin-string.go: fix return type of strLower() and strUpper() 2025-11-15 06:18:38 +01:00
camoroso 4709248828 string builtin: strUpper() and strLower() added 2025-11-13 20:53:07 +01:00
camoroso 5ecf81412e Doc: pre & post incremente/decrement 2025-01-05 12:53:50 +01:00
camoroso ff4db34f7b t_operator_test.go: test on -- and ++ prefix operators 2025-01-05 12:49:36 +01:00
camoroso 0f848071c2 New prefix operators ++ and -- 2025-01-04 18:12:38 +01:00
camoroso 6b3351b324 new prefix operator "!" as logic NOT 2025-01-04 17:47:59 +01:00
camoroso 760c1ee6da New bitwise XOR operator specified by symbol ^ (caret). Iterator dereference is now done by prefixed * (star) 2025-01-03 07:33:17 +01:00
camoroso 5ab6876ea1 term.go: new priority priDereference 2025-01-03 07:28:30 +01:00
camoroso 6268abda8c token.go: new member function Token.SetSymbol() 2025-01-03 07:27:49 +01:00
camoroso d25bd325b7 symbol-map.go: improved detection of incomplete operations 2025-01-03 06:32:55 +01:00
camoroso 01c04feea5 Doc: bitwise operators in the main operator table and special assignment operators table 2025-01-03 05:43:50 +01:00
camoroso 6fc689c46c Added a test to the context type 2025-01-03 05:40:24 +01:00
camoroso eccb0c4dc9 Added new special operators like &= and <<=.
Also made a litle change to scanner function moveOn(): now it moves on
the last char passed and only if there are more than one chars.
2024-12-29 19:26:02 +01:00
camoroso e43823740f Doc: added string splitter operator '/' 2024-12-28 19:22:26 +01:00
camoroso 526982a564 new string operator '/' 2024-12-28 19:16:45 +01:00
camoroso 252514943e parser.go: fix currentItem after parse a subexpr 2024-12-28 19:16:03 +01:00
camoroso 32686fac62 term.go: new member function errDivisionByZero() 2024-12-28 19:14:34 +01:00
camoroso 52a627c747 Edited according to preious commit 2024-12-28 09:28:12 +01:00
camoroso d91e7eb979 The list operator '<<' (append) and '>>' (prepend) have been replaced
with the new operators '<+' and '+>' respectively.
'<<' and '>>' are now used only for left and right binary shit
respectively. Further, their priority has been increased moving them to
a higher priority than that of the assignment operator.
2024-12-28 09:17:27 +01:00
camoroso cca3b76baa solved a number of problems highlighted by the syntax analyzer 2024-12-27 07:46:11 +01:00
camoroso 24e31997fc context-helpers.go: export functions no longer export control flags 2024-12-27 07:22:28 +01:00
camoroso 646710e180 builtin-base.go: new eval() function 2024-12-27 07:14:26 +01:00
camoroso b38327b841 t_builtin-string_test.go: corrected an undefinite article 2024-12-27 07:14:01 +01:00
camoroso fd912b2eb1 common-errors.go: undefinte article selection in error messages 2024-12-27 07:13:03 +01:00
camoroso 0e55f83d56 Forced the exlamation mark as a postfix operator 2024-12-26 08:57:14 +01:00
camoroso 4725145d1c Doc: changed fraction symbol and introduced binary operators 2024-12-25 07:43:06 +01:00
camoroso edf8818f51 New dedicated priority for binary operators between relational and sum ones 2024-12-25 07:41:08 +01:00
camoroso 6211be8a8f Completed transition of the symbol '|' from fraction to operator binary or. New fraction symbol is ':'.
Also, fixed and improved some parsing sections concerning collection indeces and ranges
2024-12-23 06:59:39 +01:00
camoroso f50ddf48db operator-range.go: range-term registered with symbol SymRange 2024-12-23 06:55:57 +01:00
camoroso 76e01f12d2 term.go: two error messages corrected 2024-12-23 06:53:37 +01:00
camoroso 406bced450 operator-sum.go: sum of two fraction fixed 2024-12-23 06:52:10 +01:00
camoroso 409dc86a92 symbol.go: SymRange added 2024-12-23 06:50:02 +01:00
camoroso 4184221428 symbol-map.go: changed symbol classification of some symbols like quotes and post-op 2024-12-23 06:49:17 +01:00
camoroso 8cf8b36a26 t_parser_test.go: replaced ~ with NOT 2024-12-19 15:36:16 +01:00
camoroso de87050188 scanner.go: removed SymTilde from DefaultTranslatios() -> It is not an alias for the SymNot symbol any more 2024-12-19 15:30:29 +01:00
camoroso a1ec0cc611 All assignment operators set the firstToken flag 2024-12-19 15:27:38 +01:00
camoroso 8e5550bfa7 New operator %= 2024-12-19 15:14:30 +01:00
camoroso 6ee21e10af New, more flexible, parser context datatype that includes and extends
the previous flags allowForest and allowVarRef.
Added binary operator (work in progress).
Better implementation of +=,-=,*=, and /= (new) operators.
2024-12-19 14:48:27 +01:00
camoroso 5c44532790 << && >>: left and right shift with integer operands 2024-12-07 07:06:08 +01:00
camoroso cb66c1ab19 symbol-map.go: removed unsed definitons 2024-10-13 08:44:21 +02:00
camoroso a28d24ba68 parser.go: improved terminal symbols thanks to new symbol-map.go functions 2024-10-13 08:42:55 +02:00
camoroso 523349a204 symbol-map.go: new file that helps to identify symbols by source and class 2024-10-13 08:41:30 +02:00
camoroso b185f1df3a token.go: added a few error functions 2024-10-13 08:39:56 +02:00
camoroso 5da5a61a42 Expr.doc: notes about function context 2024-10-05 05:30:22 +02:00
camoroso 6e9205abc4 t_funcs_test.go: A test add on parameters check about two params with the same name 2024-10-05 05:25:29 +02:00
camoroso f61004fb5d A test added on new implicit boolean cases in selector operator 2024-10-05 05:23:55 +02:00
camoroso 321030c8d3 parser.go: function parameter list can't specify same parameter more than once 2024-10-01 06:39:51 +02:00
camoroso 98fc89e84f operator-selector.go: Simplified selector for Boolean expressions 2024-10-01 06:37:35 +02:00
camoroso 778d00677d Doc: closure example 2024-09-18 20:48:12 +02:00
camoroso ba3dbb7f02 Doc: continuation 2024-09-16 06:52:29 +02:00
camoroso 7285109115 parser.go: number sign is now allowed after the assign operator 2024-09-16 06:49:26 +02:00
camoroso 4755774edd Doc: Fixed a lot of typos 2024-09-12 06:57:43 +02:00
camoroso d215d837f6 Reset() and Clean() have new, simpler signature 2024-09-12 05:44:29 +02:00
camoroso ad3c1e5a60 enhanced and simplified Reset(), Clean() and Next() methods 2024-09-09 15:23:07 +02:00
camoroso d6bf5ee500 common-type-names.go: TypeNil and TyperDict added 2024-09-09 15:16:42 +02:00
camoroso 4b176eb868 Fix function.go: CallFunctionByParams() dit not pass correctly received actual params 2024-08-23 10:29:57 +02:00
camoroso dceb31f542 CallFunction() has been replaced by three new functions:
CallFunctionByTerm(), CallFunctionByArgs() and CallFunctionByParams()
2024-08-02 06:39:33 +02:00
camoroso 075b0b5691 RegisterFunc() also returns the funcInfo object 2024-08-01 00:09:49 +02:00
camoroso 3c51b8d2ee Changed some function names and param types 2024-07-31 09:11:57 +02:00
camoroso a46753f453 Function buildActualParams moved from data-cursor.go ro function.go 2024-07-31 09:08:58 +02:00
camoroso 9070b5c9cc The function parameter model has been modified to support the passing of named parameters 2024-07-28 18:49:08 +02:00
camoroso ab06702e5e operator-post-inc.go: new post int decrement operator '--' 2024-07-24 06:39:35 +02:00
camoroso ffe1fa3aac op-assign expansion now end at ']' and '}' too 2024-07-24 06:37:57 +02:00
camoroso 1a1a475dd8 Added support to op-assign operators like '+='.
This feature is implemented by expansion, not by dedicated operators, e.g. a*=2+x is exapanded as a=a*(2+x)
2024-07-23 15:35:57 +02:00
camoroso 463e3634ba scanner.go: New function UnreadToken().
Currently it only supports one staging level.
2024-07-23 15:32:25 +02:00
camoroso 5f8ca47ef0 term.go: New function Clone() 2024-07-23 15:27:50 +02:00
camoroso 837b887490 token.go: New functions Clone() and IsOneOfA() 2024-07-23 15:27:36 +02:00
camoroso c76e1d3c8e symbol.go: New symbol '*=' 2024-07-23 15:24:54 +02:00
camoroso 315f5b22d3 data-cursor.go: the inizialization of the current item is done in the Next() method.
This allows the application of the filter and map operator to the first item too.
2024-07-23 05:46:37 +02:00
camoroso dfae593e86 operand-iterator.go: removed commented code 2024-07-21 16:35:13 +02:00
camoroso d572f3a129 Iterator: new function Count() 2024-07-21 16:34:52 +02:00
camoroso c461fd138e Iterator defined by data-source now only requires one method: next() 2024-07-21 05:45:22 +02:00
camoroso 6ecbe2feb1 Fixed type (expcted -> expected) 2024-07-21 05:43:55 +02:00
camoroso 80d3c6ec7d parser.go: Fixed an old bug that did not allow the parser to skip comment tokens 2024-07-21 05:37:34 +02:00
camoroso e09806c716 %q replaced by %s in some error messages 2024-07-21 05:33:06 +02:00
camoroso 1a772597cb removed/commented unused code 2024-07-19 17:03:03 +02:00
camoroso 33b3e1fc29 New function for searching and importing plugin 2024-07-19 15:37:00 +02:00
camoroso 4e3f5cfbc6 import-utils.go: Paths are now expanded with respect to env-vars and shell ~ 2024-07-19 15:33:15 +02:00
camoroso e35d4e3f70 utils.go: added function ExpandPath 2024-07-19 15:30:26 +02:00
camoroso b4529499d6 iterator.go: exported some const identifier 2024-07-18 07:27:02 +02:00
camoroso 7745dc24e2 dict-type.go: exported NewDataCursor 2024-07-18 07:25:51 +02:00
camoroso be25385d02 builtin-iterator: changed status variable from 'it_status' to 'status' 2024-07-15 06:59:13 +02:00
camoroso 79889cd8e1 New builtin module 'iterator' 2024-07-14 16:53:32 +02:00
camoroso 234759158c scanner: disabled prefix operator '()' 2024-07-13 18:10:04 +02:00
camoroso 00fda29606 operator-iter-value.go: prefix operator () used to get the current value of an iterator replaced by ^ 2024-07-13 17:17:16 +02:00
camoroso 2a2840bdf2 ListIterator now implements next and current command (e.g it.next) 2024-07-13 17:15:53 +02:00
camoroso 06373f5126 Impemented Typer interface 2024-07-13 17:14:25 +02:00
camoroso e69dad5fb5 Rename priority label priCoalesce as priDefault 2024-07-13 17:13:16 +02:00
camoroso 7459057df9 Expr embeds Typer and ast implements it 2024-07-13 17:11:39 +02:00
camoroso 176969c956 New iterator tests 2024-07-13 16:19:04 +02:00
camoroso cde2efacf1 Test on iterator filter and map 2024-07-13 15:30:04 +02:00
camoroso d7a7b3218c Merge branch 'main' into enhance_iterators 2024-07-13 09:08:16 +02:00
camoroso be3bb12f28 operator-unset.go: fixed function removal 2024-07-13 09:07:33 +02:00
camoroso 032916d4fa New operator unset to delete variables and functions from current context 2024-07-13 09:01:59 +02:00
camoroso f3cc0cc7ad operator-include.go: Fixed inclusion of a list of files.
Now returns the value of the last evaluated expression.
2024-07-13 09:00:53 +02:00
camoroso 905337f963 ExprContext: new functions VarCount(), DeleteVar(), FuncCount(), DeleteFunc() 2024-07-13 08:59:15 +02:00
camoroso 9a95a837f6 data-cursor.go: add item mapping support 2024-07-13 06:44:00 +02:00
camoroso 8547248ea2 data-cursor.go: fixed filter 2024-07-13 00:12:08 +02:00
camoroso a02f998fc6 t_iterator_test.go: Fixed test numbering and add a commentend test that is not yet fulfilled 2024-07-11 07:23:35 +02:00
camoroso e904003e6e minor changes 2024-07-11 06:53:14 +02:00
camoroso 990e04f957 operator-assign.go -- Fix: Assigning a functor to a collection's item didn't work 2024-07-11 06:49:02 +02:00
camoroso d8aed9dd7a t_iterator_test.go: added a test to verify the reset command of the list iterator 2024-07-11 05:54:22 +02:00
camoroso a73d24b171 iter-list.go -> list-iterator.go
Fix: the reset command set the initial item to the second one in the list
2024-07-11 05:52:47 +02:00
camoroso 3ebba83bce term.go: replaced self receiver 2024-07-09 07:50:50 +02:00
camoroso 6b3bfa2a11 self param replaced as opTerm 2024-07-09 07:50:06 +02:00
camoroso 867806155e scanner.go: replaced self receiver 2024-07-08 07:30:58 +02:00
camoroso a711333a2e simple-store.go: commented out an unused function 2024-07-08 07:30:26 +02:00
camoroso af3e946bd4 operator-sum.go: replaced self receiver 2024-07-08 07:29:42 +02:00
camoroso 22a36fa630 context.go renamed as expr-context.go 2024-07-07 16:20:29 +02:00
camoroso 6d9a379c92 token.go: replaced self receiver 2024-07-07 16:19:58 +02:00
camoroso dd6404c786 t_scanner_test.go: replaced t.Log(fmtStringf()) with t.Logf() 2024-07-07 16:18:39 +02:00
camoroso 34874ef663 plugins.go: replaced stringsIndex()<0 with !strings.Contains() 2024-07-07 16:17:48 +02:00
camoroso 9bc8e8ca05 operator-sum.go: replaced for-loop with append() 2024-07-07 16:15:56 +02:00
camoroso 7f367cfc49 parser.go: renamed self receiver 2024-07-07 16:14:52 +02:00
camoroso fe999acf2c operator-index.go: removed unused parameter from the function verifyKey() 2024-07-07 16:14:04 +02:00
camoroso 2ed1a1842b operand-iterator.go: commented out an unused function and replaced self receiver 2024-07-07 16:10:43 +02:00
camoroso bb9493d0cc list-type.go: commented out an unused fuction 2024-07-07 16:08:45 +02:00
camoroso f279bf163e import-utils.go: commented out unused fuctions 2024-07-07 15:59:23 +02:00
camoroso 6834d9f47b function.go: removed useless param != nil check 2024-07-07 15:58:29 +02:00
camoroso 8051faa2bf fraction-type.go: use of strings.TrimSuffix() in place of check suffix and slice 2024-07-07 15:57:17 +02:00
camoroso f30e687a79 changed the file name comment 2024-07-07 15:55:51 +02:00
camoroso 2b6e46576b byte-slider.go: renamed function receiver from self to slider 2024-07-07 15:54:26 +02:00
camoroso dc06c03112 builtin-string.go: removed useless err == nil check 2024-07-07 15:53:29 +02:00
camoroso e8f5d3e445 builtin-base.go: unused function iteratorFunc() commented out 2024-07-07 15:52:16 +02:00
camoroso 76ce0945f7 context.go splitted in two files: expr-context.go and expr-function-go.
Expr interface moved from ast.go to the new file expr.go
2024-07-07 15:51:29 +02:00
camoroso 340b99bad7 list-type.go: use of copy() for copying lists 2024-07-07 07:34:58 +02:00
camoroso 93dac956fb t_parser_test.go: more tests on ??, ?= and ?! operators 2024-07-06 17:01:23 +02:00
camoroso 307027d23d t_parser_test.go: changed all error values to string values 2024-07-06 16:47:00 +02:00
camoroso 896844e772 the common test framework now supports error, string and nil as value of the wantErr field 2024-07-06 16:43:13 +02:00
camoroso 9fc20077a1 operator-include.go: the include operator did not count the number of files included when its argument was a single string 2024-07-06 16:08:58 +02:00
camoroso d2a4adebdd dataCursor implements Typer interface 2024-07-06 16:05:54 +02:00
camoroso fd8e32e12b new operator "?!" (alternate value) 2024-07-06 05:54:53 +02:00
camoroso 1e62a51c15 t_fractions_test.go: added a test on the string() function 2024-07-06 04:56:00 +02:00
camoroso 0170baa0f5 iter-list.go: implemented the Typer interface 2024-07-06 04:54:42 +02:00
camoroso fe5c8e9619 tests improved 2024-06-26 04:29:40 +02:00
camoroso 7164e8816c operator-bool.go: error messages improved 2024-06-26 04:28:53 +02:00
camoroso e0f3b028fc builtin-string.go: replaced ToInt() with ToGoInt() 2024-06-26 04:28:07 +02:00
camoroso 522b5983bd builtin-base.go: string() function added 2024-06-26 04:27:14 +02:00
camoroso f1e2163277 replaced ToInt() with ToGoInt() 2024-06-26 04:25:49 +02:00
camoroso bbdf498cf3 function.go: commented out some unused error functions 2024-06-26 04:23:48 +02:00
camoroso f75c991ed2 common-errors.go: commented out some unused error functions 2024-06-26 04:23:14 +02:00
camoroso a7836143cc utils.go: ToInt() renamed as ToGoInt() 2024-06-26 04:22:29 +02:00
camoroso ba9b9cb28f a lot oh changes to the test framework and new test files t_builtin-fmt_test.go and t_plugin_test.go added 2024-06-25 10:59:03 +02:00
camoroso ef1baa11a8 builtin-fmt.go: print() and println() can write data to a generic Writer. They fetch the writer from the control variable '_stdout'.
If _stdout is nil, they write to os.Stdout
2024-06-25 10:55:54 +02:00
camoroso cfddbd60b9 control.go: use of UnsafeSetVar() in place of SetVar(). SetCtrl() added 2024-06-25 10:53:05 +02:00
camoroso 0760141caf utils.go: Removed CloneMap(). fromGenericAny(): check v against nil 2024-06-25 10:48:31 +02:00
camoroso b9e780e659 ExprContext: removed Merge() method 2024-06-25 10:45:54 +02:00
camoroso e41ddc664c Improved closure context persistence. Now it is possibile to define counters like this func(base){func(){@base=base+1}} 2024-06-24 07:20:17 +02:00
camoroso 3b641ac793 Doc: little changes 2024-06-21 09:06:25 +02:00
camoroso a1a62b6794 Doc: little changes 2024-06-20 07:10:59 +02:00
camoroso a18d92534f Doc: little changes 2024-06-19 22:51:37 +02:00
camoroso ec0963e26f Doc: little changes 2024-06-19 22:46:35 +02:00
camoroso be992131b1 Doc: little changes 2024-06-19 22:25:38 +02:00
camoroso 0e3960321f Doc: little changes 2024-06-19 22:22:10 +02:00
camoroso 61d34fef7d Doc: little changes 2024-06-19 22:20:28 +02:00
camoroso 581f1585e6 Doc: embedded images 2024-06-19 22:04:13 +02:00
148 changed files with 10579 additions and 3353 deletions
+1 -1
View File
@@ -144,4 +144,4 @@ Variables and functions can be added to a context both programmatically and ad a
== Expressions syntax == Expressions syntax
See #TODO link to doc/Expr.html# See https://cdn.paas.portale-stac.it/howto/go-pkg/expr/doc/Expr.html[Expr documentation] for a complete reference of the expression syntax and available functions.
+53 -41
View File
@@ -5,13 +5,11 @@
package expr package expr
import ( import (
"errors"
"strings" "strings"
)
type Expr interface { "git.portale-stac.it/go-pkg/expr/kern"
Eval(ctx ExprContext) (result any, err error) )
String() string
}
//-------- ast //-------- ast
@@ -24,65 +22,69 @@ func NewAst() *ast {
return &ast{} return &ast{}
} }
func (self *ast) ToForest() { func (ast *ast) TypeName() string {
if self.root != nil { return "Expression"
if self.forest == nil { }
self.forest = make([]*term, 0)
func (ast *ast) ToForest() {
if ast.root != nil {
if ast.forest == nil {
ast.forest = make([]*term, 0)
} }
self.forest = append(self.forest, self.root) ast.forest = append(ast.forest, ast.root)
self.root = nil ast.root = nil
} }
} }
func (self *ast) String() string { func (ast *ast) String() string {
var sb strings.Builder var sb strings.Builder
if self.root == nil { if ast.root == nil {
sb.WriteString("(nil)") sb.WriteString("(nil)")
} else { } else {
self.root.toString(&sb) ast.root.toString(&sb)
} }
return sb.String() return sb.String()
} }
func (self *ast) addTokens(tokens ...*Token) (err error) { func (ast *ast) addTokens(tokens ...*Token) (err error) {
for _, tk := range tokens { for _, tk := range tokens {
if err = self.addToken(tk); err != nil { if _, err = ast.addToken(tk); err != nil {
break break
} }
} }
return return
} }
func (self *ast) addToken(tk *Token) (err error) { // func (expr *ast) addToken(tk *Token) (err error) {
_, err = self.addToken2(tk) // _, err = expr.addToken2(tk)
return // return
} // }
func (self *ast) addToken2(tk *Token) (t *term, err error) { func (ast *ast) addToken(tk *Token) (t *term, err error) {
if t = newTerm(tk); t != nil { if t = newTerm(tk); t != nil {
err = self.addTerm(t) err = ast.addTerm(t)
} else { } else {
err = tk.Errorf("unexpected token %q", tk.String()) err = tk.Errorf("unexpected token %q", tk.String())
} }
return return
} }
func (self *ast) addTerm(node *term) (err error) { func (ast *ast) addTerm(node *term) (err error) {
if self.root == nil { if ast.root == nil {
self.root = node ast.root = node
} else { } else {
self.root, err = self.insert(self.root, node) ast.root, err = ast.insert(ast.root, node)
} }
return return
} }
func (self *ast) insert(tree, node *term) (root *term, err error) { func (ast *ast) insert(tree, node *term) (root *term, err error) {
if tree.getPriority() < node.getPriority() { if tree.getPriority() < node.getPriority() {
root = tree root = tree
if tree.isComplete() { if tree.isComplete() {
var subRoot *term var subRoot *term
last := tree.removeLastChild() last := tree.removeLastChild()
if subRoot, err = self.insert(last, node); err == nil { if subRoot, err = ast.insert(last, node); err == nil {
subRoot.setParent(tree) subRoot.setParent(tree)
} }
} else { } else {
@@ -97,22 +99,32 @@ func (self *ast) insert(tree, node *term) (root *term, err error) {
return return
} }
func (self *ast) Finish() { func (ast *ast) Finish() {
if self.root == nil && self.forest != nil && len(self.forest) >= 1 { if ast.root == nil && ast.forest != nil && len(ast.forest) >= 1 {
self.root = self.forest[len(self.forest)-1] ast.root = ast.forest[len(ast.forest)-1]
self.forest = self.forest[0 : len(self.forest)-1] ast.forest = ast.forest[0 : len(ast.forest)-1]
} }
} }
func (self *ast) Eval(ctx ExprContext) (result any, err error) { func (ast *ast) Eval(ctx kern.ExprContext) (result any, err error) {
self.Finish() defer func() {
if r := recover(); r != nil {
if errVal, ok := r.(error); ok {
err = errVal
} else {
err = errors.New("unexpected error while evaluating the expression")
}
}
}()
if self.root != nil { ast.Finish()
if ast.root != nil {
// initDefaultVars(ctx) // initDefaultVars(ctx)
if self.forest != nil { if ast.forest != nil {
for _, root := range self.forest { for _, tree := range ast.forest {
if result, err = root.compute(ctx); err == nil { if result, err = tree.Compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(kern.ControlLastResult, result)
} else { } else {
//err = fmt.Errorf("error in expression nr %d: %v", i+1, err) //err = fmt.Errorf("error in expression nr %d: %v", i+1, err)
break break
@@ -120,8 +132,8 @@ func (self *ast) Eval(ctx ExprContext) (result any, err error) {
} }
} }
if err == nil { if err == nil {
if result, err = self.root.compute(ctx); err == nil { if result, err = ast.root.Compute(ctx); err == nil {
ctx.UnsafeSetVar(ControlLastResult, result) ctx.UnsafeSetVar(kern.ControlLastResult, result)
} }
} }
// } else { // } else {
+39 -34
View File
@@ -4,55 +4,60 @@
// function.go // function.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// ---- Linking with Expr functions // ---- Linking with Expr functions
type exprFunctor struct { type exprFunctor struct {
baseFunctor kern.BaseFunctor
params []ExprFuncParam params []kern.ExprFuncParam
expr Expr expr Expr
defCtx ExprContext defCtx kern.ExprContext
} }
// func newExprFunctor(e Expr, params []string, ctx ExprContext) *exprFunctor { func (functor *exprFunctor) GetParams() (params []kern.ExprFuncParam) {
// return &exprFunctor{expr: e, params: params, defCtx: ctx} return functor.params
// }
func newExprFunctor(e Expr, params []ExprFuncParam, ctx ExprContext) *exprFunctor {
return &exprFunctor{expr: e, params: params, defCtx: ctx}
} }
func (functor *exprFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func newExprFunctor(e Expr, params []kern.ExprFuncParam, ctx kern.ExprContext) *exprFunctor {
if functor.defCtx != nil { var defCtx kern.ExprContext
ctx.Merge(functor.defCtx) if ctx != nil {
defCtx = ctx
} }
return &exprFunctor{expr: e, params: params, defCtx: defCtx}
}
for i, p := range functor.params { func (functor *exprFunctor) TypeName() string {
if i < len(args) { return "ExprFunctor"
arg := args[i] }
if funcArg, ok := arg.(Functor); ok {
// ctx.RegisterFunc(p, functor, 0, -1) func (functor *exprFunctor) GetDefinitionContext() kern.ExprContext {
return functor.defCtx
}
func (functor *exprFunctor) InvokeNamed(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var missing []string
for _, p := range functor.params {
if arg, exists := args[p.Name()]; exists {
if funcArg, ok := arg.(kern.Functor); ok {
paramSpecs := funcArg.GetParams() paramSpecs := funcArg.GetParams()
ctx.RegisterFunc(p.Name(), funcArg, TypeAny, paramSpecs) ctx.RegisterFunc(p.Name(), funcArg, kern.TypeAny, paramSpecs)
} else { } else {
ctx.UnsafeSetVar(p.Name(), arg) ctx.UnsafeSetVar(p.Name(), arg)
} }
} else { } else {
ctx.UnsafeSetVar(p.Name(), nil) if missing == nil {
missing = make([]string, 0, 1)
}
missing = append(missing, p.Name())
// ctx.UnsafeSetVar(p.Name(), nil)
} }
} }
result, err = functor.expr.Eval(ctx) if missing != nil {
err = kern.ErrMissingParams(name, missing)
} else {
result, err = functor.expr.Eval(ctx)
}
return return
} }
// 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
// }
+202 -70
View File
@@ -5,75 +5,87 @@
package expr package expr
import ( import (
"fmt"
"math" "math"
"strconv" "strconv"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) { const (
result = args[0] == nil ParamDenominator = "denominator"
)
func isNilFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = args[kern.ParamValue] == nil
return return
} }
func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isIntFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsInteger(args[0]) result = kern.IsInteger(args[kern.ParamValue])
return return
} }
func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isFloatFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFloat(args[0]) result = kern.IsFloat(args[kern.ParamValue])
return return
} }
func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isBoolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsBool(args[0]) result = kern.IsBool(args[kern.ParamValue])
return return
} }
func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isStringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsString(args[0]) result = kern.IsString(args[kern.ParamValue])
return return
} }
func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isFractionFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsFract(args[0]) result = kern.IsFraction(args[kern.ParamValue])
return return
} }
func isRationalFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isRationalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsRational(args[0]) result = kern.IsRational(args[kern.ParamValue])
return return
} }
func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isListFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsList(args[0]) result = kern.IsList(args[kern.ParamValue])
return return
} }
func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) { func isDictionaryFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result = IsDict(args[0]) result = kern.IsDict(args[kern.ParamValue])
return return
} }
func boolFunc(ctx ExprContext, name string, args []any) (result any, err error) { func boolFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = (v != 0) result = (v != 0)
case *FractionType: case *kern.FractionType:
result = v.num != 0 result = v.N() != 0
case float64: case float64:
result = v != 0.0 result = v != 0.0
case bool: case bool:
result = v result = v
case string: case string:
result = len(v) > 0 result = len(v) > 0
case *kern.ListType:
result = len(*v) > 0
case *kern.DictType:
result = len(*v) > 0
default: default:
err = ErrCantConvert(name, v, "bool") err = kern.ErrCantConvert(name, v, "bool")
} }
return return
} }
func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { func intFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = v result = v
case float64: case float64:
@@ -89,14 +101,16 @@ func intFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if i, err = strconv.Atoi(v); err == nil { if i, err = strconv.Atoi(v); err == nil {
result = int64(i) result = int64(i)
} }
case *kern.FractionType:
result = int64(v.N() / v.D())
default: default:
err = ErrCantConvert(name, v, "int") err = kern.ErrCantConvert(name, v, "int")
} }
return return
} }
func decFunc(ctx ExprContext, name string, args []any) (result any, err error) { func decFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
result = float64(v) result = float64(v)
case float64: case float64:
@@ -112,73 +126,191 @@ func decFunc(ctx ExprContext, name string, args []any) (result any, err error) {
if f, err = strconv.ParseFloat(v, 64); err == nil { if f, err = strconv.ParseFloat(v, 64); err == nil {
result = f result = f
} }
case *FractionType: case *kern.FractionType:
result = v.toFloat() result = v.ToFloat()
default: default:
err = ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
} }
return return
} }
func fractFunc(ctx ExprContext, name string, args []any) (result any, err error) { func stringFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[0].(type) { switch v := args[kern.ParamValue].(type) {
case int64: case int64:
var den int64 = 1 result = strconv.FormatInt(v, 10)
if len(args) > 1 {
var ok bool
if den, ok = args[1].(int64); !ok {
err = ErrExpectedGot(name, "integer", args[1])
} else if den == 0 {
err = ErrFuncDivisionByZero(name)
}
}
if err == nil {
result = newFraction(v, den)
}
case float64: case float64:
result, err = float64ToFraction(v) result = strconv.FormatFloat(v, 'g', -1, 64)
case bool: case bool:
if v { if v {
result = newFraction(1, 1) result = "true"
} else { } else {
result = newFraction(0, 1) result = "false"
} }
case string: case string:
result, err = makeGeneratingFraction(v) result = v
case *FractionType: case *kern.FractionType:
result = v.ToString(0)
case kern.Formatter:
result = v.ToString(0)
case fmt.Stringer:
result = v.String()
default:
err = kern.ErrCantConvert(name, v, "string")
}
return
}
func fractFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
switch v := args[kern.ParamValue].(type) {
case int64:
var den int64 = 1
var ok bool
if den, ok = args[ParamDenominator].(int64); !ok {
err = kern.ErrExpectedGot(name, "integer", args[ParamDenominator])
} else if den == 0 {
err = kern.ErrFuncDivisionByZero(name)
}
if err == nil {
result = kern.NewFraction(v, den)
}
case float64:
result, err = kern.Float64ToFraction(v)
case bool:
if v {
result = kern.NewFraction(1, 1)
} else {
result = kern.NewFraction(0, 1)
}
case string:
result, err = kern.MakeGeneratingFraction(v)
case *kern.FractionType:
result = v result = v
default: default:
err = ErrCantConvert(name, v, "float") err = kern.ErrCantConvert(name, v, "float")
} }
return return
} }
func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err error) { // func iteratorFunc(ctx expr.ExprContext, name string, args []any) (result any, err error) {
// return
// }
func evalFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[kern.ParamSource].(string); ok {
var ast Expr
parser := NewParser()
if ctx == nil {
ctx = NewSimpleStoreWithoutGlobalContext()
}
r := strings.NewReader(source)
scanner := NewScanner(r, DefaultTranslations())
if ast, err = parser.Parse(scanner); err == nil {
CtrlEnable(ctx, kern.ControlExportAll)
result, err = ast.Eval(ctx)
}
} else {
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
}
return return
} }
func ImportBuiltinsFuncs(ctx ExprContext) { func varFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
anyParams := []ExprFuncParam{ var varName string
NewFuncParam(ParamValue), var ok bool
if varName, ok = args[kern.ParamName].(string); !ok {
return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
} }
ctx.RegisterFunc("isNil", NewGolangFunctor(isNilFunc), TypeBoolean, anyParams) if result, ok = args[kern.ParamValue]; ok && result != nil {
ctx.RegisterFunc("isInt", NewGolangFunctor(isIntFunc), TypeBoolean, anyParams) ctx.GetParent().UnsafeSetVar(varName, result)
ctx.RegisterFunc("isFloat", NewGolangFunctor(isFloatFunc), TypeBoolean, anyParams) // } else {
ctx.RegisterFunc("isBool", NewGolangFunctor(isBoolFunc), TypeBoolean, anyParams) // err = expr.ErrWrongParamType(name, expr.ParamSource, expr.TypeString, args[expr.ParamSource])
ctx.RegisterFunc("isString", NewGolangFunctor(isStringFunc), TypeBoolean, anyParams) // }
ctx.RegisterFunc("isFract", NewGolangFunctor(isFractionFunc), TypeBoolean, anyParams) } else if result, ok = ctx.GetVar(varName); !ok {
ctx.RegisterFunc("isRational", NewGolangFunctor(isRationalFunc), TypeBoolean, anyParams) err = kern.ErrUnknownVar(name, varName)
ctx.RegisterFunc("isList", NewGolangFunctor(isListFunc), TypeBoolean, anyParams) }
ctx.RegisterFunc("isDict", NewGolangFunctor(isDictionaryFunc), TypeBoolean, anyParams) return
}
ctx.RegisterFunc("bool", NewGolangFunctor(boolFunc), TypeBoolean, anyParams) func setFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
ctx.RegisterFunc("int", NewGolangFunctor(intFunc), TypeInt, anyParams) var varName string
ctx.RegisterFunc("dec", NewGolangFunctor(decFunc), TypeFloat, anyParams) var ok bool
ctx.RegisterFunc("fract", NewGolangFunctor(fractFunc), TypeFraction, []ExprFuncParam{
NewFuncParam(ParamValue), if varName, ok = args[kern.ParamName].(string); !ok {
NewFuncParamFlagDef("denominator", PfDefault, 1), return nil, kern.ErrWrongParamType(name, kern.ParamName, kern.TypeString, args[kern.ParamName])
}
if result, ok = args[kern.ParamValue]; ok {
ctx.GetParent().UnsafeSetVar(varName, result)
} else {
err = kern.ErrWrongParamType(name, kern.ParamValue, kern.TypeAny, args[kern.ParamValue])
}
return
}
// func unsetFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
// var varName string
// var ok bool
// 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
// }
//// import
func ImportBuiltinsFuncs(ctx kern.ExprContext) {
anyParams := []kern.ExprFuncParam{
NewFuncParam(kern.ParamValue),
}
ctx.RegisterFunc("isNil", kern.NewGolangFunctor(isNilFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isInt", kern.NewGolangFunctor(isIntFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isFloat", kern.NewGolangFunctor(isFloatFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isBool", kern.NewGolangFunctor(isBoolFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isString", kern.NewGolangFunctor(isStringFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isFract", kern.NewGolangFunctor(isFractionFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isRational", kern.NewGolangFunctor(isRationalFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isList", kern.NewGolangFunctor(isListFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("isDict", kern.NewGolangFunctor(isDictionaryFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("bool", kern.NewGolangFunctor(boolFunc), kern.TypeBoolean, anyParams)
ctx.RegisterFunc("int", kern.NewGolangFunctor(intFunc), kern.TypeInt, anyParams)
ctx.RegisterFunc("dec", kern.NewGolangFunctor(decFunc), kern.TypeFloat, anyParams)
ctx.RegisterFunc("string", kern.NewGolangFunctor(stringFunc), kern.TypeString, anyParams)
ctx.RegisterFunc("fract", kern.NewGolangFunctor(fractFunc), kern.TypeFraction, []kern.ExprFuncParam{
NewFuncParam(kern.ParamValue),
NewFuncParamFlagDef(ParamDenominator, PfDefault, int64(1)),
}) })
ctx.RegisterFunc("eval", kern.NewGolangFunctor(evalFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(kern.ParamSource),
})
ctx.RegisterFunc("var", kern.NewGolangFunctor(varFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(kern.ParamName),
NewFuncParamFlagDef(kern.ParamValue, PfDefault, nil),
})
ctx.RegisterFunc("set", kern.NewGolangFunctor(setFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(kern.ParamName),
NewFuncParam(kern.ParamValue),
})
// ctx.RegisterFunc("unset", kern.NewGolangFunctor(unsetFunc), kern.TypeAny, []kern.ExprFuncParam{
// NewFuncParam(kern.ParamName),
// NewFuncParam(kern.ParamValue),
// })
} }
func init() { func init() {
+37 -14
View File
@@ -4,30 +4,53 @@
// builtin-fmt.go // builtin-fmt.go
package expr package expr
import "fmt" import (
"fmt"
"io"
"os"
func printFunc(ctx ExprContext, name string, args []any) (result any, err error) { "git.portale-stac.it/go-pkg/expr/kern"
var n int )
if n, err = fmt.Print(args...); err == nil {
result = int64(n) func getStdout(ctx kern.ExprContext) io.Writer {
var w io.Writer
if wany, exists := ctx.GetVar(kern.ControlStdout); exists && wany != nil {
w, _ = wany.(io.Writer)
} }
if w == nil {
w = os.Stdout
}
return w
}
func printFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var n int = 0
if v, exists := args[kern.ParamItem]; exists && v != nil {
argv := v.([]any)
n, err = fmt.Fprint(getStdout(ctx), argv...)
}
result = int64(n)
return return
} }
func printLnFunc(ctx ExprContext, name string, args []any) (result any, err error) { func printLnFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var n int var n int = 0
if n, err = fmt.Println(args...); err == nil { if v, exists := args[kern.ParamItem]; exists && v != nil {
result = int64(n) argv := v.([]any)
n, err = fmt.Fprintln(getStdout(ctx), argv...)
} else {
n, err = fmt.Fprintln(getStdout(ctx))
} }
result = int64(n)
return return
} }
func ImportFmtFuncs(ctx ExprContext) { func ImportFmtFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("print", NewGolangFunctor(printFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("print", kern.NewGolangFunctor(printFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParamFlag(ParamItem, PfRepeat), NewFuncParamFlag(kern.ParamItem, PfRepeat),
}) })
ctx.RegisterFunc("println", NewGolangFunctor(printLnFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("println", kern.NewGolangFunctor(printLnFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParamFlag(ParamItem, PfRepeat), NewFuncParamFlag(kern.ParamItem, PfRepeat),
}) })
} }
+19 -14
View File
@@ -7,29 +7,34 @@ package expr
import ( import (
"io" "io"
"os" "os"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func importFunc(ctx ExprContext, name string, args []any) (result any, err error) { func importFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
return importGeneral(ctx, name, args) return importGeneral(ctx, name, args)
} }
func importAllFunc(ctx ExprContext, name string, args []any) (result any, err error) { func importAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, kern.ControlExportAll)
return importGeneral(ctx, name, args) return importGeneral(ctx, name, args)
} }
func importGeneral(ctx ExprContext, name string, args []any) (result any, err error) { func importGeneral(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
dirList := buildSearchDirList("sources", ENV_EXPR_SOURCE_PATH) dirList := buildSearchDirList(ctx, "sources", ENV_EXPR_SOURCE_PATH)
result, err = doImport(ctx, name, dirList, NewArrayIterator(args)) if v, exists := args[kern.ParamFilepath]; exists && v != nil {
argv := v.([]any)
result, err = doImport(ctx, name, dirList, NewArrayIterator(argv))
}
return return
} }
func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (result any, err error) { func doImport(ctx kern.ExprContext, name string, dirList []string, it kern.Iterator) (result any, err error) {
var v any var v any
var sourceFilepath string var sourceFilepath string
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if err = checkStringParamExpected(name, v, it.Index()); err != nil { if err = checkStringParamExpected(name, v, int(it.Index())); err != nil {
break break
} }
if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil { if sourceFilepath, err = makeFilepath(v.(string), dirList); err != nil {
@@ -41,7 +46,7 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
var expr *ast var expr *ast
scanner := NewScanner(file, DefaultTranslations()) scanner := NewScanner(file, DefaultTranslations())
parser := NewParser() parser := NewParser()
if expr, err = parser.parseGeneral(scanner, true, true); err == nil { if expr, err = parser.parseGeneral(scanner, allowMultiExpr|allowVarRef, SymEos); err == nil {
result, err = expr.Eval(ctx) result, err = expr.Eval(ctx)
} }
if err != nil { if err != nil {
@@ -61,12 +66,12 @@ func doImport(ctx ExprContext, name string, dirList []string, it Iterator) (resu
return return
} }
func ImportImportFuncs(ctx ExprContext) { func ImportImportFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("import", NewGolangFunctor(importFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("import", kern.NewGolangFunctor(importFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat), NewFuncParamFlag(kern.ParamFilepath, PfRepeat),
}) })
ctx.RegisterFunc("importAll", NewGolangFunctor(importAllFunc), TypeAny, []ExprFuncParam{ ctx.RegisterFunc("importAll", kern.NewGolangFunctor(importAllFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(ParamFilepath, PfRepeat), NewFuncParamFlag(kern.ParamFilepath, PfRepeat),
}) })
} }
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-iterator.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
const (
iterParamOperator = "operator"
iterParamVars = "vars"
iterVarStatus = "status"
)
func parseRunArgs(localCtx kern.ExprContext, args map[string]any) (it kern.Iterator, op kern.Functor, err error) {
var ok bool
if it, ok = args[kern.ParamIterator].(kern.Iterator); !ok {
err = fmt.Errorf("paramter %q must be an iterator, passed %v [%s]", kern.ParamIterator, args[kern.ParamIterator], kern.TypeName(args[kern.ParamIterator]))
return
}
if args[iterParamOperator] != nil {
if op, ok = args[iterParamOperator].(kern.Functor); !ok || op == nil {
err = fmt.Errorf("paramter %q must be a function, passed %v [%s]", iterParamOperator, args[iterParamOperator], kern.TypeName(args[iterParamOperator]))
return
}
}
var vars *kern.DictType
if vars, ok = args[iterParamVars].(*kern.DictType); !ok && args[iterParamVars] != nil {
err = fmt.Errorf("paramter %q must be a dictionary, passed %v [%s]", iterParamVars, args[iterParamVars], kern.TypeName(args[iterParamVars]))
return
}
if vars != nil {
for key, value := range *vars {
var varName string
if varName, ok = key.(string); ok {
localCtx.UnsafeSetVar(varName, value)
}
}
}
return
}
func runFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var it kern.Iterator
var ok bool
var op kern.Functor
var v any
// var usingDefaultOp = false
var params map[string]any
var item any
localCtx := ctx.Clone()
localCtx.UnsafeSetVar(iterVarStatus, nil)
if it, op, err = parseRunArgs(localCtx, args); err != nil {
return
}
for item, err = it.Next(); err == nil; item, err = it.Next() {
if op != nil {
params = map[string]any{kern.ParamIndex: it.Index(), kern.ParamItem: item}
if v, err = op.InvokeNamed(localCtx, iterParamOperator, params); err != nil {
break
} else {
var success bool
if success, ok = kern.ToBool(v); !success || !ok {
break
}
}
}
}
if err == io.EOF {
err = nil
}
if err == nil {
if op == nil {
ctx.UnsafeSetVar(iterVarStatus, it.Count())
}
result, _ = localCtx.GetVar(iterVarStatus)
}
return
}
func ImportIterFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("run", kern.NewGolangFunctor(runFunc), kern.TypeAny, []kern.ExprFuncParam{
NewFuncParam(kern.ParamIterator),
NewFuncParamFlag(iterParamOperator, PfOptional),
NewFuncParamFlag(iterParamVars, PfOptional),
})
}
func init() {
RegisterBuiltinModule("iterator", ImportIterFuncs, "Iterator helper functions")
}
+46 -42
View File
@@ -7,67 +7,69 @@ package expr
import ( import (
"fmt" "fmt"
"io" "io"
"git.portale-stac.it/go-pkg/expr/kern"
) )
func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) {
if !(IsNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ { if !(kern.IsNumber(paramValue) || kern.IsFraction(paramValue)) /*|| isList(paramValue)*/ {
err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected",
funcName, paramPos+1, subPos+1, level, paramValue) funcName, paramPos+1, subPos+1, level, paramValue)
} }
return return
} }
func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doAdd(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
var sumAsFloat, sumAsFract bool var sumAsFloat, sumAsFract bool
var floatSum float64 = 0.0 var floatSum float64 = 0.0
var intSum int64 = 0 var intSum int64 = 0
var fractSum *FractionType var fractSum *kern.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*ListType); ok { if list, ok := v.(*kern.ListType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
if v, err = doAdd(ctx, name, subIter, count, level); err != nil { if v, err = doAdd(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
return return
} }
} }
} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { } else if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
break break
} }
count++ count++
if !sumAsFloat { if !sumAsFloat {
if IsFloat(v) { if kern.IsFloat(v) {
sumAsFloat = true sumAsFloat = true
if sumAsFract { if sumAsFract {
floatSum = fractSum.toFloat() floatSum = fractSum.ToFloat()
} else { } else {
floatSum = float64(intSum) floatSum = float64(intSum)
} }
} else if !sumAsFract && isFraction(v) { } else if !sumAsFract && kern.IsFraction(v) {
fractSum = newFraction(intSum, 1) fractSum = kern.NewFraction(intSum, 1)
sumAsFract = true sumAsFract = true
} }
} }
if sumAsFloat { if sumAsFloat {
floatSum += numAsFloat(v) floatSum += kern.NumAsFloat(v)
} else if sumAsFract { } else if sumAsFract {
var item *FractionType var item *kern.FractionType
var ok bool var ok bool
if item, ok = v.(*FractionType); !ok { if item, ok = v.(*kern.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = newFraction(iv, 1) item = kern.NewFraction(iv, 1)
} }
fractSum = sumFract(fractSum, item) fractSum = kern.SumFract(fractSum, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intSum += iv intSum += iv
@@ -86,63 +88,64 @@ func doAdd(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func addFunc(ctx ExprContext, name string, args []any) (result any, err error) { func addFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1) argv := args[kern.ParamValue].([]any)
result, err = doAdd(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result any, err error) { func doMul(ctx kern.ExprContext, name string, it kern.Iterator, count, level int) (result any, err error) {
var mulAsFloat, mulAsFract bool var mulAsFloat, mulAsFract bool
var floatProd float64 = 1.0 var floatProd float64 = 1.0
var intProd int64 = 1 var intProd int64 = 1
var fractProd *FractionType var fractProd *kern.FractionType
var v any var v any
level++ level++
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
if list, ok := v.(*ListType); ok { if list, ok := v.(*kern.ListType); ok {
v = NewListIterator(list, nil) v = NewListIterator(list, nil)
} }
if subIter, ok := v.(Iterator); ok { if subIter, ok := v.(kern.Iterator); ok {
if v, err = doMul(ctx, name, subIter, count, level); err != nil { if v, err = doMul(ctx, name, subIter, count, level); err != nil {
break break
} }
if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { if extIter, ok := v.(kern.ExtIterator); ok && extIter.HasOperation(kern.CleanName) {
if _, err = extIter.CallOperation(cleanName, nil); err != nil { if _, err = extIter.CallOperation(kern.CleanName, nil); err != nil {
return return
} }
} }
} else { } else {
if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { if err = checkNumberParamExpected(name, v, count, level, int(it.Index())); err != nil {
break break
} }
} }
count++ count++
if !mulAsFloat { if !mulAsFloat {
if IsFloat(v) { if kern.IsFloat(v) {
mulAsFloat = true mulAsFloat = true
if mulAsFract { if mulAsFract {
floatProd = fractProd.toFloat() floatProd = fractProd.ToFloat()
} else { } else {
floatProd = float64(intProd) floatProd = float64(intProd)
} }
} else if !mulAsFract && isFraction(v) { } else if !mulAsFract && kern.IsFraction(v) {
fractProd = newFraction(intProd, 1) fractProd = kern.NewFraction(intProd, 1)
mulAsFract = true mulAsFract = true
} }
} }
if mulAsFloat { if mulAsFloat {
floatProd *= numAsFloat(v) floatProd *= kern.NumAsFloat(v)
} else if mulAsFract { } else if mulAsFract {
var item *FractionType var item *kern.FractionType
var ok bool var ok bool
if item, ok = v.(*FractionType); !ok { if item, ok = v.(*kern.FractionType); !ok {
iv, _ := v.(int64) iv, _ := v.(int64)
item = newFraction(iv, 1) item = kern.NewFraction(iv, 1)
} }
fractProd = mulFract(fractProd, item) fractProd = kern.MulFract(fractProd, item)
} else { } else {
iv, _ := v.(int64) iv, _ := v.(int64)
intProd *= iv intProd *= iv
@@ -161,18 +164,19 @@ func doMul(ctx ExprContext, name string, it Iterator, count, level int) (result
return return
} }
func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { func mulFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1) argv := args[kern.ParamValue].([]any)
result, err = doMul(ctx, name, NewArrayIterator(argv), 0, -1)
return return
} }
func ImportMathFuncs(ctx ExprContext) { func ImportMathFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("add", &golangFunctor{f: addFunc}, TypeNumber, []ExprFuncParam{ ctx.RegisterFunc("add", kern.NewGolangFunctor(addFunc), kern.TypeNumber, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(0)), NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, int64(0)),
}) })
ctx.RegisterFunc("mul", &golangFunctor{f: mulFunc}, TypeNumber, []ExprFuncParam{ ctx.RegisterFunc("mul", kern.NewGolangFunctor(mulFunc), kern.TypeNumber, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, int64(1)), NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, int64(1)),
}) })
} }
+151
View File
@@ -0,0 +1,151 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// builtin-os-file.go
package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
)
const paramHandleOrPath = "handle-or-path"
const fileReadTextIteratorType = "fileReadTextIterator"
type fileReadTextIterator struct {
osReader *osReader
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 {
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 {
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 {
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 *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 fileReadIteratorFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle *osReader
var invalidFileHandle any
var ok, autoClose bool
result = nil
if handle, ok = args[paramHandleOrPath].(*osReader); !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)
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")
// }
+50 -37
View File
@@ -9,6 +9,12 @@ import (
"fmt" "fmt"
"io" "io"
"os" "os"
"git.portale-stac.it/go-pkg/expr/kern"
)
const (
osLimitCh = "limitCh"
) )
type osHandle interface { type osHandle interface {
@@ -61,8 +67,8 @@ func errInvalidFileHandle(funcName string, v any) error {
} }
} }
func createFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func createFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Create(filePath); err == nil { if fh, err = os.Create(filePath); err == nil {
result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -73,8 +79,8 @@ func createFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return return
} }
func openFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func openFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.Open(filePath); err == nil { if fh, err = os.Open(filePath); err == nil {
result = &osReader{fh: fh, reader: bufio.NewReader(fh)} result = &osReader{fh: fh, reader: bufio.NewReader(fh)}
@@ -85,8 +91,8 @@ func openFileFunc(ctx ExprContext, name string, args []any) (result any, err err
return return
} }
func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func appendFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if filePath, ok := args[0].(string); ok && len(filePath) > 0 { if filePath, ok := args[kern.ParamFilepath].(string); ok && len(filePath) > 0 {
var fh *os.File var fh *os.File
if fh, err = os.OpenFile(filePath, os.O_APPEND|os.O_WRONLY|os.O_CREATE, 0660); err == nil { 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)} result = &osWriter{fh: fh, writer: bufio.NewWriter(fh)}
@@ -97,13 +103,13 @@ func appendFileFunc(ctx ExprContext, name string, args []any) (result any, err e
return return
} }
func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err error) { func closeFileFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[0].(osHandle); !ok { if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
invalidFileHandle = args[0] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -124,18 +130,21 @@ func closeFileFunc(ctx ExprContext, name string, args []any) (result any, err er
return return
} }
func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fileWriteTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
if handle, ok = args[0].(osHandle); !ok { if handle, ok = args[kern.ParamHandle].(osHandle); !ok {
invalidFileHandle = args[0] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
if w, ok := handle.(*osWriter); ok { if w, ok := handle.(*osWriter); ok {
result, err = fmt.Fprint(w.writer, args[1:]...) if v, exists := args[kern.ParamItem]; exists {
argv := v.([]any)
result, err = fmt.Fprint(w.writer, argv...)
}
} else { } else {
invalidFileHandle = handle invalidFileHandle = handle
} }
@@ -147,21 +156,21 @@ func fileWriteTextFunc(ctx ExprContext, name string, args []any) (result any, er
return return
} }
func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fileReadTextFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil { if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[0] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
if r, ok := handle.(*osReader); ok { if r, ok := handle.(*osReader); ok {
var limit byte = '\n' var limit byte = '\n'
var v string var v string
if s, ok := args[1].(string); ok && len(s) > 0 { if s, ok := args[osLimitCh].(string); ok && len(s) > 0 {
limit = s[0] limit = s[0]
} }
@@ -187,14 +196,14 @@ func fileReadTextFunc(ctx ExprContext, name string, args []any) (result any, err
return return
} }
func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any, err error) { func fileReadTextAllFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var handle osHandle var handle osHandle
var invalidFileHandle any var invalidFileHandle any
var ok bool var ok bool
result = nil result = nil
if handle, ok = args[0].(osHandle); !ok || args[0] == nil { if handle, ok = args[kern.ParamHandle].(osHandle); !ok || args[kern.ParamHandle] == nil {
invalidFileHandle = args[0] invalidFileHandle = args[kern.ParamHandle]
} }
if handle != nil { if handle != nil {
@@ -213,35 +222,39 @@ func fileReadTextAllFunc(ctx ExprContext, name string, args []any) (result any,
return return
} }
func ImportOsFuncs(ctx ExprContext) { func ImportOsFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("fileOpen", NewGolangFunctor(openFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileOpen", kern.NewGolangFunctor(openFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileAppend", NewGolangFunctor(appendFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileAppend", kern.NewGolangFunctor(appendFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileCreate", NewGolangFunctor(createFileFunc), TypeHandle, []ExprFuncParam{ ctx.RegisterFunc("fileCreate", kern.NewGolangFunctor(createFileFunc), kern.TypeFileHandle, []kern.ExprFuncParam{
NewFuncParam(ParamFilepath), NewFuncParam(kern.ParamFilepath),
}) })
ctx.RegisterFunc("fileClose", NewGolangFunctor(closeFileFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("fileClose", kern.NewGolangFunctor(closeFileFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(kern.ParamHandle),
}) })
ctx.RegisterFunc("fileWriteText", NewGolangFunctor(fileWriteTextFunc), TypeInt, []ExprFuncParam{ ctx.RegisterFunc("fileWriteText", kern.NewGolangFunctor(fileWriteTextFunc), kern.TypeInt, []kern.ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(kern.ParamHandle),
NewFuncParamFlagDef(TypeItem, PfDefault|PfRepeat, ""), NewFuncParamFlagDef(kern.ParamItem, PfDefault|PfRepeat, ""),
}) })
ctx.RegisterFunc("fileReadText", NewGolangFunctor(fileReadTextFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadText", kern.NewGolangFunctor(fileReadTextFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(kern.ParamHandle),
NewFuncParamFlagDef("limitCh", PfDefault, "\n"), NewFuncParamFlagDef(osLimitCh, PfDefault, "\n"),
}) })
ctx.RegisterFunc("fileReadTextAll", NewGolangFunctor(fileReadTextAllFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("fileReadTextAll", kern.NewGolangFunctor(fileReadTextAllFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(TypeHandle), NewFuncParam(kern.ParamHandle),
})
ctx.RegisterFunc("fileReadIterator", kern.NewGolangFunctor(fileReadIteratorFunc), kern.TypeIterator, []kern.ExprFuncParam{
NewFuncParam(paramHandleOrPath),
}) })
} }
+129 -79
View File
@@ -8,10 +8,16 @@ import (
"fmt" "fmt"
"io" "io"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
const (
strParamOther = "other"
) )
// --- Start of function definitions // --- Start of function definitions
func doJoinStr(funcName string, sep string, it Iterator) (result any, err error) { func doJoinStr(funcName string, sep string, it kern.Iterator) (result any, err error) {
var sb strings.Builder var sb strings.Builder
var v any var v any
for v, err = it.Next(); err == nil; v, err = it.Next() { for v, err = it.Next(); err == nil; v, err = it.Next() {
@@ -21,56 +27,56 @@ func doJoinStr(funcName string, sep string, it Iterator) (result any, err error)
if s, ok := v.(string); ok { if s, ok := v.(string); ok {
sb.WriteString(s) sb.WriteString(s)
} else { } else {
err = ErrExpectedGot(funcName, TypeString, v) err = kern.ErrExpectedGot(funcName, kern.TypeString, v)
return return
} }
} }
if err == nil || err == io.EOF { if err == io.EOF {
err = nil err = nil
result = sb.String() result = sb.String()
} }
return return
} }
func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func joinStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
// if len(args) < 1 { if sep, ok := args[kern.ParamSeparator].(string); ok {
// return nil, errMissingRequiredParameter(name, paramSeparator) if v, exists := args[kern.ParamItem]; exists {
// } argv := v.([]any)
if sep, ok := args[0].(string); ok { if len(argv) == 1 {
if len(args) == 1 { if ls, ok := argv[0].(*kern.ListType); ok {
result = "" result, err = doJoinStr(name, sep, NewListIterator(ls, nil))
} else if len(args) == 2 { } else if it, ok := argv[0].(kern.Iterator); ok {
if ls, ok := args[1].(*ListType); ok { result, err = doJoinStr(name, sep, it)
result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) } else if s, ok := argv[0].(string); ok {
} else if it, ok := args[1].(Iterator); ok { result = s
result, err = doJoinStr(name, sep, it) } else {
err = kern.ErrInvalidParameterValue(name, kern.ParamItem, v)
}
} else { } else {
err = ErrInvalidParameterValue(name, ParamParts, args[1]) result, err = doJoinStr(name, sep, NewArrayIterator(argv))
} }
} else {
result, err = doJoinStr(name, sep, NewArrayIterator(args[1:]))
} }
} else { } else {
err = ErrWrongParamType(name, ParamSeparator, TypeString, args[0]) err = kern.ErrWrongParamType(name, kern.ParamSeparator, kern.TypeString, args[kern.ParamSeparator])
} }
return return
} }
func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func subStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var start = 0 var start = 0
var count = -1 var count = -1
var source string var source string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if start, err = ToInt(args[1], name+"()"); err != nil { if start, err = kern.ToGoInt(args[kern.ParamStart], name+"()"); err != nil {
return return
} }
if count, err = ToInt(args[2], name+"()"); err != nil { if count, err = kern.ToGoInt(args[kern.ParamCount], name+"()"); err != nil {
return return
} }
@@ -86,81 +92,99 @@ func subStrFunc(ctx ExprContext, name string, args []any) (result any, err error
return return
} }
func trimStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func trimStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return nil, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return nil, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
result = strings.TrimSpace(source) result = strings.TrimSpace(source)
return return
} }
func startsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func startsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source, prefix string
var ok bool var ok bool
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { if prefix, ok = args[kern.ParamPrefix].(string); !ok {
if strings.HasPrefix(source, target) { return result, kern.ErrWrongParamType(name, kern.ParamPrefix, kern.TypeString, args[kern.ParamPrefix])
result = true }
if strings.HasPrefix(source, prefix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasPrefix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
break break
} }
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
break
} }
} }
return return
} }
func endsWithStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func endsWithStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source string var source, suffix string
var ok bool var ok bool
result = false result = false
if source, ok = args[0].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
for i, targetSpec := range args[1:] {
if target, ok := targetSpec.(string); ok { if suffix, ok = args[kern.ParamSuffix].(string); !ok {
if strings.HasSuffix(source, target) { return result, kern.ErrWrongParamType(name, kern.ParamSuffix, kern.TypeString, args[kern.ParamSuffix])
result = true }
if strings.HasPrefix(source, suffix) {
result = true
} else if v, exists := args[strParamOther]; exists {
argv := v.([]any)
for i, targetSpec := range argv {
if target, ok := targetSpec.(string); ok {
if strings.HasSuffix(source, target) {
result = true
break
}
} else {
err = fmt.Errorf("target item nr %d is %s, string expected", i+1, kern.TypeName(targetSpec))
break break
} }
} else {
err = fmt.Errorf("target item nr %d is %T, expected string", i+1, targetSpec)
break
} }
} }
return return
} }
func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err error) { func splitStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
var source, sep string var source, sep string
var count int = -1 var count int = -1
var parts []string var parts []string
var ok bool var ok bool
if source, ok = args[0].(string); !ok { if source, ok = args[kern.ParamSource].(string); !ok {
return result, ErrWrongParamType(name, ParamSource, TypeString, args[0]) return result, kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
} }
if sep, ok = args[1].(string); !ok { if sep, ok = args[kern.ParamSeparator].(string); !ok {
return nil, fmt.Errorf("separator param must be string, got %T (%v)", args[1], args[1]) return nil, fmt.Errorf("separator param must be string, got %s (%v)", kern.TypeName(args[kern.ParamSeparator]), args[kern.ParamSeparator])
} }
if count64, ok := args[2].(int64); ok { // TODO replace type assertion with toInt() if count64, ok := args[kern.ParamCount].(int64); ok { // TODO replace type assertion with toInt()
count = int(count64) count = int(count64)
} else { } else {
return nil, fmt.Errorf("part count must be integer, got %T (%v)", args[2], args[2]) return nil, fmt.Errorf("part count must be integer, got %s (%v)", kern.TypeName(args[kern.ParamCount]), args[kern.ParamCount])
} }
if count > 0 { if count > 0 {
@@ -170,7 +194,7 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
} else { } else {
parts = []string{} parts = []string{}
} }
list := make(ListType, len(parts)) list := make(kern.ListType, len(parts))
for i, part := range parts { for i, part := range parts {
list[i] = part list[i] = part
} }
@@ -178,41 +202,67 @@ func splitStrFunc(ctx ExprContext, name string, args []any) (result any, err err
return return
} }
func upperStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[kern.ParamSource].(string); ok {
result = strings.ToUpper(source)
} else {
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
}
return
}
func lowerStrFunc(ctx kern.ExprContext, name string, args map[string]any) (result any, err error) {
if source, ok := args[kern.ParamSource].(string); ok {
result = strings.ToLower(source)
} else {
err = kern.ErrWrongParamType(name, kern.ParamSource, kern.TypeString, args[kern.ParamSource])
}
return
}
// --- End of function definitions // --- End of function definitions
// Import above functions in the context // Import above functions in the context
func ImportStringFuncs(ctx ExprContext) { func ImportStringFuncs(ctx kern.ExprContext) {
ctx.RegisterFunc("strJoin", NewGolangFunctor(joinStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strJoin", kern.NewGolangFunctor(joinStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSeparator), NewFuncParam(kern.ParamSeparator),
NewFuncParamFlag(ParamItem, PfRepeat), NewFuncParamFlag(kern.ParamItem, PfRepeat),
}) })
ctx.RegisterFunc("strSub", NewGolangFunctor(subStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSub", kern.NewGolangFunctor(subStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(kern.ParamSource),
NewFuncParamFlagDef(ParamStart, PfDefault, int64(0)), NewFuncParamFlagDef(kern.ParamStart, PfDefault, int64(0)),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), NewFuncParamFlagDef(kern.ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("strSplit", NewGolangFunctor(splitStrFunc), "list of "+TypeString, []ExprFuncParam{ ctx.RegisterFunc("strSplit", kern.NewGolangFunctor(splitStrFunc), "list of "+kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(kern.ParamSource),
NewFuncParamFlagDef(ParamSeparator, PfDefault, ""), NewFuncParamFlagDef(kern.ParamSeparator, PfDefault, ""),
NewFuncParamFlagDef(ParamCount, PfDefault, int64(-1)), NewFuncParamFlagDef(kern.ParamCount, PfDefault, int64(-1)),
}) })
ctx.RegisterFunc("strTrim", NewGolangFunctor(trimStrFunc), TypeString, []ExprFuncParam{ ctx.RegisterFunc("strTrim", kern.NewGolangFunctor(trimStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(kern.ParamSource),
}) })
ctx.RegisterFunc("strStartsWith", NewGolangFunctor(startsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strStartsWith", kern.NewGolangFunctor(startsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(kern.ParamSource),
NewFuncParam(ParamPrefix), NewFuncParam(kern.ParamPrefix),
NewFuncParamFlag("other "+ParamPrefix, PfRepeat), NewFuncParamFlag(strParamOther, PfRepeat),
}) })
ctx.RegisterFunc("strEndsWith", NewGolangFunctor(endsWithStrFunc), TypeBoolean, []ExprFuncParam{ ctx.RegisterFunc("strEndsWith", kern.NewGolangFunctor(endsWithStrFunc), kern.TypeBoolean, []kern.ExprFuncParam{
NewFuncParam(ParamSource), NewFuncParam(kern.ParamSource),
NewFuncParam(ParamSuffix), NewFuncParam(kern.ParamSuffix),
NewFuncParamFlag("other "+ParamSuffix, PfRepeat), NewFuncParamFlag(strParamOther, PfRepeat),
})
ctx.RegisterFunc("strUpper", kern.NewGolangFunctor(upperStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(kern.ParamSource),
})
ctx.RegisterFunc("strLower", kern.NewGolangFunctor(lowerStrFunc), kern.TypeString, []kern.ExprFuncParam{
NewFuncParam(kern.ParamSource),
}) })
} }
+5 -3
View File
@@ -6,21 +6,23 @@ package expr
import ( import (
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type builtinModule struct { type builtinModule struct {
importFunc func(ExprContext) importFunc func(kern.ExprContext)
description string description string
imported bool imported bool
} }
func newBuiltinModule(importFunc func(ExprContext), description string) *builtinModule { func newBuiltinModule(importFunc func(kern.ExprContext), description string) *builtinModule {
return &builtinModule{importFunc, description, false} return &builtinModule{importFunc, description, false}
} }
var builtinModuleRegister map[string]*builtinModule var builtinModuleRegister map[string]*builtinModule
func RegisterBuiltinModule(name string, importFunc func(ExprContext), description string) { func RegisterBuiltinModule(name string, importFunc func(kern.ExprContext), description string) {
if builtinModuleRegister == nil { if builtinModuleRegister == nil {
builtinModuleRegister = make(map[string]*builtinModule) builtinModuleRegister = make(map[string]*builtinModule)
} }
+9 -9
View File
@@ -18,17 +18,17 @@ func NewByteSlider(size int) *ByteSlider {
} }
} }
func (self *ByteSlider) PushEnd(b byte) { func (slider *ByteSlider) PushEnd(b byte) {
if self.length == cap(self.buf) { if slider.length == cap(slider.buf) {
self.length-- slider.length--
for i := 0; i < self.length; i++ { for i := 0; i < slider.length; i++ {
self.buf[i] = self.buf[i+1] slider.buf[i] = slider.buf[i+1]
} }
} }
self.buf[self.length] = b slider.buf[slider.length] = b
self.length++ slider.length++
} }
func (self *ByteSlider) Equal(target []byte) bool { func (slider *ByteSlider) Equal(target []byte) bool {
return target != nil && bytes.Equal(self.buf, target) return target != nil && bytes.Equal(slider.buf, target)
} }
-55
View File
@@ -1,55 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// common-errors.go
package expr
import (
"fmt"
)
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
} else {
err = fmt.Errorf("%s(): too few params -- expected %d, got %d", funcName, minArgs, argCount)
}
return
}
func ErrTooMuchParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too much params -- expected %d, got %d", funcName, maxArgs, argCount)
return
}
// --- General errors
func ErrCantConvert(funcName string, value any, kind string) error {
return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
}
func ErrExpectedGot(funcName string, kind string, value any) error {
return fmt.Errorf("%s(): expected %s, got %s (%v)", funcName, kind, TypeName(value), value)
}
func ErrFuncDivisionByZero(funcName string) error {
return fmt.Errorf("%s(): division by zero", funcName)
}
func ErrDivisionByZero() error {
return fmt.Errorf("division by zero")
}
// --- Parameter errors
func ErrMissingRequiredParameter(funcName, paramName string) error {
return fmt.Errorf("%s(): missing required parameter %q", funcName, paramName)
}
func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
return fmt.Errorf("%s(): invalid value %s (%v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
}
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
return fmt.Errorf("%s(): the %q parameter must be a %s, got a %s (%v)", funcName, paramName, paramType, TypeName(paramValue), paramValue)
}
-20
View File
@@ -1,20 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// common-type-names.go
package expr
const (
TypeAny = "any"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeHandle = "handle"
TypeInt = "integer"
TypeItem = "item"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)
-53
View File
@@ -1,53 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// context.go
package expr
// ---- Functor interface
type Functor interface {
Invoke(ctx ExprContext, name string, args []any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
}
// ---- Function Param Info
type ExprFuncParam interface {
Name() string
Type() string
IsDefault() bool
IsOptional() bool
IsRepeat() bool
DefaultValue() any
}
// ---- Function Info
type ExprFunc interface {
Formatter
Name() string
MinArgs() int
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ReturnType() string
}
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
Merge(ctx ExprContext)
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)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args []any) (result any, err error)
// RegisterFunc(name string, f Functor, minArgs, maxArgs int)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) error
}
-33
View File
@@ -1,33 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// control.go
package expr
// Preset control variables
const (
ControlPreset = "_preset"
ControlLastResult = "last"
ControlBoolShortcut = "_bool_shortcut"
ControlSearchPath = "_search_path"
ControlParentContext = "_parent_context"
)
// Other control variables
const (
control_export_all = "_export_all"
)
// Initial values
const (
init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
)
func initDefaultVars(ctx ExprContext) {
if _, exists := ctx.GetVar(ControlPreset); exists {
return
}
ctx.SetVar(ControlPreset, true)
ctx.SetVar(ControlBoolShortcut, true)
ctx.SetVar(ControlSearchPath, init_search_path)
}
+226 -73
View File
@@ -5,30 +5,53 @@
package expr package expr
import ( import (
"errors"
"io" "io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
) )
type dataCursor struct { type dataCursor struct {
ds map[string]Functor ds map[string]kern.Functor
ctx ExprContext ctx kern.ExprContext
index int initState bool // true if no item has produced yet (this replace di initial Next() call in the contructor)
resource any // cursorValid bool // true if resource is nil or if clean has not yet been called
nextFunc Functor index int64
cleanFunc Functor count int64
resetFunc Functor current any
currentFunc Functor lastErr error
resource any
nextFunc kern.Functor
cleanFunc kern.Functor
resetFunc kern.Functor
} }
func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { func NewDataCursor(ctx kern.ExprContext, ds map[string]kern.Functor, resource any) (dc *dataCursor) {
dc = &dataCursor{ dc = &dataCursor{
ds: ds, ds: ds,
index: -1, initState: true,
ctx: ctx.Clone(), // cursorValid: true,
index: -1,
count: 0,
current: nil,
lastErr: nil,
resource: resource,
ctx: ctx.Clone(),
nextFunc: ds[kern.NextName],
cleanFunc: ds[kern.CleanName],
resetFunc: ds[kern.ResetName],
} }
return return
} }
func (dc *dataCursor) Context() kern.ExprContext {
return dc.ctx
}
func (dc *dataCursor) TypeName() string {
return "DataCursor"
}
// func mapToString(m map[string]Functor) string { // func mapToString(m map[string]Functor) string {
// var sb strings.Builder // var sb strings.Builder
// sb.WriteByte('{') // sb.WriteByte('{')
@@ -58,97 +81,227 @@ func (dc *dataCursor) String() string {
} }
func (dc *dataCursor) HasOperation(name string) (exists bool) { func (dc *dataCursor) HasOperation(name string) (exists bool) {
exists = name == indexName exists = slices.Contains([]string{kern.CleanName, kern.ResetName, kern.CurrentName, kern.IndexName}, name)
if !exists { if !exists {
f, ok := dc.ds[name] f, ok := dc.ds[name]
exists = ok && isFunctor(f) exists = ok && kern.IsFunctor(f)
} }
return return
} }
func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { func (dc *dataCursor) CallOperation(name string, args map[string]any) (value any, err error) {
if name == indexName { if name == kern.IndexName {
value = int64(dc.Index()) value = int64(dc.Index())
} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) { } else if name == kern.CleanName {
if functor == dc.cleanFunc { err = dc.Clean()
value, err = dc.Clean() } else if name == kern.ResetName {
} else if functor == dc.resetFunc { err = dc.Reset()
value, err = dc.Reset() } else if functor, ok := dc.ds[name]; ok && kern.IsFunctor(functor) {
} else { ctx := kern.CloneContext(dc.ctx)
ctx := cloneContext(dc.ctx) value, err = functor.InvokeNamed(ctx, name, args)
value, err = functor.Invoke(ctx, name, []any{}) kern.ExportObjects(dc.ctx, ctx)
exportObjects(dc.ctx, ctx)
}
} else { } else {
err = errNoOperation(name) err = kern.ErrNoOperation(name)
} }
return return
} }
func (dc *dataCursor) Reset() (success bool, err error) { // func (dc *dataCursor) Reset() (err error) {
// if dc.resetFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.resetFunc, []any{dc.resource})
// _, err = dc.resetFunc.InvokeNamed(ctx, ResetName, actualParams)
// exportObjects(dc.ctx, ctx)
// dc.index = -1
// dc.count = 0
// dc.initState = true
// dc.current = nil
// dc.lastErr = nil
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(ResetName)
// }
// return
// }
func (dc *dataCursor) Reset() (err error) {
if dc.resetFunc != nil { if dc.resetFunc != nil {
if dc.resource != nil { ctx := kern.CloneContext(dc.ctx)
ctx := cloneContext(dc.ctx) actualParams := kern.BindActualParams(dc.resetFunc, []any{dc.resource})
if _, err = dc.resetFunc.Invoke(ctx, resetName, []any{dc.resource}); err == nil { _, err = dc.resetFunc.InvokeNamed(ctx, kern.ResetName, actualParams)
dc.index = -1 kern.ExportObjects(dc.ctx, ctx)
}
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errNoOperation(resetName)
} }
success = err == nil dc.index = -1
dc.count = 0
dc.initState = true
dc.current = nil
dc.lastErr = nil
return return
} }
func (dc *dataCursor) Clean() (success bool, err error) { func (dc *dataCursor) Clean() (err error) {
if dc.cleanFunc != nil { if dc.cleanFunc != nil {
if dc.resource != nil { ctx := kern.CloneContext(dc.ctx)
ctx := cloneContext(dc.ctx) actualParams := kern.BindActualParams(dc.cleanFunc, []any{dc.resource})
_, err = dc.cleanFunc.Invoke(ctx, cleanName, []any{dc.resource}) _, err = dc.cleanFunc.InvokeNamed(ctx, kern.CleanName, actualParams)
dc.resource = nil kern.ExportObjects(dc.ctx, ctx)
exportObjects(dc.ctx, ctx)
} else {
err = errInvalidDataSource()
}
} else {
err = errors.New("no 'clean' function defined in the data-source")
} }
success = err == nil dc.lastErr = io.EOF
return return
} }
// func (dc *dataCursor) Clean() (err error) {
// if dc.cleanFunc != nil {
// if dc.resource != nil {
// ctx := cloneContext(dc.ctx)
// actualParams := bindActualParams(dc.cleanFunc, []any{dc.resource})
// _, err = dc.cleanFunc.InvokeNamed(ctx, CleanName, actualParams)
// exportObjects(dc.ctx, ctx)
// } else {
// err = errInvalidDataSource()
// }
// } else {
// err = errNoOperation(CleanName)
// }
// return
// }
func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item func (dc *dataCursor) Current() (item any, err error) { // must return io.EOF at the last item
ctx := cloneContext(dc.ctx) dc.init()
if item, err = dc.currentFunc.Invoke(ctx, currentName, []any{}); err == nil && item == nil {
if dc.current != nil {
item = dc.current
} else {
err = io.EOF err = io.EOF
} }
exportObjects(dc.ctx, ctx)
return return
} }
func (dc *dataCursor) Next() (item any, err error) { // must return io.EOF after the last item func (dc *dataCursor) checkFilter(filter kern.Functor, item any) (accepted bool, err error) {
if dc.resource != nil { var v any
ctx := cloneContext(dc.ctx) var ok bool
// fmt.Printf("Entering Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0)) ctx := kern.CloneContext(dc.ctx)
if item, err = dc.nextFunc.Invoke(ctx, nextName, []any{dc.resource}); err == nil {
if item == nil { actualParams := kern.BindActualParams(filter, []any{item, dc.index})
err = io.EOF if v, err = filter.InvokeNamed(ctx, kern.FilterName, actualParams); err == nil && v != nil {
} else { if accepted, ok = v.(bool); !ok {
dc.index++ accepted = true // NOTE: A non-boolean value that is not nil means the item has been accepted
}
} }
// fmt.Printf("Exiting Inner-Ctx [%p]: %s\n", ctx, CtxToString(ctx, 0))
exportObjects(dc.ctx, ctx)
// fmt.Printf("Outer-Ctx [%p]: %s\n", dc.ctx, CtxToString(dc.ctx, 0))
} else {
err = errInvalidDataSource()
} }
return return
} }
func (dc *dataCursor) Index() int { func (dc *dataCursor) mapItem(mapper kern.Functor, item any) (mappedItem any, err error) {
return dc.index ctx := kern.CloneContext(dc.ctx)
actualParams := kern.BindActualParams(mapper, []any{item, dc.index})
mappedItem, err = mapper.InvokeNamed(ctx, kern.MapName, actualParams)
return
}
func (dc *dataCursor) init() {
if dc.initState {
dc.initState = false
dc.Next()
}
}
func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
if dc.initState {
dc.init()
} else if err = dc.lastErr; err != nil {
return
}
current = dc.current
filter := dc.ds[kern.FilterName]
mapper := dc.ds[kern.MapName]
var item any
for item == nil && dc.lastErr == nil {
ctx := kern.CloneContext(dc.ctx)
dc.index++
actualParams := kern.BindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, kern.NextName, actualParams); dc.lastErr == nil {
if item == nil {
dc.lastErr = io.EOF
} else {
accepted := true
if filter != nil {
if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
item = nil
}
}
if accepted {
dc.count++
}
if item != nil && mapper != nil {
item, dc.lastErr = dc.mapItem(mapper, item)
}
}
}
kern.ExportObjects(dc.ctx, ctx)
}
dc.current = item
if dc.lastErr != nil {
dc.index--
dc.Clean()
}
return
}
// func (dc *dataCursor) Next() (current any, err error) { // must return io.EOF after the last item
// if dc.initState {
// dc.init()
// } else if err = dc.lastErr; err != nil {
// return
// }
// current = dc.current
// if dc.resource != nil {
// filter := dc.ds[FilterName]
// mapper := dc.ds[MapName]
// var item any
// for item == nil && dc.lastErr == nil {
// ctx := cloneContext(dc.ctx)
// dc.index++
// actualParams := bindActualParams(dc.nextFunc, []any{dc.resource, dc.index})
// if item, dc.lastErr = dc.nextFunc.InvokeNamed(ctx, NextName, actualParams); dc.lastErr == nil {
// if item == nil {
// dc.lastErr = io.EOF
// } else {
// accepted := true
// if filter != nil {
// if accepted, dc.lastErr = dc.checkFilter(filter, item); dc.lastErr != nil || !accepted {
// item = nil
// }
// }
// if accepted {
// dc.count++
// }
// if item != nil && mapper != nil {
// item, dc.lastErr = dc.mapItem(mapper, item)
// }
// }
// }
// exportObjects(dc.ctx, ctx)
// }
// dc.current = item
// if dc.lastErr != nil {
// dc.index--
// dc.Clean()
// }
// } else {
// dc.lastErr = errInvalidDataSource()
// }
// return
// }
func (dc *dataCursor) Index() int64 {
return dc.index - 1
}
func (dc *dataCursor) Count() int64 {
return dc.count
} }
+223
View File
@@ -0,0 +1,223 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// dict-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
type dictIterMode int
const (
dictIterModeKeys dictIterMode = iota
dictIterModeValues
dictIterModeItems
)
type DictIterator struct {
a *kern.DictType
count int64
index int64
keys []any
iterMode dictIterMode
}
type sortType int
const (
sortTypeNone sortType = iota
sortTypeAsc
sortTypeDesc
sortTypeDefault = sortTypeAsc
)
func (it *DictIterator) makeKeys(m map[any]any, sort sortType) {
it.keys = make([]any, 0, len(m))
if sort == sortTypeNone {
for keyAny := range m {
it.keys = append(it.keys, keyAny)
}
} else {
scalarMap := make(map[string]any, len(m))
scalerKeys := make([]string, 0, len(m))
for keyAny := range m {
keyStr := fmt.Sprint(keyAny)
scalarMap[keyStr] = keyAny
scalerKeys = append(scalerKeys, keyStr)
}
switch sort {
case sortTypeAsc:
slices.Sort(scalerKeys)
case sortTypeDesc:
slices.Sort(scalerKeys)
slices.Reverse(scalerKeys)
}
for _, keyStr := range scalerKeys {
it.keys = append(it.keys, scalarMap[keyStr])
}
}
}
func NewDictIterator(dict *kern.DictType, args []any) (it *DictIterator, err error) {
var sortType = sortTypeNone
var s string
var argAny any
dictIt := &DictIterator{a: dict, count: 0, index: -1, keys: nil, iterMode: dictIterModeKeys}
if len(args) > 0 {
argAny = args[0]
} else {
argAny = "default"
}
if s, err = kern.ToGoString(argAny, "sort type"); err == nil {
switch strings.ToLower(s) {
case "a", "asc":
sortType = sortTypeAsc
case "d", "desc":
sortType = sortTypeDesc
case "n", "none", "nosort", "no-sort":
sortType = sortTypeNone
case "", "default":
sortType = sortTypeDefault
default:
err = fmt.Errorf("invalid sort type %q", s)
}
if err == nil {
if len(args) > 1 {
argAny = args[1]
} else {
argAny = "default"
}
if s, err = kern.ToGoString(argAny, "iteration mode"); err == nil {
switch strings.ToLower(s) {
case "k", "key", "keys":
dictIt.iterMode = dictIterModeKeys
case "v", "value", "values":
dictIt.iterMode = dictIterModeValues
case "i", "item", "items":
dictIt.iterMode = dictIterModeItems
case "", "default":
dictIt.iterMode = dictIterModeKeys
default:
err = fmt.Errorf("invalid iteration mode %q", s)
}
}
}
}
dictIt.makeKeys(*dict, sortType)
return dictIt, err
}
func NewMapIterator(m map[any]any) (it *DictIterator) {
it = &DictIterator{a: (*kern.DictType)(&m), count: 0, index: -1, keys: nil}
it.makeKeys(m, sortTypeNone)
return
}
func (it *DictIterator) String() string {
var l = int64(0)
if it.a != nil {
l = int64(len(*it.a))
}
return fmt.Sprintf("$({#%d})", l)
}
func (it *DictIterator) TypeName() string {
return "DictIterator"
}
func (it *DictIterator) HasOperation(name string) bool {
// yes := name == NextName || name == ResetName || name == IndexName || name == CountName || name == CurrentName
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName, kern.CleanName, kern.KeyName, kern.ValueName}, name)
return yes
}
func (it *DictIterator) 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
case kern.KeyName:
if it.index >= 0 && it.index < int64(len(it.keys)) {
v = it.keys[it.index]
} else {
err = io.EOF
}
case kern.ValueName:
if it.index >= 0 && it.index < int64(len(it.keys)) {
a := *(it.a)
v = a[it.keys[it.index]]
} else {
err = io.EOF
}
default:
err = kern.ErrNoOperation(name)
}
return
}
func (it *DictIterator) Current() (item any, err error) {
if it.index >= 0 && it.index < int64(len(it.keys)) {
switch it.iterMode {
case dictIterModeKeys:
item = it.keys[it.index]
case dictIterModeValues:
a := *(it.a)
item = a[it.keys[it.index]]
case dictIterModeItems:
a := *(it.a)
pair := []any{it.keys[it.index], a[it.keys[it.index]]}
item = kern.NewList(pair)
}
} else {
err = io.EOF
}
return
}
func (it *DictIterator) Next() (item any, err error) {
it.index++
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *DictIterator) Index() int64 {
return it.index
}
func (it *DictIterator) Count() int64 {
return it.count
}
func (it *DictIterator) Reset() error {
it.index = -1
it.count = 0
return nil
}
func (it *DictIterator) Clean() error {
return nil
}
+1267 -202
View File
File diff suppressed because it is too large Load Diff
+1948 -288
View File
File diff suppressed because one or more lines are too long
+16
View File
@@ -0,0 +1,16 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr.go
package expr
import (
kern "git.portale-stac.it/go-pkg/expr/kern"
)
// ----Expression interface
type Expr interface {
kern.Typer
Eval(ctx kern.ExprContext) (result any, err error)
String() string
}
+230 -83
View File
@@ -7,41 +7,47 @@ package expr
import ( import (
"fmt" "fmt"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
// ---- Function template // ---- Function template
type FuncTemplate func(ctx ExprContext, name string, args []any) (result any, err error) // type FuncTemplate func(ctx expr.ExprContext, name string, args map[string]any) (result any, err error)
// ---- Common functor definition // ---- Common functor definition
type baseFunctor struct { type BaseFunctor struct {
info ExprFunc info kern.ExprFunc
} }
func (functor *baseFunctor) ToString(opt FmtOpt) (s string) { func (functor *BaseFunctor) ToString(opt kern.FmtOpt) (s string) {
if functor.info != nil { if functor.info != nil {
s = functor.info.ToString(opt) s = functor.info.ToString(opt)
} else { } else {
s = "func() {}" s = "func(){}"
} }
return s return s
} }
func (functor *baseFunctor) GetParams() (params []ExprFuncParam) { func (functor *BaseFunctor) GetParams() (params []kern.ExprFuncParam) {
if functor.info != nil { if functor.info != nil {
return functor.info.Params() return functor.info.Params()
} else { } else {
return []ExprFuncParam{} return []kern.ExprFuncParam{}
} }
} }
func (functor *baseFunctor) SetFunc(info ExprFunc) { func (functor *BaseFunctor) SetFunc(info kern.ExprFunc) {
functor.info = info functor.info = info
} }
func (functor *baseFunctor) GetFunc() ExprFunc { func (functor *BaseFunctor) GetFunc() kern.ExprFunc {
return functor.info return functor.info
} }
func (functor *BaseFunctor) GetDefinitionContext() kern.ExprContext {
return nil
}
// ---- Function Parameters // ---- Function Parameters
type paramFlags uint16 type paramFlags uint16
@@ -57,11 +63,11 @@ type funcParamInfo struct {
defaultValue any defaultValue any
} }
func NewFuncParam(name string) ExprFuncParam { func NewFuncParam(name string) kern.ExprFuncParam {
return &funcParamInfo{name: name} return &funcParamInfo{name: name}
} }
func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam { func NewFuncParamFlag(name string, flags paramFlags) kern.ExprFuncParam {
return &funcParamInfo{name: name, flags: flags} return &funcParamInfo{name: name, flags: flags}
} }
@@ -74,7 +80,7 @@ func (param *funcParamInfo) Name() string {
} }
func (param *funcParamInfo) Type() string { func (param *funcParamInfo) Type() string {
return TypeAny return kern.TypeAny
} }
func (param *funcParamInfo) IsDefault() bool { func (param *funcParamInfo) IsDefault() bool {
@@ -94,57 +100,54 @@ func (param *funcParamInfo) DefaultValue() any {
} }
// --- Functions // --- Functions
// funcInfo implements expr.ExprFunc
type funcInfo struct { type funcInfo struct {
name string name string
minArgs int minArgs int
maxArgs int maxArgs int
functor Functor functor kern.Functor
params []ExprFuncParam formalParams []kern.ExprFuncParam
returnType string returnType string
} }
func newFuncInfo(name string, functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { func newFuncInfo(name string, functor kern.Functor, returnType string, params []kern.ExprFuncParam) (info *funcInfo, err error) {
var minArgs = 0 var minArgs = 0
var maxArgs = 0 var maxArgs = 0
if params != nil { for _, p := range params {
for _, p := range params { if maxArgs == -1 {
if maxArgs == -1 { return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name())
return nil, fmt.Errorf("no more params can be specified after the ellipsis symbol: %q", p.Name()) }
} if p.IsDefault() || p.IsOptional() {
if p.IsDefault() || p.IsOptional() { maxArgs++
maxArgs++ } else if maxArgs == minArgs {
} else if maxArgs == minArgs { minArgs++
minArgs++ maxArgs++
maxArgs++ } else {
} else { return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name())
return nil, fmt.Errorf("can't specify non-optional param after optional ones: %q", p.Name()) }
} if p.IsRepeat() {
if p.IsRepeat() { minArgs--
minArgs-- maxArgs = -1
maxArgs = -1
}
} }
} }
info = &funcInfo{ info = &funcInfo{
name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, params: params, name: name, minArgs: minArgs, maxArgs: maxArgs, functor: functor, returnType: returnType, formalParams: params,
} }
functor.SetFunc(info) functor.SetFunc(info)
return info, nil return info, nil
} }
func newUnnamedFuncInfo(functor Functor, returnType string, params []ExprFuncParam) (info *funcInfo, err error) { func (info *funcInfo) Params() []kern.ExprFuncParam {
return newFuncInfo("unnamed", functor, returnType, params) return info.formalParams
}
func (info *funcInfo) Params() []ExprFuncParam {
return info.params
} }
func (info *funcInfo) ReturnType() string { func (info *funcInfo) ReturnType() string {
return info.returnType return info.returnType
} }
func (info *funcInfo) ToString(opt FmtOpt) string { func (info *funcInfo) ToString(opt kern.FmtOpt) string {
var sb strings.Builder var sb strings.Builder
if len(info.Name()) == 0 { if len(info.Name()) == 0 {
sb.WriteString("func") sb.WriteString("func")
@@ -152,8 +155,8 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
sb.WriteString(info.Name()) sb.WriteString(info.Name())
} }
sb.WriteByte('(') sb.WriteByte('(')
if info.params != nil { if info.formalParams != nil {
for i, p := range info.params { for i, p := range info.formalParams {
if i > 0 { if i > 0 {
sb.WriteString(", ") sb.WriteString(", ")
} }
@@ -178,9 +181,9 @@ func (info *funcInfo) ToString(opt FmtOpt) string {
if len(info.returnType) > 0 { if len(info.returnType) > 0 {
sb.WriteString(info.returnType) sb.WriteString(info.returnType)
} else { } else {
sb.WriteString(TypeAny) sb.WriteString(kern.TypeAny)
} }
sb.WriteString(" {}") sb.WriteString("{}")
return sb.String() return sb.String()
} }
@@ -196,45 +199,189 @@ func (info *funcInfo) MaxArgs() int {
return info.maxArgs return info.maxArgs
} }
func (info *funcInfo) Functor() Functor { func (info *funcInfo) Functor() kern.Functor {
return info.functor return info.functor
} }
func (info *funcInfo) AllocContext(parentCtx kern.ExprContext) (ctx kern.ExprContext) {
if defCtx := info.functor.GetDefinitionContext(); defCtx != nil {
ctx = defCtx.Clone()
ctx.SetParent(defCtx)
} else {
ctx = parentCtx.Clone()
ctx.SetParent(parentCtx)
}
return
}
func (info *funcInfo) ParamSpec(paramName string) kern.ExprFuncParam {
for _, spec := range info.formalParams {
if spec.Name() == paramName {
return spec
}
}
return nil
}
func initActualParams(ctx kern.ExprContext, info kern.ExprFunc, callTerm *term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
for i, tree := range callTerm.children {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.Compute(paramCtx); err != nil {
break
}
if paramName, namedParam := kern.GetAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, len(callTerm.children)-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = kern.ErrTooManyParams(info.Name(), len(formalParams), len(callTerm.children))
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
passedCount := len(actualParams)
if info.MinArgs() > passedCount {
err = kern.ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
return
}
if passedCount < len(info.formalParams) {
for _, p := range info.formalParams {
if _, exists := actualParams[p.Name()]; !exists {
if !p.IsDefault() {
break
}
if p.IsRepeat() {
varArgs := make([]any, 1)
varArgs[0] = p.DefaultValue()
actualParams[p.Name()] = varArgs
} else {
actualParams[p.Name()] = p.DefaultValue()
}
}
}
}
if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
err = kern.ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
}
return
}
// ----- Call a function --- // ----- Call a function ---
func checkFunctionCall(ctx ExprContext, name string, varParams *[]any) (err error) { // func getAssignVarName(t *term) (name string, ok bool) {
if info, exists, owner := GetFuncInfo(ctx, name); exists { // if ok = t.symbol() == SymEqual; ok {
passedCount := len(*varParams) // name = t.children[0].source()
if info.MinArgs() > passedCount { // }
err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount) // return
} // }
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) { // func CallFunctionByTerm(parentCtx expr.ExprContext, name string, callTerm *term) (result any, err error) {
ctx := cloneContext(parentCtx) // var actualParams map[string]any
ctx.SetParent(parentCtx) // if info, exists := GetFuncInfo(parentCtx, name); exists {
// if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
// ctx := info.AllocContext(parentCtx)
// if err = info.PrepareCall(name, actualParams); err == nil {
// functor := info.Functor()
// result, err = functor.InvokeNamed(ctx, name, actualParams)
// exportObjectsToParent(ctx)
// }
// }
// } else {
// err = fmt.Errorf("unknown function %s()", name)
// }
// return
// }
if err = checkFunctionCall(ctx, name, &params); err == nil { // func CallFunctionByArgs(parentCtx expr.ExprContext, name string, args []any) (result any, err error) {
result, err = ctx.Call(name, params) // var actualParams map[string]any
exportObjectsToParent(ctx) // if info, exists := GetFuncInfo(parentCtx, name); exists {
} // functor := info.Functor()
return // actualParams = bindActualParams(functor, args)
} // ctx := info.AllocContext(parentCtx)
// if err = info.PrepareCall(name, actualParams); err == nil {
// result, err = functor.InvokeNamed(ctx, name, actualParams)
// exportObjectsToParent(ctx)
// }
// } else {
// err = fmt.Errorf("unknown function %s()", name)
// }
// return
// }
// func CallFunctionByParams(parentCtx expr.ExprContext, name string, actualParams map[string]any) (result any, err error) {
// //var actualParams map[string]any
// if info, exists := GetFuncInfo(parentCtx, name); exists {
// functor := info.Functor()
// ctx := info.AllocContext(parentCtx)
// if err = info.PrepareCall(name, actualParams); err == nil {
// result, err = functor.InvokeNamed(ctx, name, actualParams)
// exportObjectsToParent(ctx)
// }
// } else {
// err = fmt.Errorf("unknown function %s()", name)
// }
// return
// }
// func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
// if value, exists = args[paramName]; !exists {
// if paramNum > 0 && paramNum <= len(args) {
// value, exists = args["arg"+strconv.Itoa(paramNum)]
// }
// }
// return
// }
// func bindActualParams(functor Functor, args []any) (actualParams map[string]any) {
// formalParams := functor.GetParams()
// actualParams = make(map[string]any, len(args))
// for i, arg := range args {
// if i < len(formalParams) {
// actualParams[formalParams[i].Name()] = arg
// } else {
// actualParams["arg"+strconv.Itoa(i+1)] = arg
// }
// }
// return
// }
+64 -81
View File
@@ -7,88 +7,68 @@ package expr
import ( import (
"path/filepath" "path/filepath"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
var globalCtx *SimpleStore //var globalCtx *SimpleStore
func ImportInContext(name string) (exists bool) { func ImportInContext(ctx kern.ExprContext, name string) (exists bool) {
var mod *builtinModule if globalCtx := ctx.GetGlobal(); globalCtx != nil {
if mod, exists = builtinModuleRegister[name]; exists { var mod *builtinModule
mod.importFunc(globalCtx) if mod, exists = builtinModuleRegister[name]; exists {
mod.imported = true mod.importFunc(globalCtx)
mod.imported = true
}
} }
return return
} }
func ImportInContextByGlobPattern(pattern string) (count int, err error) { func ImportInContextByGlobPattern(ctx kern.ExprContext, pattern string) (count int, err error) {
var matched bool if globalCtx := ctx.GetGlobal(); globalCtx != nil {
for name, mod := range builtinModuleRegister { var matched bool
if matched, err = filepath.Match(pattern, name); err == nil { for name, mod := range builtinModuleRegister {
if matched { if matched, err = filepath.Match(pattern, name); err == nil {
count++ if matched {
mod.importFunc(globalCtx) count++
mod.imported = true mod.importFunc(globalCtx)
mod.imported = true
}
} else {
break
} }
} else {
break
} }
} }
return return
} }
func GetVar(ctx ExprContext, name string) (value any, exists bool) { func GlobalCtrlSet(ctx kern.ExprContext, name string, newValue any) (currentValue any) {
if value, exists = ctx.GetVar(name); !exists { if globalCtx := ctx.GetGlobal(); globalCtx != nil {
value, exists = globalCtx.GetVar(name) if !strings.HasPrefix(name, "_") {
} name = "_" + name
return
}
func GetLocalFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool) {
var v any
if len(name) > 0 {
if v, exists = ctx.GetVar(name); exists && isFunctor(v) {
f, _ := v.(Functor)
item = f.GetFunc()
} else {
item, exists = ctx.GetFuncInfo(name)
} }
}
return
}
func GetFuncInfo(ctx ExprContext, name string) (item ExprFunc, exists bool, ownerCtx ExprContext) {
if len(name) > 0 {
if item, exists = GetLocalFuncInfo(ctx, name); exists {
ownerCtx = ctx
} else if item, exists = globalCtx.GetFuncInfo(name); exists {
ownerCtx = globalCtx
}
}
return
}
func GlobalCtrlSet(name string, newValue any) (currentValue any) { currentValue, _ = globalCtx.GetVar(name)
if !strings.HasPrefix(name, "_") { globalCtx.SetVar(name, newValue)
name = "_" + name
} }
currentValue, _ = globalCtx.GetVar(name)
globalCtx.SetVar(name, newValue)
return currentValue return currentValue
} }
func GlobalCtrlGet(name string) (currentValue any) { func GlobalCtrlGet(ctx kern.ExprContext, name string) (currentValue any) {
if !strings.HasPrefix(name, "_") { if globalCtx := ctx.GetGlobal(); globalCtx != nil {
name = "_" + name if !strings.HasPrefix(name, "_") {
name = "_" + name
}
currentValue, _ = globalCtx.GetVar(name)
} }
currentValue, _ = globalCtx.GetVar(name)
return currentValue return currentValue
} }
func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) { func CtrlEnable(ctx kern.ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") { if !strings.HasPrefix(name, "_") {
name = "_" + name name = "_" + name
} }
if v, exists := ctx.GetVar(name); exists && IsBool(v) { if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
@@ -96,11 +76,11 @@ func CtrlEnable(ctx ExprContext, name string) (currentStatus bool) {
return currentStatus return currentStatus
} }
func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) { func CtrlDisable(ctx kern.ExprContext, name string) (currentStatus bool) {
if !strings.HasPrefix(name, "_") { if !strings.HasPrefix(name, "_") {
name = "_" + name name = "_" + name
} }
if v, exists := ctx.GetVar(name); exists && IsBool(v) { if v, exists := ctx.GetVar(name); exists && kern.IsBool(v) {
currentStatus, _ = v.(bool) currentStatus, _ = v.(bool)
} }
@@ -108,36 +88,39 @@ func CtrlDisable(ctx ExprContext, name string) (currentStatus bool) {
return currentStatus return currentStatus
} }
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) { // func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
var v any // var v any
var exists bool // var exists bool
if !strings.HasPrefix(name, "_") { // if !strings.HasPrefix(name, "_") {
name = "_" + name // name = "_" + name
} // }
if v, exists = ctx.GetVar(name); !exists { // if v, exists = ctx.GetVar(name); !exists {
v, exists = globalCtx.GetVar(name) // v, exists = globalCtx.GetVar(name)
} // }
if exists { // if exists {
if b, ok := v.(bool); ok { // if b, ok := v.(bool); ok {
status = b // status = b
// }
// }
// return
// }
func getControlString(ctx kern.ExprContext, name string) (s string, exists bool) {
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
var v any
if v, exists = globalCtx.GetVar(name); exists {
s, exists = v.(string)
} }
} }
return return
} }
func getControlString(name string) (s string, exists bool) { func InitGlobal() (ctx kern.ExprContext) {
var v any ctx = NewSimpleStoreWithoutGlobalContext()
if v, exists = globalCtx.GetVar(name); exists { kern.InitDefaultVars(ctx)
s, exists = v.(string) ImportBuiltinsFuncs(ctx)
}
return return
} }
func init() {
globalCtx = NewSimpleStore()
initDefaultVars(globalCtx)
ImportBuiltinsFuncs(globalCtx)
}
+5 -1
View File
@@ -1,3 +1,7 @@
module git.portale-stac.it/go-pkg/expr module git.portale-stac.it/go-pkg/expr
go 1.21.6 go 1.22.0
toolchain go1.23.3
require golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d
+4
View File
@@ -0,0 +1,4 @@
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe h1:bWYrKmmfv37uNgXTdwkLSKYiYPJ1yfWmjBnvtMyAYzk=
github.com/yqylovy/goimportdot v0.0.0-20170519021755-eb181a7eeabe/go.mod h1:alTKUpAJ/zbp17qvZwcFNwzufrb5DljMDY4mgJlIHao=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d h1:0olWaB5pg3+oychR51GUVCEsGkeCU/2JxjBgIo4f3M0=
golang.org/x/exp v0.0.0-20241204233417-43b7b7cde48d/go.mod h1:qj5a5QZpwLU2NLQudwIN5koi3beDhSAlJwa67PuM98c=
+3 -1
View File
@@ -1,6 +1,8 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com). // Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved. // All rights reserved.
//go:build graph
// graph.go // graph.go
package expr package expr
@@ -53,7 +55,7 @@ func (r *Reticle) computeCharWidth() {
if v := ref.node.value(); v != nil { if v := ref.node.value(); v != nil {
ref.label = fmt.Sprintf("%v", v) ref.label = fmt.Sprintf("%v", v)
} else { } else {
ref.label = ref.node.source() ref.label = ref.node.Source()
} }
r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets r.colsWidth[c] = max(r.colsWidth[c], len(ref.label)+2) // +2 to make room for brakets
} }
+14 -11
View File
@@ -9,9 +9,12 @@ import (
"io" "io"
"os" "os"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/util"
) )
func EvalString(ctx ExprContext, source string) (result any, err error) { func EvalString(ctx kern.ExprContext, source string) (result any, err error) {
var tree *ast var tree *ast
r := strings.NewReader(source) r := strings.NewReader(source)
@@ -34,21 +37,21 @@ func EvalStringA(source string, args ...Arg) (result any, err error) {
} }
func EvalStringV(source string, args []Arg) (result any, err error) { func EvalStringV(source string, args []Arg) (result any, err error) {
ctx := NewSimpleStore() ctx := NewSimpleStoreWithoutGlobalContext()
for _, arg := range args { for _, arg := range args {
if isFunc(arg.Value) { if util.IsFunc(arg.Value) {
if f, ok := arg.Value.(FuncTemplate); ok { if f, ok := arg.Value.(kern.FuncTemplate); ok {
functor := NewGolangFunctor(f) functor := kern.NewGolangFunctor(f)
// ctx.RegisterFunc(arg.Name, functor, 0, -1) // ctx.RegisterFunc(arg.Name, functor, 0, -1)
ctx.RegisterFunc(arg.Name, functor, TypeAny, []ExprFuncParam{ ctx.RegisterFunc(arg.Name, functor, kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlagDef(ParamValue, PfDefault|PfRepeat, 0), NewFuncParamFlagDef(kern.ParamValue, PfDefault|PfRepeat, 0),
}) })
} else { } else {
err = fmt.Errorf("invalid function specification: %q", arg.Name) err = fmt.Errorf("invalid function specification: %q", arg.Name)
} }
} else if integer, ok := anyInteger(arg.Value); ok { } else if integer, ok := kern.AnyInteger(arg.Value); ok {
ctx.SetVar(arg.Name, integer) ctx.SetVar(arg.Name, integer)
} else if float, ok := anyFloat(arg.Value); ok { } else if float, ok := kern.AnyFloat(arg.Value); ok {
ctx.SetVar(arg.Name, float) ctx.SetVar(arg.Name, float)
} else if _, ok := arg.Value.(string); ok { } else if _, ok := arg.Value.(string); ok {
ctx.SetVar(arg.Name, arg.Value) ctx.SetVar(arg.Name, arg.Value)
@@ -65,7 +68,7 @@ func EvalStringV(source string, args []Arg) (result any, err error) {
return return
} }
func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) { func EvalStream(ctx kern.ExprContext, r io.Reader) (result any, err error) {
var tree *ast var tree *ast
scanner := NewScanner(r, DefaultTranslations()) scanner := NewScanner(r, DefaultTranslations())
parser := NewParser() parser := NewParser()
@@ -76,7 +79,7 @@ func EvalStream(ctx ExprContext, r io.Reader) (result any, err error) {
return return
} }
func EvalFile(ctx ExprContext, filePath string) (result any, err error) { func EvalFile(ctx kern.ExprContext, filePath string) (result any, err error) {
var fh *os.File var fh *os.File
if fh, err = os.Open(filePath); err != nil { if fh, err = os.Open(filePath); err != nil {
return nil, err return nil, err
+35 -12
View File
@@ -11,6 +11,9 @@ import (
"path" "path"
"path/filepath" "path/filepath"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/util"
) )
const ( const (
@@ -19,19 +22,19 @@ const (
) )
func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) { func checkStringParamExpected(funcName string, paramValue any, paramPos int) (err error) {
if !(IsString(paramValue) /*|| isList(paramValue)*/) { if !(kern.IsString(paramValue) /*|| isList(paramValue)*/) {
err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, TypeName(paramValue)) err = fmt.Errorf("%s(): param nr %d has wrong type %s, string expected", funcName, paramPos+1, kern.TypeName(paramValue))
} }
return return
} }
func addSourceEnvImportDirs(varName string, dirList []string) []string { // func addSourceEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_SOURCE_PATH, dirList)
} // }
func addPluginEnvImportDirs(varName string, dirList []string) []string { // func addPluginEnvImportDirs(varName string, dirList []string) []string {
return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList) // return addEnvImportDirs(ENV_EXPR_PLUGIN_PATH, dirList)
} // }
func addEnvImportDirs(envVarName string, dirList []string) []string { func addEnvImportDirs(envVarName string, dirList []string) []string {
if dirSpec, exists := os.LookupEnv(envVarName); exists { if dirSpec, exists := os.LookupEnv(envVarName); exists {
@@ -45,8 +48,8 @@ func addEnvImportDirs(envVarName string, dirList []string) []string {
return dirList return dirList
} }
func addSearchDirs(endingPath string, dirList []string) []string { func addSearchDirs(ctx kern.ExprContext, endingPath string, dirList []string) []string {
if dirSpec, exists := getControlString(ControlSearchPath); exists { if dirSpec, exists := getControlString(ctx, kern.ControlSearchPath); exists {
dirs := strings.Split(dirSpec, ":") dirs := strings.Split(dirSpec, ":")
if dirList == nil { if dirList == nil {
dirList = dirs dirList = dirs
@@ -63,9 +66,9 @@ func addSearchDirs(endingPath string, dirList []string) []string {
return dirList return dirList
} }
func buildSearchDirList(endingPath, envVarName string) (dirList []string) { func buildSearchDirList(ctx kern.ExprContext, endingPath, envVarName string) (dirList []string) {
dirList = addEnvImportDirs(envVarName, dirList) dirList = addEnvImportDirs(envVarName, dirList)
dirList = addSearchDirs(endingPath, dirList) dirList = addSearchDirs(ctx, endingPath, dirList)
return return
} }
@@ -75,11 +78,27 @@ func isFile(filePath string) bool {
} }
func searchAmongPath(filename string, dirList []string) (filePath string) { func searchAmongPath(filename string, dirList []string) (filePath string) {
var err error
suffix := SHAREDLIBRARY_EXTENSION
if strings.HasSuffix(filename, ".debug") {
suffix += ".debug"
}
for _, dir := range dirList { for _, dir := range dirList {
if dir, err = util.ExpandPath(dir); err != nil {
continue
}
if fullPath := path.Join(dir, filename); isFile(fullPath) { if fullPath := path.Join(dir, filename); isFile(fullPath) {
filePath = fullPath filePath = fullPath
break break
} }
subdir := strings.TrimSuffix(filename, suffix)
if fullPath := path.Join(dir, subdir, filename); isFile(fullPath) {
filePath = fullPath
break
}
} }
return return
} }
@@ -90,6 +109,10 @@ func isPathRelative(filePath string) bool {
} }
func makeFilepath(filename string, dirList []string) (filePath string, err error) { func makeFilepath(filename string, dirList []string) (filePath string, err error) {
if filename, err = util.ExpandPath(filename); err != nil {
return
}
if path.IsAbs(filename) || isPathRelative(filename) { if path.IsAbs(filename) || isPathRelative(filename) {
if isFile(filename) { if isFile(filename) {
filePath = filename filePath = filename
+137
View File
@@ -0,0 +1,137 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// int-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
)
type IntIterator struct {
count int64
index int64
start int64
stop int64
step int64
}
func NewIntIterator(args []any) (it *IntIterator, err error) {
var argc int = 0
if args != nil {
argc = len(args)
}
it = &IntIterator{count: 0, index: -1, start: 0, stop: 0, step: 1}
if argc >= 1 {
if it.stop, err = kern.ToGoInt64(args[0], "start index"); err != nil {
return
}
if argc >= 2 {
it.start = it.stop
if it.stop, err = kern.ToGoInt64(args[1], "stop index"); err != nil {
return
}
if argc >= 3 {
if it.step, err = kern.ToGoInt64(args[2], "step"); err != nil {
return
}
} else if it.start > it.stop {
it.step = -1
}
}
}
if it.step == 0 {
err = fmt.Errorf("step cannot be zero")
return
}
if it.start < it.stop && it.step < 0 {
err = fmt.Errorf("step cannot be negative when start < stop")
return
}
if it.start > it.stop && it.step > 0 {
err = fmt.Errorf("step cannot be positive when start > stop")
return
}
it.Reset()
return
}
func (it *IntIterator) String() string {
return fmt.Sprintf("$(%d..%d..%d)", it.start, it.stop, it.step)
}
func (it *IntIterator) TypeName() string {
return "IntIterator"
}
func (it *IntIterator) HasOperation(name string) bool {
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
return yes
}
func (it *IntIterator) 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 *IntIterator) Current() (item any, err error) {
if it.start <= it.stop {
if it.index >= it.start && it.index < it.stop {
item = it.index
} else {
err = io.EOF
}
} else {
if it.index > it.stop && it.index <= it.start {
item = it.index
} else {
err = io.EOF
}
}
return
}
func (it *IntIterator) Next() (item any, err error) {
it.index += it.step
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *IntIterator) Index() int64 {
return it.index
}
func (it *IntIterator) Count() int64 {
return it.count
}
func (it *IntIterator) Reset() error {
it.index = it.start - it.step
it.count = 0
return nil
}
func (it *IntIterator) Clean() error {
return nil
}
+29
View File
@@ -0,0 +1,29 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iter-factory.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
func NewIterator(value any) (it kern.Iterator, err error) {
if value == nil {
return NewArrayIterator([]any{}), nil
}
switch v := value.(type) {
case *kern.ListType:
it = NewListIterator(v, nil)
case *kern.DictType:
it, err = NewDictIterator(v, nil)
case []any:
it = NewArrayIterator(v)
case kern.Iterator:
it = v
default:
it = NewArrayIterator([]any{value})
}
return
}
-141
View File
@@ -1,141 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iter-list.go
package expr
import (
"fmt"
"io"
)
type ListIterator struct {
a *ListType
count int
index int
start int
stop int
step int
}
func NewListIterator(list *ListType, args []any) (it *ListIterator) {
var argc int = 0
listLen := len(([]any)(*list))
if args != nil {
argc = len(args)
}
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
if argc >= 1 {
if i, err := ToInt(args[0], "start index"); err == nil {
if i < 0 {
i = listLen + i
}
it.start = i
}
if argc >= 2 {
if i, err := ToInt(args[1], "stop index"); err == nil {
if i < 0 {
i = listLen + i
}
it.stop = i
}
if argc >= 3 {
if i, err := ToInt(args[2], "step"); err == nil {
if i < 0 {
i = -i
}
if it.start > it.stop {
it.step = -i
} else {
it.step = i
}
}
}
}
}
it.index = it.start - it.step
return
}
func NewArrayIterator(array []any) (it *ListIterator) {
it = &ListIterator{a: (*ListType)(&array), count: 0, index: -1, start: 0, stop: len(array) - 1, step: 1}
return
}
func NewAnyIterator(value any) (it *ListIterator) {
if value == nil {
it = NewArrayIterator([]any{})
} else if list, ok := value.(*ListType); ok {
it = NewListIterator(list, nil)
} else if array, ok := value.([]any); ok {
it = NewArrayIterator(array)
} else if it1, ok := value.(*ListIterator); ok {
it = it1
} else {
it = NewArrayIterator([]any{value})
}
return
}
func (it *ListIterator) String() string {
var l = 0
if it.a != nil {
l = len(*it.a)
}
return fmt.Sprintf("$(#%d)", l)
}
func (it *ListIterator) HasOperation(name string) bool {
yes := name == resetName || name == indexName || name == countName
return yes
}
func (it *ListIterator) CallOperation(name string, args []any) (v any, err error) {
switch name {
case resetName:
v, err = it.Reset()
case indexName:
v = int64(it.Index())
case countName:
v = it.count
default:
err = errNoOperation(name)
}
return
}
func (it *ListIterator) Current() (item any, err error) {
a := *(it.a)
if it.start <= it.stop {
if it.stop < len(a) && it.index >= it.start && it.index <= it.stop {
item = a[it.index]
} else {
err = io.EOF
}
} else {
if it.start < len(a) && it.index >= it.stop && it.index <= it.start {
item = a[it.index]
} else {
err = io.EOF
}
}
return
}
func (it *ListIterator) Next() (item any, err error) {
it.index += it.step
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *ListIterator) Index() int {
return it.index
}
func (it *ListIterator) Reset() (bool, error) {
it.index = it.start
return true, nil
}
-42
View File
@@ -1,42 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iterator.go
package expr
import (
"errors"
"fmt"
)
// Operator names
const (
initName = "init"
cleanName = "clean"
resetName = "reset"
nextName = "next"
currentName = "current"
indexName = "index"
countName = "count"
)
type Iterator interface {
Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error)
Index() int
}
type ExtIterator interface {
Iterator
HasOperation(name string) bool
CallOperation(name string, args []any) (value any, err error)
}
func errNoOperation(name string) error {
return fmt.Errorf("no %s() function defined in the data-source", name)
}
func errInvalidDataSource() error {
return errors.New("invalid data-source")
}
@@ -2,11 +2,11 @@
// All rights reserved. // All rights reserved.
// bind-go-function.go // bind-go-function.go
package expr package kern
// ---- Linking with Go functions // ---- Linking with Go functions
type golangFunctor struct { type golangFunctor struct {
baseFunctor BaseFunctor
f FuncTemplate f FuncTemplate
} }
@@ -14,6 +14,10 @@ func NewGolangFunctor(f FuncTemplate) *golangFunctor {
return &golangFunctor{f: f} return &golangFunctor{f: f}
} }
func (functor *golangFunctor) Invoke(ctx ExprContext, name string, args []any) (result any, err error) { func (functor *golangFunctor) TypeName() string {
return "GoFunctor"
}
func (functor *golangFunctor) InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error) {
return functor.f(ctx, name, args) return functor.f(ctx, name, args)
} }
+27
View File
@@ -0,0 +1,27 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
func IsBool(v any) (ok bool) {
_, ok = v.(bool)
return ok
}
func ToBool(v any) (b bool, ok bool) {
ok = true
switch x := v.(type) {
case string:
b = len(x) > 0
case float64:
b = x != 0.0
case int64:
b = x != 0
case bool:
b = x
default:
ok = false
}
return
}
+91
View File
@@ -0,0 +1,91 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// common-errors.go
package kern
import (
"fmt"
"strings"
)
func ErrMissingParams(funcName string, missing []string) (err error) {
return fmt.Errorf("%s(): missing params -- %s", funcName, strings.Join(missing, ", "))
}
func ErrTooFewParams(funcName string, minArgs, maxArgs, argCount int) (err error) {
if maxArgs < 0 {
err = fmt.Errorf("%s(): too few params -- expected %d or more, got %d", funcName, minArgs, argCount)
} else {
err = fmt.Errorf("%s(): too few params -- expected %d, got %d", funcName, minArgs, argCount)
}
return
}
func ErrTooManyParams(funcName string, maxArgs, argCount int) (err error) {
err = fmt.Errorf("%s(): too many params -- expected %d, got %d", funcName, maxArgs, argCount)
return
}
// --- General errors
func ErrCantConvert(funcName string, value any, kind string) error {
return fmt.Errorf("%s(): can't convert %s to %s", funcName, TypeName(value), kind)
}
func ErrExpectedGot(funcName string, kind string, value any) error {
return fmt.Errorf("%s(): expected %s, got %s (%#v)", funcName, kind, TypeName(value), value)
}
func ErrFuncDivisionByZero(funcName string) error {
return fmt.Errorf("%s(): division by zero", funcName)
}
// func ErrDivisionByZero() error {
// return fmt.Errorf("division by zero")
// }
// --- Parameter errors
// func ErrMissingRequiredParameter(funcName, paramName string) error {
// return fmt.Errorf("%s(): missing required parameter %q", funcName, paramName)
// }
func ErrInvalidParameterValue(funcName, paramName string, paramValue any) error {
return fmt.Errorf("%s(): invalid value %s (%#v) for parameter %q", funcName, TypeName(paramValue), paramValue, paramName)
}
func undefArticle(s string) (article string) {
if len(s) > 0 && strings.Contains("aeiou", s[0:1]) {
article = "an"
} else {
article = "a"
}
return
}
func prependUndefArticle(s string) (result string) {
return undefArticle(s) + " " + s
}
func ErrWrongParamType(funcName, paramName, paramType string, paramValue any) error {
var artWantType, artGotType string
gotType := TypeName(paramValue)
artGotType = prependUndefArticle(gotType)
artWantType = prependUndefArticle(paramType)
return fmt.Errorf("%s(): the %q parameter must be %s, got %s (%v)", funcName, paramName, artWantType, artGotType, paramValue)
}
func ErrUnknownParam(funcName, paramName string) error {
return fmt.Errorf("%s(): unknown parameter %q", funcName, paramName)
}
func ErrUnknownVar(funcName, varName string) error {
return fmt.Errorf("%s(): unknown variable %q", funcName, varName)
}
// --- Operator errors
func ErrLeftOperandMustBeVariable(leftTerm, opTerm Term) error {
return leftTerm.Errorf("left operand of %q must be a variable", opTerm.Source())
}
+6 -1
View File
@@ -2,11 +2,13 @@
// All rights reserved. // All rights reserved.
// common-params.go // common-params.go
package expr package kern
const ( const (
ParamArgs = "args"
ParamCount = "count" ParamCount = "count"
ParamItem = "item" ParamItem = "item"
ParamIndex = "index"
ParamParts = "parts" ParamParts = "parts"
ParamSeparator = "separator" ParamSeparator = "separator"
ParamSource = "source" ParamSource = "source"
@@ -19,6 +21,9 @@ const (
ParamEllipsis = "..." ParamEllipsis = "..."
ParamFilepath = "filepath" ParamFilepath = "filepath"
ParamDirpath = "dirpath" ParamDirpath = "dirpath"
ParamHandle = "handle"
ParamResource = "resource"
ParamIterator = "iterator"
) )
// to be moved in its own source file // to be moved in its own source file
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// common-type-names.go
package kern
const (
TypeAny = "any"
TypeNil = "nil"
TypeBoolean = "boolean"
TypeFloat = "float"
TypeFraction = "fraction"
TypeFileHandle = "file-handle"
TypeInt = "integer"
TypeItem = "item"
TypeIterator = "iterator"
TypeNumber = "number"
TypePair = "pair"
TypeString = "string"
TypeDict = "dict"
TypeListOf = "list-of-"
TypeListOfStrings = "list-of-strings"
)
+37
View File
@@ -0,0 +1,37 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
package kern
import "reflect"
func Equal(value1, value2 any) (equal bool) {
if value1 == nil && value2 == nil {
equal = true
} else if value1 == nil || value2 == nil {
equal = false
} else if IsBool(value1) && IsBool(value2) {
equal = value1.(bool) == value2.(bool)
} else if IsList(value1) && IsList(value2) {
ls1 := value1.(*ListType)
ls2 := value2.(*ListType)
equal = ls1.Equals(*ls2)
} else if IsDict(value1) && IsDict(value2) {
d1 := value1.(*DictType)
d2 := value2.(*DictType)
equal = d1.Equals(*d2)
} else if IsInteger(value1) && IsInteger(value2) {
equal = value1.(int64) == value2.(int64)
} else if IsString(value1) && IsString(value2) {
equal = value1.(string) == value2.(string)
} else if IsFloat(value1) && IsFloat(value2) {
equal = value1.(float64) == value2.(float64)
} else if IsNumOrFract(value1) && IsNumOrFract(value2) {
if eq, err := CmpAnyFract(value1, value2); err == nil {
equal = eq == 0
}
} else if !reflect.DeepEqual(value1, value2) {
equal = false
}
return
}
@@ -2,9 +2,9 @@
// All rights reserved. // All rights reserved.
// context-helpers.go // context-helpers.go
package expr package kern
func cloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) { func CloneContext(sourceCtx ExprContext) (clonedCtx ExprContext) {
if sourceCtx != nil { if sourceCtx != nil {
clonedCtx = sourceCtx.Clone() clonedCtx = sourceCtx.Clone()
} }
@@ -25,11 +25,11 @@ func exportFunc(ctx ExprContext, name string, info ExprFunc) {
ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params()) ctx.RegisterFunc(name, info.Functor(), info.ReturnType(), info.Params())
} }
func exportObjects(destCtx, sourceCtx ExprContext) { func ExportObjects(destCtx, sourceCtx ExprContext) {
exportAll := CtrlIsEnabled(sourceCtx, control_export_all) exportAll := CtrlIsEnabled(sourceCtx, ControlExportAll)
// fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll) // fmt.Printf("Exporting from sourceCtx [%p] to destCtx [%p] -- exportAll=%t\n", sourceCtx, destCtx, exportAll)
// Export variables // Export variables
for _, refName := range sourceCtx.EnumVars(func(name string) bool { return exportAll || name[0] == '@' }) { for _, refName := range sourceCtx.EnumVars(func(name string) bool { return (exportAll || name[0] == '@') && !(name[0] == '_') }) {
// fmt.Printf("\tExporting %q\n", refName) // fmt.Printf("\tExporting %q\n", refName)
refValue, _ := sourceCtx.GetVar(refName) refValue, _ := sourceCtx.GetVar(refName)
exportVar(destCtx, refName, refValue) exportVar(destCtx, refName, refValue)
@@ -44,6 +44,6 @@ func exportObjects(destCtx, sourceCtx ExprContext) {
func exportObjectsToParent(sourceCtx ExprContext) { func exportObjectsToParent(sourceCtx ExprContext) {
if parentCtx := sourceCtx.GetParent(); parentCtx != nil { if parentCtx := sourceCtx.GetParent(); parentCtx != nil {
exportObjects(parentCtx, sourceCtx) ExportObjects(parentCtx, sourceCtx)
} }
} }
+63
View File
@@ -0,0 +1,63 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// control.go
package kern
import "strings"
// Preset control variables
const (
ControlPreset = "_preset"
ControlLastResult = "last"
ControlBoolShortcut = "_bool_shortcut"
ControlSearchPath = "_search_path"
ControlParentContext = "_parent_context"
ControlStdout = "_stdout"
)
// Other control variables
const (
ControlExportAll = "_export_all"
)
// Initial values
const (
init_search_path = "~/.local/lib/go-pkg/expr:/usr/local/lib/go-pkg/expr:/usr/lib/go-pkg/expr"
)
func CtrlIsEnabled(ctx ExprContext, name string) (status bool) {
var v any
var exists bool
if !strings.HasPrefix(name, "_") {
name = "_" + name
}
if v, exists = ctx.GetVar(name); !exists {
if globalCtx := ctx.GetGlobal(); globalCtx != nil {
v, exists = globalCtx.GetVar(name)
}
}
if exists {
if b, ok := v.(bool); ok {
status = b
}
}
return
}
func SetCtrl(ctx ExprContext, name string, value any) (current any) {
current, _ = ctx.GetVar(name)
ctx.UnsafeSetVar(name, value)
return
}
func InitDefaultVars(ctx ExprContext) {
if _, exists := ctx.GetVar(ControlPreset); exists {
return
}
ctx.UnsafeSetVar(ControlPreset, true)
ctx.UnsafeSetVar(ControlBoolShortcut, true)
ctx.UnsafeSetVar(ControlSearchPath, init_search_path)
}
+50 -8
View File
@@ -2,7 +2,7 @@
// All rights reserved. // All rights reserved.
// dict-type.go // dict-type.go
package expr package kern
import ( import (
"fmt" "fmt"
@@ -12,13 +12,33 @@ import (
type DictType map[any]any type DictType map[any]any
func IsDict(v any) (ok bool) {
_, ok = v.(*DictType)
return ok
}
func MakeDict() (dict *DictType) { func MakeDict() (dict *DictType) {
d := make(DictType) d := make(DictType)
dict = &d dict = &d
return return
} }
func newDict(dictAny map[any]*term) (dict *DictType) { func NewDict(dictAny map[any]any) (dict *DictType) {
var d DictType
if dictAny != nil {
d = make(DictType, len(dictAny))
for i, item := range dictAny {
d[i] = item
}
} else {
d = make(DictType)
}
dict = &d
return
}
func newDict(dictAny map[any]Term) (dict *DictType) {
// TODO Change with a call to NewDict()
var d DictType var d DictType
if dictAny != nil { if dictAny != nil {
d = make(DictType, len(dictAny)) d = make(DictType, len(dictAny))
@@ -95,7 +115,7 @@ func (dict *DictType) ToString(opt FmtOpt) string {
sb.WriteString(": ") sb.WriteString(": ")
if formatter, ok := value.(Formatter); ok { if formatter, ok := value.(Formatter); ok {
sb.WriteString(formatter.ToString(opt)) sb.WriteString(formatter.ToString(opt))
} else if t, ok := value.(*term); ok { } else if t, ok := value.(Term); ok {
sb.WriteString(t.String()) sb.WriteString(t.String())
} else { } else {
sb.WriteString(fmt.Sprintf("%#v", value)) sb.WriteString(fmt.Sprintf("%#v", value))
@@ -114,7 +134,7 @@ func (dict *DictType) TypeName() string {
return "dict" return "dict"
} }
func (dict *DictType) hasKey(target any) (ok bool) { func (dict *DictType) HasKey(target any) (ok bool) {
for key := range *dict { for key := range *dict {
if ok = reflect.DeepEqual(key, target); ok { if ok = reflect.DeepEqual(key, target); ok {
break break
@@ -123,7 +143,16 @@ func (dict *DictType) hasKey(target any) (ok bool) {
return return
} }
func (dict *DictType) clone() (c *DictType) { func (dict *DictType) SetItem(key any, value any) {
(*dict)[key] = value
}
func (dict *DictType) GetItem(key any) (value any, exists bool) {
value, exists = (*dict)[key]
return
}
func (dict *DictType) Clone() (c *DictType) {
c = newDict(nil) c = newDict(nil)
for k, v := range *dict { for k, v := range *dict {
(*c)[k] = v (*c)[k] = v
@@ -131,7 +160,7 @@ func (dict *DictType) clone() (c *DictType) {
return return
} }
func (dict *DictType) merge(second *DictType) { func (dict *DictType) Merge(second *DictType) {
if second != nil { if second != nil {
for k, v := range *second { for k, v := range *second {
(*dict)[k] = v (*dict)[k] = v
@@ -139,8 +168,21 @@ func (dict *DictType) merge(second *DictType) {
} }
} }
func (dict *DictType) setItem(key any, value any) (err error) { func (dict *DictType) Equals(dict2 DictType) (answer bool) {
(*dict)[key] = value if dict2 != nil && len(*dict) == len(dict2) {
answer = true
for key, value1 := range *dict {
if value2, exists := dict2.GetItem(key); exists {
if !Equal(value1, value2) {
answer = false
break
}
} else {
answer = false
break
}
}
}
return return
} }
+31
View File
@@ -0,0 +1,31 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr-context.go
package kern
// ----Expression Context
type ExprContext interface {
Clone() ExprContext
SetParent(ctx ExprContext)
GetParent() (ctx ExprContext)
GetGlobal() (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)
VarCount() int
DeleteVar(varName string)
EnumFuncs(func(name string) (accept bool)) (funcNames []string)
FuncCount() int
DeleteFunc(funcName string)
GetLocalFuncInfo(name string) (info ExprFunc, exists bool)
GetFuncInfo(name string) (item ExprFunc, exists bool)
Call(name string, args map[string]any) (result any, err error)
RegisterFuncInfo(info ExprFunc)
RegisterFunc(name string, f Functor, returnType string, param []ExprFuncParam) (funcInfo ExprFunc, err error)
}
+39
View File
@@ -0,0 +1,39 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// expr-function.go
package kern
// ---- Functor interface
type Functor interface {
Typer
InvokeNamed(ctx ExprContext, name string, args map[string]any) (result any, err error)
SetFunc(info ExprFunc)
GetFunc() ExprFunc
GetParams() []ExprFuncParam
GetDefinitionContext() ExprContext
}
// ---- Function Param Info
type ExprFuncParam interface {
Name() string
Type() string
IsDefault() bool
IsOptional() bool
IsRepeat() bool
DefaultValue() any
}
// ---- Function Info
type ExprFunc interface {
Formatter
Name() string
MinArgs() int
MaxArgs() int
Functor() Functor
Params() []ExprFuncParam
ParamSpec(paramName string) ExprFuncParam
ReturnType() string
PrepareCall(name string, actualParams map[string]any) (err error)
AllocContext(parentCtx ExprContext) (ctx ExprContext)
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// float.go
package kern
func IsFloat(v any) (ok bool) {
_, ok = v.(float64)
return ok
}
func AnyFloat(v any) (float float64, ok bool) {
ok = true
switch floatval := v.(type) {
case float32:
float = float64(floatval)
case float64:
float = floatval
default:
ok = false
}
return
}
+2 -2
View File
@@ -2,7 +2,7 @@
// All rights reserved. // All rights reserved.
// formatter.go // formatter.go
package expr package kern
import "fmt" import "fmt"
@@ -46,7 +46,7 @@ type Formatter interface {
ToString(options FmtOpt) string ToString(options FmtOpt) string
} }
func getFormatted(v any, opt FmtOpt) (text string) { func GetFormatted(v any, opt FmtOpt) (text string) {
if v == nil { if v == nil {
text = "(nil)" text = "(nil)"
} else if s, ok := v.(string); ok { } else if s, ok := v.(string); ok {
+73 -48
View File
@@ -2,7 +2,7 @@
// All rights reserved. // All rights reserved.
// fraction-type.go // fraction-type.go
package expr package kern
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html //https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
@@ -18,12 +18,12 @@ type FractionType struct {
num, den int64 num, den int64
} }
func newFraction(num, den int64) *FractionType { func NewFraction(num, den int64) *FractionType {
num, den = simplifyIntegers(num, den) num, den = simplifyIntegers(num, den)
return &FractionType{num, den} return &FractionType{num, den}
} }
func float64ToFraction(f float64) (fract *FractionType, err error) { func Float64ToFraction(f float64) (fract *FractionType, err error) {
var sign string var sign string
intPart, decPart := math.Modf(f) intPart, decPart := math.Modf(f)
if decPart < 0.0 { if decPart < 0.0 {
@@ -33,11 +33,11 @@ func float64ToFraction(f float64) (fract *FractionType, err error) {
} }
dec := fmt.Sprintf("%.12f", decPart) dec := fmt.Sprintf("%.12f", decPart)
s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:]) s := fmt.Sprintf("%s%.f%s", sign, intPart, dec[1:])
return makeGeneratingFraction(s) return MakeGeneratingFraction(s)
} }
// Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39 // Based on https://cs.opensource.google/go/go/+/refs/tags/go1.22.3:src/math/big/rat.go;l=39
func makeGeneratingFraction(s string) (f *FractionType, err error) { func MakeGeneratingFraction(s string) (f *FractionType, err error) {
var num, den int64 var num, den int64
var sign int64 = 1 var sign int64 = 1
var parts []string var parts []string
@@ -50,15 +50,16 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
} else if s[0] == '+' { } else if s[0] == '+' {
s = s[1:] s = s[1:]
} }
if strings.HasSuffix(s, "()") { // if strings.HasSuffix(s, "()") {
s = s[0 : len(s)-2] // s = s[0 : len(s)-2]
} // }
s = strings.TrimSuffix(s, "()")
parts = strings.SplitN(s, ".", 2) parts = strings.SplitN(s, ".", 2)
if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil { if num, err = strconv.ParseInt(parts[0], 10, 64); err != nil {
return return
} }
if len(parts) == 1 { if len(parts) == 1 {
f = newFraction(sign*num, 1) f = NewFraction(sign*num, 1)
} else if len(parts) == 2 { } else if len(parts) == 2 {
subParts := strings.SplitN(parts[1], "(", 2) subParts := strings.SplitN(parts[1], "(", 2)
if len(subParts) == 1 { if len(subParts) == 1 {
@@ -75,7 +76,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
num = num*10 + int64(c-'0') num = num*10 + int64(c-'0')
den = den * 10 den = den * 10
} }
f = newFraction(sign*num, den) f = NewFraction(sign*num, den)
} else if len(subParts) == 2 { } else if len(subParts) == 2 {
sub := num sub := num
mul := int64(1) mul := int64(1)
@@ -102,7 +103,7 @@ func makeGeneratingFraction(s string) (f *FractionType, err error) {
den *= mul den *= mul
} }
num -= sub num -= sub
f = newFraction(sign*num, den) f = NewFraction(sign*num, den)
} }
} }
exit: exit:
@@ -112,7 +113,15 @@ exit:
return return
} }
func (f *FractionType) toFloat() float64 { func (f *FractionType) N() int64 {
return f.num
}
func (f *FractionType) D() int64 {
return f.den
}
func (f *FractionType) ToFloat() float64 {
return float64(f.num) / float64(f.den) return float64(f.num) / float64(f.den)
} }
@@ -123,36 +132,38 @@ func (f *FractionType) String() string {
func (f *FractionType) ToString(opt FmtOpt) string { func (f *FractionType) ToString(opt FmtOpt) string {
var sb strings.Builder var sb strings.Builder
if opt&MultiLine == 0 { if opt&MultiLine == 0 {
sb.WriteString(fmt.Sprintf("%d|%d", f.num, f.den)) sb.WriteString(fmt.Sprintf("%d:%d", f.num, f.den))
} else { } else {
var s, num string var sign, num string
if f.num < 0 && opt&TTY == 0 { if f.num < 0 && opt&TTY == 0 {
num = strconv.FormatInt(-f.num, 10) num = strconv.FormatInt(-f.num, 10)
s = "-" sign = "-"
} else { } else {
num = strconv.FormatInt(f.num, 10) num = strconv.FormatInt(f.num, 10)
} }
den := strconv.FormatInt(f.den, 10) den := strconv.FormatInt(f.den, 10)
size := max(len(num), len(den)) size := max(len(num), len(den))
if opt&TTY != 0 { if opt&TTY != 0 {
sb.WriteString(fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, s+num))) sNum := fmt.Sprintf("\x1b[4m%[1]*s\x1b[0m\n", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, sign+num))
sb.WriteString(sNum)
} else { } else {
if len(s) > 0 { if len(sign) > 0 {
sb.WriteString(" ") sb.WriteString(" ")
} }
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num))) sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(num))/2, num)))
sb.WriteByte('\n') sb.WriteByte('\n')
if len(s) > 0 { if len(sign) > 0 {
sb.WriteString(s) sb.WriteString(sign)
sb.WriteByte(' ') sb.WriteByte(' ')
} }
sb.WriteString(strings.Repeat("-", size)) sb.WriteString(strings.Repeat("-", size))
sb.WriteByte('\n') sb.WriteByte('\n')
if len(s) > 0 { if len(sign) > 0 {
sb.WriteString(" ") sb.WriteString(" ")
} }
} }
sb.WriteString(fmt.Sprintf("%[1]*s", -size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))) sDen := fmt.Sprintf("%[1]*s", size, fmt.Sprintf("%[1]*s", (size+len(den))/2, den))
sb.WriteString(sDen)
} }
return sb.String() return sb.String()
@@ -165,7 +176,7 @@ func (f *FractionType) TypeName() string {
// -------- fraction utility functions // -------- fraction utility functions
// greatest common divider // greatest common divider
func gcd(a, b int64) (g int64) { func Gcd(a, b int64) (g int64) {
if a < 0 { if a < 0 {
a = -a a = -a
} }
@@ -186,21 +197,21 @@ func gcd(a, b int64) (g int64) {
// lower common multiple // lower common multiple
func lcm(a, b int64) (l int64) { func lcm(a, b int64) (l int64) {
g := gcd(a, b) g := Gcd(a, b)
l = a * b / g l = a * b / g
return return
} }
// Sum two fractions // Sum two fractions
func sumFract(f1, f2 *FractionType) (sum *FractionType) { func SumFract(f1, f2 *FractionType) (sum *FractionType) {
m := lcm(f1.den, f2.den) m := lcm(f1.den, f2.den)
sum = newFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m) sum = NewFraction(f1.num*(m/f1.den)+f2.num*(m/f2.den), m)
return return
} }
// Multiply two fractions // Multiply two fractions
func mulFract(f1, f2 *FractionType) (prod *FractionType) { func MulFract(f1, f2 *FractionType) (prod *FractionType) {
prod = newFraction(f1.num*f2.num, f1.den*f2.den) prod = NewFraction(f1.num*f2.num, f1.den*f2.den)
return return
} }
@@ -209,11 +220,12 @@ func anyToFract(v any) (f *FractionType, err error) {
if f, ok = v.(*FractionType); !ok { if f, ok = v.(*FractionType); !ok {
if n, ok := v.(int64); ok { if n, ok := v.(int64); ok {
f = intToFraction(n) f = intToFraction(n)
} else if dec, ok := v.(float64); ok {
f, err = Float64ToFraction(dec)
} else {
err = ErrExpectedGot("fract", TypeFraction, v)
} }
} }
if f == nil {
err = ErrExpectedGot("fract", TypeFraction, v)
}
return return
} }
@@ -227,12 +239,12 @@ func anyPairToFract(v1, v2 any) (f1, f2 *FractionType, err error) {
return return
} }
func sumAnyFract(af1, af2 any) (sum any, err error) { func SumAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
} }
f := sumFract(f1, f2) f := SumFract(f1, f2)
if f.num == 0 { if f.num == 0 {
sum = 0 sum = 0
} else { } else {
@@ -247,7 +259,7 @@ func sumAnyFract(af1, af2 any) (sum any, err error) {
// =0 if af1 == af2 // =0 if af1 == af2
// >0 if af1 > af2 // >0 if af1 > af2
// err if af1 or af2 is not convertible to fraction // err if af1 or af2 is not convertible to fraction
func cmpAnyFract(af1, af2 any) (result int, err error) { func CmpAnyFract(af1, af2 any) (result int, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -262,25 +274,27 @@ func cmpAnyFract(af1, af2 any) (result int, err error) {
// =0 if af1 == af2 // =0 if af1 == af2
// >0 if af1 > af2 // >0 if af1 > af2
func cmpFract(f1, f2 *FractionType) (result int) { func cmpFract(f1, f2 *FractionType) (result int) {
f2.num = -f2.num if f1 != nil && f2 != nil {
f := sumFract(f1, f2) f2.num = -f2.num
if f.num < 0 { f := SumFract(f1, f2)
result = -1 if f.num < 0 {
} else if f.num > 0 { result = -1
result = 1 } else if f.num > 0 {
} else { result = 1
result = 0 } else {
result = 0
}
} }
return return
} }
func subAnyFract(af1, af2 any) (sum any, err error) { func SubAnyFract(af1, af2 any) (sum any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
} }
f2.num = -f2.num f2.num = -f2.num
f := sumFract(f1, f2) f := SumFract(f1, f2)
if f.num == 0 { if f.num == 0 {
sum = 0 sum = 0
} else { } else {
@@ -289,7 +303,7 @@ func subAnyFract(af1, af2 any) (sum any, err error) {
return return
} }
func mulAnyFract(af1, af2 any) (prod any, err error) { func MulAnyFract(af1, af2 any) (prod any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -303,7 +317,7 @@ func mulAnyFract(af1, af2 any) (prod any, err error) {
return return
} }
func divAnyFract(af1, af2 any) (quot any, err error) { func DivAnyFract(af1, af2 any) (quot any, err error) {
var f1, f2 *FractionType var f1, f2 *FractionType
if f1, f2, err = anyPairToFract(af1, af2); err != nil { if f1, f2, err = anyPairToFract(af1, af2); err != nil {
return return
@@ -311,7 +325,6 @@ func divAnyFract(af1, af2 any) (quot any, err error) {
if f2.num == 0 { if f2.num == 0 {
err = errors.New("division by zero") err = errors.New("division by zero")
return return
return
} }
if f1.num == 0 || f2.den == 0 { if f1.num == 0 || f2.den == 0 {
quot = 0 quot = 0
@@ -343,7 +356,7 @@ func simplifyIntegers(num, den int64) (a, b int64) {
den = -den den = -den
num = -num num = -num
} }
g := gcd(num, den) g := Gcd(num, den)
a = num / g a = num / g
b = den / g b = den / g
return return
@@ -353,7 +366,19 @@ func intToFraction(n int64) *FractionType {
return &FractionType{n, 1} return &FractionType{n, 1}
} }
func isFraction(v any) (ok bool) { func IsFraction(v any) (ok bool) {
_, ok = v.(*FractionType) _, ok = v.(*FractionType)
return ok return ok
} }
// func IsFract(v any) (ok bool) {
// _, ok = v.(*FractionType)
// return ok
// }
func IsRational(v any) (ok bool) {
if _, ok = v.(*FractionType); !ok {
_, ok = v.(int64)
}
return ok
}
+278
View File
@@ -0,0 +1,278 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// function.go
package kern
import (
"fmt"
"strconv"
)
// ---- Function templates
type FuncTemplate func(ctx ExprContext, name string, args map[string]any) (result any, err error)
type DeepFuncTemplate func(a, b any) (eq bool, err error)
func IsFunctor(v any) (ok bool) {
_, ok = v.(Functor)
return
}
// ---- Common functor definition
type BaseFunctor struct {
info ExprFunc
}
func (functor *BaseFunctor) ToString(opt FmtOpt) (s string) {
if functor.info != nil {
s = functor.info.ToString(opt)
} else {
s = "func(){}"
}
return s
}
func (functor *BaseFunctor) GetParams() (params []ExprFuncParam) {
if functor.info != nil {
return functor.info.Params()
} else {
return []ExprFuncParam{}
}
}
func (functor *BaseFunctor) SetFunc(info ExprFunc) {
functor.info = info
}
func (functor *BaseFunctor) GetFunc() ExprFunc {
return functor.info
}
func (functor *BaseFunctor) GetDefinitionContext() ExprContext {
return nil
}
// ---- Function Parameters
type paramFlags uint16
const (
PfDefault paramFlags = 1 << iota
PfOptional
PfRepeat
)
type funcParamInfo struct {
name string
flags paramFlags
defaultValue any
}
func NewFuncParam(name string) ExprFuncParam {
return &funcParamInfo{name: name}
}
func NewFuncParamFlag(name string, flags paramFlags) ExprFuncParam {
return &funcParamInfo{name: name, flags: flags}
}
func NewFuncParamFlagDef(name string, flags paramFlags, defValue any) *funcParamInfo {
return &funcParamInfo{name: name, flags: flags, defaultValue: defValue}
}
func (param *funcParamInfo) Name() string {
return param.name
}
func (param *funcParamInfo) Type() string {
return TypeAny
}
func (param *funcParamInfo) IsDefault() bool {
return (param.flags & PfDefault) != 0
}
func (param *funcParamInfo) IsOptional() bool {
return (param.flags & PfOptional) != 0
}
func (param *funcParamInfo) IsRepeat() bool {
return (param.flags & PfRepeat) != 0
}
func (param *funcParamInfo) DefaultValue() any {
return param.defaultValue
}
func initActualParams(ctx ExprContext, info ExprFunc, callTerm Term) (actualParams map[string]any, err error) {
var varArgs []any
var varName string
namedParamsStarted := false
formalParams := info.Params()
actualParams = make(map[string]any, len(formalParams))
if callTerm == nil {
return
}
childCount := callTerm.GetChildCount()
for i := range childCount {
tree := callTerm.GetChild(i)
// for i, tree := range callTerm.Children() {
var paramValue any
paramCtx := ctx.Clone()
if paramValue, err = tree.Compute(paramCtx); err != nil {
break
}
if paramName, namedParam := GetAssignVarName(tree); namedParam {
if info.ParamSpec(paramName) == nil {
err = fmt.Errorf("%s(): unknown param %q", info.Name(), paramName)
break
}
actualParams[paramName] = paramValue
namedParamsStarted = true
} else if !namedParamsStarted {
if varArgs != nil {
varArgs = append(varArgs, paramValue)
} else if i < len(formalParams) {
spec := formalParams[i]
if spec.IsRepeat() {
varArgs = make([]any, 0, childCount-i)
varArgs = append(varArgs, paramValue)
varName = spec.Name()
} else {
actualParams[spec.Name()] = paramValue
}
} else {
err = ErrTooManyParams(info.Name(), len(formalParams), childCount)
break
}
} else {
err = fmt.Errorf("%s(): positional param nr %d not allowed after named params", info.Name(), i+1)
break
}
}
if err == nil {
if varArgs != nil {
actualParams[varName] = varArgs
}
}
return
}
// func (info *funcInfo) PrepareCall(name string, actualParams map[string]any) (err error) {
// passedCount := len(actualParams)
// if info.MinArgs() > passedCount {
// err = ErrTooFewParams(name, info.MinArgs(), info.MaxArgs(), passedCount)
// return
// }
// if passedCount < len(info.formalParams) {
// for _, p := range info.formalParams {
// if _, exists := actualParams[p.Name()]; !exists {
// if !p.IsDefault() {
// break
// }
// if p.IsRepeat() {
// varArgs := make([]any, 1)
// varArgs[0] = p.DefaultValue()
// actualParams[p.Name()] = varArgs
// } else {
// actualParams[p.Name()] = p.DefaultValue()
// }
// }
// }
// }
// if info.MaxArgs() >= 0 && info.MaxArgs() < len(actualParams) {
// err = ErrTooManyParams(name, info.MaxArgs(), len(actualParams))
// }
// return
// }
// ----- Call a function ---
// func getAssignVarName(t *term) (name string, ok bool) {
// if ok = t.symbol() == SymEqual; ok {
// name = t.children[0].source()
// }
// return
// }
func GetAssignVarName(t Term) (name string, ok bool) {
if ok = t.IsAssign(); ok {
name = t.GetChildSource(0)
}
return
}
func CallFunctionByTerm(parentCtx ExprContext, name string, callTerm Term) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
if actualParams, err = initActualParams(parentCtx, info, callTerm); err == nil {
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
functor := info.Functor()
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByArgs(parentCtx ExprContext, name string, args []any) (result any, err error) {
var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
actualParams = BindActualParams(functor, args)
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func CallFunctionByParams(parentCtx ExprContext, name string, actualParams map[string]any) (result any, err error) {
//var actualParams map[string]any
if info, exists := parentCtx.GetFuncInfo(name); exists {
functor := info.Functor()
ctx := info.AllocContext(parentCtx)
if err = info.PrepareCall(name, actualParams); err == nil {
result, err = functor.InvokeNamed(ctx, name, actualParams)
exportObjectsToParent(ctx)
}
} else {
err = fmt.Errorf("unknown function %s()", name)
}
return
}
func GetParam(args map[string]any, paramName string, paramNum int) (value any, exists bool) {
if value, exists = args[paramName]; !exists {
if paramNum > 0 && paramNum <= len(args) {
value, exists = args["arg"+strconv.Itoa(paramNum)]
}
}
return
}
func BindActualParams(functor Functor, args []any) (actualParams map[string]any) {
formalParams := functor.GetParams()
actualParams = make(map[string]any, len(args))
for i, arg := range args {
if i < len(formalParams) {
actualParams[formalParams[i].Name()] = arg
} else {
actualParams["arg"+strconv.Itoa(i+1)] = arg
}
}
return
}
+52
View File
@@ -0,0 +1,52 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// iterator.go
package kern
import (
// "errors"
"fmt"
)
// Operator names
const (
InitName = "init"
CleanName = "clean"
ResetName = "reset"
NextName = "next"
CurrentName = "current"
IndexName = "index"
CountName = "count"
FilterName = "filter"
MapName = "map"
KeyName = "key"
ValueName = "value"
)
type Iterator interface {
Typer
fmt.Stringer
Next() (item any, err error) // must return io.EOF after the last item
Current() (item any, err error)
Index() int64
Count() int64
HasOperation(name string) bool
CallOperation(name string, args map[string]any) (value any, err error)
}
type ExtIterator interface {
Iterator
Reset() error
Clean() error
}
func ErrNoOperation(name string) error {
return fmt.Errorf("no %s() function defined in the data-source", name)
}
func IsIterator(v any) (ok bool) {
_, ok = v.(Iterator)
return
}
+54 -46
View File
@@ -2,7 +2,7 @@
// All rights reserved. // All rights reserved.
// list-type.go // list-type.go
package expr package kern
import ( import (
"fmt" "fmt"
@@ -12,23 +12,22 @@ import (
type ListType []any type ListType []any
func newListA(listAny ...any) (list *ListType) { func IsList(v any) (ok bool) {
_, ok = v.(*ListType)
return ok
}
func NewListA(listAny ...any) (list *ListType) {
if listAny == nil { if listAny == nil {
listAny = []any{} listAny = []any{}
} }
return newList(listAny)
}
func newList(listAny []any) (list *ListType) {
return NewList(listAny) return NewList(listAny)
} }
func NewList(listAny []any) (list *ListType) { func NewList(listAny []any) (list *ListType) {
if listAny != nil { if listAny != nil {
ls := make(ListType, len(listAny)) ls := make(ListType, len(listAny))
for i, item := range listAny { copy(ls, listAny)
ls[i] = item
}
list = &ls list = &ls
} }
return return
@@ -51,21 +50,21 @@ func ListFromStrings(stringList []string) (list *ListType) {
return return
} }
func (ls *ListType) ToString(opt FmtOpt) (s string) { func (dict *ListType) ToString(opt FmtOpt) (s string) {
indent := GetFormatIndent(opt) indent := GetFormatIndent(opt)
flags := GetFormatFlags(opt) flags := GetFormatFlags(opt)
var sb strings.Builder var sb strings.Builder
sb.WriteByte('[') sb.WriteByte('[')
if len(*ls) > 0 { if len(*dict) > 0 {
innerOpt := MakeFormatOptions(flags, indent+1) innerOpt := MakeFormatOptions(flags, indent+1)
nest := strings.Repeat(" ", indent+1) nest := strings.Repeat(" ", indent+1)
if flags&MultiLine != 0 { if flags&MultiLine != 0 {
sb.WriteByte('\n') sb.WriteByte('\n')
sb.WriteString(nest) sb.WriteString(nest)
} }
for i, item := range []any(*ls) { for i, item := range []any(*dict) {
if i > 0 { if i > 0 {
if flags&MultiLine != 0 { if flags&MultiLine != 0 {
sb.WriteString(",\n") sb.WriteString(",\n")
@@ -97,30 +96,19 @@ func (ls *ListType) ToString(opt FmtOpt) (s string) {
return return
} }
func (ls *ListType) String() string { func (dict *ListType) String() string {
return ls.ToString(0) return dict.ToString(0)
} }
func (ls *ListType) TypeName() string { func (dict *ListType) TypeName() string {
return "list" return "list"
} }
func (list *ListType) indexDeepCmp(target any) (index int) { func (dict *ListType) Contains(t *ListType) (answer bool) {
index = -1 if len(*dict) >= len(*t) {
for i, item := range *list {
if reflect.DeepEqual(item, target) {
index = i
break
}
}
return
}
func (ls *ListType) contains(t *ListType) (answer bool) {
if len(*ls) >= len(*t) {
answer = true answer = true
for _, item := range *t { for _, item := range *t {
if answer = ls.indexDeepSameCmp(item) >= 0; !answer { if answer = dict.IndexDeepSameCmp(item) >= 0; !answer {
break break
} }
} }
@@ -128,12 +116,29 @@ func (ls *ListType) contains(t *ListType) (answer bool) {
return return
} }
func (list *ListType) indexDeepSameCmp(target any) (index int) { func (ls1 *ListType) Equals(ls2 ListType) (answer bool) {
if ls2 != nil && len(*ls1) == len(ls2) {
answer = true
for index, i1 := range *ls1 {
// if !reflect.DeepEqual(i1, ls2[index]) {
// answer = false
// break
// }
if !Equal(i1, ls2[index]) {
answer = false
break
}
}
}
return
}
func (dict *ListType) IndexDeepSameCmp(target any) (index int) {
var eq bool var eq bool
var err error var err error
index = -1 index = -1
for i, item := range *list { for i, item := range *dict {
if eq, err = deepSame(item, target, sameContent); err != nil { if eq, err = deepSame(item, target, SameContent); err != nil {
break break
} else if eq { } else if eq {
index = i index = i
@@ -143,13 +148,13 @@ func (list *ListType) indexDeepSameCmp(target any) (index int) {
return return
} }
func sameContent(a, b any) (same bool, err error) { func SameContent(a, b any) (same bool, err error) {
la, _ := a.(*ListType) la, _ := a.(*ListType)
lb, _ := b.(*ListType) lb, _ := b.(*ListType)
if len(*la) == len(*lb) { if len(*la) == len(*lb) {
same = true same = true
for _, item := range *la { for _, item := range *la {
if pos := lb.indexDeepSameCmp(item); pos < 0 { if pos := lb.IndexDeepSameCmp(item); pos < 0 {
same = false same = false
break break
} }
@@ -158,19 +163,19 @@ func sameContent(a, b any) (same bool, err error) {
return return
} }
func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) { func deepSame(a, b any, deepCmp DeepFuncTemplate) (eq bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if IsNumOrFract(a) && IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if IsNumber(a) && IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if IsInteger(a) && IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
eq = li == ri eq = li == ri
} else { } else {
eq = numAsFloat(a) == numAsFloat(b) eq = NumAsFloat(a) == NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = CmpAnyFract(a, b); err == nil {
eq = cmp == 0 eq = cmp == 0
} }
} }
@@ -183,12 +188,15 @@ func deepSame(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func (list *ListType) setItem(index int64, value any) (err error) { func (dict *ListType) SetItem(index int64, value any) (err error) {
if index >= 0 && index < int64(len(*list)) { if index >= 0 && index < int64(len(*dict)) {
(*list)[index] = value (*dict)[index] = value
} else { } else {
err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*list)-1) err = fmt.Errorf("index %d out of bounds (0, %d)", index, len(*dict)-1)
} }
return return
} }
func (dict *ListType) AppendItem(value any) {
*dict = append(*dict, value)
}
+88
View File
@@ -0,0 +1,88 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// number.go
package kern
import (
"fmt"
)
func IsInteger(v any) (ok bool) {
_, ok = v.(int64)
return ok
}
func IsNumber(v any) (ok bool) {
return IsFloat(v) || IsInteger(v)
}
func IsNumOrFract(v any) (ok bool) {
return IsFloat(v) || IsInteger(v) || IsFraction(v)
}
func IsNumberString(v any) (ok bool) {
return IsString(v) || IsNumber(v)
}
func NumAsFloat(v any) (f float64) {
var ok bool
if f, ok = v.(float64); !ok {
if fract, ok := v.(*FractionType); ok {
f = fract.ToFloat()
} else {
i, _ := v.(int64)
f = float64(i)
}
}
return
}
func AnyInteger(v any) (i int64, ok bool) {
ok = true
switch intval := v.(type) {
case int:
i = int64(intval)
case uint8:
i = int64(intval)
case uint16:
i = int64(intval)
case uint64:
i = int64(intval)
case uint32:
i = int64(intval)
case int8:
i = int64(intval)
case int16:
i = int64(intval)
case int32:
i = int64(intval)
case int64:
i = intval
default:
ok = false
}
return
}
func ToGoInt(value any, description string) (i int, err error) {
if valueInt64, ok := value.(int64); ok {
i = int(valueInt64)
} else if valueInt, ok := value.(int); ok {
i = valueInt
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
func ToGoInt64(value any, description string) (i int64, err error) {
if valueInt64, ok := value.(int64); ok {
i = valueInt64
} else if valueInt, ok := value.(int); ok {
i = int64(valueInt)
} else {
err = fmt.Errorf("%s expected integer, got %s (%v)", description, TypeName(value), value)
}
return
}
+23
View File
@@ -0,0 +1,23 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// string.go
package kern
import (
"fmt"
)
func IsString(v any) (ok bool) {
_, ok = v.(string)
return ok
}
func ToGoString(value any, description string) (s string, err error) {
if s, ok := value.(string); ok {
return s, nil
} else {
err = fmt.Errorf("%s expected string, got %s (%v)", description, TypeName(value), value)
}
return
}
+21
View File
@@ -0,0 +1,21 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// term.go
package kern
import (
"fmt"
)
type Term interface {
fmt.Stringer
// Children() []Term
Source() string
GetChildCount() (count int)
GetChild(index int) Term
GetChildSource(index int) string
Compute(ctx ExprContext) (result any, err error)
IsAssign() bool
Errorf(template string, args ...any) (err error)
}
+9
View File
@@ -0,0 +1,9 @@
//go:build darwin
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-darwin.go
package expr
const SHAREDLIBRARY_EXTENSION = ".dylib"
+9
View File
@@ -0,0 +1,9 @@
//go:build linux
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-linux.go
package expr
const SHAREDLIBRARY_EXTENSION = ".so"
+9
View File
@@ -0,0 +1,9 @@
//go:build windows
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// lib-ext-windows.go
package expr
const SHAREDLIBRARY_EXTENSION = ".dll"
+149
View File
@@ -0,0 +1,149 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// list-iterator.go
package expr
import (
"fmt"
"io"
"slices"
"git.portale-stac.it/go-pkg/expr/kern"
)
type ListIterator struct {
a *kern.ListType
count int64
index int64
start int64
stop int64
step int64
}
func NewListIterator(list *kern.ListType, args []any) (it *ListIterator) {
var argc int = 0
listLen := int64(len(([]any)(*list)))
if args != nil {
argc = len(args)
}
it = &ListIterator{a: list, count: 0, index: -1, start: 0, stop: listLen - 1, step: 1}
if argc >= 1 {
if i, err := kern.ToGoInt64(args[0], "start index"); err == nil {
if i < 0 {
i = listLen + i
}
it.start = i
}
if argc >= 2 {
if i, err := kern.ToGoInt64(args[1], "stop index"); err == nil {
if i < 0 {
i = listLen + i
}
it.stop = i
}
if argc >= 3 {
if i, err := kern.ToGoInt64(args[2], "step"); err == nil {
if i < 0 {
i = -i
}
if it.start > it.stop {
it.step = -i
} else {
it.step = i
}
}
}
}
}
it.index = it.start - it.step
return
}
func NewArrayIterator(array []any) (it *ListIterator) {
it = &ListIterator{a: (*kern.ListType)(&array), count: 0, index: -1, start: 0, stop: int64(len(array)) - 1, step: 1}
return
}
func (it *ListIterator) String() string {
var l = int64(0)
if it.a != nil {
l = int64(len(*it.a))
}
return fmt.Sprintf("$([#%d])", l)
}
func (it *ListIterator) TypeName() string {
return "ListIterator"
}
func (it *ListIterator) HasOperation(name string) bool {
//yes := name == expr.NextName || name == expr.ResetName || name == expr.IndexName || name == expr.CountName || name == expr.CurrentName
yes := slices.Contains([]string{kern.NextName, kern.ResetName, kern.IndexName, kern.CountName, kern.CurrentName}, name)
return yes
}
func (it *ListIterator) 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 *ListIterator) Current() (item any, err error) {
a := *(it.a)
if it.start <= it.stop {
if it.stop < int64(len(a)) && it.index >= it.start && it.index <= it.stop {
item = a[it.index]
} else {
err = io.EOF
}
} else {
if it.start < int64(len(a)) && it.index >= it.stop && it.index <= it.start {
item = a[it.index]
} else {
err = io.EOF
}
}
return
}
func (it *ListIterator) Next() (item any, err error) {
it.index += it.step
if item, err = it.Current(); err != io.EOF {
it.count++
}
return
}
func (it *ListIterator) Index() int64 {
return it.index
}
func (it *ListIterator) Count() int64 {
return it.count
}
func (it *ListIterator) Reset() error {
it.index = it.start - it.step
it.count = 0
return nil
}
func (it *ListIterator) Clean() error {
return nil
}
+7 -4
View File
@@ -4,6 +4,9 @@
// operand-dict.go // operand-dict.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- dict term // -------- dict term
func newDictTerm(args map[any]*term) *term { func newDictTerm(args map[any]*term) *term {
@@ -18,12 +21,12 @@ func newDictTerm(args map[any]*term) *term {
} }
// -------- dict func // -------- dict func
func evalDict(ctx ExprContext, self *term) (v any, err error) { func evalDict(ctx kern.ExprContext, opTerm *term) (v any, err error) {
dict, _ := self.value().(map[any]*term) dict, _ := opTerm.value().(map[any]*term)
items := make(DictType, len(dict)) items := make(kern.DictType, len(dict))
for key, tree := range dict { for key, tree := range dict {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
} }
items[key] = param items[key] = param
+10 -6
View File
@@ -4,11 +4,15 @@
// operand-expr.go // operand-expr.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- expr term // -------- expr term
func newExprTerm(root *term) *term { func newExprTerm(root *term) *term {
tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.source(), root) tk := NewValueToken(root.tk.row, root.tk.col, SymExpression, root.Source(), root)
return &term{ return &term{
tk: *tk, tk: *tk,
parent: nil, parent: nil,
@@ -20,11 +24,11 @@ func newExprTerm(root *term) *term {
} }
// -------- eval expr // -------- eval expr
func evalExpr(ctx ExprContext, self *term) (v any, err error) { func evalExpr(ctx kern.ExprContext, opTerm *term) (v any, err error) {
if expr, ok := self.value().(*term); ok { if ast, ok := opTerm.value().(*term); ok {
v, err = expr.compute(ctx) v, err = ast.Compute(ctx)
} else { } else {
err = fmt.Errorf("expression expected, got %T", self.value()) err = fmt.Errorf("expression expected, got %T", opTerm.value())
} }
return return
} }
+34 -22
View File
@@ -6,35 +6,47 @@ package expr
import ( import (
"errors" "errors"
"git.portale-stac.it/go-pkg/expr/kern"
) )
// -------- function call term // -------- function call term
func newFuncCallTerm(tk *Token, args []*term) *term { func newFuncCallTerm(tk *Token, args []*term) *term {
var pos termPosition = posLeaf
if len(args) > 0 {
pos = posMultifix
}
return &term{ return &term{
tk: *tk, tk: *tk,
parent: nil, parent: nil,
children: args, children: args,
position: posLeaf, position: pos,
priority: priValue, priority: priValue,
evalFunc: evalFuncCall, evalFunc: evalFuncCall,
} }
} }
// -------- eval func call // -------- eval func call
func evalFuncCall(ctx ExprContext, self *term) (v any, err error) { // func _evalFuncCall(ctx ExprContext, opTerm *term) (v any, err error) {
name, _ := self.tk.Value.(string) // name, _ := opTerm.tk.Value.(string)
params := make([]any, len(self.children), len(self.children)+5) // params := make([]any, len(opTerm.children), len(opTerm.children)+5)
for i, tree := range self.children { // for i, tree := range opTerm.children {
var param any // var param any
if param, err = tree.compute(ctx); err != nil { // if param, err = tree.compute(ctx); err != nil {
break // break
} // }
params[i] = param // params[i] = param
} // }
if err == nil { // if err == nil {
v, err = CallFunction(ctx, name, params) // v, err = CallFunction(ctx, name, params)
} // }
// return
// }
func evalFuncCall(ctx kern.ExprContext, opTerm *term) (v any, err error) {
name, _ := opTerm.tk.Value.(string)
v, err = kern.CallFunctionByTerm(ctx, name, opTerm)
return return
} }
@@ -51,23 +63,23 @@ func newFuncDefTerm(tk *Token, args []*term) *term {
} }
// -------- eval func def // -------- eval func def
func evalFuncDef(ctx ExprContext, self *term) (v any, err error) { func evalFuncDef(ctx kern.ExprContext, opTerm *term) (v any, err error) {
bodySpec := self.value() bodySpec := opTerm.value()
if expr, ok := bodySpec.(*ast); ok { if ast, ok := bodySpec.(*ast); ok {
paramList := make([]ExprFuncParam, 0, len(self.children)) paramList := make([]kern.ExprFuncParam, 0, len(opTerm.children))
for _, param := range self.children { for _, param := range opTerm.children {
var defValue any var defValue any
flags := paramFlags(0) flags := paramFlags(0)
if len(param.children) > 0 { if len(param.children) > 0 {
flags |= PfDefault flags |= PfDefault
if defValue, err = param.children[0].compute(ctx); err != nil { if defValue, err = param.children[0].Compute(ctx); err != nil {
return return
} }
} }
info := NewFuncParamFlagDef(param.source(), flags, defValue) info := NewFuncParamFlagDef(param.Source(), flags, defValue)
paramList = append(paramList, info) paramList = append(paramList, info)
} }
v = newExprFunctor(expr, paramList, ctx) v = newExprFunctor(ast, paramList, ctx)
} else { } else {
err = errors.New("invalid function definition: the body specification must be an expression") err = errors.New("invalid function definition: the body specification must be an expression")
} }
+62 -60
View File
@@ -5,29 +5,14 @@
package expr package expr
import ( import (
"fmt"
"slices" "slices"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
// -------- iterator term // -------- iterator term
func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term {
tk.Sym = SymIterator
children := make([]*term, 0, 1+len(args))
children = append(children, dsTerm)
children = append(children, args...)
return &term{
tk: *tk,
parent: nil,
children: children,
position: posLeaf,
priority: priValue,
evalFunc: evalIterator,
}
}
func newIteratorTerm(tk *Token, args []*term) *term { func newIteratorTerm(tk *Token, args []*term) *term {
tk.Sym = SymIterator tk.Sym = SymIterator
return &term{ return &term{
@@ -42,11 +27,11 @@ func newIteratorTerm(tk *Token, args []*term) *term {
// -------- eval iterator // -------- eval iterator
func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { func evalTermArray(ctx kern.ExprContext, terms []*term) (values []any, err error) {
values = make([]any, len(a)) values = make([]any, len(terms))
for i, t := range a { for i, t := range terms {
var value any var value any
if value, err = t.compute(ctx); err == nil { if value, err = t.Compute(ctx); err == nil {
values[i] = value values[i] = value
} else { } else {
break break
@@ -55,27 +40,25 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) {
return return
} }
func evalFirstChild(ctx ExprContext, self *term) (value any, err error) { func evalFirstChild(ctx kern.ExprContext, iteratorTerm *term) (value any, err error) {
if len(self.children) < 1 || self.children[0] == nil { if len(iteratorTerm.children) < 1 || iteratorTerm.children[0] == nil {
err = self.Errorf("missing the data-source parameter") err = iteratorTerm.Errorf("missing the data-source parameter")
return return
} }
value, err = self.children[0].compute(ctx) value, err = iteratorTerm.children[0].Compute(ctx)
return return
} }
func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { func getDataSourceDict(iteratorTerm *term, firstChildValue any) (ds map[string]kern.Functor, err error) {
// if dictAny, ok := firstChildValue.(map[any]any); ok { if dictAny, ok := firstChildValue.(*kern.DictType); ok {
if dictAny, ok := firstChildValue.(*DictType); ok { requiredFields := []string{kern.NextName}
requiredFields := []string{currentName, nextName} fieldsMask := 0b1
fieldsMask := 0b11
foundFields := 0 foundFields := 0
ds = make(map[string]Functor) ds = make(map[string]kern.Functor)
for keyAny, item := range *dictAny { for keyAny, item := range *dictAny {
if key, ok := keyAny.(string); ok { if key, ok := keyAny.(string); ok {
//if functor, ok := item.(*funcDefFunctor); ok { if functor, ok := item.(kern.Functor); ok {
if functor, ok := item.(Functor); ok {
ds[key] = functor ds[key] = functor
if index := slices.Index(requiredFields, key); index >= 0 { if index := slices.Index(requiredFields, key); index >= 0 {
foundFields |= 1 << index foundFields |= 1 << index
@@ -91,64 +74,83 @@ func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map
missingFields = append(missingFields, field) missingFields = append(missingFields, field)
} }
} }
err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) err = iteratorTerm.children[0].Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", "))
} }
} }
return return
} }
func evalIterator(ctx ExprContext, self *term) (v any, err error) { func evalIterator(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var firstChildValue any var firstChildValue any
var ds map[string]Functor var ds map[string]kern.Functor
if firstChildValue, err = evalFirstChild(ctx, self); err != nil { if firstChildValue, err = evalFirstChild(ctx, opTerm); err != nil {
return return
} }
if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil { if ds, err = getDataSourceDict(opTerm, firstChildValue); err != nil && ds == nil {
return return
} }
err = nil
if ds != nil { if ds != nil {
dc := newDataCursor(ctx, ds) if len(ds) > 0 {
if initFunc, exists := ds[initName]; exists && initFunc != nil { var dc *dataCursor
var args []any dcCtx := ctx.Clone()
if len(self.children) > 1 { if initFunc, exists := ds[kern.InitName]; exists && initFunc != nil {
if args, err = evalTermArray(ctx, self.children[1:]); err != nil { var args []any
var resource any
if len(opTerm.children) > 1 {
if args, err = evalTermArray(ctx, opTerm.children[1:]); err != nil {
return
}
} else {
args = []any{}
}
actualParams := kern.BindActualParams(initFunc, args)
initCtx := ctx.Clone()
if resource, err = initFunc.InvokeNamed(initCtx, kern.InitName, actualParams); err != nil {
return return
} }
kern.ExportObjects(dcCtx, initCtx)
dc = NewDataCursor(dcCtx, ds, resource)
} else { } else {
args = []any{} dc = NewDataCursor(dcCtx, ds, nil)
} }
initCtx := dc.ctx.Clone() v = dc
if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { } else {
return if dictIt, ok := firstChildValue.(*kern.DictType); ok {
var args []any
if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
v, err = NewDictIterator(dictIt, args)
}
} else {
err = opTerm.children[0].Errorf("the data-source must be a dictionary")
} }
exportObjects(dc.ctx, initCtx)
} }
} else if list, ok := firstChildValue.(*kern.ListType); ok {
dc.nextFunc, _ = ds[nextName]
dc.currentFunc, _ = ds[currentName]
dc.cleanFunc, _ = ds[cleanName]
dc.resetFunc, _ = ds[resetName]
v = dc
} else if list, ok := firstChildValue.(*ListType); ok {
var args []any var args []any
if args, err = evalSibling(ctx, self.children, nil); err == nil { if args, err = evalSibling(ctx, opTerm.children, nil); err == nil {
v = NewListIterator(list, args) v = NewListIterator(list, args)
} }
} else if intVal, ok := firstChildValue.(int64); ok {
var args []any
if args, err = evalSibling(ctx, opTerm.children, intVal); err == nil {
v, err = NewIntIterator(args)
}
} else { } else {
var list []any var list []any
if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil { if list, err = evalSibling(ctx, opTerm.children, firstChildValue); err == nil {
v = NewArrayIterator(list) v = NewArrayIterator(list)
} }
} }
return return
} }
func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []any, err error) { func evalSibling(ctx kern.ExprContext, terms []*term, firstChildValue any) (list []any, err error) {
items := make([]any, 0, len(terms)) items := make([]any, 0, len(terms))
for i, tree := range terms { for i, tree := range terms {
var param any var param any
@@ -157,7 +159,7 @@ func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []an
continue continue
} }
param = firstChildValue param = firstChildValue
} else if param, err = tree.compute(ctx); err != nil { } else if param, err = tree.Compute(ctx); err != nil {
break break
} }
items = append(items, param) items = append(items, param)
+8 -4
View File
@@ -4,6 +4,10 @@
// operand-list.go // operand-list.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- list term // -------- list term
func newListTermA(args ...*term) *term { func newListTermA(args ...*term) *term {
return newListTerm(0, 0, args) return newListTerm(0, 0, args)
@@ -21,12 +25,12 @@ func newListTerm(row, col int, args []*term) *term {
} }
// -------- list func // -------- list func
func evalList(ctx ExprContext, self *term) (v any, err error) { func evalList(ctx kern.ExprContext, opTerm *term) (v any, err error) {
list, _ := self.value().([]*term) list, _ := opTerm.value().([]*term)
items := make(ListType, len(list)) items := make(kern.ListType, len(list))
for i, tree := range list { for i, tree := range list {
var param any var param any
if param, err = tree.compute(ctx); err != nil { if param, err = tree.Compute(ctx); err != nil {
break break
} }
items[i] = param items[i] = param
+6 -2
View File
@@ -4,6 +4,10 @@
// operand-literal.go // operand-literal.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- literal term // -------- literal term
func newLiteralTerm(tk *Token) *term { func newLiteralTerm(tk *Token) *term {
return &term{ return &term{
@@ -17,8 +21,8 @@ func newLiteralTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalLiteral(ctx ExprContext, self *term) (v any, err error) { func evalLiteral(ctx kern.ExprContext, opTerm *term) (v any, err error) {
v = self.tk.Value v = opTerm.tk.Value
return return
} }
+5 -3
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"fmt" "fmt"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
// -------- selector case term // -------- selector case term
@@ -41,10 +43,10 @@ func newSelectorCaseTerm(row, col int, filterList *term, caseExpr Expr) *term {
} }
// -------- eval selector case // -------- eval selector case
func evalSelectorCase(ctx ExprContext, self *term) (v any, err error) { func evalSelectorCase(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var ok bool var ok bool
if v, ok = self.value().(*selectorCase); !ok { if v, ok = opTerm.value().(*selectorCase); !ok {
err = fmt.Errorf("selector-case expected, got %T", self.value()) err = fmt.Errorf("selector-case expected, got %T", opTerm.value())
} }
return return
} }
+9 -5
View File
@@ -4,7 +4,11 @@
// operand-var.go // operand-var.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- variable term // -------- variable term
func newVarTerm(tk *Token) *term { func newVarTerm(tk *Token) *term {
@@ -21,11 +25,11 @@ func newVarTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalVar(ctx ExprContext, self *term) (v any, err error) { func evalVar(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var exists bool var exists bool
name := self.source() name := opTerm.Source()
if v, exists = GetVar(ctx, name); !exists { if v, exists = ctx.GetVar(name); !exists {
if info, exists, _ := GetFuncInfo(ctx, name); exists { if info, exists := ctx.GetFuncInfo(name); exists {
v = info.Functor() v = info.Functor()
} else { } else {
err = fmt.Errorf("undefined variable or function %q", name) err = fmt.Errorf("undefined variable or function %q", name)
+150 -26
View File
@@ -4,6 +4,11 @@
// operator-assign.go // operator-assign.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
"git.portale-stac.it/go-pkg/expr/util"
)
//-------- assign term //-------- assign term
func newAssignTerm(tk *Token) (inst *term) { func newAssignTerm(tk *Token) (inst *term) {
@@ -16,19 +21,19 @@ func newAssignTerm(tk *Token) (inst *term) {
} }
} }
func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, value any) (err error) { func assignCollectionItem(ctx kern.ExprContext, collectionTerm, keyListTerm *term, value any) (err error) {
var collectionValue, keyListValue, keyValue any var collectionValue, keyListValue, keyValue any
var keyList *ListType var keyList *kern.ListType
var ok bool var ok bool
if collectionValue, err = collectionTerm.compute(ctx); err != nil { if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
return return
} }
if keyListValue, err = keyListTerm.compute(ctx); err != nil { if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return return
} else if keyList, ok = keyListValue.(*ListType); !ok || len(*keyList) != 1 { } else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, TypeName(keyListValue)) err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return return
} }
if keyValue = (*keyList)[0]; keyValue == nil { if keyValue = (*keyList)[0]; keyValue == nil {
@@ -37,53 +42,57 @@ func assignCollectionItem(ctx ExprContext, collectionTerm, keyListTerm *term, va
} }
switch collection := collectionValue.(type) { switch collection := collectionValue.(type) {
case *ListType: case *kern.ListType:
if index, ok := keyValue.(int64); ok { if index, ok := keyValue.(int64); ok {
err = collection.setItem(index, value) err = collection.SetItem(index, value)
} else { } else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, TypeName(keyValue)) err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
} }
case *DictType: case *kern.DictType:
err = collection.setItem(keyValue, value) collection.SetItem(keyValue, value)
default: default:
err = collectionTerm.Errorf("collection expected") err = collectionTerm.Errorf("collection expected")
} }
return return
} }
func assignValue(ctx ExprContext, leftTerm *term, v any) (err error) { func assignValue(ctx kern.ExprContext, leftTerm *term, v any) (err error) {
if leftTerm.symbol() == SymIndex { if leftTerm.symbol() == SymIndex {
err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v) err = assignCollectionItem(ctx, leftTerm.children[0], leftTerm.children[1], v)
} else { } else {
ctx.UnsafeSetVar(leftTerm.source(), v) ctx.UnsafeSetVar(leftTerm.Source(), v)
} }
return return
} }
func evalAssign(ctx ExprContext, self *term) (v any, err error) { func evalAssign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
leftTerm := self.children[0] leftTerm := opTerm.children[0]
leftSym := leftTerm.symbol() leftSym := leftTerm.symbol()
if leftSym != SymVariable && leftSym != SymIndex { if leftSym != SymVariable && leftSym != SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", self.tk.source) err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
return return
} }
rightChild := self.children[1] rightChild := opTerm.children[1]
if v, err = rightChild.compute(ctx); err == nil { if v, err = rightChild.Compute(ctx); err == nil {
if functor, ok := v.(Functor); ok { if functor, ok := v.(kern.Functor); ok {
if info := functor.GetFunc(); info != nil { if leftSym == SymVariable {
ctx.RegisterFunc(leftTerm.source(), info.Functor(), info.ReturnType(), info.Params()) if info := functor.GetFunc(); info != nil {
} else if funcDef, ok := functor.(*exprFunctor); ok { ctx.RegisterFunc(leftTerm.Source(), info.Functor(), info.ReturnType(), info.Params())
paramSpecs := ForAll(funcDef.params, func(p ExprFuncParam) ExprFuncParam { return p }) } else if funcDef, ok := functor.(*exprFunctor); ok {
paramSpecs := util.ForAll(funcDef.params, func(p kern.ExprFuncParam) kern.ExprFuncParam { return p })
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, paramSpecs) ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, paramSpecs)
} else {
err = opTerm.Errorf("unknown function %s()", rightChild.Source())
}
} else { } else {
err = self.Errorf("unknown function %s()", rightChild.source()) err = assignValue(ctx, leftTerm, v)
} }
} else { } else {
err = assignValue(ctx, leftTerm, v) err = assignValue(ctx, leftTerm, v)
@@ -95,7 +104,122 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) {
return return
} }
//-------- assign term
func newOpAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priAssign,
evalFunc: evalOpAssign,
}
}
func getCollectionItemValue(ctx kern.ExprContext, collectionTerm, keyListTerm *term) (value any, err error) {
var collectionValue, keyListValue, keyValue any
var keyList *kern.ListType
var ok bool
if collectionValue, err = collectionTerm.Compute(ctx); err != nil {
return
}
if keyListValue, err = keyListTerm.Compute(ctx); err != nil {
return
} else if keyList, ok = keyListValue.(*kern.ListType); !ok || len(*keyList) != 1 {
err = keyListTerm.Errorf("index/key specification expected, got %v [%s]", keyListValue, kern.TypeName(keyListValue))
return
}
if keyValue = (*keyList)[0]; keyValue == nil {
err = keyListTerm.Errorf("index/key is nil")
return
}
switch collection := collectionValue.(type) {
case *kern.ListType:
if index, ok := keyValue.(int64); ok {
value = (*collection)[index]
} else {
err = keyListTerm.Errorf("integer expected, got %v [%s]", keyValue, kern.TypeName(keyValue))
}
case *kern.DictType:
value = (*collection)[keyValue]
default:
err = collectionTerm.Errorf("collection expected")
}
return
}
func getAssignValue(ctx kern.ExprContext, leftTerm *term) (value any, err error) {
if leftTerm.symbol() == SymIndex {
value, err = getCollectionItemValue(ctx, leftTerm.children[0], leftTerm.children[1])
} else {
value, _ = ctx.GetVar(leftTerm.Source())
}
return
}
func evalOpAssign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue, leftValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
leftSym := leftTerm.symbol()
if leftSym != SymVariable && leftSym != SymIndex {
err = leftTerm.tk.Errorf("left operand of %q must be a variable or a collection's item", opTerm.tk.source)
return
}
rightChild := opTerm.children[1]
if rightValue, err = rightChild.Compute(ctx); err == nil {
if leftValue, err = getAssignValue(ctx, leftTerm); err == nil {
switch opTerm.symbol() {
case SymPlusEqual:
v, err = sumValues(opTerm, leftValue, rightValue)
case SymMinusEqual:
v, err = diffValues(opTerm, leftValue, rightValue)
case SymStarEqual:
v, err = mulValues(opTerm, leftValue, rightValue)
case SymSlashEqual:
v, err = divValues(opTerm, leftValue, rightValue)
case SymPercEqual:
v, err = remainderValues(opTerm, leftValue, rightValue)
case SymAmpersandEqual:
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
case SymVertBarEqual:
v, err = bitwiseOr(opTerm, leftValue, rightValue)
case SymCaretEqual:
v, err = bitwiseXor(opTerm, leftValue, rightValue)
case SymDoubleLessEqual:
v, err = bitLeftShift(opTerm, leftValue, rightValue)
case SymDoubleGreaterEqual:
v, err = bitRightShift(opTerm, leftValue, rightValue)
default:
err = opTerm.Errorf("unsupported assign operator %q", opTerm.Source())
}
if err == nil {
err = assignValue(ctx, leftTerm, v)
}
}
}
return
}
// init // init
func init() { func init() {
registerTermConstructor(SymEqual, newAssignTerm) registerTermConstructor(SymEqual, newAssignTerm)
registerTermConstructor(SymPlusEqual, newOpAssignTerm)
registerTermConstructor(SymMinusEqual, newOpAssignTerm)
registerTermConstructor(SymStarEqual, newOpAssignTerm)
registerTermConstructor(SymSlashEqual, newOpAssignTerm)
registerTermConstructor(SymPercEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleLessEqual, newOpAssignTerm)
registerTermConstructor(SymDoubleGreaterEqual, newOpAssignTerm)
registerTermConstructor(SymAmpersandEqual, newOpAssignTerm)
registerTermConstructor(SymVertBarEqual, newOpAssignTerm)
registerTermConstructor(SymCaretEqual, newOpAssignTerm)
} }
+158
View File
@@ -0,0 +1,158 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-bitwise.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- Bitwise NOT term
func newBitwiseNotTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priBitwiseNot,
evalFunc: evalBitwiseNot,
}
}
func evalBitwiseNot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var value any
if value, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if kern.IsInteger(value) {
i, _ := value.(int64)
v = ^i
} else {
err = opTerm.errIncompatiblePrefixPostfixType(value)
}
return
}
//-------- Bitwise AND term
func newBitwiseAndTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseAnd,
evalFunc: evalBitwiseAnd,
}
}
func bitwiseAnd(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt & rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseAnd(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseAnd(opTerm, leftValue, rightValue)
return
}
//-------- Bitwise OR term
func newBitwiseOrTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseOr,
evalFunc: evalBitwiseOr,
}
}
func bitwiseOr(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt | rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseOr(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseOr(opTerm, leftValue, rightValue)
return
}
//-------- Bitwise XOR term
func newBitwiseXorTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBitwiseOr,
evalFunc: evalBitwiseXor,
}
}
func bitwiseXor(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftInt, rightInt int64
var lok, rok bool
leftInt, lok = leftValue.(int64)
rightInt, rok = rightValue.(int64)
if lok && rok {
v = leftInt ^ rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalBitwiseXor(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitwiseXor(opTerm, leftValue, rightValue)
return
}
// init
func init() {
registerTermConstructor(SymTilde, newBitwiseNotTerm)
registerTermConstructor(SymAmpersand, newBitwiseAndTerm)
registerTermConstructor(SymVertBar, newBitwiseOrTerm)
registerTermConstructor(SymCaret, newBitwiseXorTerm)
}
+35 -34
View File
@@ -4,7 +4,11 @@
// operator-bool.go // operator-bool.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- NOT term //-------- NOT term
@@ -18,17 +22,17 @@ func newNotTerm(tk *Token) (inst *term) {
} }
} }
func evalNot(ctx ExprContext, self *term) (v any, err error) { func evalNot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if b, ok := ToBool(rightValue); ok { if b, ok := kern.ToBool(rightValue); ok {
v = !b v = !b
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatiblePrefixPostfixType(rightValue)
} }
return return
} }
@@ -37,9 +41,7 @@ func evalNot(ctx ExprContext, self *term) (v any, err error) {
func newAndTerm(tk *Token) (inst *term) { func newAndTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindBool,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priAnd, priority: priAnd,
@@ -47,8 +49,8 @@ func newAndTerm(tk *Token) (inst *term) {
} }
} }
func evalAnd(ctx ExprContext, self *term) (v any, err error) { func evalAnd(ctx kern.ExprContext, self *term) (v any, err error) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
v, err = evalAndWithShortcut(ctx, self) v, err = evalAndWithShortcut(ctx, self)
} else { } else {
v, err = evalAndWithoutShortcut(ctx, self) v, err = evalAndWithoutShortcut(ctx, self)
@@ -56,7 +58,7 @@ func evalAnd(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { func evalAndWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var leftBool, rightBool bool var leftBool, rightBool bool
var lok, rok bool var lok, rok bool
@@ -65,8 +67,8 @@ func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
leftBool, lok = ToBool(leftValue) leftBool, lok = kern.ToBool(leftValue)
rightBool, rok = ToBool(rightValue) rightBool, rok = kern.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool && rightBool v = leftBool && rightBool
@@ -76,24 +78,25 @@ func evalAndWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) { func evalAndWithShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = self.checkOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = self.children[0].Compute(ctx); err != nil {
return return
} }
if leftBool, lok := ToBool(leftValue); !lok { if leftBool, lok := kern.ToBool(leftValue); !lok {
err = fmt.Errorf("got %T as left operand type of 'and' operator, it must be bool", leftBool) // err = fmt.Errorf("got %s as left operand type of 'AND' operator, it must be bool", expr.TypeName(leftValue))
return // return
err = self.errIncompatibleType(leftValue, "left")
} else if !leftBool { } else if !leftBool {
v = false v = false
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.children[1].Compute(ctx); err == nil {
if rightBool, rok := ToBool(rightValue); rok { if rightBool, rok := kern.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
@@ -106,9 +109,7 @@ func evalAndWithShortcut(ctx ExprContext, self *term) (v any, err error) {
func newOrTerm(tk *Token) (inst *term) { func newOrTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindBool,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priOr, priority: priOr,
@@ -116,8 +117,8 @@ func newOrTerm(tk *Token) (inst *term) {
} }
} }
func evalOr(ctx ExprContext, self *term) (v any, err error) { func evalOr(ctx kern.ExprContext, self *term) (v any, err error) {
if CtrlIsEnabled(ctx, ControlBoolShortcut) { if kern.CtrlIsEnabled(ctx, kern.ControlBoolShortcut) {
v, err = evalOrWithShortcut(ctx, self) v, err = evalOrWithShortcut(ctx, self)
} else { } else {
v, err = evalOrWithoutShortcut(ctx, self) v, err = evalOrWithoutShortcut(ctx, self)
@@ -125,7 +126,7 @@ func evalOr(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) { func evalOrWithoutShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var leftBool, rightBool bool var leftBool, rightBool bool
var lok, rok bool var lok, rok bool
@@ -134,8 +135,8 @@ func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
leftBool, lok = ToBool(leftValue) leftBool, lok = kern.ToBool(leftValue)
rightBool, rok = ToBool(rightValue) rightBool, rok = kern.ToBool(rightValue)
if lok && rok { if lok && rok {
v = leftBool || rightBool v = leftBool || rightBool
@@ -145,24 +146,24 @@ func evalOrWithoutShortcut(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func evalOrWithShortcut(ctx ExprContext, self *term) (v any, err error) { func evalOrWithShortcut(ctx kern.ExprContext, self *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = self.checkOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = self.children[0].Compute(ctx); err != nil {
return return
} }
if leftBool, lok := ToBool(leftValue); !lok { if leftBool, lok := kern.ToBool(leftValue); !lok {
err = fmt.Errorf("got %T as left operand type of 'or' operator, it must be bool", leftBool) err = fmt.Errorf("got %s as left operand type of 'OR' operator, it must be bool", kern.TypeName(leftValue))
return return
} else if leftBool { } else if leftBool {
v = true v = true
} else if rightValue, err = self.children[1].compute(ctx); err == nil { } else if rightValue, err = self.children[1].Compute(ctx); err == nil {
if rightBool, rok := ToBool(rightValue); rok { if rightBool, rok := kern.ToBool(rightValue); rok {
v = rightBool v = rightBool
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = self.errIncompatibleTypes(leftValue, rightValue)
+16 -9
View File
@@ -4,7 +4,11 @@
// operator-builtin.go // operator-builtin.go
package expr package expr
import "io" import (
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- builtin term //-------- builtin term
@@ -18,30 +22,33 @@ func newBuiltinTerm(tk *Token) (inst *term) {
} }
} }
func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { func evalBuiltin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsString(childValue) { if kern.IsString(childValue) {
module, _ := childValue.(string) module, _ := childValue.(string)
count, err = ImportInContextByGlobPattern(module) count, err = ImportInContextByGlobPattern(ctx, module)
} else { } else {
var moduleSpec any var moduleSpec any
it := NewAnyIterator(childValue) var it kern.Iterator
if it, err = NewIterator(childValue); err != nil {
return
}
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() { for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok { if module, ok := moduleSpec.(string); ok {
if ImportInContext(module) { if ImportInContext(ctx, module) {
count++ count++
} else { } else {
err = self.Errorf("unknown builtin module %q", module) err = opTerm.Errorf("unknown builtin module %q", module)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %s", it.Index()+1, TypeName(moduleSpec)) err = opTerm.Errorf("expected string at item nr %d, got %s", it.Index()+1, kern.TypeName(moduleSpec))
break break
} }
} }
+6 -2
View File
@@ -4,6 +4,10 @@
// operator-but.go // operator-but.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- but term //-------- but term
func newButTerm(tk *Token) (inst *term) { func newButTerm(tk *Token) (inst *term) {
@@ -16,8 +20,8 @@ func newButTerm(tk *Token) (inst *term) {
} }
} }
func evalBut(ctx ExprContext, self *term) (v any, err error) { func evalBut(ctx kern.ExprContext, opTerm *term) (v any, err error) {
_, v, err = self.evalInfix(ctx) _, v, err = opTerm.evalInfix(ctx)
return return
} }
-85
View File
@@ -1,85 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-coalesce.go
package expr
//-------- null coalesce term
func newNullCoalesceTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalNullCoalesce,
}
}
func evalNullCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- coalesce assign term
func newCoalesceAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priCoalesce,
evalFunc: evalAssignCoalesce,
}
}
func evalAssignCoalesce(ctx ExprContext, self *term) (v any, err error) {
var rightValue any
if err = self.checkOperands(); err != nil {
return
}
leftTerm := self.children[0]
if leftTerm.tk.Sym != SymVariable {
err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.source()); exists {
v = leftValue
} else if rightValue, err = self.children[1].compute(ctx); err == nil {
if functor, ok := rightValue.(Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.source(), functor, TypeAny, []ExprFuncParam{
NewFuncParamFlag(ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newNullCoalesceTerm)
registerTermConstructor(SymQuestionEqual, newCoalesceAssignTerm)
}
+13 -9
View File
@@ -4,6 +4,10 @@
// operator-context-value.go // operator-context-value.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- context term //-------- context term
func newContextTerm(tk *Token) (inst *term) { func newContextTerm(tk *Token) (inst *term) {
@@ -16,24 +20,24 @@ func newContextTerm(tk *Token) (inst *term) {
} }
} }
func evalContextValue(ctx ExprContext, self *term) (v any, err error) { func evalContextValue(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var sourceCtx ExprContext var sourceCtx kern.ExprContext
if self.children == nil || len(self.children) == 0 { if len(opTerm.children) == 0 {
sourceCtx = ctx sourceCtx = ctx
} else if self.children[0].symbol() == SymVariable && self.children[0].source() == "global" { } else if opTerm.children[0].symbol() == SymVariable && opTerm.children[0].Source() == "global" {
sourceCtx = globalCtx sourceCtx = ctx.GetGlobal()
} else if childValue, err = self.evalPrefix(ctx); err == nil { } else if childValue, err = opTerm.evalPrefix(ctx); err == nil {
if dc, ok := childValue.(*dataCursor); ok { if dc, ok := childValue.(*dataCursor); ok {
sourceCtx = dc.ctx sourceCtx = dc.ctx
} }
} }
if sourceCtx != nil { if sourceCtx != nil {
if formatter, ok := sourceCtx.(DictFormat); ok { if formatter, ok := sourceCtx.(kern.DictFormat); ok {
v = formatter.ToDict() v = formatter.ToDict()
} else if formatter, ok := sourceCtx.(Formatter); ok { } else if formatter, ok := sourceCtx.(kern.Formatter); ok {
v = formatter.ToString(0) v = formatter.ToString(0)
} else { } else {
// keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' }) // keys := sourceCtx.EnumVars(func(name string) bool { return name[0] != '_' })
@@ -49,7 +53,7 @@ func evalContextValue(ctx ExprContext, self *term) (v any, err error) {
v = d v = d
} }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
+6 -2
View File
@@ -4,6 +4,10 @@
// operator-ctrl.go // operator-ctrl.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- export all term //-------- export all term
func newExportAllTerm(tk *Token) (inst *term) { func newExportAllTerm(tk *Token) (inst *term) {
@@ -16,8 +20,8 @@ func newExportAllTerm(tk *Token) (inst *term) {
} }
} }
func evalExportAll(ctx ExprContext, self *term) (v any, err error) { func evalExportAll(ctx kern.ExprContext, opTerm *term) (v any, err error) {
CtrlEnable(ctx, control_export_all) CtrlEnable(ctx, kern.ControlExportAll)
return return
} }
+128
View File
@@ -0,0 +1,128 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-default.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- default term
func newDefaultTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalDefault,
}
}
func evalDefault(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
v = rightValue
}
return
}
//-------- alternate term
func newAlternateTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAlternate,
}
}
func evalAlternate(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists && leftValue != nil {
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
v = rightValue
}
} else {
v = leftValue
}
return
}
//-------- default assign term
func newDefaultAssignTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priDefault,
evalFunc: evalAssignDefault,
}
}
func evalAssignDefault(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue any
if err = opTerm.checkOperands(); err != nil {
return
}
leftTerm := opTerm.children[0]
if leftTerm.tk.Sym != SymVariable {
// err = leftTerm.Errorf("left operand of %q must be a variable", self.tk.source)
err = kern.ErrLeftOperandMustBeVariable(leftTerm, opTerm)
return
}
if leftValue, exists := ctx.GetVar(leftTerm.Source()); exists {
v = leftValue
} else if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
if functor, ok := rightValue.(kern.Functor); ok {
//ctx.RegisterFunc(leftTerm.source(), functor, 0, -1)
ctx.RegisterFunc(leftTerm.Source(), functor, kern.TypeAny, []kern.ExprFuncParam{
NewFuncParamFlag(kern.ParamValue, PfDefault|PfRepeat),
})
} else {
v = rightValue
ctx.UnsafeSetVar(leftTerm.Source(), rightValue)
}
}
return
}
// init
func init() {
registerTermConstructor(SymDoubleQuestion, newDefaultTerm)
registerTermConstructor(SymQuestionEqual, newDefaultAssignTerm)
registerTermConstructor(SymQuestionExclam, newAlternateTerm)
}
+72
View File
@@ -0,0 +1,72 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-digest.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- digest term
func newDigestTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalDigest,
}
}
func evalDigest(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item, lastValue any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of DIGEST must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
lastValue = nil
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
if rightValue == nil {
break
} else {
lastValue = rightValue
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = lastValue
return
}
// init
func init() {
registerTermConstructor(SymKwDigest, newDigestTerm)
}
+29 -9
View File
@@ -4,6 +4,10 @@
// operator-dot.go // operator-dot.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- dot term // -------- dot term
func newDotTerm(tk *Token) (inst *term) { func newDotTerm(tk *Token) (inst *term) {
return &term{ return &term{
@@ -15,24 +19,24 @@ func newDotTerm(tk *Token) (inst *term) {
} }
} }
func evalDot(ctx ExprContext, self *term) (v any, err error) { func evalDot(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case ExtIterator: case kern.ExtIterator:
if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ { if indexTerm.symbol() == SymVariable /*|| indexTerm.symbol() == SymString */ {
opName := indexTerm.source() opName := indexTerm.Source()
if unboxedValue.HasOperation(opName) { if unboxedValue.HasOperation(opName) {
v, err = unboxedValue.CallOperation(opName, []any{}) v, err = unboxedValue.CallOperation(opName, map[string]any{})
} else { } else {
err = indexTerm.Errorf("this iterator do not support the %q command", opName) err = indexTerm.Errorf("this iterator do not support the %q command", opName)
v = false v = false
@@ -40,9 +44,25 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) {
} else { } else {
err = indexTerm.tk.ErrorExpectedGot("identifier") err = indexTerm.tk.ErrorExpectedGot("identifier")
} }
case *kern.DictType:
var ok bool
s := opTerm.children[1].symbol()
if s == SymVariable || s == SymString {
src := opTerm.children[1].Source()
if len(src) > 1 && src[0] == '"' && src[len(src)-1] == '"' {
src = src[1 : len(src)-1]
}
if v, ok = unboxedValue.GetItem(src); !ok {
err = opTerm.errKeyNotFound(src)
}
} else if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
if v, ok = unboxedValue.GetItem(rightValue); !ok {
err = opTerm.errKeyNotFound(rightValue)
}
}
default: default:
if rightValue, err = self.children[1].compute(ctx); err == nil { if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} }
return return
+9 -5
View File
@@ -4,7 +4,11 @@
// operator-fact.go // operator-fact.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- fact term //-------- fact term
@@ -18,14 +22,14 @@ func newFactTerm(tk *Token) (inst *term) {
} }
} }
func evalFact(ctx ExprContext, self *term) (v any, err error) { func evalFact(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue any var leftValue any
if leftValue, err = self.evalPrefix(ctx); err != nil { if leftValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if IsInteger(leftValue) { if kern.IsInteger(leftValue) {
if i, _ := leftValue.(int64); i >= 0 { if i, _ := leftValue.(int64); i >= 0 {
f := int64(1) f := int64(1)
for k := int64(1); k <= i; k++ { for k := int64(1); k <= i; k++ {
@@ -36,7 +40,7 @@ func evalFact(ctx ExprContext, self *term) (v any, err error) {
err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i) err = fmt.Errorf("factorial of a negative integer (%d) is not allowed", i)
} }
} else { } else {
err = self.errIncompatibleType(leftValue) err = opTerm.errIncompatiblePrefixPostfixType(leftValue)
} }
return return
} }
+74
View File
@@ -0,0 +1,74 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-filter.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- map term
func newFilterTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalFilter,
}
}
func evalFilter(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of FILTER must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
values := kern.NewListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
if success, valid := kern.ToBool(rightValue); valid {
if success {
values.AppendItem(item)
}
} else {
err = fmt.Errorf("filter expression must return a boolean or a castable to boolean, got %v [%T]", rightValue, rightValue)
}
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwFilter, newFilterTerm)
}
+20 -11
View File
@@ -7,8 +7,9 @@ package expr
//https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html //https://www.youmath.it/lezioni/algebra-elementare/lezioni-di-algebra-e-aritmetica-per-scuole-medie/553-dalle-frazioni-a-numeri-decimali.html
import ( import (
"errors"
"fmt" "fmt"
"git.portale-stac.it/go-pkg/expr/kern"
) )
// -------- fraction term // -------- fraction term
@@ -24,12 +25,12 @@ func newFractionTerm(tk *Token) *term {
} }
// -------- eval func // -------- eval func
func evalFraction(ctx ExprContext, self *term) (v any, err error) { func evalFraction(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var numValue, denValue any var numValue, denValue any
var num, den int64 var num, den int64
var ok bool var ok bool
if numValue, denValue, err = self.evalInfix(ctx); err != nil { if numValue, denValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if num, ok = numValue.(int64); !ok { if num, ok = numValue.(int64); !ok {
@@ -41,7 +42,7 @@ func evalFraction(ctx ExprContext, self *term) (v any, err error) {
return return
} }
if den == 0 { if den == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
return return
} }
@@ -49,18 +50,26 @@ func evalFraction(ctx ExprContext, self *term) (v any, err error) {
den = -den den = -den
num = -num num = -num
} }
g := gcd(num, den) if num != 0 {
num = num / g if g := kern.Gcd(num, den); g != 1 {
den = den / g num = num / g
if den == 1 { den = den / g
v = num }
if den == 1 {
v = num
} else {
// v = &expr.FractionType{num, den}
v = kern.NewFraction(num, den)
}
} else { } else {
v = &FractionType{num, den} // v = &FractionType{0, den}
v = kern.NewFraction(0, den)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymVertBar, newFractionTerm) // registerTermConstructor(SymVertBar, newFractionTerm)
registerTermConstructor(SymColon, newFractionTerm)
} }
+105
View File
@@ -0,0 +1,105 @@
// Copyright (c) 2024-2026 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-groupby.go
package expr
import (
"fmt"
"io"
"strconv"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- group by term
func newGroupByTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalGroupBy,
}
}
func evalGroupBy(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
var sKey string
var keyByIndex bool
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
rightTk := opTerm.children[1].tk
if rightTk.IsSymbol(SymVariable) && rightTk.source == "__" {
keyByIndex = true
} else if rightValue, err = opTerm.children[1].Compute(ctx); err != nil {
return
} else if kern.IsString(rightValue) {
sKey = rightValue.(string)
} else {
return nil, fmt.Errorf("right operand of GROUPBY must be a string or identifier '__'; got %s", kern.TypeName(rightValue))
}
values := kern.MakeDict()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
var sItemKey string
if d, ok := item.(*kern.DictType); ok {
if keyByIndex || len(sKey) == 0 {
sItemKey = strconv.Itoa(int(it.Index()))
} else if d.HasKey(sKey) {
if keyValue, exists := d.GetItem(sKey); exists {
sItemKey = fmt.Sprintf("%v", keyValue)
} else {
sItemKey = "_"
}
} else {
sItemKey = "_"
}
} else {
sItemKey = strconv.Itoa(int(it.Index()))
}
var ls *kern.ListType
if lsAny, exists := values.GetItem(sItemKey); exists && lsAny != nil {
ls = lsAny.(*kern.ListType)
}
if ls == nil {
ls = kern.NewListA()
}
ls.AppendItem(item)
values.SetItem(sItemKey, ls)
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwGroupBy, newGroupByTerm)
}
+13 -9
View File
@@ -4,6 +4,10 @@
// operator-in.go // operator-in.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- in term //-------- in term
func newInTerm(tk *Token) (inst *term) { func newInTerm(tk *Token) (inst *term) {
@@ -21,21 +25,21 @@ func newInTerm(tk *Token) (inst *term) {
// return // return
// } // }
func evalIn(ctx ExprContext, self *term) (v any, err error) { func evalIn(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if IsList(rightValue) { if kern.IsList(rightValue) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*kern.ListType)
v = list.indexDeepSameCmp(leftValue) >= 0 v = list.IndexDeepSameCmp(leftValue) >= 0
} else if IsDict(rightValue) { } else if kern.IsDict(rightValue) {
dict, _ := rightValue.(*DictType) dict, _ := rightValue.(*kern.DictType)
v = dict.hasKey(leftValue) v = dict.HasKey(leftValue)
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
+19 -12
View File
@@ -4,6 +4,10 @@
// operator-include.go // operator-include.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- include term //-------- include term
func newIncludeTerm(tk *Token) (inst *term) { func newIncludeTerm(tk *Token) (inst *term) {
@@ -16,37 +20,40 @@ func newIncludeTerm(tk *Token) (inst *term) {
} }
} }
func evalInclude(ctx ExprContext, self *term) (v any, err error) { func evalInclude(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
count := 0 count := 0
if IsList(childValue) { if kern.IsList(childValue) {
list, _ := childValue.([]any) list, _ := childValue.(*kern.ListType)
for i, filePathSpec := range list { for i, filePathSpec := range *list {
if filePath, ok := filePathSpec.(string); ok { if filePath, ok := filePathSpec.(string); ok {
if v, err = EvalFile(ctx, filePath); err == nil { if v, err = EvalFile(ctx, filePath); err == nil {
count++ count++
} else { } else {
err = self.Errorf("can't load file %q", filePath) err = opTerm.Errorf("can't load file %q", filePath)
break break
} }
} else { } else {
err = self.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec) err = opTerm.Errorf("expected string at item nr %d, got %T", i+1, filePathSpec)
break break
} }
} }
} else if IsString(childValue) { } else if kern.IsString(childValue) {
filePath, _ := childValue.(string) filePath, _ := childValue.(string)
v, err = EvalFile(ctx, filePath) if v, err = EvalFile(ctx, filePath); err == nil {
count++
}
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatiblePrefixPostfixType(childValue)
} }
if err == nil { if err != nil {
v = count //v = count
v = nil
} }
return return
} }
+29 -37
View File
@@ -4,6 +4,10 @@
// operator-index.go // operator-index.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- index term // -------- index term
func newIndexTerm(tk *Token) (inst *term) { func newIndexTerm(tk *Token) (inst *term) {
return &term{ return &term{
@@ -15,15 +19,15 @@ func newIndexTerm(tk *Token) (inst *term) {
} }
} }
func verifyKey(indexTerm *term, indexList *ListType) (index any, err error) { func verifyKey(indexList *kern.ListType) (index any, err error) {
index = (*indexList)[0] index = (*indexList)[0]
return return
} }
func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int, err error) { func verifyIndex(indexTerm *term, indexList *kern.ListType, maxValue int) (index int, err error) {
var v int var v int
if v, err = ToInt((*indexList)[0], "index expression"); err == nil { if v, err = kern.ToGoInt((*indexList)[0], "index expression"); err == nil {
if v < 0 && v >= -maxValue { if v < 0 && v >= -maxValue {
v = maxValue + v v = maxValue + v
} }
@@ -36,11 +40,11 @@ func verifyIndex(indexTerm *term, indexList *ListType, maxValue int) (index int,
return return
} }
func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex, endIndex int, err error) { func verifyRange(indexTerm *term, indexList *kern.ListType, maxValue int) (startIndex, endIndex int, err error) {
v, _ := ((*indexList)[0]).(*intPair) v, _ := ((*indexList)[0]).(*intPair)
startIndex = v.a startIndex = v.a
endIndex = v.b endIndex = v.b
if endIndex == ConstLastIndex { if endIndex == kern.ConstLastIndex {
endIndex = maxValue endIndex = maxValue
} }
if startIndex < 0 && startIndex >= -maxValue { if startIndex < 0 && startIndex >= -maxValue {
@@ -59,27 +63,27 @@ func verifyRange(indexTerm *term, indexList *ListType, maxValue int) (startIndex
return return
} }
func evalIndex(ctx ExprContext, self *term) (v any, err error) { func evalIndex(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
var indexList *ListType var indexList *kern.ListType
var ok bool var ok bool
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
indexTerm := self.children[1] indexTerm := opTerm.children[1]
if indexList, ok = rightValue.(*ListType); !ok { if indexList, ok = rightValue.(*kern.ListType); !ok {
err = self.Errorf("invalid index expression") err = opTerm.Errorf("invalid index expression")
return return
} else if len(*indexList) != 1 { } else if len(*indexList) != 1 {
err = indexTerm.Errorf("one index only is allowed") err = indexTerm.Errorf("one index only is allowed")
return return
} }
if IsInteger((*indexList)[0]) { if kern.IsInteger((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case *ListType: case *kern.ListType:
var index int var index int
if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil { if index, err = verifyIndex(indexTerm, indexList, len(*unboxedValue)); err == nil {
v = (*unboxedValue)[index] v = (*unboxedValue)[index]
@@ -89,24 +93,17 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil { if index, err = verifyIndex(indexTerm, indexList, len(unboxedValue)); err == nil {
v = string(unboxedValue[index]) v = string(unboxedValue[index])
} }
case *DictType: case *kern.DictType:
/* 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) v, err = getDictItem(unboxedValue, indexTerm, indexList, rightValue)
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if isIntPair((*indexList)[0]) { } else if isIntPair((*indexList)[0]) {
switch unboxedValue := leftValue.(type) { switch unboxedValue := leftValue.(type) {
case *ListType: case *kern.ListType:
var start, end int var start, end int
if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil { if start, end, err = verifyRange(indexTerm, indexList, len(*unboxedValue)); err == nil {
sublist := ListType((*unboxedValue)[start:end]) sublist := kern.ListType((*unboxedValue)[start:end])
v = &sublist v = &sublist
} }
case string: case string:
@@ -115,28 +112,23 @@ func evalIndex(ctx ExprContext, self *term) (v any, err error) {
v = unboxedValue[start:end] v = unboxedValue[start:end]
} }
default: default:
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
} else if IsDict(leftValue) { } else if kern.IsDict(leftValue) {
d := leftValue.(*DictType) d := leftValue.(*kern.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) v, err = getDictItem(d, indexTerm, indexList, rightValue)
} else {
rightChild := opTerm.children[1]
err = rightChild.Errorf("invalid index type: %v", (*indexList)[0])
} }
return return
} }
func getDictItem(d *DictType, indexTerm *term, indexList *ListType, rightValue any) (v any, err error) { func getDictItem(d *kern.DictType, indexTerm *term, indexList *kern.ListType, rightValue any) (v any, err error) {
var ok bool var ok bool
var indexValue any var indexValue any
if indexValue, err = verifyKey(indexTerm, indexList); err == nil { if indexValue, err = verifyKey(indexList); err == nil {
if v, ok = (*d)[indexValue]; !ok { if v, ok = (*d)[indexValue]; !ok {
err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue) err = indexTerm.Errorf("key %v does not belong to the dictionary", rightValue)
} }
+26 -22
View File
@@ -4,15 +4,19 @@
// operator-insert.go // operator-insert.go
package expr package expr
//-------- insert term import (
"git.portale-stac.it/go-pkg/expr/kern"
)
func newInsertTerm(tk *Token) (inst *term) { //-------- prepend term
func newPrependTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priAssign, priority: priInsert,
evalFunc: evalInsert, evalFunc: evalPrepend,
} }
} }
@@ -21,47 +25,47 @@ func newAppendTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priAssign, priority: priInsert,
evalFunc: evalAppend, evalFunc: evalAppend,
} }
} }
func evalInsert(ctx ExprContext, self *term) (v any, err error) { func evalPrepend(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if IsList(rightValue) { if kern.IsList(rightValue) {
list, _ := rightValue.(*ListType) list, _ := rightValue.(*kern.ListType)
newList := append(ListType{leftValue}, *list...) newList := append(kern.ListType{leftValue}, *list...)
v = &newList v = &newList
if self.children[1].symbol() == SymVariable { if opTerm.children[1].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[1].source(), v) ctx.UnsafeSetVar(opTerm.children[1].Source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalAppend(ctx ExprContext, self *term) (v any, err error) { func evalAppend(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if IsList(leftValue) { if kern.IsList(leftValue) {
list, _ := leftValue.(*ListType) list, _ := leftValue.(*kern.ListType)
newList := append(*list, rightValue) newList := append(*list, rightValue)
v = &newList v = &newList
if self.children[0].symbol() == SymVariable { if opTerm.children[0].symbol() == SymVariable {
ctx.UnsafeSetVar(self.children[0].source(), v) ctx.UnsafeSetVar(opTerm.children[0].Source(), v)
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
@@ -86,6 +90,6 @@ func evalAppend(ctx ExprContext, self *term) (v any, err error) {
// init // init
func init() { func init() {
registerTermConstructor(SymInsert, newInsertTerm) registerTermConstructor(SymPlusGreater, newPrependTerm)
registerTermConstructor(SymAppend, newAppendTerm) registerTermConstructor(SymLessPlus, newAppendTerm)
} }
+11 -6
View File
@@ -4,6 +4,10 @@
// operator-iter-value.go // operator-iter-value.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- iter value term //-------- iter value term
func newIterValueTerm(tk *Token) (inst *term) { func newIterValueTerm(tk *Token) (inst *term) {
@@ -11,27 +15,28 @@ func newIterValueTerm(tk *Token) (inst *term) {
tk: *tk, tk: *tk,
children: make([]*term, 0, 1), children: make([]*term, 0, 1),
position: posPrefix, position: posPrefix,
priority: priIterValue, priority: priDereference,
evalFunc: evalIterValue, evalFunc: evalIterValue,
} }
} }
func evalIterValue(ctx ExprContext, self *term) (v any, err error) { func evalIterValue(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if it, ok := childValue.(Iterator); ok { if it, ok := childValue.(kern.Iterator); ok {
v, err = it.Current() v, err = it.Current()
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
// init // init
func init() { func init() {
registerTermConstructor(SymOpenClosedRound, newIterValueTerm) // registerTermConstructor(SymOpenClosedRound, newIterValueTerm)
registerTermConstructor(SymDereference, newIterValueTerm)
} }
+67
View File
@@ -0,0 +1,67 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-join.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- join term
func newJoinTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalJoin,
}
}
func evalJoin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var itLeft, itRight kern.Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return
}
if rightValue, err = opTerm.children[1].Compute(ctx); err != nil {
return
}
if itLeft, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of JOIN must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
if itRight, err = NewIterator(rightValue); err != nil {
return nil, fmt.Errorf("right operand of JOIN must be an iterable data-source; got %s", kern.TypeName(rightValue))
}
values := kern.NewListA()
for _, it := range []kern.Iterator{itLeft, itRight} {
for item, err = it.Next(); err == nil; item, err = it.Next() {
values.AppendItem(item)
}
}
if err == io.EOF {
err = nil
}
v = values
return
}
// init
func init() {
registerTermConstructor(SymKwJoin, newJoinTerm)
}
+20 -16
View File
@@ -4,6 +4,10 @@
// operator-length.go // operator-length.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- length term //-------- length term
func newLengthTerm(tk *Token) (inst *term) { func newLengthTerm(tk *Token) (inst *term) {
@@ -16,32 +20,32 @@ func newLengthTerm(tk *Token) (inst *term) {
} }
} }
func evalLength(ctx ExprContext, self *term) (v any, err error) { func evalLength(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if IsList(childValue) { if kern.IsList(childValue) {
ls, _ := childValue.(*ListType) ls, _ := childValue.(*kern.ListType)
v = int64(len(*ls)) v = int64(len(*ls))
} else if IsString(childValue) { } else if kern.IsString(childValue) {
s, _ := childValue.(string) s, _ := childValue.(string)
v = int64(len(s)) v = int64(len(s))
} else if IsDict(childValue) { } else if kern.IsDict(childValue) {
// m, _ := childValue.(map[any]any) m, _ := childValue.(*kern.DictType)
m, _ := childValue.(*DictType)
v = int64(len(*m)) v = int64(len(*m))
} else if it, ok := childValue.(Iterator); ok { } else if it, ok := childValue.(kern.Iterator); ok {
if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { v = int64(it.Count())
count, _ := extIt.CallOperation(countName, nil) // if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(CountName) {
v, _ = ToInt(count, "") // count, _ := extIt.CallOperation(CountName, nil)
} else { // v, _ = ToGoInt(count, "")
v = int64(it.Index() + 1) // } else {
} // v = int64(it.Index() + 1)
// }
} else { } else {
err = self.errIncompatibleType(childValue) err = opTerm.errIncompatiblePrefixPostfixType(childValue)
} }
return return
} }
+70
View File
@@ -0,0 +1,70 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-map.go
package expr
import (
"fmt"
"io"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- map term
func newMapTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priIterOp,
evalFunc: evalMap,
}
}
func evalMap(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
var it kern.Iterator
var item any
if err = opTerm.checkOperands(); err != nil {
return
}
if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return
}
if it, err = NewIterator(leftValue); err != nil {
return nil, fmt.Errorf("left operand of MAP must be an iterable data-source; got %s", kern.TypeName(leftValue))
}
values := kern.NewListA()
for item, err = it.Next(); err == nil; item, err = it.Next() {
ctx.SetVar("_", item)
ctx.SetVar("__", it.Index())
ctx.SetVar("_#", it.Count())
if rightValue, err = opTerm.children[1].Compute(ctx); err == nil {
values.AppendItem(rightValue)
}
ctx.DeleteVar("_#")
ctx.DeleteVar("__")
ctx.DeleteVar("_")
if err != nil {
break
}
}
if err == io.EOF {
err = nil
}
if err == nil {
v = values
}
return
}
// init
func init() {
registerTermConstructor(SymKwMap, newMapTerm)
}
+5 -23
View File
@@ -5,7 +5,7 @@
package expr package expr
import ( import (
"io" "git.portale-stac.it/go-pkg/expr/kern"
) )
//-------- plugin term //-------- plugin term
@@ -20,33 +20,15 @@ func newPluginTerm(tk *Token) (inst *term) {
} }
} }
func evalPlugin(ctx ExprContext, self *term) (v any, err error) { func evalPlugin(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any var childValue any
var moduleSpec any var count int
if childValue, err = self.evalPrefix(ctx); err != nil { if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
dirList := buildSearchDirList("plugin", ENV_EXPR_PLUGIN_PATH) if count, err = importPluginFromSearchPath(ctx, childValue); err == nil {
count := 0
it := NewAnyIterator(childValue)
for moduleSpec, err = it.Next(); err == nil; moduleSpec, err = it.Next() {
if module, ok := moduleSpec.(string); ok {
if err = importPlugin(dirList, module); err != nil {
break
}
count++
} else {
err = self.Errorf("expected string as item nr %d, got %s", it.Index()+1, TypeName(moduleSpec))
break
}
}
if err == io.EOF {
err = nil
}
if err == nil {
v = int64(count) v = int64(count)
} }
return return
+97
View File
@@ -0,0 +1,97 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-post-inc-dec.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- post increment term
func newPostIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostInc,
}
}
func evalPostInc(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if it, ok := childValue.(kern.Iterator); ok {
var namePrefix string
v, err = it.Next()
if opTerm.children[0].symbol() == SymVariable {
namePrefix = opTerm.children[0].Source()
}
ctx.UnsafeSetVar(namePrefix+"_index", it.Index())
if c, err1 := it.Current(); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_current", c)
}
if it.HasOperation(kern.KeyName) {
if k, err1 := it.CallOperation(kern.KeyName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_key", k)
}
}
if it.HasOperation(kern.ValueName) {
if v1, err1 := it.CallOperation(kern.ValueName, nil); err1 == nil {
ctx.UnsafeSetVar(namePrefix+"_value", v1)
}
}
} else if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].Source(), i+1)
} else {
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
}
return
}
// -------- post decrement term
func newPostDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostDec,
}
}
func evalPostDec(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
/* if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else */if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(opTerm.children[0].Source(), i-1)
} else {
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm)
registerTermConstructor(SymDoubleMinus, newPostDecTerm)
}
-41
View File
@@ -1,41 +0,0 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-post-inc.go
package expr
// -------- post increment term
func newPostIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPostfix,
priority: priIncDec,
evalFunc: evalPostInc,
}
}
func evalPostInc(ctx ExprContext, self *term) (v any, err error) {
var childValue any
if childValue, err = self.evalPrefix(ctx); err != nil {
return
}
if it, ok := childValue.(Iterator); ok {
v, err = it.Next()
} else if IsInteger(childValue) && self.children[0].symbol() == SymVariable {
v = childValue
i, _ := childValue.(int64)
ctx.SetVar(self.children[0].source(), i+1)
} else {
err = self.errIncompatibleType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymDoublePlus, newPostIncTerm)
}
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-pre-inc-dec.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- pre increment term
func newPreIncTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priIncDec,
evalFunc: evalPreInc,
}
}
func evalPreInc(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
i := childValue.(int64) + 1
ctx.SetVar(opTerm.children[0].Source(), i)
v = i
} else {
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
}
return
}
// -------- pre decrement term
func newPreDecTerm(tk *Token) *term {
return &term{
tk: *tk,
parent: nil,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priIncDec,
evalFunc: evalPreDec,
}
}
func evalPreDec(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
if kern.IsInteger(childValue) && opTerm.children[0].symbol() == SymVariable {
i := childValue.(int64) - 1
ctx.SetVar(opTerm.children[0].Source(), i)
v = i
} else {
err = opTerm.errIncompatiblePrefixPostfixType(childValue)
}
return
}
// init
func init() {
registerTermConstructor(SymPreInc, newPreIncTerm)
registerTermConstructor(SymPreDec, newPreDecTerm)
}
+92 -61
View File
@@ -5,17 +5,16 @@
package expr package expr
import ( import (
"errors"
"strings" "strings"
"git.portale-stac.it/go-pkg/expr/kern"
) )
//-------- multiply term //-------- multiply term
func newMultiplyTerm(tk *Token) (inst *term) { func newMultiplyTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -23,40 +22,42 @@ func newMultiplyTerm(tk *Token) (inst *term) {
} }
} }
func evalMultiply(ctx ExprContext, self *term) (v any, err error) { func mulValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftValue, rightValue any if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
return
}
if IsString(leftValue) && IsInteger(rightValue) {
s, _ := leftValue.(string) s, _ := leftValue.(string)
n, _ := rightValue.(int64) n, _ := rightValue.(int64)
v = strings.Repeat(s, int(n)) v = strings.Repeat(s, int(n))
} else if isNumOrFract(leftValue) && isNumOrFract(rightValue) { } else if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) * numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) * kern.NumAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) { } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
v, err = mulAnyFract(leftValue, rightValue) v, err = kern.MulAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt * rightInt v = leftInt * rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalMultiply(ctx kern.ExprContext, prodTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = prodTerm.evalInfix(ctx); err != nil {
return
}
return mulValues(prodTerm, leftValue, rightValue)
}
//-------- divide term //-------- divide term
func newDivideTerm(tk *Token) (inst *term) { func newDivideTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
@@ -64,37 +65,66 @@ func newDivideTerm(tk *Token) (inst *term) {
} }
} }
func evalDivide(ctx ExprContext, self *term) (v any, err error) { func divValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
var leftValue, rightValue any if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { d := kern.NumAsFloat(rightValue)
return
}
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) {
d := numAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = kern.NumAsFloat(leftValue) / d
} }
} else if isFraction(leftValue) || isFraction(rightValue) { } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
v, err = divAnyFract(leftValue, rightValue) v, err = kern.DivAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
if rightInt, _ := rightValue.(int64); rightInt == 0 { if rightInt, _ := rightValue.(int64); rightInt == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
v = leftInt / rightInt v = leftInt / rightInt
} }
} }
} else if kern.IsString(leftValue) && kern.IsString(rightValue) {
source := leftValue.(string)
sep := rightValue.(string)
v = kern.ListFromStrings(strings.Split(source, sep))
} else if kern.IsString(leftValue) && kern.IsInteger(rightValue) {
source := leftValue.(string)
partSize := int(rightValue.(int64))
if partSize == 0 {
err = opTerm.errDivisionByZero()
} else {
partCount := len(source) / partSize
remainder := len(source) % partSize
listSize := partCount
if remainder > 0 {
listSize++
}
parts := make([]any, 0, listSize)
for i := 0; i < partCount; i++ {
parts = append(parts, source[i*partSize:(i+1)*partSize])
}
if remainder > 0 {
parts = append(parts, source[len(source)-remainder:])
}
v = kern.NewList(parts)
}
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalDivide(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
return divValues(opTerm, leftValue, rightValue)
}
//-------- divide as float term //-------- divide as float term
func newDivideAsFloatTerm(tk *Token) (inst *term) { func newDivideAsFloatTerm(tk *Token) (inst *term) {
@@ -107,65 +137,66 @@ func newDivideAsFloatTerm(tk *Token) (inst *term) {
} }
} }
func evalDivideAsFloat(ctx ExprContext, self *term) (v any, err error) { func evalDivideAsFloat(ctx kern.ExprContext, floatDivTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = floatDivTerm.evalInfix(ctx); err != nil {
return return
} }
if isNumOrFract(leftValue) && isNumOrFract(rightValue) { if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
d := numAsFloat(rightValue) d := kern.NumAsFloat(rightValue)
if d == 0.0 { if d == 0.0 {
err = errors.New("division by zero") err = floatDivTerm.errDivisionByZero()
} else { } else {
v = numAsFloat(leftValue) / d v = kern.NumAsFloat(leftValue) / d
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = floatDivTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
//-------- reminder term //-------- reminder term
func newReminderTerm(tk *Token) (inst *term) { func newRemainderTerm(tk *Token) (inst *term) {
return &term{ return &term{
tk: *tk, tk: *tk,
// class: classOperator,
// kind: kindUnknown,
children: make([]*term, 0, 2), children: make([]*term, 0, 2),
position: posInfix, position: posInfix,
priority: priProduct, priority: priProduct,
evalFunc: evalReminder, evalFunc: evalRemainder,
} }
} }
func remainderValues(opTerm *term, leftValue, rightValue any) (v any, err error) {
func evalReminder(ctx ExprContext, self *term) (v any, err error) { if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
return
}
if IsInteger(leftValue) && IsInteger(rightValue) {
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
if rightInt == 0 { if rightInt == 0 {
err = errors.New("division by zero") err = opTerm.errDivisionByZero()
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
v = leftInt % rightInt v = leftInt % rightInt
} }
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = opTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalRemainder(ctx kern.ExprContext, remainderTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = remainderTerm.evalInfix(ctx); err != nil {
return
}
return remainderValues(remainderTerm, leftValue, rightValue)
}
// init // init
func init() { func init() {
registerTermConstructor(SymStar, newMultiplyTerm) registerTermConstructor(SymStar, newMultiplyTerm)
registerTermConstructor(SymSlash, newDivideTerm) registerTermConstructor(SymSlash, newDivideTerm)
registerTermConstructor(SymDotSlash, newDivideAsFloatTerm) registerTermConstructor(SymDotSlash, newDivideAsFloatTerm)
registerTermConstructor(SymPercent, newReminderTerm) registerTermConstructor(SymPercent, newRemainderTerm)
} }
+32 -15
View File
@@ -4,7 +4,11 @@
// operator-range.go // operator-range.go
package expr package expr
import "fmt" import (
"fmt"
"git.portale-stac.it/go-pkg/expr/kern"
)
// -------- range term // -------- range term
type intPair struct { type intPair struct {
@@ -12,10 +16,10 @@ type intPair struct {
} }
func (p *intPair) TypeName() string { func (p *intPair) TypeName() string {
return TypePair return kern.TypePair
} }
func (p *intPair) ToString(opt FmtOpt) string { func (p *intPair) ToString(opt kern.FmtOpt) string {
return fmt.Sprintf("(%d, %d)", p.a, p.b) return fmt.Sprintf("(%d, %d)", p.a, p.b)
} }
@@ -34,25 +38,30 @@ func newRangeTerm(tk *Token) (inst *term) {
} }
} }
func evalRange(ctx ExprContext, self *term) (v any, err error) { func changeColonToRange(t *term) {
if t.tk.IsSymbol(SymColon) {
t.tk.Sym = SymRange
t.evalFunc = evalRange
}
}
func evalRange(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
// if err = self.checkOperands(); err != nil { if len(opTerm.children) == 0 {
// return
// }
if len(self.children) == 0 {
leftValue = int64(0) leftValue = int64(0)
rightValue = int64(-1) rightValue = int64(-1)
} else if len(self.children) == 1 { } else if len(opTerm.children) == 1 {
if leftValue, err = self.children[0].compute(ctx); err != nil { if leftValue, err = opTerm.children[0].Compute(ctx); err != nil {
return return
} }
rightValue = int64(ConstLastIndex) rightValue = int64(kern.ConstLastIndex)
} else if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { } else if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
if !(IsInteger(leftValue) && IsInteger(rightValue)) { if !(kern.IsInteger(leftValue) && kern.IsInteger(rightValue)) {
err = self.errIncompatibleTypes(leftValue, rightValue) // err = opTerm.errIncompatibleTypes(leftValue, rightValue)
err = errRangeInvalidSpecification(opTerm)
return return
} }
@@ -63,7 +72,15 @@ func evalRange(ctx ExprContext, self *term) (v any, err error) {
return return
} }
func errRangeInvalidSpecification(t *term) error {
return t.Errorf("invalid range specification")
}
func errRangeUnexpectedExpression(t *term) error {
return t.Errorf("unexpected range expression")
}
// init // init
func init() { func init() {
registerTermConstructor(SymColon, newRangeTerm) registerTermConstructor(SymRange, newRangeTerm)
} }
+42 -38
View File
@@ -4,7 +4,11 @@
// operator-rel.go // operator-rel.go
package expr package expr
import "reflect" import (
"reflect"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- equal term //-------- equal term
@@ -18,25 +22,25 @@ func newEqualTerm(tk *Token) (inst *term) {
} }
} }
type deepFuncTemplate func(a, b any) (eq bool, err error) // type deepFuncTemplate func(a, b any) (eq bool, err error)
func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) { func equals(a, b any, deepCmp kern.DeepFuncTemplate) (eq bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if kern.IsNumber(a) && kern.IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if kern.IsInteger(a) && kern.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
eq = li == ri eq = li == ri
} else { } else {
eq = numAsFloat(a) == numAsFloat(b) eq = kern.NumAsFloat(a) == kern.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = kern.CmpAnyFract(a, b); err == nil {
eq = cmp == 0 eq = cmp == 0
} }
} }
} else if deepCmp != nil && IsList(a) && IsList(b) { } else if deepCmp != nil && kern.IsList(a) && kern.IsList(b) {
eq, err = deepCmp(a, b) eq, err = deepCmp(a, b)
} else { } else {
eq = reflect.DeepEqual(a, b) eq = reflect.DeepEqual(a, b)
@@ -45,10 +49,10 @@ func equals(a, b any, deepCmp deepFuncTemplate) (eq bool, err error) {
return return
} }
func evalEqual(ctx ExprContext, self *term) (v any, err error) { func evalEqual(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
@@ -68,9 +72,9 @@ func newNotEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalNotEqual(ctx ExprContext, self *term) (v any, err error) { func evalNotEqual(ctx kern.ExprContext, opTerm *term) (v any, err error) {
if v, err = evalEqual(ctx, self); err == nil { if v, err = evalEqual(ctx, opTerm); err == nil {
b, _ := ToBool(v) b, _ := kern.ToBool(v)
v = !b v = !b
} }
return return
@@ -89,43 +93,43 @@ func newLessTerm(tk *Token) (inst *term) {
} }
func lessThan(self *term, a, b any) (isLess bool, err error) { func lessThan(self *term, a, b any) (isLess bool, err error) {
if isNumOrFract(a) && isNumOrFract(b) { if kern.IsNumOrFract(a) && kern.IsNumOrFract(b) {
if IsNumber(a) && IsNumber(b) { if kern.IsNumber(a) && kern.IsNumber(b) {
if IsInteger(a) && IsInteger(b) { if kern.IsInteger(a) && kern.IsInteger(b) {
li, _ := a.(int64) li, _ := a.(int64)
ri, _ := b.(int64) ri, _ := b.(int64)
isLess = li < ri isLess = li < ri
} else { } else {
isLess = numAsFloat(a) < numAsFloat(b) isLess = kern.NumAsFloat(a) < kern.NumAsFloat(b)
} }
} else { } else {
var cmp int var cmp int
if cmp, err = cmpAnyFract(a, b); err == nil { if cmp, err = kern.CmpAnyFract(a, b); err == nil {
isLess = cmp < 0 isLess = cmp < 0
} }
} }
} else if IsString(a) && IsString(b) { } else if kern.IsString(a) && kern.IsString(b) {
ls, _ := a.(string) ls, _ := a.(string)
rs, _ := b.(string) rs, _ := b.(string)
isLess = ls < rs isLess = ls < rs
// Inclusion test // Inclusion test
} else if IsList(a) && IsList(b) { } else if kern.IsList(a) && kern.IsList(b) {
aList, _ := a.(*ListType) aList, _ := a.(*kern.ListType)
bList, _ := b.(*ListType) bList, _ := b.(*kern.ListType)
isLess = len(*aList) < len(*bList) && bList.contains(aList) isLess = len(*aList) < len(*bList) && bList.Contains(aList)
} else { } else {
err = self.errIncompatibleTypes(a, b) err = self.errIncompatibleTypes(a, b)
} }
return return
} }
func evalLess(ctx ExprContext, self *term) (v any, err error) { func evalLess(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, leftValue, rightValue) v, err = lessThan(opTerm, leftValue, rightValue)
return return
} }
@@ -144,8 +148,8 @@ func newLessEqualTerm(tk *Token) (inst *term) {
func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) { func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
if isLessEq, err = lessThan(self, a, b); err == nil { if isLessEq, err = lessThan(self, a, b); err == nil {
if !isLessEq { if !isLessEq {
if IsList(a) && IsList(b) { if kern.IsList(a) && kern.IsList(b) {
isLessEq, err = sameContent(a, b) isLessEq, err = kern.SameContent(a, b)
} else { } else {
isLessEq, err = equals(a, b, nil) isLessEq, err = equals(a, b, nil)
} }
@@ -154,14 +158,14 @@ func lessThanOrEqual(self *term, a, b any) (isLessEq bool, err error) {
return return
} }
func evalLessEqual(ctx ExprContext, self *term) (v any, err error) { func evalLessEqual(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, leftValue, rightValue) v, err = lessThanOrEqual(opTerm, leftValue, rightValue)
return return
} }
@@ -178,14 +182,14 @@ func newGreaterTerm(tk *Token) (inst *term) {
} }
} }
func evalGreater(ctx ExprContext, self *term) (v any, err error) { func evalGreater(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThan(self, rightValue, leftValue) v, err = lessThan(opTerm, rightValue, leftValue)
return return
} }
@@ -201,14 +205,14 @@ func newGreaterEqualTerm(tk *Token) (inst *term) {
} }
} }
func evalGreaterEqual(ctx ExprContext, self *term) (v any, err error) { func evalGreaterEqual(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any var leftValue, rightValue any
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return return
} }
v, err = lessThanOrEqual(self, rightValue, leftValue) v, err = lessThanOrEqual(opTerm, rightValue, leftValue)
return return
} }
+24 -10
View File
@@ -4,6 +4,10 @@
// operator-selector.go // operator-selector.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- selector term //-------- selector term
func newSelectorTerm(tk *Token) (inst *term) { func newSelectorTerm(tk *Token) (inst *term) {
@@ -16,19 +20,29 @@ func newSelectorTerm(tk *Token) (inst *term) {
} }
} }
func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) { func trySelectorCase(ctx kern.ExprContext, exprValue, caseSel any, caseIndex int) (match bool, selectedValue any, err error) {
caseData, _ := caseSel.(*selectorCase) caseData, _ := caseSel.(*selectorCase)
if caseData.filterList == nil { if caseData.filterList == nil {
selectedValue, err = caseData.caseExpr.Eval(ctx) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
} else if filterList, ok := caseData.filterList.value().([]*term); ok { } else if filterList, ok := caseData.filterList.value().([]*term); ok {
if len(filterList) == 0 && exprValue == int64(caseIndex) { if len(filterList) == 0 {
selectedValue, err = caseData.caseExpr.Eval(ctx) var valueAsInt = int64(0)
match = true if b, ok := exprValue.(bool); ok {
if !b {
valueAsInt = 1
}
} else if valueAsInt, ok = exprValue.(int64); !ok {
return
}
if valueAsInt == int64(caseIndex) {
selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true
}
} else { } else {
var caseValue any var caseValue any
for _, caseTerm := range filterList { for _, caseTerm := range filterList {
if caseValue, err = caseTerm.compute(ctx); err != nil || caseValue == exprValue { if caseValue, err = caseTerm.Compute(ctx); err != nil || caseValue == exprValue {
selectedValue, err = caseData.caseExpr.Eval(ctx) selectedValue, err = caseData.caseExpr.Eval(ctx)
match = true match = true
break break
@@ -39,19 +53,19 @@ func trySelectorCase(ctx ExprContext, exprValue, caseSel any, caseIndex int) (ma
return return
} }
func evalSelector(ctx ExprContext, self *term) (v any, err error) { func evalSelector(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var exprValue any var exprValue any
var match bool var match bool
if err = self.checkOperands(); err != nil { if err = opTerm.checkOperands(); err != nil {
return return
} }
exprTerm := self.children[0] exprTerm := opTerm.children[0]
if exprValue, err = exprTerm.compute(ctx); err != nil { if exprValue, err = exprTerm.Compute(ctx); err != nil {
return return
} }
caseListTerm := self.children[1] caseListTerm := opTerm.children[1]
caseList, _ := caseListTerm.value().([]*term) caseList, _ := caseListTerm.value().([]*term)
for i, caseTerm := range caseList { for i, caseTerm := range caseList {
caseSel := caseTerm.value() caseSel := caseTerm.value()
+81
View File
@@ -0,0 +1,81 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-shift.go
package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- bit right shift term
func newRightShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalRightShift,
}
}
func bitRightShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt >> rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalRightShift(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitRightShift(opTerm, leftValue, rightValue)
return
}
func newLeftShiftTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 2),
position: posInfix,
priority: priBinShift,
evalFunc: evalLeftShift,
}
}
func bitLeftShift(opTerm *term, leftValue, rightValue any) (v any, err error) {
if kern.IsInteger(leftValue) && kern.IsInteger(rightValue) {
leftInt := leftValue.(int64)
rightInt := rightValue.(int64)
v = leftInt << rightInt
} else {
err = opTerm.errIncompatibleTypes(leftValue, rightValue)
}
return
}
func evalLeftShift(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = opTerm.evalInfix(ctx); err != nil {
return
}
v, err = bitLeftShift(opTerm, leftValue, rightValue)
return
}
// init
func init() {
registerTermConstructor(SymDoubleGreater, newRightShiftTerm)
registerTermConstructor(SymDoubleLess, newLeftShiftTerm)
}
+11 -7
View File
@@ -4,6 +4,10 @@
// operator-sign.go // operator-sign.go
package expr package expr
import (
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- plus sign term //-------- plus sign term
func newPlusSignTerm(tk *Token) (inst *term) { func newPlusSignTerm(tk *Token) (inst *term) {
@@ -28,29 +32,29 @@ func newMinusSignTerm(tk *Token) (inst *term) {
} }
} }
func evalSign(ctx ExprContext, self *term) (v any, err error) { func evalSign(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var rightValue any var rightValue any
if rightValue, err = self.evalPrefix(ctx); err != nil { if rightValue, err = opTerm.evalPrefix(ctx); err != nil {
return return
} }
if IsFloat(rightValue) { if kern.IsFloat(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
f, _ := rightValue.(float64) f, _ := rightValue.(float64)
v = -f v = -f
} else { } else {
v = rightValue v = rightValue
} }
} else if IsInteger(rightValue) { } else if kern.IsInteger(rightValue) {
if self.tk.Sym == SymChangeSign { if opTerm.tk.Sym == SymChangeSign {
i, _ := rightValue.(int64) i, _ := rightValue.(int64)
v = -i v = -i
} else { } else {
v = rightValue v = rightValue
} }
} else { } else {
err = self.errIncompatibleType(rightValue) err = opTerm.errIncompatiblePrefixPostfixType(rightValue)
} }
return return
} }
+58 -50
View File
@@ -7,6 +7,8 @@ package expr
import ( import (
"fmt" "fmt"
"slices" "slices"
"git.portale-stac.it/go-pkg/expr/kern"
) )
//-------- plus term //-------- plus term
@@ -21,52 +23,54 @@ func newPlusTerm(tk *Token) (inst *term) {
} }
} }
func evalPlus(ctx ExprContext, self *term) (v any, err error) { func sumValues(plusTerm *term, leftValue, rightValue any) (v any, err error) {
var leftValue, rightValue any if (kern.IsString(leftValue) && kern.IsNumberString(rightValue)) || (kern.IsString(rightValue) && kern.IsNumberString(leftValue)) {
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil {
return
}
if (IsString(leftValue) && isNumberString(rightValue)) || (IsString(rightValue) && isNumberString(leftValue)) {
v = fmt.Sprintf("%v%v", leftValue, rightValue) v = fmt.Sprintf("%v%v", leftValue, rightValue)
} else if IsNumber(leftValue) && IsNumber(rightValue) { } else if kern.IsNumber(leftValue) && kern.IsNumber(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt + rightInt v = leftInt + rightInt
} }
} else if IsList(leftValue) && IsList(rightValue) { } else if kern.IsList(leftValue) && kern.IsList(rightValue) {
var leftList, rightList *ListType var leftList, rightList *kern.ListType
leftList, _ = leftValue.(*ListType) leftList, _ = leftValue.(*kern.ListType)
rightList, _ = rightValue.(*ListType) rightList, _ = rightValue.(*kern.ListType)
sumList := make(ListType, 0, len(*leftList)+len(*rightList)) sumList := make(kern.ListType, 0, len(*leftList)+len(*rightList))
for _, item := range *leftList { sumList = append(sumList, *leftList...)
sumList = append(sumList, item) sumList = append(sumList, *rightList...)
}
for _, item := range *rightList {
sumList = append(sumList, item)
}
v = &sumList v = &sumList
} else if (isFraction(leftValue) && IsNumber(rightValue)) || (isFraction(rightValue) && IsNumber(leftValue)) { } else if (kern.IsFraction(leftValue) && kern.IsNumber(rightValue)) || (kern.IsFraction(rightValue) && kern.IsNumber(leftValue)) {
if IsFloat(leftValue) || IsFloat(rightValue) { if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
v = numAsFloat(leftValue) + numAsFloat(rightValue) v = kern.NumAsFloat(leftValue) + kern.NumAsFloat(rightValue)
} else { } else {
v, err = sumAnyFract(leftValue, rightValue) v, err = kern.SumAnyFract(leftValue, rightValue)
} }
} else if IsDict(leftValue) && IsDict(rightValue) { } else if kern.IsDict(leftValue) && kern.IsDict(rightValue) {
leftDict, _ := leftValue.(*DictType) leftDict, _ := leftValue.(*kern.DictType)
rightDict, _ := rightValue.(*DictType) rightDict, _ := rightValue.(*kern.DictType)
c := leftDict.clone() c := leftDict.Clone()
c.merge(rightDict) c.Merge(rightDict)
v = c v = c
} else if kern.IsFraction(leftValue) && kern.IsFraction(rightValue) {
v, err = kern.SumAnyFract(leftValue, rightValue)
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = plusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return v, err
}
func evalPlus(ctx kern.ExprContext, plusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = plusTerm.evalInfix(ctx); err != nil {
return
}
return sumValues(plusTerm, leftValue, rightValue)
} }
//-------- minus term //-------- minus term
@@ -81,27 +85,21 @@ func newMinusTerm(tk *Token) (inst *term) {
} }
} }
func evalMinus(ctx ExprContext, self *term) (v any, err error) { func diffValues(minusTerm *term, leftValue, rightValue any) (v any, err error) {
var leftValue, rightValue any if kern.IsNumOrFract(leftValue) && kern.IsNumOrFract(rightValue) {
if kern.IsFloat(leftValue) || kern.IsFloat(rightValue) {
if leftValue, rightValue, err = self.evalInfix(ctx); err != nil { v = kern.NumAsFloat(leftValue) - kern.NumAsFloat(rightValue)
return } else if kern.IsFraction(leftValue) || kern.IsFraction(rightValue) {
} v, err = kern.SubAnyFract(leftValue, rightValue)
if isNumOrFract(leftValue) && isNumOrFract(rightValue) {
if IsFloat(leftValue) || IsFloat(rightValue) {
v = numAsFloat(leftValue) - numAsFloat(rightValue)
} else if isFraction(leftValue) || isFraction(rightValue) {
v, err = subAnyFract(leftValue, rightValue)
} else { } else {
leftInt, _ := leftValue.(int64) leftInt, _ := leftValue.(int64)
rightInt, _ := rightValue.(int64) rightInt, _ := rightValue.(int64)
v = leftInt - rightInt v = leftInt - rightInt
} }
} else if IsList(leftValue) && IsList(rightValue) { } else if kern.IsList(leftValue) && kern.IsList(rightValue) {
leftList, _ := leftValue.(*ListType) leftList, _ := leftValue.(*kern.ListType)
rightList, _ := rightValue.(*ListType) rightList, _ := rightValue.(*kern.ListType)
diffList := make(ListType, 0, len(*leftList)-len(*rightList)) diffList := make(kern.ListType, 0, len(*leftList)-len(*rightList))
for _, item := range *leftList { for _, item := range *leftList {
if slices.Index(*rightList, item) < 0 { if slices.Index(*rightList, item) < 0 {
diffList = append(diffList, item) diffList = append(diffList, item)
@@ -109,11 +107,21 @@ func evalMinus(ctx ExprContext, self *term) (v any, err error) {
} }
v = &diffList v = &diffList
} else { } else {
err = self.errIncompatibleTypes(leftValue, rightValue) err = minusTerm.errIncompatibleTypes(leftValue, rightValue)
} }
return return
} }
func evalMinus(ctx kern.ExprContext, minusTerm *term) (v any, err error) {
var leftValue, rightValue any
if leftValue, rightValue, err = minusTerm.evalInfix(ctx); err != nil {
return
}
return diffValues(minusTerm, leftValue, rightValue)
}
// init // init
func init() { func init() {
registerTermConstructor(SymPlus, newPlusTerm) registerTermConstructor(SymPlus, newPlusTerm)
+73
View File
@@ -0,0 +1,73 @@
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
// All rights reserved.
// operator-unset.go
package expr
import (
"strings"
"git.portale-stac.it/go-pkg/expr/kern"
)
//-------- unset term
func newUnsetTerm(tk *Token) (inst *term) {
return &term{
tk: *tk,
children: make([]*term, 0, 1),
position: posPrefix,
priority: priSign,
evalFunc: evalUnset,
}
}
func deleteContextItem(ctx kern.ExprContext, opTerm *term, item any) (deleted bool, err error) {
if name, ok := item.(string); ok {
var size int
if strings.HasSuffix(name, "()") {
size = ctx.FuncCount()
ctx.DeleteFunc(strings.TrimRight(name, "()"))
deleted = ctx.FuncCount() < size
} else {
size = ctx.VarCount()
ctx.DeleteVar(name)
deleted = ctx.VarCount() < size
}
} else {
err = opTerm.errIncompatiblePrefixPostfixType(item)
}
return
}
func evalUnset(ctx kern.ExprContext, opTerm *term) (v any, err error) {
var childValue any
var deleted bool
if childValue, err = opTerm.evalPrefix(ctx); err != nil {
return
}
count := 0
if kern.IsList(childValue) {
list, _ := childValue.(*kern.ListType)
for _, item := range *list {
if deleted, err = deleteContextItem(ctx, opTerm, item); err != nil {
break
} else if deleted {
count++
}
}
} else if deleted, err = deleteContextItem(ctx, opTerm, childValue); err == nil && deleted {
count++
}
if err == nil {
v = int64(count)
}
return
}
// init
func init() {
registerTermConstructor(SymKwUnset, newUnsetTerm)
}

Some files were not shown because too many files have changed in this diff Show More