From cc4c01c69c238c92602b393ed87575ba5edad352 Mon Sep 17 00:00:00 2001 From: Alexander Kiryukhin Date: Fri, 1 Jul 2022 02:16:07 +0300 Subject: first commit --- defaults.go | 536 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 536 insertions(+) create mode 100644 defaults.go (limited to 'defaults.go') diff --git a/defaults.go b/defaults.go new file mode 100644 index 0000000..e68bc9b --- /dev/null +++ b/defaults.go @@ -0,0 +1,536 @@ +package expression + +import ( + "fmt" + "go/token" + "strconv" +) + +var DefaultOperators = map[token.Token]Operator{ + token.ADD: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + if !a.IsNumber() { + return fmt.Errorf("Token %s must be number", a.Literal) + } + if !b.IsNumber() { + return fmt.Errorf("Token %s must be number", b.Literal) + } + n1, isInt1 := a.Int() + n2, isInt2 := b.Int() + switch { + case isInt1 && isInt2: + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 + n1), + Pos: b.Pos, + }) + default: + stack.Push(Token{ + Token: token.FLOAT, + Literal: strconv.FormatFloat((b.Float() + a.Float()), 'g', 5, 64), + Pos: b.Pos, + }) + } + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.SUB: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + if !a.IsNumber() { + return fmt.Errorf("Token %s must be number", a.Literal) + } + if !b.IsNumber() { + return fmt.Errorf("Token %s must be number", b.Literal) + } + n1, isInt1 := a.Int() + n2, isInt2 := b.Int() + switch { + case isInt1 && isInt2: + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 - n1), + Pos: b.Pos, + }) + default: + stack.Push(Token{ + Token: token.FLOAT, + Literal: strconv.FormatFloat((b.Float() - a.Float()), 'g', 5, 64), + Pos: b.Pos, + }) + } + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.MUL: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + if !a.IsNumber() { + return fmt.Errorf("Token %s must be number", a.Literal) + } + if !b.IsNumber() { + return fmt.Errorf("Token %s must be number", b.Literal) + } + n1, isInt1 := a.Int() + n2, isInt2 := b.Int() + switch { + case isInt1 && isInt2: + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 * n1), + Pos: b.Pos, + }) + default: + stack.Push(Token{ + Token: token.FLOAT, + Literal: strconv.FormatFloat((b.Float() * a.Float()), 'g', 5, 64), + Pos: b.Pos, + }) + } + return nil + }, + priority: 20, + isLeftAssoc: false, + }, + token.QUO: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + if !a.IsNumber() { + return fmt.Errorf("Token %s must be number", a.Literal) + } + if !b.IsNumber() { + return fmt.Errorf("Token %s must be number", b.Literal) + } + n1, isInt1 := a.Int() + n2, isInt2 := b.Int() + switch { + case isInt1 && isInt2: + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 / n1), + Pos: b.Pos, + }) + default: + stack.Push(Token{ + Token: token.FLOAT, + Literal: strconv.FormatFloat((b.Float() / a.Float()), 'g', 5, 64), + Pos: b.Pos, + }) + } + return nil + }, + priority: 20, + isLeftAssoc: false, + }, + token.REM: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + if !a.IsNumber() { + return fmt.Errorf("Token %s must be number", a.Literal) + } + if !b.IsNumber() { + return fmt.Errorf("Token %s must be number", b.Literal) + } + n1, isInt1 := a.Int() + n2, isInt2 := b.Int() + switch { + case isInt1 && isInt2: + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 % n1), + Pos: b.Pos, + }) + default: + return fmt.Errorf("rem operation valid only for ints") + } + return nil + }, + priority: 20, + isLeftAssoc: false, + }, + + token.AND: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 & n1), + Pos: b.Pos, + }) + return nil + }, + priority: 20, + isLeftAssoc: false, + }, + token.OR: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 | n1), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.XOR: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 ^ n1), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.SHL: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 << n1), + Pos: b.Pos, + }) + return nil + }, + priority: 30, + isLeftAssoc: false, + }, + token.SHR: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(n2 >> n1), + Pos: b.Pos, + }) + return nil + }, + priority: 30, + isLeftAssoc: false, + }, + + token.LAND: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n1 != 0 && n2 != 0 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + + return nil + }, + priority: 20, + isLeftAssoc: false, + }, + token.LOR: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n1 != 0 || n2 != 0 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.EQL: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + r := 0 + if a.Literal == b.Literal { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.LSS: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n2 < n1 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.GTR: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n2 > n1 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.NEQ: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + r := 0 + if a.Literal != b.Literal { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.LEQ: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n2 <= n1 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.GEQ: { + fn: func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := 0 + if n2 >= n1 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + priority: 10, + isLeftAssoc: false, + }, + token.NOT: { + fn: func(stack *Stack) error { + a := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + r := 0 + if n1 == 0 { + r = 1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: a.Pos, + }) + + return nil + }, + priority: 40, + isLeftAssoc: false, + }, +} + +var DefaultFunctions = map[string]func(stack *Stack) error{ + "max": func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := n2 + if n2 < n1 { + r = n1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, + "min": func(stack *Stack) error { + a := stack.Pop() + b := stack.Pop() + n1, isInt1 := a.Int() + if !isInt1 { + return fmt.Errorf("Token %s must be integer", a.Literal) + } + n2, isInt2 := b.Int() + if !isInt2 { + return fmt.Errorf("Token %s must be integer", b.Literal) + } + r := n2 + if n2 > n1 { + r = n1 + } + stack.Push(Token{ + Token: token.INT, + Literal: strconv.Itoa(r), + Pos: b.Pos, + }) + return nil + }, +} -- cgit v1.2.3