Compare commits
	
		
			5 Commits
		
	
	
		
			f028485caa
			...
			c9db4b84e3
		
	
	| Author | SHA1 | Date | |
|---|---|---|---|
| c9db4b84e3 | |||
| e7e9330b71 | |||
| 8eb25bbc86 | |||
| efc92d434b | |||
| 4151f3f5e2 | 
@ -26,6 +26,7 @@ func TestDictParser(t *testing.T) {
 | 
				
			|||||||
		/*  3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
 | 
							/*  3 */ {`{1:"one",2:"two",3:"three"}`, map[int64]any{int64(1): "one", int64(2): "two", int64(3): "three"}, nil},
 | 
				
			||||||
		/*  4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
 | 
							/*  4 */ {`{1:"one",2:"two",3:"three"}.2`, "three", nil},
 | 
				
			||||||
		/*  5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
 | 
							/*  5 */ {`#{1:"one",2:"two",3:"three"}`, int64(3), nil},
 | 
				
			||||||
 | 
							/*  6 */ {`{1:"one"} + {2:"two"}`, map[any]any{1: "one", 2: "two"}, nil},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	succeeded := 0
 | 
						succeeded := 0
 | 
				
			||||||
 | 
				
			|||||||
@ -68,6 +68,15 @@ func TestFuncs(t *testing.T) {
 | 
				
			|||||||
		/*  55 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
 | 
							/*  55 */ {`builtin "math.arith"; add(1,2)`, int64(3), nil},
 | 
				
			||||||
		/*  56 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
 | 
							/*  56 */ {`fract("2.2(3)")`, newFraction(67, 30), nil},
 | 
				
			||||||
		/*  57 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
 | 
							/*  57 */ {`fract("1.21(3)")`, newFraction(91, 75), nil},
 | 
				
			||||||
 | 
							/*  58 */ {`fract(1.21(3))`, newFraction(91, 75), nil},
 | 
				
			||||||
 | 
							/*  59 */ {`fract(1.21)`, newFraction(121, 100), nil},
 | 
				
			||||||
 | 
							/*  60 */ {`dec(2)`, float64(2), nil},
 | 
				
			||||||
 | 
							/*  61 */ {`dec(2.0)`, float64(2), nil},
 | 
				
			||||||
 | 
							/*  62 */ {`dec("2.0")`, float64(2), nil},
 | 
				
			||||||
 | 
							/*  63 */ {`dec(true)`, float64(1), nil},
 | 
				
			||||||
 | 
							/*  64 */ {`dec(true")`, nil, errors.New("[1:11] missing string termination \"")},
 | 
				
			||||||
 | 
							/*  65 */ {`builtin "string"; joinStr("-", [1, "two", "three"])`, nil, errors.New(`joinStr() expected string, got int64 (1)`)},
 | 
				
			||||||
 | 
							// /*  64 */ {`string(true)`, "true", nil},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	t.Setenv("EXPR_PATH", ".")
 | 
						t.Setenv("EXPR_PATH", ".")
 | 
				
			||||||
 | 
				
			|||||||
@ -1,32 +0,0 @@
 | 
				
			|||||||
// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
					 | 
				
			||||||
// All rights reserved.
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// operand-const.go
 | 
					 | 
				
			||||||
package expr
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// -------- const term
 | 
					 | 
				
			||||||
func newConstTerm(tk *Token) *term {
 | 
					 | 
				
			||||||
	return &term{
 | 
					 | 
				
			||||||
		tk:       *tk,
 | 
					 | 
				
			||||||
		parent:   nil,
 | 
					 | 
				
			||||||
		children: nil,
 | 
					 | 
				
			||||||
		position: posLeaf,
 | 
					 | 
				
			||||||
		priority: priValue,
 | 
					 | 
				
			||||||
		evalFunc: evalConst,
 | 
					 | 
				
			||||||
	}
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// -------- eval func
 | 
					 | 
				
			||||||
func evalConst(ctx ExprContext, self *term) (v any, err error) {
 | 
					 | 
				
			||||||
	v = self.tk.Value
 | 
					 | 
				
			||||||
	return
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
// init
 | 
					 | 
				
			||||||
func init() {
 | 
					 | 
				
			||||||
	registerTermConstructor(SymString, newConstTerm)
 | 
					 | 
				
			||||||
	registerTermConstructor(SymInteger, newConstTerm)
 | 
					 | 
				
			||||||
	registerTermConstructor(SymFloat, newConstTerm)
 | 
					 | 
				
			||||||
	registerTermConstructor(SymBool, newConstTerm)
 | 
					 | 
				
			||||||
	registerTermConstructor(SymKwNil, newConstTerm)
 | 
					 | 
				
			||||||
}
 | 
					 | 
				
			||||||
							
								
								
									
										33
									
								
								operand-literal.go
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										33
									
								
								operand-literal.go
									
									
									
									
									
										Normal file
									
								
							@ -0,0 +1,33 @@
 | 
				
			|||||||
 | 
					// Copyright (c) 2024 Celestino Amoroso (celestino.amoroso@gmail.com).
 | 
				
			||||||
 | 
					// All rights reserved.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// operand-literal.go
 | 
				
			||||||
 | 
					package expr
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// -------- literal term
 | 
				
			||||||
 | 
					func newLiteralTerm(tk *Token) *term {
 | 
				
			||||||
 | 
						return &term{
 | 
				
			||||||
 | 
							tk:       *tk,
 | 
				
			||||||
 | 
							parent:   nil,
 | 
				
			||||||
 | 
							children: nil,
 | 
				
			||||||
 | 
							position: posLeaf,
 | 
				
			||||||
 | 
							priority: priValue,
 | 
				
			||||||
 | 
							evalFunc: evalLiteral,
 | 
				
			||||||
 | 
						}
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// -------- eval func
 | 
				
			||||||
 | 
					func evalLiteral(ctx ExprContext, self *term) (v any, err error) {
 | 
				
			||||||
 | 
						v = self.tk.Value
 | 
				
			||||||
 | 
						return
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					// init
 | 
				
			||||||
 | 
					func init() {
 | 
				
			||||||
 | 
						registerTermConstructor(SymString, newLiteralTerm)
 | 
				
			||||||
 | 
						registerTermConstructor(SymInteger, newLiteralTerm)
 | 
				
			||||||
 | 
						registerTermConstructor(SymFloat, newLiteralTerm)
 | 
				
			||||||
 | 
						registerTermConstructor(SymFraction, newLiteralTerm)
 | 
				
			||||||
 | 
						registerTermConstructor(SymBool, newLiteralTerm)
 | 
				
			||||||
 | 
						registerTermConstructor(SymKwNil, newLiteralTerm)
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@ -61,6 +61,14 @@ func evalPlus(ctx ExprContext, self *term) (v any, err error) {
 | 
				
			|||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			v, err = sumAnyFract(leftValue, rightValue)
 | 
								v, err = sumAnyFract(leftValue, rightValue)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
						} else if IsDict(leftValue) && IsDict(rightValue) {
 | 
				
			||||||
 | 
							leftDict, _ := leftValue.(map[any]any)
 | 
				
			||||||
 | 
							rightDict, _ := rightValue.(map[any]any)
 | 
				
			||||||
 | 
							c := CloneMap(leftDict)
 | 
				
			||||||
 | 
							for key, value := range rightDict {
 | 
				
			||||||
 | 
								c[key] = value
 | 
				
			||||||
 | 
							}
 | 
				
			||||||
 | 
							v = c
 | 
				
			||||||
	} else {
 | 
						} else {
 | 
				
			||||||
		err = self.errIncompatibleTypes(leftValue, rightValue)
 | 
							err = self.errIncompatibleTypes(leftValue, rightValue)
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
				
			|||||||
							
								
								
									
										43
									
								
								scanner.go
									
									
									
									
									
								
							
							
						
						
									
										43
									
								
								scanner.go
									
									
									
									
									
								
							@ -385,20 +385,37 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
 | 
				
			|||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
		if err == nil && (ch == 'e' || ch == 'E') {
 | 
							if err == nil {
 | 
				
			||||||
			sym = SymFloat
 | 
								if ch == 'e' || ch == 'E' {
 | 
				
			||||||
			sb.WriteByte(ch)
 | 
									sym = SymFloat
 | 
				
			||||||
			if ch, err = self.readChar(); err == nil {
 | 
									sb.WriteByte(ch)
 | 
				
			||||||
				if ch == '+' || ch == '-' {
 | 
									if ch, err = self.readChar(); err == nil {
 | 
				
			||||||
					sb.WriteByte(ch)
 | 
										if ch == '+' || ch == '-' {
 | 
				
			||||||
					ch, err = self.readChar()
 | 
					 | 
				
			||||||
				}
 | 
					 | 
				
			||||||
				if ch >= '0' && ch <= '9' {
 | 
					 | 
				
			||||||
					for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
 | 
					 | 
				
			||||||
						sb.WriteByte(ch)
 | 
											sb.WriteByte(ch)
 | 
				
			||||||
 | 
											ch, err = self.readChar()
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
										if ch >= '0' && ch <= '9' {
 | 
				
			||||||
 | 
											for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
 | 
				
			||||||
 | 
												sb.WriteByte(ch)
 | 
				
			||||||
 | 
											}
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											err = fmt.Errorf("[%d:%d] expected integer exponent, got %c", self.row, self.column, ch)
 | 
				
			||||||
 | 
										}
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
								} else if ch == '(' {
 | 
				
			||||||
 | 
									sym = SymFraction
 | 
				
			||||||
 | 
									sb.WriteByte(ch)
 | 
				
			||||||
 | 
									ch, err = self.readChar()
 | 
				
			||||||
 | 
									for ; err == nil && (ch >= '0' && ch <= '9'); ch, err = self.readChar() {
 | 
				
			||||||
 | 
										sb.WriteByte(ch)
 | 
				
			||||||
 | 
									}
 | 
				
			||||||
 | 
									if err == nil {
 | 
				
			||||||
 | 
										if ch != ')' {
 | 
				
			||||||
 | 
											err = fmt.Errorf("[%d:%d] expected ')', got '%c'", self.row, self.column, ch)
 | 
				
			||||||
 | 
										} else {
 | 
				
			||||||
 | 
											sb.WriteByte(ch)
 | 
				
			||||||
 | 
											_, err = self.readChar()
 | 
				
			||||||
					}
 | 
										}
 | 
				
			||||||
				} else {
 | 
					 | 
				
			||||||
					err = errors.New("expected integer exponent")
 | 
					 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
@ -412,6 +429,8 @@ func (self *scanner) parseNumber(firstCh byte) (tk *Token) {
 | 
				
			|||||||
		txt := sb.String()
 | 
							txt := sb.String()
 | 
				
			||||||
		if sym == SymFloat {
 | 
							if sym == SymFloat {
 | 
				
			||||||
			value, err = strconv.ParseFloat(txt, 64)
 | 
								value, err = strconv.ParseFloat(txt, 64)
 | 
				
			||||||
 | 
							} else if sym == SymFraction {
 | 
				
			||||||
 | 
								value, err = makeGeneratingFraction(txt)
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
			value, err = strconv.ParseInt(txt, numBase, 64)
 | 
								value, err = strconv.ParseInt(txt, numBase, 64)
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
@ -7,6 +7,7 @@ package expr
 | 
				
			|||||||
import (
 | 
					import (
 | 
				
			||||||
	"errors"
 | 
						"errors"
 | 
				
			||||||
	"fmt"
 | 
						"fmt"
 | 
				
			||||||
 | 
						"reflect"
 | 
				
			||||||
	"strings"
 | 
						"strings"
 | 
				
			||||||
	"testing"
 | 
						"testing"
 | 
				
			||||||
)
 | 
					)
 | 
				
			||||||
@ -55,15 +56,18 @@ func TestScanner(t *testing.T) {
 | 
				
			|||||||
		/* 33 */ {`(`, SymOpenRound, nil, nil},
 | 
							/* 33 */ {`(`, SymOpenRound, nil, nil},
 | 
				
			||||||
		/* 34 */ {`)`, SymClosedRound, nil, nil},
 | 
							/* 34 */ {`)`, SymClosedRound, nil, nil},
 | 
				
			||||||
		/* 35 */ {`1E+2`, SymFloat, float64(100), nil},
 | 
							/* 35 */ {`1E+2`, SymFloat, float64(100), nil},
 | 
				
			||||||
		/* 36 */ {`1E+x`, SymError, errors.New("expected integer exponent"), nil},
 | 
							/* 36 */ {`1E+x`, SymError, errors.New("[1:5] expected integer exponent, got x"), nil},
 | 
				
			||||||
		/* 37 */ {`$`, SymDollar, nil, nil},
 | 
							/* 37 */ {`$`, SymDollar, nil, nil},
 | 
				
			||||||
		/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil},
 | 
							/* 38 */ {`\`, SymError, errors.New("incomplete escape sequence"), nil},
 | 
				
			||||||
		/* 39 */ {`"string"`, SymString, "string", nil},
 | 
							/* 39 */ {`"string"`, SymString, "string", nil},
 | 
				
			||||||
		/* 39 */ {`identifier`, SymIdentifier, "identifier", nil},
 | 
							/* 40 */ {`identifier`, SymIdentifier, "identifier", nil},
 | 
				
			||||||
 | 
							/* 41 */ {`1.2(3)`, SymFraction, newFraction(37, 30), nil},
 | 
				
			||||||
	}
 | 
						}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
	for i, input := range inputs {
 | 
						for i, input := range inputs {
 | 
				
			||||||
 | 
							// if i != 40 {
 | 
				
			||||||
 | 
							// 	continue
 | 
				
			||||||
 | 
							// }
 | 
				
			||||||
		if input.wantErr == nil {
 | 
							if input.wantErr == nil {
 | 
				
			||||||
			t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source))
 | 
								t.Log(fmt.Sprintf("[+]Test nr %2d -- %q", i+1, input.source))
 | 
				
			||||||
		} else {
 | 
							} else {
 | 
				
			||||||
@ -75,7 +79,8 @@ func TestScanner(t *testing.T) {
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
		if tk := scanner.Next(); tk == nil {
 | 
							if tk := scanner.Next(); tk == nil {
 | 
				
			||||||
			t.Errorf("%d: %q -> got = (nil), want %v (value %v [%T])", i+1, input.source, input.wantSym, input.wantValue, input.wantValue)
 | 
								t.Errorf("%d: %q -> got = (nil), want %v (value %v [%T])", i+1, input.source, input.wantSym, input.wantValue, input.wantValue)
 | 
				
			||||||
		} else if tk.Sym != input.wantSym || tk.Value != input.wantValue {
 | 
								// } else if tk.Sym != input.wantSym || tk.Value != input.wantValue {
 | 
				
			||||||
 | 
							} else if tk.Sym != input.wantSym || !reflect.DeepEqual(tk.Value, input.wantValue) {
 | 
				
			||||||
			if tk.Sym == SymError && input.wantSym == tk.Sym {
 | 
								if tk.Sym == SymError && input.wantSym == tk.Sym {
 | 
				
			||||||
				if tkErr, tkOk := tk.Value.(error); tkOk {
 | 
									if tkErr, tkOk := tk.Value.(error); tkOk {
 | 
				
			||||||
					if inputErr, inputOk := input.wantValue.(error); inputOk {
 | 
										if inputErr, inputOk := input.wantValue.(error); inputOk {
 | 
				
			||||||
@ -86,7 +91,7 @@ func TestScanner(t *testing.T) {
 | 
				
			|||||||
					}
 | 
										}
 | 
				
			||||||
				}
 | 
									}
 | 
				
			||||||
			} else {
 | 
								} else {
 | 
				
			||||||
				t.Errorf("%d: %q -> got = %v (value=%v [%T), want %v (value=%v [%T)", i+1,
 | 
									t.Errorf("%d: %q -> got = %v (value=%v [%T]), want %v (value=%v [%T])", i+1,
 | 
				
			||||||
					input.source, tk.Sym, tk.Value, tk.Value, input.wantSym, input.wantValue, input.wantValue)
 | 
										input.source, tk.Sym, tk.Value, tk.Value, input.wantSym, input.wantValue, input.wantValue)
 | 
				
			||||||
			}
 | 
								}
 | 
				
			||||||
		}
 | 
							}
 | 
				
			||||||
 | 
				
			|||||||
		Loading…
	
		Reference in New Issue
	
	Block a user