Compare commits
	
		
			7 Commits
		
	
	
		
			dce49fd2b7
			...
			8346e28340
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| 8346e28340 | |||
| 9c2eca40d7 | |||
| c3198e4c79 | |||
| 99e0190b9c | |||
| d4f63a3837 | |||
| 389d48b646 | |||
| 1f7b9131fc | 
							
								
								
									
										1
									
								
								ast.go
									
									
									
									
									
								
							
							
						
						
									
										1
									
								
								ast.go
									
									
									
									
									
								
							| @ -128,6 +128,7 @@ func (self *ast) eval(ctx ExprContext, preset bool) (result any, err error) { | |||||||
| 		} | 		} | ||||||
| 		if err == nil { | 		if err == nil { | ||||||
| 			result, err = self.root.compute(ctx) | 			result, err = self.root.compute(ctx) | ||||||
|  | 			ctx.setVar(ControlLastResult, result) | ||||||
| 		} | 		} | ||||||
| 		// } else {
 | 		// } else {
 | ||||||
| 		// 	err = errors.New("empty expression")
 | 		// 	err = errors.New("empty expression")
 | ||||||
|  | |||||||
| @ -141,7 +141,7 @@ Numbers can be integers (GO int64) or float (GO float64). In mixed operations in | |||||||
| |===  | |===  | ||||||
| 
 | 
 | ||||||
| === Fractions | === Fractions | ||||||
| _Expr_ also suports fractions. Fraction literals are made with tow integers separated by a vertical bar `|`. | _Expr_ also supports fractions. Fraction literals are made with two integers separated by a vertical bar `|`. | ||||||
| 
 | 
 | ||||||
| .Examples | .Examples | ||||||
| // [source,go] | // [source,go] | ||||||
| @ -149,7 +149,7 @@ _Expr_ also suports fractions. Fraction literals are made with tow integers sepa | |||||||
| `>>>` [blue]`1 | 2` + | `>>>` [blue]`1 | 2` + | ||||||
| [green]`1|2` + | [green]`1|2` + | ||||||
| `>>>` [blue]`4|6` + | `>>>` [blue]`4|6` + | ||||||
| [green]`2|3` [gray]_Fractions are always reduced to theri lowest terms_ + | [green]`2|3` [gray]_Fractions are always reduced to their lowest terms_ + | ||||||
| `>>>` [blue]`1|2 + 2|3` + | `>>>` [blue]`1|2 + 2|3` + | ||||||
| [green]`7|6` + | [green]`7|6` + | ||||||
| `>>>` [blue]`1|2 * 2|3` + | `>>>` [blue]`1|2 * 2|3` + | ||||||
| @ -245,7 +245,7 @@ Currently, boolean operations are evaluated using _short cut evaluation_. This m | |||||||
| <1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_. | <1> This multi-expression returns _1_ because in the first expression the left value of [blue]`or` is _true_ and as a conseguence its right value is not computed. Therefore the _a_ variable only receives the integer _1_. | ||||||
| ==== | ==== | ||||||
| 
 | 
 | ||||||
| === List | === Lists | ||||||
| _Expr_ supports list of mixed-type values, also specified by normal expressions. | _Expr_ supports list of mixed-type values, also specified by normal expressions. | ||||||
| 
 | 
 | ||||||
| .List examples | .List examples | ||||||
| @ -258,7 +258,6 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions. | |||||||
| [ [1,"one"], [2,"two"]]     // List of lists | [ [1,"one"], [2,"two"]]     // List of lists | ||||||
| ---- | ---- | ||||||
| 
 | 
 | ||||||
| 
 |  | ||||||
| .List operators | .List operators | ||||||
| [cols="^2,^2,5,4"] | [cols="^2,^2,5,4"] | ||||||
| |=== | |=== | ||||||
| @ -269,6 +268,34 @@ _Expr_ supports list of mixed-type values, also specified by normal expressions. | |||||||
| | [blue]`-` | _Difference_ | Left list without elements in the right list  | [blue]`[1,2,3] - [2]` _[ [1,3] ]_ | | [blue]`-` | _Difference_ | Left list without elements in the right list  | [blue]`[1,2,3] - [2]` _[ [1,3] ]_ | ||||||
| |=== | |=== | ||||||
| 
 | 
 | ||||||
|  | The items of array can be accessed using the dot `.` operator. | ||||||
|  | 
 | ||||||
|  | .Item access syntax | ||||||
|  | [source,bnf] | ||||||
|  | ---- | ||||||
|  | <item> ::= <list-expr>"."<index-expr> | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | .Items of list | ||||||
|  | [source,go] | ||||||
|  | ---- | ||||||
|  | `>>>` [blue]`[1,2,3].1` | ||||||
|  | [green]`2` | ||||||
|  | `>>>` [blue]`list=[1,2,3]; list.1` | ||||||
|  | [green]`2` | ||||||
|  | `>>>` [blue]`["one","two","three"].1` | ||||||
|  | [green]`two` | ||||||
|  | `>>>` [blue]`list=["one","two","three"]; list.(2-1)` | ||||||
|  | [green]`two` | ||||||
|  | `>>>` [blue]`list.(-1)` | ||||||
|  | [green]`three` | ||||||
|  | `>>>` [blue]`list.(-1)` | ||||||
|  | [green]`three` | ||||||
|  | `>>>` [blue]`list.(10)` | ||||||
|  | ---- | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| == Dictionaries | == Dictionaries | ||||||
| The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_. Dictionary literals are sequences of pairs separated by comma `,`; sequences are enclosed between brace brackets. | The _dictionary_ data-type is set of pairs _key/value_. It is also known as _map_ or _associative array_. Dictionary literals are sequences of pairs separated by comma `,`; sequences are enclosed between brace brackets. | ||||||
| 
 | 
 | ||||||
|  | |||||||
							
								
								
									
										49
									
								
								func-base.go
									
									
									
									
									
								
							
							
						
						
									
										49
									
								
								func-base.go
									
									
									
									
									
								
							| @ -18,6 +18,42 @@ func isNilFunc(ctx ExprContext, name string, args []any) (result any, err error) | |||||||
| 	return | 	return | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func isIntFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsInteger(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isFloatFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsFloat(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isBoolFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsBool(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isStringFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsString(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isFractionFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsFract(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isListFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsList(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | func isDictionaryFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
|  | 	result = IsDict(args[0]) | ||||||
|  | 	return | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | 
 | ||||||
| func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { | func intFunc(ctx ExprContext, name string, args []any) (result any, err error) { | ||||||
| 	if len(args) == 1 { | 	if len(args) == 1 { | ||||||
| 		switch v := args[0].(type) { | 		switch v := args[0].(type) { | ||||||
| @ -50,8 +86,17 @@ func iteratorFunc(ctx ExprContext, name string, args []any) (result any, err err | |||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func ImportBuiltinsFuncs(ctx ExprContext) { | func ImportBuiltinsFuncs(ctx ExprContext) { | ||||||
| 	ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1) | 	ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, 1) | ||||||
| 	ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1) | 	ctx.RegisterFunc("isInt", &simpleFunctor{f: isIntFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isFloat", &simpleFunctor{f: isFloatFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isBool", &simpleFunctor{f: isBoolFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isString", &simpleFunctor{f: isStringFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isFraction", &simpleFunctor{f: isFractionFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isFract", &simpleFunctor{f: isFractionFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isList", &simpleFunctor{f: isListFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isDictionary", &simpleFunctor{f: isDictionaryFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("isDict", &simpleFunctor{f: isDictionaryFunc}, 1, 1) | ||||||
|  | 	ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, 1) | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| func init() { | func init() { | ||||||
|  | |||||||
| @ -20,7 +20,7 @@ func TestFuncs(t *testing.T) { | |||||||
| 		/*   7 */ {`int(3.9)`, int64(3), nil}, | 		/*   7 */ {`int(3.9)`, int64(3), nil}, | ||||||
| 		/*   8 */ {`int("432")`, int64(432), nil}, | 		/*   8 */ {`int("432")`, int64(432), nil}, | ||||||
| 		/*   9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, | 		/*   9 */ {`int("1.5")`, nil, errors.New(`strconv.Atoi: parsing "1.5": invalid syntax`)}, | ||||||
| 		/*  10 */ {`int("432", 4)`, nil, errors.New(`int() requires exactly one param`)}, | 		/*  10 */ {`int("432", 4)`, nil, errors.New(`too much params -- expected 1, got 2`)}, | ||||||
| 		/*  11 */ {`int(nil)`, nil, errors.New(`int() can't convert <nil> to int`)}, | 		/*  11 */ {`int(nil)`, nil, errors.New(`int() can't convert <nil> to int`)}, | ||||||
| 		/*  12 */ {`two=func(){2}; two()`, int64(2), nil}, | 		/*  12 */ {`two=func(){2}; two()`, int64(2), nil}, | ||||||
| 		/*  13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, | 		/*  13 */ {`double=func(x) {2*x}; (double(3))`, int64(6), nil}, | ||||||
| @ -54,9 +54,15 @@ func TestFuncs(t *testing.T) { | |||||||
| 		/*  41 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil}, | 		/*  41 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "789")`, true, nil}, | ||||||
| 		/*  42 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil}, | 		/*  42 */ {`builtin "string"; endsWithStr("0123456789", "xyz", "0125")`, false, nil}, | ||||||
| 		/*  43 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`too few params -- expected 2 or more, got 1`)}, | 		/*  43 */ {`builtin "string"; endsWithStr("0123456789")`, nil, errors.New(`too few params -- expected 2 or more, got 1`)}, | ||||||
| 		/*  44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one","two","three"), nil}, | 		/*  44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil}, | ||||||
| 		/*  45 */ {`["a", "b", "c"]`, newListA("a","b","c"), nil}, | 		/*  45 */ {`isInt(2+1)`, true, nil}, | ||||||
| 		/*  46 */ {`["a", "b", "c"]`, newList([]any{"a","b","c"}), nil}, | 		/*  46 */ {`isInt(3.1)`, false, nil}, | ||||||
|  | 		/*  46 */ {`isFloat(3.1)`, true, nil}, | ||||||
|  | 		/*  47 */ {`isString("3.1")`, true, nil}, | ||||||
|  | 		/*  48 */ {`isString("3" + 1)`, true, nil}, | ||||||
|  | 		/*  49 */ {`isList(["3",  1])`, true, nil}, | ||||||
|  | 		/*  50 */ {`isDict({"a":"3",  "b":1})`, true, nil}, | ||||||
|  | 		/*  51 */ {`isFract(3|1)`, true, nil}, | ||||||
| 	} | 	} | ||||||
| 
 | 
 | ||||||
| 	t.Setenv("EXPR_PATH", ".") | 	t.Setenv("EXPR_PATH", ".") | ||||||
|  | |||||||
| @ -36,6 +36,9 @@ func TestListParser(t *testing.T) { | |||||||
| 		/*  14 */ {`[1,2,3].1`, int64(2), nil}, | 		/*  14 */ {`[1,2,3].1`, int64(2), nil}, | ||||||
| 		/*  15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil}, | 		/*  15 */ {`ls=[1,2,3] but ls.1`, int64(2), nil}, | ||||||
| 		/*  16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil}, | 		/*  16 */ {`ls=[1,2,3] but ls.(-1)`, int64(3), nil}, | ||||||
|  | 		/*  17 */ {`list=["one","two","three"]; list.10`, nil, errors.New(`[1:36] index 10 out of bounds`)}, | ||||||
|  | 		/*  18 */ {`["a", "b", "c"]`, newListA("a", "b", "c"), nil}, | ||||||
|  | 		/*  19 */ {`["a", "b", "c"]`, newList([]any{"a", "b", "c"}), 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},
 | ||||||
|  | |||||||
| @ -22,10 +22,11 @@ func verifyIndex(ctx ExprContext, indexTerm *term, maxValue int) (index int, err | |||||||
| 	var indexValue any | 	var indexValue any | ||||||
| 	if indexValue, err = indexTerm.compute(ctx); err == nil { | 	if indexValue, err = indexTerm.compute(ctx); err == nil { | ||||||
| 		if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil { | 		if v, err = indexTerm.toInt(indexValue, "index expression value must be integer"); err == nil { | ||||||
|  | 			if v < 0 && v >= -maxValue { | ||||||
|  | 				v = maxValue + v | ||||||
|  | 			} | ||||||
| 			if v >= 0 && v < maxValue { | 			if v >= 0 && v < maxValue { | ||||||
| 				index = v | 				index = v | ||||||
| 			} else if index >= -maxValue { |  | ||||||
| 				index = maxValue + v |  | ||||||
| 			} else { | 			} else { | ||||||
| 				err = indexTerm.Errorf("index %d out of bounds", v) | 				err = indexTerm.Errorf("index %d out of bounds", v) | ||||||
| 			} | 			} | ||||||
|  | |||||||
| @ -177,7 +177,7 @@ func parserTest(t *testing.T, section string, inputs []inputType) { | |||||||
| 		ctx := NewSimpleFuncStore() | 		ctx := NewSimpleFuncStore() | ||||||
| 		parser := NewParser(ctx) | 		parser := NewParser(ctx) | ||||||
| 
 | 
 | ||||||
| 		logTest(t, i+1, "Iterator", input.source, input.wantResult, input.wantErr) | 		logTest(t, i+1, section, input.source, input.wantResult, input.wantErr) | ||||||
| 
 | 
 | ||||||
| 		r := strings.NewReader(input.source) | 		r := strings.NewReader(input.source) | ||||||
| 		scanner := NewScanner(r, DefaultTranslations()) | 		scanner := NewScanner(r, DefaultTranslations()) | ||||||
|  | |||||||
							
								
								
									
										10
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								utils.go
									
									
									
									
									
								
							| @ -24,6 +24,11 @@ func IsFloat(v any) (ok bool) { | |||||||
| 	return ok | 	return ok | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func IsBool(v any) (ok bool) { | ||||||
|  | 	_, ok = v.(bool) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func IsList(v any) (ok bool) { | func IsList(v any) (ok bool) { | ||||||
| 	_, ok = v.(*ListType) | 	_, ok = v.(*ListType) | ||||||
| 	return ok | 	return ok | ||||||
| @ -34,6 +39,11 @@ func IsDict(v any) (ok bool) { | |||||||
| 	return ok | 	return ok | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
|  | func IsFract(v any) (ok bool) { | ||||||
|  | 	_, ok = v.(*fraction) | ||||||
|  | 	return ok | ||||||
|  | } | ||||||
|  | 
 | ||||||
| func IsNumber(v any) (ok bool) { | func IsNumber(v any) (ok bool) { | ||||||
| 	return IsFloat(v) || IsInteger(v) | 	return IsFloat(v) || IsInteger(v) | ||||||
| } | } | ||||||
|  | |||||||
		Loading…
	
		Reference in New Issue
	
	Block a user