Compare commits
	
		
			25 Commits
		
	
	
		
			0bca3333aa
			...
			c977e82d9e
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c977e82d9e | |||
| 903f1ae1ce | |||
| 9c66056c18 | |||
| f55a48aa26 | |||
| 434ddee733 | |||
| fcced6149f | |||
| 1f0f9cae22 | |||
| 1d8569d3a9 | |||
| 0fdd51049d | |||
| f9ed5776cd | |||
| a2c0a24494 | |||
| 2c5f02cc69 | |||
| d9fbe6f36d | |||
| e6174aca82 | |||
| a736bba2c7 | |||
| 7724cabdcc | |||
| 16557d70de | |||
| 04e71a1b3f | |||
| e463bd61d8 | |||
| 419af7bfea | |||
| 6c604812ee | |||
| 5cf0bfbad4 | |||
| a838361ea8 | |||
| 0dbb0ba515 | |||
| 7a0ba26aa3 | 
							
								
								
									
										2
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								ast.go
									
									
									
									
									
								
							| @ -63,7 +63,7 @@ func (self *ast) addToken2(tk *Token) (t *term, err error) { | |||||||
| 	if t = newTerm(tk, nil); t != nil { | 	if t = newTerm(tk, nil); t != nil { | ||||||
| 		err = self.addTerm(t) | 		err = self.addTerm(t) | ||||||
| 	} else { | 	} else { | ||||||
| 		err = tk.Errorf("No term constructor for token %q", tk.String()) | 		err = tk.Errorf("unexpected token %q", tk.String()) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -44,7 +44,7 @@ func TestAddTokensBad(t *testing.T) { | |||||||
| func TestAddUnknownTokens(t *testing.T) { | func TestAddUnknownTokens(t *testing.T) { | ||||||
| 	tk0 := NewToken(0, 0, SymPercent, "%") | 	tk0 := NewToken(0, 0, SymPercent, "%") | ||||||
| 
 | 
 | ||||||
| 	wantErr := errors.New(`No term constructor for token "%"`) | 	wantErr := errors.New(`unexpected token "%"`) | ||||||
| 
 | 
 | ||||||
| 	tree := NewAst() | 	tree := NewAst() | ||||||
| 	if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { | 	if gotErr := tree.addToken(tk0); gotErr != nil && gotErr.Error() != wantErr.Error() { | ||||||
|  | |||||||
| @ -6,17 +6,7 @@ package expr | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" |  | ||||||
| 	"io" | 	"io" | ||||||
| 	"strings" |  | ||||||
| ) |  | ||||||
| 
 |  | ||||||
| const ( |  | ||||||
| 	initName    = "init" |  | ||||||
| 	cleanName   = "clean" |  | ||||||
| 	resetName   = "reset" |  | ||||||
| 	nextName    = "next" |  | ||||||
| 	currentName = "current" |  | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| type dataCursor struct { | type dataCursor struct { | ||||||
| @ -39,58 +29,63 @@ func newDataCursor(ctx ExprContext, ds map[string]Functor) (dc *dataCursor) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| 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('{')
 | ||||||
| 	for key, _ := range m { | // 	for key, _ := range m {
 | ||||||
| 		if sb.Len() > 1 { | // 		if sb.Len() > 1 {
 | ||||||
| 			sb.WriteString(fmt.Sprintf(", %q: func(){}", key)) | // 			sb.WriteString(fmt.Sprintf(", %q: func(){}", key))
 | ||||||
| 		} else { | // 		} else {
 | ||||||
| 			sb.WriteString(fmt.Sprintf("%q: func(){}", key)) | // 			sb.WriteString(fmt.Sprintf("%q: func(){}", key))
 | ||||||
| 		} | // 		}
 | ||||||
| 	} | // 	}
 | ||||||
| 	sb.WriteByte('}') | // 	sb.WriteByte('}')
 | ||||||
| 	return sb.String() | // 	return sb.String()
 | ||||||
| } | // }
 | ||||||
| 
 | 
 | ||||||
| func (dc *dataCursor) String() string { | func (dc *dataCursor) String() string { | ||||||
| 	return "$()" | 	return "$()" | ||||||
| /*	var sb strings.Builder | 	/* | ||||||
| 	sb.WriteString(fmt.Sprintf(`$( | 	   var sb strings.Builder | ||||||
| 	index: %d, | 	   sb.WriteString(fmt.Sprintf(`$( | ||||||
| 	ds:    %s, | 	   index: %d, | ||||||
| 	ctx:   `, dc.index, mapToString(dc.ds))) | 	   ds:    %s, | ||||||
| 	CtxToBuilder(&sb, dc.ctx, 1) | 	   ctx:   `, dc.index, mapToString(dc.ds))) | ||||||
| 	sb.WriteByte(')') | 	   CtxToBuilder(&sb, dc.ctx, 1) | ||||||
| 	return sb.String() | 	   sb.WriteByte(')') | ||||||
| */ | 	   return sb.String() | ||||||
|  | 	*/ | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (dc *dataCursor) HasOperation(name string) (exists bool) { | func (dc *dataCursor) HasOperation(name string) (exists bool) { | ||||||
| 	f, ok := dc.ds[name] | 	exists = name == indexName | ||||||
| 	exists = ok && isFunctor(f) | 	if !exists { | ||||||
|  | 		f, ok := dc.ds[name] | ||||||
|  | 		exists = ok && isFunctor(f) | ||||||
|  | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { | func (dc *dataCursor) CallOperation(name string, args []any) (value any, err error) { | ||||||
| 	if functor, ok := dc.ds[name]; ok && isFunctor(functor) { | 	if name == indexName { | ||||||
|  | 		value = int64(dc.Index()) | ||||||
|  | 	} else if functor, ok := dc.ds[name]; ok && isFunctor(functor) { | ||||||
| 		if functor == dc.cleanFunc { | 		if functor == dc.cleanFunc { | ||||||
| 			return nil, dc.Clean() | 			value, err = dc.Clean() | ||||||
| 		} else if functor == dc.resetFunc { | 		} else if functor == dc.resetFunc { | ||||||
| 			return nil, dc.Reset() | 			value, err = dc.Reset() | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx := cloneContext(dc.ctx) | 			ctx := cloneContext(dc.ctx) | ||||||
| 			value, err = functor.Invoke(ctx, name, []any{}) | 			value, err = functor.Invoke(ctx, name, []any{}) | ||||||
| 			exportObjects(dc.ctx, ctx) | 			exportObjects(dc.ctx, ctx) | ||||||
| 		} | 		} | ||||||
| 
 |  | ||||||
| 	} else { | 	} else { | ||||||
| 		err = errNoOperation(name) | 		err = errNoOperation(name) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (dc *dataCursor) Reset() (err error) { | func (dc *dataCursor) Reset() (success bool, err error) { | ||||||
| 	if dc.resetFunc != nil { | 	if dc.resetFunc != nil { | ||||||
| 		if dc.resource != nil { | 		if dc.resource != nil { | ||||||
| 			ctx := cloneContext(dc.ctx) | 			ctx := cloneContext(dc.ctx) | ||||||
| @ -104,10 +99,11 @@ func (dc *dataCursor) Reset() (err error) { | |||||||
| 	} else { | 	} else { | ||||||
| 		err = errNoOperation(resetName) | 		err = errNoOperation(resetName) | ||||||
| 	} | 	} | ||||||
|  | 	success = err == nil | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (dc *dataCursor) Clean() (err error) { | func (dc *dataCursor) Clean() (success bool, err error) { | ||||||
| 	if dc.cleanFunc != nil { | 	if dc.cleanFunc != nil { | ||||||
| 		if dc.resource != nil { | 		if dc.resource != nil { | ||||||
| 			ctx := cloneContext(dc.ctx) | 			ctx := cloneContext(dc.ctx) | ||||||
| @ -120,6 +116,7 @@ func (dc *dataCursor) Clean() (err error) { | |||||||
| 	} else { | 	} else { | ||||||
| 		err = errors.New("no 'clean' function defined in the data-source") | 		err = errors.New("no 'clean' function defined in the data-source") | ||||||
| 	} | 	} | ||||||
|  | 	success = err == nil | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -169,7 +169,7 @@ x = 1; y = 2*x | |||||||
| == Other operations | == Other operations | ||||||
| 
 | 
 | ||||||
| === [blue]`;` operator | === [blue]`;` operator | ||||||
| The semicolon operator [blue]`;` is an infixed operator. It evaluates the left expression first and then the right expression. The latter is the final result. | The semicolon operator [blue]`;` is an infixed pseudo-operator. It evaluates the left expression first and then the right expression. The latter is the final result. | ||||||
| 
 | 
 | ||||||
| IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions. | IMPORTANT: Technically [blue]`;` is not treated as a real operator. It acts as a separator in lists of expressions. | ||||||
| 
 | 
 | ||||||
| @ -187,7 +187,7 @@ a=1; b=2; c=3; a+b+c    // returns 6 | |||||||
| [blue]`but` is very similar to [blue]`;`. The only difference is that [blue]`;` can't be used inside parenthesis [blue]`(` and [blue]`)`. | [blue]`but` is very similar to [blue]`;`. The only difference is that [blue]`;` can't be used inside parenthesis [blue]`(` and [blue]`)`. | ||||||
| 
 | 
 | ||||||
| === Assignment operator [blue]`=` | === Assignment operator [blue]`=` | ||||||
| The assignment operator [blue]`=` is used to define variable in the evaluation context or to change their value (see _ExprContext_). | The assignment operator [blue]`=` is used to define variables in the evaluation context or to change their value (see _ExprContext_). | ||||||
| The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation. | The value on the left side of [blue]`=` must be an identifier. The value on the right side can be any expression and it becomes the result of the assignment operation. | ||||||
| 
 | 
 | ||||||
| .Example | .Example | ||||||
| @ -202,13 +202,22 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a | |||||||
| .Syntax | .Syntax | ||||||
| [source,bnf] | [source,bnf] | ||||||
| ---- | ---- | ||||||
| <selector-operator> ::= <expression> "?" <selector-case> { ":" <selector-case> } ["::" <case-value>] | <selector-operator> ::= <select-expression> "?" <selector-case> { ":" <selector-case> } ["::" <default-multi-expression>] | ||||||
| <selector-case> ::= [<list>] <case-value> | <selector-case> ::= [<match-list>] <case-value> | ||||||
| <case-value> ::= "{" <multi-expr> "}" | <match-list> ::= "["<item>{","<items>}"]" | ||||||
| <multi-expr> ::= <expr> {";" <expr>} | <item> ::= <expression | ||||||
|  | <case-multi-expression> ::= "{" <multi-expression> "}" | ||||||
|  | <multi-expression> ::= <expression> {";" <expression>} | ||||||
| ---- | ---- | ||||||
| 
 | 
 | ||||||
| .Example | In other words, the selector operator evaluates the expression (`<select-expression>`) on the left-hand side of the `?` symbol; it then compares the result obtained with the values listed in the `<match-list>`'s. If the comparision find a match with a value in a match-list, the associated  `<case-multi-expression>` is evaluted, and its result will be the final result of the selection operation. | ||||||
|  | 
 | ||||||
|  | The match lists are optional. In that case, the position, from left to right, of the `<selector-case>` is used as match-list. Of course, that only works if the select-expression results in an integer. | ||||||
|  | 
 | ||||||
|  | The `:` symbol (colon) is the separator of the selector-cases. Note that if the value of the select-expression does not match any match-list, an error will be issued. Therefore, it is strongly recommended to provide a default (multi-)expression introduced by the `::` symbol (double-colon). Also note that the default expression has no match-list. | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | .Examples | ||||||
| [source,go] | [source,go] | ||||||
| ---- | ---- | ||||||
| 1 ? {"a"} : {"b"}                           // returns "b" | 1 ? {"a"} : {"b"}                           // returns "b" | ||||||
| @ -224,33 +233,38 @@ The _selector operator_ is very similar to the _switch/case/default_ statement a | |||||||
| The table below shows all supported operators by decreasing priorities. | The table below shows all supported operators by decreasing priorities. | ||||||
| 
 | 
 | ||||||
| .Operators priorities | .Operators priorities | ||||||
| [cols="^2,^2,^2,^5,<5"] | [cols="^2,^2,^2,^5,^5"] | ||||||
| |=== | |=== | ||||||
| | Priority | Operators     | Position | Operation | Operands and results | | Priority | Operators     | Position | Operation | Operands and results | ||||||
| 
 | 
 | ||||||
| 1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ "!" -> _integer_ | .1+|*ITEM*| [blue]`.` | _Infix_ | _Item_| _collection_ `"."` _key_ -> _any_ | ||||||
| 1+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| ("+"\|"-") _number_ -> _number_ | .2+|*INC*| [blue]`++` | _Postfix_ | _Post increment_| _integer-variable_ `"++"` -> _integer_ | ||||||
| .5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ "*" _number_ -> _number_ | | [blue]`++` | _Postfix_ | _Next item_ | _iterator_ `"++"` -> _any_ | ||||||
| | [blue]`*` | _Infix_ | _String-repeat_ | _string_ "*" _integer_ -> _string_ | .1+|*FACT*| [blue]`!` | _Postfix_ | _Factorial_| _integer_ `"!"` -> _integer_ | ||||||
| | [blue]`/` | _Infix_ | _Division_ | _number_ "/" _number_ -> _number_ | .3+|*SIGN*| [blue]`+`, [blue]`-` | _Prefix_ | _Change-sign_| (`"+"`\|`"-"`) _number_ -> _number_ | ||||||
| | [blue]`./` | _Infix_ | _Float-division_ | __number__ "./" _number_ -> _float_ | | [blue]`#` | _Prefix_ | _Lenght-of_ | `"#"` _collection_ -> _integer_ | ||||||
| | [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ "%" _integer_ -> _integer_ | | [blue]`#` | _Prefix_ | _Size-of_ | `"#"` _iterator_ -> _integer_ | ||||||
| .5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ "+" _number_ -> _number_ | .5+|*PROD*| [blue]`*` | _Infix_ | _Product_ | _number_ `"*"` _number_ -> _number_ | ||||||
| | [blue]`+` | _Infix_ |  _String-concat_ | (_string_\|_number_) "+" (_string_\|_number_) -> _string_ | | [blue]`*` | _Infix_ | _String-repeat_ | _string_ `"*"` _integer_ -> _string_ | ||||||
| | [blue]`+` | _Infix_ |  _List-join_ | _list_ "+" _list_ -> _list_ | | [blue]`/` | _Infix_ | _Division_ | _number_ `"/"` _number_ -> _number_ | ||||||
| | [blue]`-` | _Infix_ | _Subtraction_ | _number_ "-" _number_ -> _number_ | | [blue]`./` | _Infix_ | _Float-division_ | __number__ `"./"` _number_ -> _float_ | ||||||
| | [blue]`-` | _Infix_ | _List-difference_ | _list_ "-" _list_ -> _list_ | | [blue]`%` | _Infix_ | _Integer-remainder_ | _integer_ `"%"` _integer_ -> _integer_ | ||||||
| .6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ "<" _comparable_ -> _boolean_ | .5+|*SUM*| [blue]`+` | _Infix_ | _Sum_ | _number_ `"+"` _number_ -> _number_ | ||||||
| | [blue]`\<=` | _Infix_ |  _less-equal_ | _comparable_ "\<=" _comparable_ -> _boolean_ | | [blue]`+` | _Infix_ |  _String-concat_ | (_string_\|_number_) `"+"` (_string_\|_number_) -> _string_ | ||||||
| | [blue]`>` | _Infix_ |  _greater_ | _comparable_ ">" _comparable_ -> _boolean_ | | [blue]`+` | _Infix_ |  _List-join_ | _list_ `"+"` _list_ -> _list_ | ||||||
| | [blue]`>=` | _Infix_ |  _greater-equal_ | _comparable_ ">=" _comparable_ -> _boolean_ | | [blue]`-` | _Infix_ | _Subtraction_ | _number_ `"-"` _number_ -> _number_ | ||||||
| | [blue]`==` | _Infix_ |  _equal_ | _comparable_ "==" _comparable_ -> _boolean_ | | [blue]`-` | _Infix_ | _List-difference_ | _list_ `"-"` _list_ -> _list_ | ||||||
| | [blue]`!=` | _Infix_ |  _not-equal_ | _comparable_ "!=" _comparable_ -> _boolean_ | .6+|*RELATION*| [blue]`<` | _Infix_ | _less_ | _comparable_ `"<"` _comparable_ -> _boolean_ | ||||||
| .1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | "not" _boolean_ -> _boolean_ | | [blue]`\<=` | _Infix_ |  _less-equal_ | _comparable_ `"\<="` _comparable_ -> _boolean_ | ||||||
| .2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ "and" _boolean_ -> _boolean_ | | [blue]`>` | _Infix_ |  _greater_ | _comparable_ `">"` _comparable_ -> _boolean_ | ||||||
| | [blue]`&&` | _Infix_ | _and_ | _boolean_ "&&" _boolean_ -> _boolean_ | | [blue]`>=` | _Infix_ |  _greater-equal_ | _comparable_ `">="` _comparable_ -> _boolean_ | ||||||
| .2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ "or" _boolean_ -> _boolean_ | | [blue]`==` | _Infix_ |  _equal_ | _comparable_ `"=="` _comparable_ -> _boolean_ | ||||||
| | [blue]`\|\|` | _Infix_ | _or_ | _boolean_ "\|\|" _boolean_ -> _boolean_ | | [blue]`!=` | _Infix_ |  _not-equal_ | _comparable_ `"!="` _comparable_ -> _boolean_ | ||||||
|  | .1+|*NOT*| [blue]`not` | _Prefix_ | _not_ | `"not"` _boolean_ -> _boolean_ | ||||||
|  | .2+|*AND*| [blue]`and` | _Infix_ | _and_ | _boolean_ `"and"` _boolean_ -> _boolean_ | ||||||
|  | | [blue]`&&` | _Infix_ | _and_ | _boolean_ `"&&"` _boolean_ -> _boolean_ | ||||||
|  | .2+|*OR*| [blue]`or` | _Infix_ | _or_ | _boolean_ `"or"` _boolean_ -> _boolean_ | ||||||
|  | | [blue]`\|\|` | _Infix_ | _or_ | _boolean_ `"\|\|"` _boolean_ -> _boolean_ | ||||||
| .1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_ | .1+|*ASSIGN*| [blue]`=` | _Infix_ | _assignment_ | _identifier_ "=" _any_ -> _any_ | ||||||
| .1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_ | .1+|*BUT*| [blue]`but` | _Infix_ | _but_ | _any_ "but" _any_ -> _any_ | ||||||
| |=== | |=== | ||||||
|  | |||||||
| @ -5,7 +5,6 @@ | |||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"fmt" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
| @ -53,7 +52,7 @@ func TestExpr(t *testing.T) { | |||||||
| 		ImportOsFuncs(ctx) | 		ImportOsFuncs(ctx) | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser(ctx) | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, "Expr", input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
| 		r := strings.NewReader(input.source) | 		r := strings.NewReader(input.source) | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) | 		scanner := NewScanner(r, DefaultTranslations()) | ||||||
| @ -81,5 +80,5 @@ func TestExpr(t *testing.T) { | |||||||
| 			failed++ | 			failed++ | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) | 	t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										33
									
								
								file-reader.expr
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								file-reader.expr
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,33 @@ | |||||||
|  | builtin ["os.file", "base"]; | ||||||
|  | 
 | ||||||
|  | readInt=func(fh){ | ||||||
|  | 	line=readFile(fh); | ||||||
|  | 	line ? [nil] {nil} :: {int(line)} | ||||||
|  | }; | ||||||
|  | 
 | ||||||
|  | ds={ | ||||||
|  | 	"init":func(filename){ | ||||||
|  | 		fh=openFile(filename); | ||||||
|  | 		fh ? [nil] {nil} :: { @current=readInt(fh); @prev=@current }; | ||||||
|  | 		fh | ||||||
|  | 	}, | ||||||
|  | 	"current":func(){ | ||||||
|  | 		prev | ||||||
|  | 	}, | ||||||
|  | 	"next":func(fh){ | ||||||
|  | 		current ?  | ||||||
|  | 			[nil] {current} | ||||||
|  | 			:: {@prev=current; @current=readInt(fh) but current} | ||||||
|  | 	}, | ||||||
|  | 	"clean":func(fh){ | ||||||
|  | 		closeFile(fh) | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | //;f=$(ds, "int.list") | ||||||
|  | /* | ||||||
|  | ;f++ | ||||||
|  | ;f++ | ||||||
|  | ;f++ | ||||||
|  | */ | ||||||
|  | //;add(f) | ||||||
							
								
								
									
										59
									
								
								func-math.go
									
									
									
									
									
								
							
							
						
						
									
										59
									
								
								func-math.go
									
									
									
									
									
								
							| @ -9,40 +9,40 @@ import ( | |||||||
| 	"io" | 	"io" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| func checkNumberParamExpected(funcName string, paramValue any, paramPos int) (err error) { | func checkNumberParamExpected(funcName string, paramValue any, paramPos, level, subPos int) (err error) { | ||||||
| 	if !(isNumber(paramValue) || isList(paramValue) || isFraction(paramValue)) { | 	if !(isNumber(paramValue) || isFraction(paramValue)) /*|| isList(paramValue)*/ { | ||||||
| 		err = fmt.Errorf("%s(): param nr %d has wrong type %T, number expected", funcName, paramPos+1, paramValue) | 		err = fmt.Errorf("%s(): param nr %d (%d in %d) has wrong type %T, number expected", | ||||||
|  | 			funcName, paramPos+1, subPos+1, level, paramValue) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | func doAdd(ctx ExprContext, name string, it 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 *fraction | 	var fractSum *fraction | ||||||
| 	var v any | 	var v any | ||||||
| 
 | 
 | ||||||
|  | 	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 { | ||||||
|  | 			v = NewListIterator(list, nil) | ||||||
|  | 		} | ||||||
| 		if subIter, ok := v.(Iterator); ok { | 		if subIter, ok := v.(Iterator); ok { | ||||||
| 			if v, err = doAdd(ctx, name, subIter); err != nil { | 			if v, err = doAdd(ctx, name, subIter, count, level); err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 			if subIter.HasOperation(cleanName) { | 			if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { | ||||||
| 				if _, err = subIter.CallOperation(cleanName, nil); err != nil { | 				if _, err = extIter.CallOperation(cleanName, nil); err != nil { | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { | ||||||
| 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | 			break | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 			if list, ok := v.(*ListType); ok { |  | ||||||
| 				if v, err = doAdd(ctx, name, NewListIterator(list)); err != nil { |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  | 		count++ | ||||||
| 
 | 
 | ||||||
| 		if !sumAsFloat { | 		if !sumAsFloat { | ||||||
| 			if isFloat(v) { | 			if isFloat(v) { | ||||||
| @ -87,38 +87,37 @@ func doAdd(ctx ExprContext, name string, it Iterator) (result any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func addFunc(ctx ExprContext, name string, args []any) (result any, err error) { | func addFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	result, err = doAdd(ctx, name, NewArrayIterator(args)) | 	result, err = doAdd(ctx, name, NewArrayIterator(args), 0, -1) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) { | func doMul(ctx ExprContext, name string, it 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 *fraction | 	var fractProd *fraction | ||||||
| 	var v any | 	var v any | ||||||
| 
 | 
 | ||||||
|  | 	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 { | ||||||
|  | 			v = NewListIterator(list, nil) | ||||||
|  | 		} | ||||||
| 		if subIter, ok := v.(Iterator); ok { | 		if subIter, ok := v.(Iterator); ok { | ||||||
| 			if v, err = doMul(ctx, name, subIter); err != nil { | 			if v, err = doMul(ctx, name, subIter, count, level); err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 			if subIter.HasOperation(cleanName) { | 			if extIter, ok := v.(ExtIterator); ok && extIter.HasOperation(cleanName) { | ||||||
| 				if _, err = subIter.CallOperation(cleanName, nil); err != nil { | 				if _, err = extIter.CallOperation(cleanName, nil); err != nil { | ||||||
| 					return | 					return | ||||||
| 				} | 				} | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| 			if err = checkNumberParamExpected(name, v, it.Index()); err != nil { | 			if err = checkNumberParamExpected(name, v, count, level, it.Index()); err != nil { | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 
 |  | ||||||
| 			if list, ok := v.(*ListType); ok { |  | ||||||
| 				if v, err = doMul(ctx, name, NewListIterator(list)); err != nil { |  | ||||||
| 					break |  | ||||||
| 				} |  | ||||||
| 			} |  | ||||||
| 		} | 		} | ||||||
|  | 		count++ | ||||||
| 
 | 
 | ||||||
| 		if !mulAsFloat { | 		if !mulAsFloat { | ||||||
| 			if isFloat(v) { | 			if isFloat(v) { | ||||||
| @ -163,7 +162,7 @@ func doMul(ctx ExprContext, name string, it Iterator) (result any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { | func mulFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	result, err = doMul(ctx, name, NewArrayIterator(args)) | 	result, err = doMul(ctx, name, NewArrayIterator(args), 0, -1) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| @ -173,5 +172,5 @@ func ImportMathFuncs(ctx ExprContext) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
| 	registerImport("math.arith", ImportMathFuncs, "Function add() and mul()") | 	registerImport("math.arith", ImportMathFuncs, "Functions add() and mul()") | ||||||
| } | } | ||||||
|  | |||||||
| @ -40,7 +40,7 @@ func joinStrFunc(ctx ExprContext, name string, args []any) (result any, err erro | |||||||
| 			result = "" | 			result = "" | ||||||
| 		} else if len(args) == 2 { | 		} else if len(args) == 2 { | ||||||
| 			if ls, ok := args[1].(*ListType); ok { | 			if ls, ok := args[1].(*ListType); ok { | ||||||
| 				result, err = doJoinStr(name, sep, NewListIterator(ls)) | 				result, err = doJoinStr(name, sep, NewListIterator(ls, nil)) | ||||||
| 			} else if it, ok := args[1].(Iterator); ok { | 			} else if it, ok := args[1].(Iterator); ok { | ||||||
| 				result, err = doJoinStr(name, sep, it) | 				result, err = doJoinStr(name, sep, it) | ||||||
| 			} else { | 			} else { | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ package expr | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" |  | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
| ) | ) | ||||||
| @ -50,7 +49,7 @@ func TestFuncs(t *testing.T) { | |||||||
| 		// ImportOsFuncs(ctx)
 | 		// ImportOsFuncs(ctx)
 | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser(ctx) | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, "Funcs", input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
| 		r := strings.NewReader(input.source) | 		r := strings.NewReader(input.source) | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) | 		scanner := NewScanner(r, DefaultTranslations()) | ||||||
| @ -78,5 +77,5 @@ func TestFuncs(t *testing.T) { | |||||||
| 			failed++ | 			failed++ | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) | 	t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										100
									
								
								iter-list.go
									
									
									
									
									
								
							
							
						
						
									
										100
									
								
								iter-list.go
									
									
									
									
									
								
							| @ -4,26 +4,69 @@ | |||||||
| // iter-list.go
 | // iter-list.go
 | ||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| import "io" | import ( | ||||||
|  | 	"fmt" | ||||||
|  | 	"io" | ||||||
|  | ) | ||||||
| 
 | 
 | ||||||
| type ListIterator struct { | type ListIterator struct { | ||||||
| 	a     *ListType | 	a     *ListType | ||||||
|  | 	count int | ||||||
| 	index int | 	index int | ||||||
|  | 	start int | ||||||
|  | 	stop  int | ||||||
|  | 	step  int | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewListIterator(list *ListType) *ListIterator { | func NewListIterator(list *ListType, args []any) (it *ListIterator) { | ||||||
| 	return &ListIterator{a: list, index: 0} | 	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) *ListIterator { | func NewArrayIterator(array []any) (it *ListIterator) { | ||||||
| 	return &ListIterator{a: (*ListType)(&array), index: 0} | 	it = &ListIterator{a: (*ListType)(&array), count: 0, index: -1, start: 0, stop: len(array) - 1, step: 1} | ||||||
|  | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func NewAnyIterator(value any) (it *ListIterator) { | func NewAnyIterator(value any) (it *ListIterator) { | ||||||
| 	if value == nil { | 	if value == nil { | ||||||
| 		it = NewArrayIterator([]any{}) | 		it = NewArrayIterator([]any{}) | ||||||
| 	} else if list, ok := value.(*ListType); ok { | 	} else if list, ok := value.(*ListType); ok { | ||||||
| 		it = NewListIterator(list) | 		it = NewListIterator(list, nil) | ||||||
| 	} else if array, ok := value.([]any); ok { | 	} else if array, ok := value.([]any); ok { | ||||||
| 		it = NewArrayIterator(array) | 		it = NewArrayIterator(array) | ||||||
| 	} else if it1, ok := value.(*ListIterator); ok { | 	} else if it1, ok := value.(*ListIterator); ok { | ||||||
| @ -34,17 +77,36 @@ func NewAnyIterator(value any) (it *ListIterator) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (it *ListIterator) HasOperation(name string) bool { | func (it *ListIterator) String() string { | ||||||
| 	return false | 	var l = 0 | ||||||
|  | 	if it.a != nil { | ||||||
|  | 		l = len(*it.a) | ||||||
|  | 	} | ||||||
|  | 	return fmt.Sprintf("$(#%d)", l) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (it *ListIterator) CallOperation(name string, args []any) (any, error) { | func (it *ListIterator) HasOperation(name string) bool { | ||||||
| 	return nil, errNoOperation(name) | 	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) { | func (it *ListIterator) Current() (item any, err error) { | ||||||
| 	a := *(it.a) | 	a := *(it.a) | ||||||
| 	if it.index >= 0 && it.index < len(a) { | 	if it.index >= 0 && it.index <= it.stop { | ||||||
| 		item = a[it.index] | 		item = a[it.index] | ||||||
| 	} else { | 	} else { | ||||||
| 		err = io.EOF | 		err = io.EOF | ||||||
| @ -53,18 +115,18 @@ func (it *ListIterator) Current() (item any, err error) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (it *ListIterator) Next() (item any, err error) { | func (it *ListIterator) Next() (item any, err error) { | ||||||
|  | 	it.index += it.step | ||||||
| 	if item, err = it.Current(); err != io.EOF { | 	if item, err = it.Current(); err != io.EOF { | ||||||
| 		it.index++ | 		it.count++ | ||||||
| 	} | 	} | ||||||
| 	// if it.index < len(it.a) {
 |  | ||||||
| 	// 	item = it.a[it.index]
 |  | ||||||
| 	// 	it.index++
 |  | ||||||
| 	// } else {
 |  | ||||||
| 	// 	err = io.EOF
 |  | ||||||
| 	// }
 |  | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (it *ListIterator) Index() int { | func (it *ListIterator) Index() int { | ||||||
| 	return it.index - 1 | 	return it.index | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (it *ListIterator) Reset() (bool, error) { | ||||||
|  | 	it.index = it.start | ||||||
|  | 	return true, nil | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										18
									
								
								iterator.expr
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										18
									
								
								iterator.expr
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,18 @@ | |||||||
|  | ds={ | ||||||
|  | 	"init":func(end){@end=end; @current=0; @prev=@current}, | ||||||
|  | 	"current":func(){prev}, | ||||||
|  | 	"next":func(){ | ||||||
|  | 		(current <= end) ? [true] {@current=current+1; @prev=current} :: {nil} | ||||||
|  | 	}, | ||||||
|  | 	"reset":func(){@current=0; @prev=@current} | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // Example | ||||||
|  | //; | ||||||
|  | //it=$(ds,3); | ||||||
|  | //it++; | ||||||
|  | //it."reset" | ||||||
|  | //it++; | ||||||
|  | //it++; | ||||||
|  | //add(it) | ||||||
|  | 
 | ||||||
							
								
								
									
										16
									
								
								iterator.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								iterator.go
									
									
									
									
									
								
							| @ -9,10 +9,26 @@ import ( | |||||||
| 	"fmt" | 	"fmt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
|  | // Operator names
 | ||||||
|  | 
 | ||||||
|  | const ( | ||||||
|  | 	initName    = "init" | ||||||
|  | 	cleanName   = "clean" | ||||||
|  | 	resetName   = "reset" | ||||||
|  | 	nextName    = "next" | ||||||
|  | 	currentName = "current" | ||||||
|  | 	indexName   = "index" | ||||||
|  | 	countName   = "count" | ||||||
|  | ) | ||||||
|  | 
 | ||||||
| type Iterator interface { | type Iterator interface { | ||||||
| 	Next() (item any, err error) // must return io.EOF after the last item
 | 	Next() (item any, err error) // must return io.EOF after the last item
 | ||||||
| 	Current() (item any, err error) | 	Current() (item any, err error) | ||||||
| 	Index() int | 	Index() int | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | type ExtIterator interface { | ||||||
|  | 	Iterator | ||||||
| 	HasOperation(name string) bool | 	HasOperation(name string) bool | ||||||
| 	CallOperation(name string, args []any) (value any, err error) | 	CallOperation(name string, args []any) (value any, err error) | ||||||
| } | } | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ package expr | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
|  | 	"fmt" | ||||||
| ) | ) | ||||||
| 
 | 
 | ||||||
| // -------- function call term
 | // -------- function call term
 | ||||||
| @ -21,6 +22,20 @@ func newFuncCallTerm(tk *Token, args []*term) *term { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // -------- eval func call
 | // -------- eval func call
 | ||||||
|  | func checkFunctionCall(ctx ExprContext, name string, params []any) (err error) { | ||||||
|  | 	if info, exists := ctx.GetFuncInfo(name); exists { | ||||||
|  | 		if info.MinArgs() > len(params) { | ||||||
|  | 			err = fmt.Errorf("too few params -- expected %d, got %d", info.MinArgs(), len(params)) | ||||||
|  | 		} | ||||||
|  | 		if info.MaxArgs() >= 0 && info.MaxArgs() < len(params) { | ||||||
|  | 			err = fmt.Errorf("too much params -- expected %d, got %d", info.MaxArgs(), len(params)) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		err = fmt.Errorf("unknown function %s()", name) | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | ||||||
| 	ctx := cloneContext(parentCtx) | 	ctx := cloneContext(parentCtx) | ||||||
| 	name, _ := self.tk.Value.(string) | 	name, _ := self.tk.Value.(string) | ||||||
| @ -34,8 +49,10 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | |||||||
| 		params[i] = param | 		params[i] = param | ||||||
| 	} | 	} | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		if v, err = ctx.Call(name, params); err == nil { | 		if err = checkFunctionCall(ctx, name, params); err == nil { | ||||||
| 			exportObjects(parentCtx, ctx) | 			if v, err = ctx.Call(name, params); err == nil { | ||||||
|  | 				exportObjects(parentCtx, ctx) | ||||||
|  | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| @ -44,9 +61,9 @@ func evalFuncCall(parentCtx ExprContext, self *term) (v any, err error) { | |||||||
| // -------- function definition term
 | // -------- function definition term
 | ||||||
| func newFuncDefTerm(tk *Token, args []*term) *term { | func newFuncDefTerm(tk *Token, args []*term) *term { | ||||||
| 	return &term{ | 	return &term{ | ||||||
| 		tk:       *tk, | 		tk:       *tk, // value is the expression body
 | ||||||
| 		parent:   nil, | 		parent:   nil, | ||||||
| 		children: args, // arg[0]=formal-param-list, arg[1]=*ast
 | 		children: args, // function params
 | ||||||
| 		position: posLeaf, | 		position: posLeaf, | ||||||
| 		priority: priValue, | 		priority: priValue, | ||||||
| 		evalFunc: evalFuncDef, | 		evalFunc: evalFuncDef, | ||||||
|  | |||||||
| @ -12,7 +12,7 @@ import ( | |||||||
| 
 | 
 | ||||||
| // -------- iterator term
 | // -------- iterator term
 | ||||||
| 
 | 
 | ||||||
| func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { | func newDsIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { | ||||||
| 	tk.Sym = SymIterator | 	tk.Sym = SymIterator | ||||||
| 
 | 
 | ||||||
| 	children := make([]*term, 0, 1+len(args)) | 	children := make([]*term, 0, 1+len(args)) | ||||||
| @ -28,6 +28,18 @@ func newIteratorTerm(tk *Token, dsTerm *term, args []*term) *term { | |||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func newIteratorTerm(tk *Token, args []*term) *term { | ||||||
|  | 	tk.Sym = SymIterator | ||||||
|  | 	return &term{ | ||||||
|  | 		tk:       *tk, | ||||||
|  | 		parent:   nil, | ||||||
|  | 		children: args, | ||||||
|  | 		position: posLeaf, | ||||||
|  | 		priority: priValue, | ||||||
|  | 		evalFunc: evalIterator, | ||||||
|  | 	} | ||||||
|  | } | ||||||
|  | 
 | ||||||
| // -------- eval iterator
 | // -------- eval iterator
 | ||||||
| 
 | 
 | ||||||
| func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { | func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { | ||||||
| @ -43,59 +55,21 @@ func evalTermArray(ctx ExprContext, a []*term) (values []any, err error) { | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| // func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) {
 | func evalFirstChild(ctx ExprContext, self *term) (value any, err error) { | ||||||
| // 	var value any
 |  | ||||||
| // 	if len(self.children) < 1 || self.children[0] == nil {
 |  | ||||||
| // 		err = self.Errorf("missing the data-source parameter")
 |  | ||||||
| // 		return
 |  | ||||||
| // 	}
 |  | ||||||
| 
 |  | ||||||
| // 	if value, err = self.children[0].compute(ctx); err != nil {
 |  | ||||||
| // 		return
 |  | ||||||
| // 	}
 |  | ||||||
| 
 |  | ||||||
| // 	if dictAny, ok := value.(map[any]any); ok {
 |  | ||||||
| // 		ds = make(map[string]Functor)
 |  | ||||||
| // 		// required functions
 |  | ||||||
| // 		for _, k := range []string{currentName, nextName} {
 |  | ||||||
| // 			if item, exists := dictAny[k]; exists && item != nil {
 |  | ||||||
| // 				if functor, ok := item.(*funcDefFunctor); ok {
 |  | ||||||
| // 					ds[k] = functor
 |  | ||||||
| // 				}
 |  | ||||||
| // 			} else {
 |  | ||||||
| // 				err = fmt.Errorf("the data-source must provide a non-nil %q operator", k)
 |  | ||||||
| // 				return
 |  | ||||||
| // 			}
 |  | ||||||
| // 		}
 |  | ||||||
| // 		// Optional functions
 |  | ||||||
| // 		for _, k := range []string{initName, resetName, cleanName} {
 |  | ||||||
| // 			if item, exists := dictAny[k]; exists && item != nil {
 |  | ||||||
| // 				if functor, ok := item.(*funcDefFunctor); ok {
 |  | ||||||
| // 					ds[k] = functor
 |  | ||||||
| // 				}
 |  | ||||||
| // 			}
 |  | ||||||
| // 		}
 |  | ||||||
| // 	} else {
 |  | ||||||
| // 		err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value)
 |  | ||||||
| // 	}
 |  | ||||||
| // 	return
 |  | ||||||
| // }
 |  | ||||||
| 
 |  | ||||||
| func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err error) { |  | ||||||
| 	var value any |  | ||||||
| 	if len(self.children) < 1 || self.children[0] == nil { | 	if len(self.children) < 1 || self.children[0] == nil { | ||||||
| 		err = self.Errorf("missing the data-source parameter") | 		err = self.Errorf("missing the data-source parameter") | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if value, err = self.children[0].compute(ctx); err != nil { | 	value, err = self.children[0].compute(ctx) | ||||||
| 		return | 	return | ||||||
| 	} | } | ||||||
| 
 | 
 | ||||||
| 	requiredFields := []string{currentName, nextName} | func getDataSourceDict(ctx ExprContext, self *term, firstChildValue any) (ds map[string]Functor, err error) { | ||||||
| 	fieldsMask := 0b11 | 	if dictAny, ok := firstChildValue.(map[any]any); ok { | ||||||
| 	foundFields := 0 | 		requiredFields := []string{currentName, nextName} | ||||||
| 	if dictAny, ok := value.(map[any]any); ok { | 		fieldsMask := 0b11 | ||||||
|  | 		foundFields := 0 | ||||||
| 		ds = make(map[string]Functor) | 		ds = make(map[string]Functor) | ||||||
| 		for keyAny, item := range dictAny { | 		for keyAny, item := range dictAny { | ||||||
| 			if key, ok := keyAny.(string); ok { | 			if key, ok := keyAny.(string); ok { | ||||||
| @ -117,45 +91,94 @@ func getDataSourceDict(ctx ExprContext, self *term) (ds map[string]Functor, err | |||||||
| 			} | 			} | ||||||
| 			err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) | 			err = fmt.Errorf("the data-source must provide a non-nil %q operator(s)", strings.Join(missingFields, ", ")) | ||||||
| 		} | 		} | ||||||
| 	} else { |  | ||||||
| 		err = self.Errorf("the first param (data-source) of an iterator must be a dict, not a %T", value) |  | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalIterator(ctx ExprContext, self *term) (v any, err error) { | func evalIterator(ctx ExprContext, self *term) (v any, err error) { | ||||||
|  | 	var firstChildValue any | ||||||
| 	var ds map[string]Functor | 	var ds map[string]Functor | ||||||
| 
 | 
 | ||||||
| 	if ds, err = getDataSourceDict(ctx, self); err != nil { | 	if firstChildValue, err = evalFirstChild(ctx, self); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dc := newDataCursor(ctx, ds) | 	if ds, err = getDataSourceDict(ctx, self, firstChildValue); err != nil { | ||||||
| 	// var it Iterator = dc
 | 		return | ||||||
| 	// fmt.Println(it)
 |  | ||||||
| 	if initFunc, exists := ds[initName]; exists && initFunc != nil { |  | ||||||
| 		var args []any |  | ||||||
| 		if len(self.children) > 1 { |  | ||||||
| 			if args, err = evalTermArray(ctx, self.children[1:]); err != nil { |  | ||||||
| 				return |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			args = []any{} |  | ||||||
| 		} |  | ||||||
| 
 |  | ||||||
| 		initCtx := dc.ctx.Clone() |  | ||||||
| 		if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { |  | ||||||
| 			return |  | ||||||
| 		} |  | ||||||
| 		exportObjects(dc.ctx, initCtx) |  | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	dc.nextFunc, _ = ds[nextName] | 	if ds != nil { | ||||||
| 	dc.currentFunc, _ = ds[currentName] | 		dc := newDataCursor(ctx, ds) | ||||||
| 	dc.cleanFunc, _ = ds[cleanName] | 		if initFunc, exists := ds[initName]; exists && initFunc != nil { | ||||||
| 	dc.resetFunc, _ = ds[resetName] | 			var args []any | ||||||
|  | 			if len(self.children) > 1 { | ||||||
|  | 				if args, err = evalTermArray(ctx, self.children[1:]); err != nil { | ||||||
|  | 					return | ||||||
|  | 				} | ||||||
|  | 			} else { | ||||||
|  | 				args = []any{} | ||||||
|  | 			} | ||||||
| 
 | 
 | ||||||
| 	v = dc | 			initCtx := dc.ctx.Clone() | ||||||
|  | 			if dc.resource, err = initFunc.Invoke(initCtx, initName, args); err != nil { | ||||||
|  | 				return | ||||||
|  | 			} | ||||||
|  | 			exportObjects(dc.ctx, initCtx) | ||||||
|  | 		} | ||||||
| 
 | 
 | ||||||
|  | 		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 | ||||||
|  | 		if args, err = evalSibling(ctx, self.children, nil); err == nil { | ||||||
|  | 			v = NewListIterator(list, args) | ||||||
|  | 		} | ||||||
|  | 	} else { | ||||||
|  | 		var list []any | ||||||
|  | 		if list, err = evalSibling(ctx, self.children, firstChildValue); err == nil { | ||||||
|  | 			v = NewArrayIterator(list) | ||||||
|  | 		} | ||||||
|  | 	} | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | // func evalChildren(ctx ExprContext, terms []*term, firstChildValue any) (list *ListType, err error) {
 | ||||||
|  | // 	items := make(ListType, len(terms))
 | ||||||
|  | // 	for i, tree := range terms {
 | ||||||
|  | // 		var param any
 | ||||||
|  | // 		if i == 0 && firstChildValue != nil {
 | ||||||
|  | // 			param = firstChildValue
 | ||||||
|  | // 		} else if param, err = tree.compute(ctx); err != nil {
 | ||||||
|  | // 			break
 | ||||||
|  | // 		}
 | ||||||
|  | // 		items[i] = param
 | ||||||
|  | // 	}
 | ||||||
|  | // 	if err == nil {
 | ||||||
|  | // 		list = &items
 | ||||||
|  | // 	}
 | ||||||
|  | // 	return
 | ||||||
|  | // }
 | ||||||
|  | 
 | ||||||
|  | func evalSibling(ctx ExprContext, terms []*term, firstChildValue any) (list []any, err error) { | ||||||
|  | 	items := make([]any, 0, len(terms)) | ||||||
|  | 	for i, tree := range terms { | ||||||
|  | 		var param any | ||||||
|  | 		if i == 0 { | ||||||
|  | 			if firstChildValue == nil { | ||||||
|  | 				continue | ||||||
|  | 			} | ||||||
|  | 			param = firstChildValue | ||||||
|  | 		} else if param, err = tree.compute(ctx); err != nil { | ||||||
|  | 			break | ||||||
|  | 		} | ||||||
|  | 		items = append(items, param) | ||||||
|  | 	} | ||||||
|  | 	if err == nil { | ||||||
|  | 		list = items | ||||||
|  | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -29,7 +29,14 @@ func evalAssign(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 
 | 
 | ||||||
| 	if v, err = self.children[1].compute(ctx); err == nil { | 	if v, err = self.children[1].compute(ctx); err == nil { | ||||||
| 		if functor, ok := v.(Functor); ok { | 		if functor, ok := v.(Functor); ok { | ||||||
| 			ctx.RegisterFunc(leftTerm.source(), functor, 0, -1) | 			var minArgs, maxArgs int = 0, 0 | ||||||
|  | 
 | ||||||
|  | 			if funcDef, ok := functor.(*funcDefFunctor); ok { | ||||||
|  | 				l := len(funcDef.params) | ||||||
|  | 				minArgs = l | ||||||
|  | 				maxArgs = l | ||||||
|  | 			} | ||||||
|  | 			ctx.RegisterFunc(leftTerm.source(), functor, minArgs, maxArgs) | ||||||
| 		} else { | 		} else { | ||||||
| 			ctx.setVar(leftTerm.source(), v) | 			ctx.setVar(leftTerm.source(), v) | ||||||
| 		} | 		} | ||||||
|  | |||||||
| @ -50,7 +50,7 @@ func evalBuiltin(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		v = count | 		v = int64(count) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -11,7 +11,7 @@ func newContextTerm(tk *Token) (inst *term) { | |||||||
| 		tk:       *tk, | 		tk:       *tk, | ||||||
| 		children: make([]*term, 0, 1), | 		children: make([]*term, 0, 1), | ||||||
| 		position: posPrefix, | 		position: posPrefix, | ||||||
| 		priority: priPrePost, | 		priority: priIncDec, | ||||||
| 		evalFunc: evalContextValue, | 		evalFunc: evalContextValue, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -47,10 +47,11 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 	indexTerm := self.children[1] | 	indexTerm := self.children[1] | ||||||
| 
 | 
 | ||||||
| 	switch unboxedValue := leftValue.(type) { | 	switch unboxedValue := leftValue.(type) { | ||||||
| 	case []any: | 	case *ListType: | ||||||
| 		var index int | 		var index int | ||||||
| 		if index, err = verifyIndex(ctx, indexTerm, len(unboxedValue)); err == nil { | 		array := ([]any)(*unboxedValue) | ||||||
| 			v = unboxedValue[index] | 		if index, err = verifyIndex(ctx, indexTerm, len(array)); err == nil { | ||||||
|  | 			v = array[index] | ||||||
| 		} | 		} | ||||||
| 	case string: | 	case string: | ||||||
| 		var index int | 		var index int | ||||||
| @ -65,17 +66,27 @@ func evalDot(ctx ExprContext, self *term) (v any, err error) { | |||||||
| 				err = fmt.Errorf("key %v does not belong to the dictionary", rightValue) | 				err = fmt.Errorf("key %v does not belong to the dictionary", rightValue) | ||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	case *dataCursor: | 	// case *dataCursor:
 | ||||||
|  | 	// 	if indexTerm.symbol() == SymIdentifier {
 | ||||||
|  | 	// 		opName := indexTerm.source()
 | ||||||
|  | 	// 		if opName == resetName {
 | ||||||
|  | 	// 			_, err = unboxedValue.Reset()
 | ||||||
|  | 	// 		} else if opName == cleanName {
 | ||||||
|  | 	// 			_, err = unboxedValue.Clean()
 | ||||||
|  | 	// 		} else {
 | ||||||
|  | 	// 			err = indexTerm.Errorf("iterators do not support command %q", opName)
 | ||||||
|  | 	// 		}
 | ||||||
|  | 	// 		v = err == nil
 | ||||||
|  | 	// 	}
 | ||||||
|  | 	case ExtIterator: | ||||||
| 		if indexTerm.symbol() == SymIdentifier { | 		if indexTerm.symbol() == SymIdentifier { | ||||||
| 			opName := indexTerm.source() | 			opName := indexTerm.source() | ||||||
| 			if opName == resetName { | 			if unboxedValue.HasOperation(opName) { | ||||||
| 				err = unboxedValue.Reset() | 				v, err = unboxedValue.CallOperation(opName, []any{}) | ||||||
| 			} else if opName == cleanName { |  | ||||||
| 				err = unboxedValue.Clean() |  | ||||||
| 			} else { | 			} else { | ||||||
| 				err = indexTerm.Errorf("iterators do not support command %q", opName) | 				err = indexTerm.Errorf("this iterator do not support the %q command", opName) | ||||||
|  | 				v = false | ||||||
| 			} | 			} | ||||||
| 			v = err == nil |  | ||||||
| 		} | 		} | ||||||
| 	default: | 	default: | ||||||
| 		err = self.errIncompatibleTypes(leftValue, rightValue) | 		err = self.errIncompatibleTypes(leftValue, rightValue) | ||||||
|  | |||||||
| @ -4,7 +4,6 @@ | |||||||
| // operator-iter-value.go
 | // operator-iter-value.go
 | ||||||
| package expr | package expr | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| //-------- iter value term
 | //-------- iter value term
 | ||||||
| 
 | 
 | ||||||
| func newIterValueTerm(tk *Token) (inst *term) { | func newIterValueTerm(tk *Token) (inst *term) { | ||||||
| @ -18,16 +17,16 @@ func newIterValueTerm(tk *Token) (inst *term) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalIterValue(ctx ExprContext, self *term) (v any, err error) { | func evalIterValue(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	var leftValue any | 	var childValue any | ||||||
| 
 | 
 | ||||||
| 	if leftValue, err = self.evalPrefix(ctx); err != nil { | 	if childValue, err = self.evalPrefix(ctx); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if dc, ok := leftValue.(*dataCursor); ok { | 	if it, ok := childValue.(Iterator); ok { | ||||||
| 		v, err = dc.Current() | 		v, err = it.Current() | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleType(leftValue) | 		err = self.errIncompatibleType(childValue) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -17,22 +17,27 @@ func newLengthTerm(tk *Token) (inst *term) { | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalLength(ctx ExprContext, self *term) (v any, err error) { | func evalLength(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	var rightValue any | 	var childValue any | ||||||
| 
 | 
 | ||||||
| 	if rightValue, err = self.evalPrefix(ctx); err != nil { | 	if childValue, err = self.evalPrefix(ctx); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if isList(rightValue) { | 	if isList(childValue) { | ||||||
| 		list, _ := rightValue.([]any) | 		list, _ := childValue.([]any) | ||||||
| 		v = int64(len(list)) | 		v = int64(len(list)) | ||||||
| 	} else if isString(rightValue) { | 	} else if isString(childValue) { | ||||||
| 		s, _ := rightValue.(string) | 		s, _ := childValue.(string) | ||||||
| 		v = int64(len(s)) | 		v = int64(len(s)) | ||||||
| 	} else if it, ok := rightValue.(Iterator); ok { | 	} else if it, ok := childValue.(Iterator); ok { | ||||||
| 		v = int64(it.Index()) | 		if extIt, ok := childValue.(ExtIterator); ok && extIt.HasOperation(countName) { | ||||||
|  | 			count, _ := extIt.CallOperation(countName, nil) | ||||||
|  | 			v, _ = toInt(count, "") | ||||||
|  | 		} else { | ||||||
|  | 			v = int64(it.Index() + 1) | ||||||
|  | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		err = self.errIncompatibleType(rightValue) | 		err = self.errIncompatibleType(childValue) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
| @ -12,25 +12,25 @@ func newPostIncTerm(tk *Token) *term { | |||||||
| 		parent:   nil, | 		parent:   nil, | ||||||
| 		children: make([]*term, 0, 1), | 		children: make([]*term, 0, 1), | ||||||
| 		position: posPostfix, | 		position: posPostfix, | ||||||
| 		priority: priPrePost, | 		priority: priIncDec, | ||||||
| 		evalFunc: evalPostInc, | 		evalFunc: evalPostInc, | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func evalPostInc(ctx ExprContext, self *term) (v any, err error) { | func evalPostInc(ctx ExprContext, self *term) (v any, err error) { | ||||||
| 	var leftValue any | 	var childValue any | ||||||
| 	if leftValue, err = self.evalPrefix(ctx); err != nil { | 	if childValue, err = self.evalPrefix(ctx); err != nil { | ||||||
| 		return | 		return | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if dc, ok := leftValue.(*dataCursor); ok { | 	if it, ok := childValue.(Iterator); ok { | ||||||
| 		v, err = dc.Next() | 		v, err = it.Next() | ||||||
| 	} else if isInteger(leftValue) && self.children[0].symbol() == SymIdentifier { | 	} else if isInteger(childValue) && self.children[0].symbol() == SymIdentifier { | ||||||
| 		v = leftValue | 		v = childValue | ||||||
| 		i, _ := leftValue.(int64) | 		i, _ := childValue.(int64) | ||||||
| 		ctx.SetVar(self.children[0].source(), i+1) | 		ctx.SetVar(self.children[0].source(), i+1) | ||||||
| 	} else { | 	} else { | ||||||
| 		self.errIncompatibleType(leftValue) | 		err = self.errIncompatibleType(childValue) | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | |||||||
							
								
								
									
										103
									
								
								parser.go
									
									
									
									
									
								
							
							
						
						
									
										103
									
								
								parser.go
									
									
									
									
									
								
							| @ -62,30 +62,45 @@ func (self *parser) parseFuncCall(scanner *scanner, allowVarRef bool, tk *Token) | |||||||
| // 	// Example: "add = func(x,y) {x+y}
 | // 	// Example: "add = func(x,y) {x+y}
 | ||||||
| // 	var body *ast
 | // 	var body *ast
 | ||||||
| // 	args := make([]*term, 0)
 | // 	args := make([]*term, 0)
 | ||||||
| // 	tk := scanner.Next()
 | // 	lastSym := SymUnknown
 | ||||||
| // 	for tk.Sym != SymClosedRound && tk.Sym != SymEos {
 | // 	itemExpected := false
 | ||||||
| // 		if tk.Sym == SymIdentifier {
 | // 	tk := scanner.Previous()
 | ||||||
| // 			t := newTerm(tk, nil)
 | // 	for lastSym != SymClosedRound && lastSym != SymEos {
 | ||||||
| // 			args = append(args, t)
 | // 		var subTree *ast
 | ||||||
|  | // 		if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil {
 | ||||||
|  | // 			if subTree.root != nil {
 | ||||||
|  | // 				if subTree.root.symbol() == SymIdentifier {
 | ||||||
|  | // 					args = append(args, subTree.root)
 | ||||||
|  | // 				} else {
 | ||||||
|  | // 					err = tk.ErrorExpectedGotString("param-name", subTree.root.String())
 | ||||||
|  | // 				}
 | ||||||
|  | // 			} else if itemExpected {
 | ||||||
|  | // 				prev := scanner.Previous()
 | ||||||
|  | // 				err = prev.ErrorExpectedGot("function-param")
 | ||||||
|  | // 				break
 | ||||||
|  | // 			}
 | ||||||
| // 		} else {
 | // 		} else {
 | ||||||
| // 			err = tk.Errorf("invalid param %q, variable identifier expected", tk.source)
 |  | ||||||
| // 			break
 | // 			break
 | ||||||
| // 		}
 | // 		}
 | ||||||
| // 		tk = scanner.Next()
 | // 		lastSym = scanner.Previous().Sym
 | ||||||
|  | // 		itemExpected = lastSym == SymComma
 | ||||||
| // 	}
 | // 	}
 | ||||||
| // 	if err == nil && tk.Sym != SymClosedRound {
 | 
 | ||||||
| // 		err = tk.Errorf("unterminate function params list")
 | // 	if err == nil && lastSym != SymClosedRound {
 | ||||||
|  | // 		err = tk.ErrorExpectedGot(")")
 | ||||||
| // 	}
 | // 	}
 | ||||||
| // 	if err == nil {
 | // 	if err == nil {
 | ||||||
| // 		tk = scanner.Next()
 | // 		tk = scanner.Next()
 | ||||||
| // 		if tk.Sym == SymOpenBrace {
 | // 		if tk.Sym == SymOpenBrace {
 | ||||||
| // 			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
 | // 			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace)
 | ||||||
|  | // 		} else {
 | ||||||
|  | // 			err = tk.ErrorExpectedGot("{")
 | ||||||
| // 		}
 | // 		}
 | ||||||
| // 	}
 | // 	}
 | ||||||
| // 	if err == nil {
 | // 	if err == nil {
 | ||||||
| // 		// TODO Check arguments
 | // 		// TODO Check arguments
 | ||||||
| // 		if scanner.Previous().Sym != SymClosedBrace {
 | // 		if scanner.Previous().Sym != SymClosedBrace {
 | ||||||
| // 			err = scanner.Previous().Errorf("not properly terminated function body")
 | // 			err = scanner.Previous().ErrorExpectedGot("}")
 | ||||||
| // 		} else {
 | // 		} else {
 | ||||||
| // 			tk = scanner.makeValueToken(SymExpression, "", body)
 | // 			tk = scanner.makeValueToken(SymExpression, "", body)
 | ||||||
| // 			tree = newFuncDefTerm(tk, args)
 | // 			tree = newFuncDefTerm(tk, args)
 | ||||||
| @ -102,20 +117,14 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { | |||||||
| 	itemExpected := false | 	itemExpected := false | ||||||
| 	tk := scanner.Previous() | 	tk := scanner.Previous() | ||||||
| 	for lastSym != SymClosedRound && lastSym != SymEos { | 	for lastSym != SymClosedRound && lastSym != SymEos { | ||||||
| 		var subTree *ast | 		tk = scanner.Next() | ||||||
| 		if subTree, err = self.parseItem(scanner, true, SymComma, SymClosedRound); err == nil { | 		if tk.IsSymbol(SymIdentifier) { | ||||||
| 			if subTree.root != nil { | 			param := newTerm(tk, nil) | ||||||
| 				if subTree.root.symbol() == SymIdentifier { | 			args = append(args, param) | ||||||
| 					args = append(args, subTree.root) | 			tk = scanner.Next() | ||||||
| 				} else { | 		} else if itemExpected { | ||||||
| 					err = tk.Errorf("exptected identifier, got %q", subTree.root) | 			prev := scanner.Previous() | ||||||
| 				} | 			err = prev.ErrorExpectedGot("function-param") | ||||||
| 			} else if itemExpected { |  | ||||||
| 				prev := scanner.Previous() |  | ||||||
| 				err = prev.Errorf("expected function parameter, got %q", prev) |  | ||||||
| 				break |  | ||||||
| 			} |  | ||||||
| 		} else { |  | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| 		lastSym = scanner.Previous().Sym | 		lastSym = scanner.Previous().Sym | ||||||
| @ -123,18 +132,20 @@ func (self *parser) parseFuncDef(scanner *scanner) (tree *term, err error) { | |||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err == nil && lastSym != SymClosedRound { | 	if err == nil && lastSym != SymClosedRound { | ||||||
| 		err = tk.Errorf("unterminated function parameters list") | 		err = tk.ErrorExpectedGot(")") | ||||||
| 	} | 	} | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		tk = scanner.Next() | 		tk = scanner.Next() | ||||||
| 		if tk.Sym == SymOpenBrace { | 		if tk.IsSymbol(SymOpenBrace) { | ||||||
| 			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace) | 			body, err = self.parseGeneral(scanner, true, true, SymClosedBrace) | ||||||
|  | 		} else { | ||||||
|  | 			err = tk.ErrorExpectedGot("{") | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		// TODO Check arguments
 | 		// TODO Check arguments
 | ||||||
| 		if scanner.Previous().Sym != SymClosedBrace { | 		if scanner.Previous().Sym != SymClosedBrace { | ||||||
| 			err = scanner.Previous().Errorf("not properly terminated function body") | 			err = scanner.Previous().ErrorExpectedGot("}") | ||||||
| 		} else { | 		} else { | ||||||
| 			tk = scanner.makeValueToken(SymExpression, "", body) | 			tk = scanner.makeValueToken(SymExpression, "", body) | ||||||
| 			tree = newFuncDefTerm(tk, args) | 			tree = newFuncDefTerm(tk, args) | ||||||
| @ -154,7 +165,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term | |||||||
| 				args = append(args, subTree.root) | 				args = append(args, subTree.root) | ||||||
| 			} else if itemExpected { | 			} else if itemExpected { | ||||||
| 				prev := scanner.Previous() | 				prev := scanner.Previous() | ||||||
| 				err = prev.Errorf("expected list item, got %q", prev) | 				err = prev.ErrorExpectedGot("list-item") | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @ -166,7 +177,7 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term | |||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		// TODO Check arguments
 | 		// TODO Check arguments
 | ||||||
| 		if lastSym != SymClosedSquare { | 		if lastSym != SymClosedSquare { | ||||||
| 			err = scanner.Previous().Errorf("unterminate items list") | 			err = scanner.Previous().ErrorExpectedGot("]") | ||||||
| 		} else { | 		} else { | ||||||
| 			subtree = newListTerm(args) | 			subtree = newListTerm(args) | ||||||
| 		} | 		} | ||||||
| @ -175,29 +186,18 @@ func (self *parser) parseList(scanner *scanner, allowVarRef bool) (subtree *term | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) { | func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *term, err error) { | ||||||
| 	var ds *term |  | ||||||
| 	tk := scanner.Previous() | 	tk := scanner.Previous() | ||||||
| 	args := make([]*term, 0) | 	args := make([]*term, 0) | ||||||
| 	lastSym := SymUnknown | 	lastSym := SymUnknown | ||||||
| 	dsExpected := true |  | ||||||
| 	itemExpected := false | 	itemExpected := false | ||||||
| 	for lastSym != SymClosedRound && lastSym != SymEos { | 	for lastSym != SymClosedRound && lastSym != SymEos { | ||||||
| 		var subTree *ast | 		var subTree *ast | ||||||
| 		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil { | 		if subTree, err = self.parseItem(scanner, allowVarRef, SymComma, SymClosedRound); err == nil { | ||||||
| 			if subTree.root != nil { | 			if subTree.root != nil { | ||||||
| 				if dsExpected { | 				args = append(args, subTree.root) | ||||||
| 					if sym := subTree.root.symbol(); sym == SymDict || sym == SymIdentifier { |  | ||||||
| 						ds = subTree.root |  | ||||||
| 					} else { |  | ||||||
| 						err = subTree.root.Errorf("data-source dictionary expected, got %q", subTree.root.source()) |  | ||||||
| 					} |  | ||||||
| 					dsExpected = false |  | ||||||
| 				} else { |  | ||||||
| 					args = append(args, subTree.root) |  | ||||||
| 				} |  | ||||||
| 			} else if itemExpected { | 			} else if itemExpected { | ||||||
| 				prev := scanner.Previous() | 				prev := scanner.Previous() | ||||||
| 				err = prev.Errorf("expected iterator argument, got %q", prev) | 				err = prev.ErrorExpectedGot("iterator-param") | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @ -209,11 +209,9 @@ func (self *parser) parseIterDef(scanner *scanner, allowVarRef bool) (subtree *t | |||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		// TODO Check arguments
 | 		// TODO Check arguments
 | ||||||
| 		if lastSym != SymClosedRound { | 		if lastSym != SymClosedRound { | ||||||
| 			err = scanner.Previous().Errorf("unterminate iterator param list") | 			err = scanner.Previous().ErrorExpectedGot(")") | ||||||
| 		} else if ds != nil { |  | ||||||
| 			subtree = newIteratorTerm(tk, ds, args) |  | ||||||
| 		} else { | 		} else { | ||||||
| 			tk.Errorf("missing data-source param") | 			subtree = newIteratorTerm(tk, args) | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| @ -231,12 +229,13 @@ func (self *parser) parseDictKey(scanner *scanner, allowVarRef bool) (key any, e | |||||||
| 	if tk.Sym == SymInteger || tk.Sym == SymString { | 	if tk.Sym == SymInteger || tk.Sym == SymString { | ||||||
| 		tkSep := scanner.Next() | 		tkSep := scanner.Next() | ||||||
| 		if tkSep.Sym != SymColon { | 		if tkSep.Sym != SymColon { | ||||||
| 			err = tkSep.Errorf("expected \":\", got %q", tkSep) | 			err = tkSep.ErrorExpectedGot(":") | ||||||
| 		} else { | 		} else { | ||||||
| 			key = tk.Value | 			key = tk.Value | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		err = tk.Errorf("expected dictionary key or closed brace, got %q", tk) | 		// err = tk.Errorf("expected dictionary key or closed brace, got %q", tk)
 | ||||||
|  | 		err = tk.ErrorExpectedGot("dictionary-key or }") | ||||||
| 	} | 	} | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
| @ -254,7 +253,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree | |||||||
| 			tk := scanner.Previous() | 			tk := scanner.Previous() | ||||||
| 			lastSym = tk.Sym | 			lastSym = tk.Sym | ||||||
| 			if itemExpected { | 			if itemExpected { | ||||||
| 				err = tk.Errorf("expected dictionary key, got %q", tk) | 				err = tk.ErrorExpectedGot("dictionary-key") | ||||||
| 			} | 			} | ||||||
| 			break | 			break | ||||||
| 		} | 		} | ||||||
| @ -263,7 +262,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree | |||||||
| 				args[key] = subTree.root | 				args[key] = subTree.root | ||||||
| 			} else if key != nil { | 			} else if key != nil { | ||||||
| 				prev := scanner.Previous() | 				prev := scanner.Previous() | ||||||
| 				err = prev.Errorf("expected dictionary value, got %q", prev) | 				err = prev.ErrorExpectedGot("dictionary-value") | ||||||
| 				break | 				break | ||||||
| 			} | 			} | ||||||
| 		} else { | 		} else { | ||||||
| @ -275,7 +274,7 @@ func (self *parser) parseDictionary(scanner *scanner, allowVarRef bool) (subtree | |||||||
| 	if err == nil { | 	if err == nil { | ||||||
| 		// TODO Check arguments
 | 		// TODO Check arguments
 | ||||||
| 		if lastSym != SymClosedBrace { | 		if lastSym != SymClosedBrace { | ||||||
| 			err = scanner.Previous().Errorf("unterminated dictionary") | 			err = scanner.Previous().ErrorExpectedGot("}") | ||||||
| 		} else { | 		} else { | ||||||
| 			subtree = newDictTerm(args) | 			subtree = newDictTerm(args) | ||||||
| 		} | 		} | ||||||
| @ -309,7 +308,7 @@ func (self *parser) parseSelectorCase(scanner *scanner, allowVarRef bool, defaul | |||||||
| 			return | 			return | ||||||
| 		} | 		} | ||||||
| 	} else { | 	} else { | ||||||
| 		err = tk.Errorf("selector-case expected, got %q", tk.source) | 		err = tk.ErrorExpectedGot("{") | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	if err == nil { | 	if err == nil { | ||||||
|  | |||||||
| @ -6,7 +6,6 @@ package expr | |||||||
| 
 | 
 | ||||||
| import ( | import ( | ||||||
| 	"errors" | 	"errors" | ||||||
| 	"fmt" |  | ||||||
| 	"reflect" | 	"reflect" | ||||||
| 	"strings" | 	"strings" | ||||||
| 	"testing" | 	"testing" | ||||||
| @ -160,7 +159,7 @@ func TestParser(t *testing.T) { | |||||||
| 		/* 138 */ {`nil`, nil, nil}, | 		/* 138 */ {`nil`, nil, nil}, | ||||||
| 		/* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, | 		/* 139 */ {`null`, nil, errors.New(`undefined variable or function "null"`)}, | ||||||
| 		/* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, | 		/* 140 */ {`{"key"}`, nil, errors.New(`[1:8] expected ":", got "}"`)}, | ||||||
| 		/* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected dictionary value, got "}"`)}, | 		/* 141 */ {`{"key":}`, nil, errors.New(`[1:9] expected "dictionary-value", got "}"`)}, | ||||||
| 		/* 142 */ {`{}`, map[any]any{}, nil}, | 		/* 142 */ {`{}`, map[any]any{}, nil}, | ||||||
| 		/* 143 */ {`1|2`, newFraction(1, 2), nil}, | 		/* 143 */ {`1|2`, newFraction(1, 2), nil}, | ||||||
| 		/* 144 */ {`1|2 + 1`, newFraction(3, 2), nil}, | 		/* 144 */ {`1|2 + 1`, newFraction(3, 2), nil}, | ||||||
| @ -174,11 +173,15 @@ func TestParser(t *testing.T) { | |||||||
| 		/* 152 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, | 		/* 152 */ {`builtin "math.arith"; mul(1|2, 1.0, 2)`, float64(1.0), nil}, | ||||||
| 		/* 153 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, | 		/* 153 */ {`include "iterator.expr"; it=$(ds,3); ()it`, int64(0), nil}, | ||||||
| 		/* 154 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, | 		/* 154 */ {`include "iterator.expr"; it=$(ds,3); it++; it++`, int64(1), nil}, | ||||||
| 		/* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(1), nil}, | 		/* 155 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; #it`, int64(2), nil}, | ||||||
| 		/* 156 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, | 		/* 156 */ {`include "iterator.expr"; it=$(ds,3); it++; it++; it.reset; ()it`, int64(0), nil}, | ||||||
| 		/* 157 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, | 		/* 157 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); add(it)`, int64(6), nil}, | ||||||
| 		/* 158 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, | 		/* 158 */ {`builtin "math.arith"; include "iterator.expr"; it=$(ds,3); mul(it)`, int64(0), nil}, | ||||||
| 		/* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, | 		/* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil}, | ||||||
|  | 		/* 160 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it++; it.index`, int64(0), nil}, | ||||||
|  | 		/* 161 */ {`include "file-reader.expr"; it=$(ds,"int.list"); it.clean`, true, nil}, | ||||||
|  | 		/* 162 */ {`builtin "os.file"`, int64(1), nil}, | ||||||
|  | 		/* 163 */ {`it=$(1,2,3); it++`, int64(1), nil}, | ||||||
| 	} | 	} | ||||||
| 	check_env_expr_path := 113 | 	check_env_expr_path := 113 | ||||||
| 
 | 
 | ||||||
| @ -186,7 +189,7 @@ func TestParser(t *testing.T) { | |||||||
| 	failed := 0 | 	failed := 0 | ||||||
| 
 | 
 | ||||||
| 	// inputs1 := []inputType{
 | 	// inputs1 := []inputType{
 | ||||||
| 	// 	/* 140 */ {`ds={}; $(ds)`, nil, nil},
 | 	// 	/* 159 */ {`include "file-reader.expr"; it=$(ds,"int.list"); mul(it)`, int64(12000), nil},
 | ||||||
| 	// }
 | 	// }
 | ||||||
| 
 | 
 | ||||||
| 	for i, input := range inputs { | 	for i, input := range inputs { | ||||||
| @ -201,7 +204,7 @@ func TestParser(t *testing.T) { | |||||||
| 		ImportImportFuncs(ctx) | 		ImportImportFuncs(ctx) | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser(ctx) | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, "General", input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
| 		r := strings.NewReader(input.source) | 		r := strings.NewReader(input.source) | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) | 		scanner := NewScanner(r, DefaultTranslations()) | ||||||
| @ -234,7 +237,7 @@ func TestParser(t *testing.T) { | |||||||
| 			} | 			} | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) | 	t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func TestListParser(t *testing.T) { | func TestListParser(t *testing.T) { | ||||||
| @ -254,8 +257,9 @@ func TestListParser(t *testing.T) { | |||||||
| 		/*  7 */ {`add([1,4,3,2])`, int64(10), nil}, | 		/*  7 */ {`add([1,4,3,2])`, int64(10), nil}, | ||||||
| 		/*  8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, | 		/*  8 */ {`add([1,[2,2],3,2])`, int64(10), nil}, | ||||||
| 		/*  9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, | 		/*  9 */ {`mul([1,4,3.0,2])`, float64(24.0), nil}, | ||||||
| 		/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 has wrong type string, number expected`)}, | 		/* 10 */ {`add([1,"hello"])`, nil, errors.New(`add(): param nr 2 (2 in 1) has wrong type string, number expected`)}, | ||||||
| 		/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, | 		/* 11 */ {`[a=1,b=2,c=3] but a+b+c`, int64(6), nil}, | ||||||
|  | 
 | ||||||
| 		// /*  8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
 | 		// /*  8 */ {`[int(x)|x=csv("test.csv",1,all(),1)]`, []any{int64(10), int64(40), int64(20)}, nil},
 | ||||||
| 		// /*  9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
 | 		// /*  9 */ {`sum(@[int(x)|x=csv("test.csv",1,all(),1)])`, []any{int64(10), int64(40), int64(20)}, nil},
 | ||||||
| 	} | 	} | ||||||
| @ -278,7 +282,7 @@ func TestListParser(t *testing.T) { | |||||||
| 		ImportMathFuncs(ctx) | 		ImportMathFuncs(ctx) | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser(ctx) | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, "List", input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
| 		r := strings.NewReader(input.source) | 		r := strings.NewReader(input.source) | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) | 		scanner := NewScanner(r, DefaultTranslations()) | ||||||
| @ -330,13 +334,13 @@ func TestListParser(t *testing.T) { | |||||||
| 			failed++ | 			failed++ | ||||||
| 		} | 		} | ||||||
| 	} | 	} | ||||||
| 	t.Log(fmt.Sprintf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed)) | 	t.Logf("test count: %d, succeeded count: %d, failed count: %d", len(inputs), succeeded, failed) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func logTest(t *testing.T, n int, source string, wantResult any, wantErr error) { | func logTest(t *testing.T, n int, section, source string, wantResult any, wantErr error) { | ||||||
| 	if wantErr == nil { | 	if wantErr == nil { | ||||||
| 		t.Log(fmt.Sprintf("[+]Test nr %3d -- %q  --> %v", n, source, wantResult)) | 		t.Logf("[+]%s nr %3d -- %q  --> %v", section, n, source, wantResult) | ||||||
| 	} else { | 	} else { | ||||||
| 		t.Log(fmt.Sprintf("[-]Test nr %3d -- %q  --> %v", n, source, wantErr)) | 		t.Logf("[-]%s nr %3d -- %q  --> %v", section, n, source, wantErr) | ||||||
| 	} | 	} | ||||||
| } | } | ||||||
|  | |||||||
| @ -160,6 +160,12 @@ func (self *scanner) fetchNextToken() (tk *Token) { | |||||||
| 			//} else if next == '/' {
 | 			//} else if next == '/' {
 | ||||||
| 			if next, _ := self.peek(); next == '/' { | 			if next, _ := self.peek(); next == '/' { | ||||||
| 				tk = self.moveOn(SymDotSlash, ch, next) | 				tk = self.moveOn(SymDotSlash, ch, next) | ||||||
|  | 			} else if next == '.' { | ||||||
|  | 				if next1, _ := self.peek(); next1 == '.' { | ||||||
|  | 					tk = self.moveOn(SymTripleDot, ch, next, next1) | ||||||
|  | 				} else { | ||||||
|  | 					tk = self.moveOn(SymDoubleDot, ch, next) | ||||||
|  | 				} | ||||||
| 			} else { | 			} else { | ||||||
| 				tk = self.makeToken(SymDot, ch) | 				tk = self.makeToken(SymDot, ch) | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -65,6 +65,8 @@ const ( | |||||||
| 	SymDollarRound                       //  54: '$('
 | 	SymDollarRound                       //  54: '$('
 | ||||||
| 	SymOpenClosedRound                   //  55: '()'
 | 	SymOpenClosedRound                   //  55: '()'
 | ||||||
| 	SymDoubleDollar                      //  56: '$$'
 | 	SymDoubleDollar                      //  56: '$$'
 | ||||||
|  | 	SymDoubleDot                         //  57: '..'
 | ||||||
|  | 	SymTripleDot                         //  58: '...'
 | ||||||
| 	SymChangeSign | 	SymChangeSign | ||||||
| 	SymUnchangeSign | 	SymUnchangeSign | ||||||
| 	SymIdentifier | 	SymIdentifier | ||||||
|  | |||||||
							
								
								
									
										2
									
								
								term.go
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								term.go
									
									
									
									
									
								
							| @ -26,7 +26,7 @@ const ( | |||||||
| 	priFact | 	priFact | ||||||
| 	priIterValue | 	priIterValue | ||||||
| 	priCoalesce | 	priCoalesce | ||||||
| 	priPrePost | 	priIncDec | ||||||
| 	priDot | 	priDot | ||||||
| 	priValue | 	priValue | ||||||
| ) | ) | ||||||
|  | |||||||
							
								
								
									
										16
									
								
								token.go
									
									
									
									
									
								
							
							
						
						
									
										16
									
								
								token.go
									
									
									
									
									
								
							| @ -46,7 +46,7 @@ func NewValueToken(row, col int, sym Symbol, source string, value any) *Token { | |||||||
| 
 | 
 | ||||||
| func NewErrorToken(row, col int, err error) *Token { | func NewErrorToken(row, col int, err error) *Token { | ||||||
| 	if err == io.EOF { | 	if err == io.EOF { | ||||||
| 		return NewToken(row, col, SymEos, "") | 		return NewToken(row, col, SymEos, "<EOF>") | ||||||
| 	} | 	} | ||||||
| 	return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) | 	return NewValueToken(row, col, SymError, fmt.Sprintf("[%d:%d]", row, col), err) | ||||||
| } | } | ||||||
| @ -63,6 +63,10 @@ func (tk *Token) IsTerm(termSymbols []Symbol) bool { | |||||||
| 	return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0) | 	return tk.IsEos() || tk.IsError() || (termSymbols != nil && slices.Index(termSymbols, tk.Sym) >= 0) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func (tk *Token) IsSymbol(sym Symbol) bool { | ||||||
|  | 	return tk.Sym == sym | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func (self *Token) Errorf(template string, args ...any) (err error) { | func (self *Token) Errorf(template string, args ...any) (err error) { | ||||||
| 	err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) | 	err = fmt.Errorf(fmt.Sprintf("[%d:%d] ", self.row, self.col)+template, args...) | ||||||
| 	return | 	return | ||||||
| @ -81,3 +85,13 @@ func (self *Token) Errors(msg string) (err error) { | |||||||
| 	err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) | 	err = fmt.Errorf("[%d:%d] %v", self.row, self.col, msg) | ||||||
| 	return | 	return | ||||||
| } | } | ||||||
|  | 
 | ||||||
|  | func (self *Token) ErrorExpectedGot(symbol string) (err error) { | ||||||
|  | 	err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, self) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func (self *Token) ErrorExpectedGotString(symbol, got string) (err error) { | ||||||
|  | 	err = fmt.Errorf("[%d:%d] expected %q, got %q", self.row, self.col, symbol, got) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user