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 { | ||||
| 			result, err = self.root.compute(ctx) | ||||
| 			ctx.setVar(ControlLastResult, result) | ||||
| 		} | ||||
| 		// } else {
 | ||||
| 		// 	err = errors.New("empty expression")
 | ||||
|  | ||||
| @ -141,7 +141,7 @@ Numbers can be integers (GO int64) or float (GO float64). In mixed operations in | ||||
| |===  | ||||
| 
 | ||||
| === 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 | ||||
| // [source,go] | ||||
| @ -149,7 +149,7 @@ _Expr_ also suports fractions. Fraction literals are made with tow integers sepa | ||||
| `>>>` [blue]`1 | 2` + | ||||
| [green]`1|2` + | ||||
| `>>>` [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` + | ||||
| [green]`7|6` + | ||||
| `>>>` [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_. | ||||
| ==== | ||||
| 
 | ||||
| === List | ||||
| === Lists | ||||
| _Expr_ supports list of mixed-type values, also specified by normal expressions. | ||||
| 
 | ||||
| .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 | ||||
| ---- | ||||
| 
 | ||||
| 
 | ||||
| .List operators | ||||
| [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] ]_ | ||||
| |=== | ||||
| 
 | ||||
| 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 | ||||
| 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 | ||||
| } | ||||
| 
 | ||||
| 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) { | ||||
| 	if len(args) == 1 { | ||||
| 		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) { | ||||
| 	ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 1, -1) | ||||
| 	ctx.RegisterFunc("int", &simpleFunctor{f: intFunc}, 1, -1) | ||||
| 	ctx.RegisterFunc("isNil", &simpleFunctor{f: isNilFunc}, 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() { | ||||
|  | ||||
| @ -20,7 +20,7 @@ func TestFuncs(t *testing.T) { | ||||
| 		/*   7 */ {`int(3.9)`, int64(3), nil}, | ||||
| 		/*   8 */ {`int("432")`, int64(432), nil}, | ||||
| 		/*   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`)}, | ||||
| 		/*  12 */ {`two=func(){2}; two()`, int64(2), 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}, | ||||
| 		/*  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`)}, | ||||
| 		/*  44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one","two","three"), nil}, | ||||
| 		/*  45 */ {`["a", "b", "c"]`, newListA("a","b","c"), nil}, | ||||
| 		/*  46 */ {`["a", "b", "c"]`, newList([]any{"a","b","c"}), nil}, | ||||
| 		/*  44 */ {`builtin "string"; splitStr("one-two-three", "-", )`, newListA("one", "two", "three"), nil}, | ||||
| 		/*  45 */ {`isInt(2+1)`, true, 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", ".") | ||||
|  | ||||
							
								
								
									
										35
									
								
								list_test.go
									
									
									
									
									
								
							
							
						
						
									
										35
									
								
								list_test.go
									
									
									
									
									
								
							| @ -20,22 +20,25 @@ func TestListParser(t *testing.T) { | ||||
| 	} | ||||
| 
 | ||||
| 	inputs := []inputType{ | ||||
| 		/*  1 */ {`[]`, []any{}, nil}, | ||||
| 		/*  2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/*  3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil}, | ||||
| 		/*  4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil}, | ||||
| 		/*  5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/*  6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil}, | ||||
| 		/*  7 */ {`add([1,4,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}, | ||||
| 		/* 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}, | ||||
| 		/* 12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil}, | ||||
| 		/* 13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/* 14 */ {`[1,2,3].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}, | ||||
| 		/*   1 */ {`[]`, []any{}, nil}, | ||||
| 		/*   2 */ {`[1,2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/*   3 */ {`[1,2,"hello"]`, []any{int64(1), int64(2), "hello"}, nil}, | ||||
| 		/*   4 */ {`[1+2, not true, "hello"]`, []any{int64(3), false, "hello"}, nil}, | ||||
| 		/*   5 */ {`[1,2]+[3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/*   6 */ {`[1,4,3,2]-[3]`, []any{int64(1), int64(4), int64(2)}, nil}, | ||||
| 		/*   7 */ {`add([1,4,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}, | ||||
| 		/*  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}, | ||||
| 		/*  12 */ {`[1,2,3] << 2+2`, []any{int64(1), int64(2), int64(3), int64(4)}, nil}, | ||||
| 		/*  13 */ {`2-1 >> [2,3]`, []any{int64(1), int64(2), int64(3)}, nil}, | ||||
| 		/*  14 */ {`[1,2,3].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}, | ||||
| 		/*  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},
 | ||||
| 		// /*  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 | ||||
| 	if indexValue, err = indexTerm.compute(ctx); 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 { | ||||
| 				index = v | ||||
| 			} else if index >= -maxValue { | ||||
| 				index = maxValue + v | ||||
| 			} else { | ||||
| 				err = indexTerm.Errorf("index %d out of bounds", v) | ||||
| 			} | ||||
|  | ||||
| @ -177,7 +177,7 @@ func parserTest(t *testing.T, section string, inputs []inputType) { | ||||
| 		ctx := NewSimpleFuncStore() | ||||
| 		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) | ||||
| 		scanner := NewScanner(r, DefaultTranslations()) | ||||
|  | ||||
							
								
								
									
										10
									
								
								utils.go
									
									
									
									
									
								
							
							
						
						
									
										10
									
								
								utils.go
									
									
									
									
									
								
							| @ -24,6 +24,11 @@ func IsFloat(v any) (ok bool) { | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func IsBool(v any) (ok bool) { | ||||
| 	_, ok = v.(bool) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func IsList(v any) (ok bool) { | ||||
| 	_, ok = v.(*ListType) | ||||
| 	return ok | ||||
| @ -34,6 +39,11 @@ func IsDict(v any) (ok bool) { | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func IsFract(v any) (ok bool) { | ||||
| 	_, ok = v.(*fraction) | ||||
| 	return ok | ||||
| } | ||||
| 
 | ||||
| func IsNumber(v any) (ok bool) { | ||||
| 	return IsFloat(v) || IsInteger(v) | ||||
| } | ||||
|  | ||||
		Loading…
	
		Reference in New Issue
	
	Block a user