├── .travis.yml
├── .gitignore
├── lang
├── builtin.go
├── env.go
├── LICENSE.md
├── ast.go
├── util.go
├── lang.go
├── value_test.go
├── value.go
├── lang_test.go
└── operator.go
├── LICENSE.md
├── lambda.go
└── README.md
/.travis.yml:
--------------------------------------------------------------------------------
1 | language: go
2 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | lambda
2 | *.sw*
3 | *.framework
4 |
--------------------------------------------------------------------------------
/lang/builtin.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | // This methods returns all the builtin operators to the environment
4 | func builtinOperators() map[string]*Operator {
5 | opMap := make(map[string]*Operator)
6 | addBuiltinOperators(opMap)
7 | return opMap
8 | }
9 |
10 | func builtinTypes() []Value {
11 | types := make([]Value, 0)
12 | // Append the values in the order of prefence, i.e, more specific types
13 | // should be first.
14 | types = append(types, new(stringValue))
15 | types = append(types, new(intValue))
16 | types = append(types, new(bigIntValue))
17 | types = append(types, new(floatValue))
18 | types = append(types, new(boolValue))
19 | types = append(types, new(varValue))
20 | return types
21 | }
22 |
--------------------------------------------------------------------------------
/lang/env.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | // Data required for interpretation of the language.
4 | // We start with the default environment, and build on top of it, over time.
5 | type LangEnv struct {
6 | opMap map[string]*Operator
7 | types []Value
8 | varMap map[string]Value
9 | recursionDepth int
10 | }
11 |
12 | func NewEnv() *LangEnv {
13 | env := new(LangEnv)
14 | env.Init()
15 | return env
16 | }
17 |
18 | // Initialize the environment
19 | func (e *LangEnv) Init() {
20 | // e.opMap = make(map[string]*Operator)
21 | e.opMap = builtinOperators()
22 | e.types = builtinTypes()
23 | e.varMap = make(map[string]Value)
24 | e.recursionDepth = 0
25 | }
26 |
27 | func (e *LangEnv) getOperator(sym string) *Operator {
28 | return e.opMap[sym]
29 | }
30 |
31 | func (e *LangEnv) getValue(sym string) Value {
32 | return e.varMap[sym]
33 | }
34 |
--------------------------------------------------------------------------------
/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Gaurav Menghani
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/lang/LICENSE.md:
--------------------------------------------------------------------------------
1 | The MIT License (MIT)
2 |
3 | Copyright (c) 2015 Gaurav Menghani
4 |
5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
6 |
7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
8 |
9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
10 |
--------------------------------------------------------------------------------
/lambda.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "bufio"
5 | "bytes"
6 | "flag"
7 | "fmt"
8 | l "github.com/reddragon/lambda/lang"
9 | "github.com/tiborvass/uniline"
10 | "os"
11 | )
12 |
13 | func process(env *l.LangEnv, line string) {
14 | evalResult := l.Eval(line, env)
15 | tokens := evalResult.RemainingTokens
16 | if len(evalResult.ErrStr) > 0 {
17 | fmt.Printf("Error: %s\n", evalResult.ErrStr)
18 | } else {
19 | if len(evalResult.ValStr) > 0 {
20 | fmt.Printf("%s\n", evalResult.ValStr)
21 | } else {
22 | fmt.Printf("\n")
23 | }
24 |
25 | if len(tokens) > 0 {
26 | process(env, tokens)
27 | }
28 | }
29 | }
30 |
31 | func initREPL() {
32 | // Setup the language environment
33 | env := l.NewEnv()
34 |
35 | prompt := "lambda> "
36 | scanner := uniline.DefaultScanner()
37 | for scanner.Scan(prompt) {
38 | line := scanner.Text()
39 | if len(line) > 0 {
40 | scanner.AddToHistory(line)
41 | // printType(line)
42 | process(env, line)
43 | }
44 | }
45 |
46 | if err := scanner.Err(); err != nil {
47 | panic(err)
48 | } else {
49 | fmt.Println("Goodbye!")
50 | }
51 | }
52 |
53 | func processScriptFile(scriptFilePath string) {
54 | file, err := os.Open(scriptFilePath)
55 | if err != nil {
56 | panic(err)
57 | }
58 | defer file.Close()
59 |
60 | var concBuf bytes.Buffer
61 | scanner := bufio.NewScanner(file)
62 | for scanner.Scan() {
63 | concBuf.WriteString(scanner.Text())
64 | concBuf.WriteString(" ")
65 | }
66 |
67 | if scanner.Err() != nil {
68 | panic(scanner.Err())
69 | }
70 | process(l.NewEnv(), concBuf.String())
71 | }
72 |
73 | func main() {
74 | var scriptFile = flag.String("f", "", "path of the file to read from")
75 | flag.Parse()
76 |
77 | if len(*scriptFile) > 0 {
78 | processScriptFile(*scriptFile)
79 | } else {
80 | initREPL()
81 | }
82 | }
83 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # lambda
2 |
3 |
4 | A ~~WIP~~ Lisp dialect written in Golang, written purely for fun :-)
5 |
6 | ### Introduction
7 |
8 | I have been amazed at the kind of things that we can achieve with simple s-expressions. s-expressions or symbolic exressions, are nothing but expressions of this format: `(operator operand1 operand2 ...)`, where we pass the operands to the operator for evaluation. For example, `(+ 1 2 3)` is the same as `1 + 2 + 3`. You can nest several such expressions like this. For example, `(* (+ 1 2) (+ 4 5))`, which is the same as `((1 + 2) * (4 + 5))`. In general, s-expressions make it easy to write tree-structured / recursive code and data.
9 |
10 | Lisp, is a family of programming languages that have popularized the use of s-expressions. I found it interesting, and this is my attempt at writing yet another Lisp dialect. This is a purely academic pursuit, so I would not recommend using this in production. The crux of the work lies in the `lang` directory, but I have provided a simple REPL (Read-Eval-Print-Loop) to try out the language.
11 |
12 | #### What works so far
13 | * Integer, floating point and string types
14 | * Mathematical operators (`+`, `-`, `*`, `/`)
15 | * Comparison operators (`=`, `>`, `>=`, `<`, `<=`)
16 | * Logical operators (`or`, `and`)
17 | * Conditional (`cond`)
18 | * Defining variables (`defvar`)
19 | * Defining methods (`defun`) (Can't define multi-expressions methods yet)
20 | * Methods as first-class citizens
21 | * Support for Big Int calculations
22 |
23 | #### What might come*
24 | * Full Support for anonymous methods
25 | * Multi-expression methods
26 | * Support for comments
27 |
28 | **Update**: I am going to to shift my attention to other projects as of July 2016. If you feel strongly about a particular feature, either feel free to implement it and send a pull request (I can help with giving pointers), or let me know and I will try to prioritize it.
29 |
30 | ### How to Use
31 | * `go get github.com/reddragon/lambda`
32 | * `go build $GOPATH/src/github.com/reddragon/lambda/lambda.go`
33 | * `$GOPATH/bin/lambda`
34 |
35 | ### Sample Usage
36 | ```
37 | > ./lambda
38 | lambda> (+ 1 2)
39 | 3
40 |
41 | lambda> (- (/ (* (+ 1 2) 3) 3) 2)
42 | 1
43 |
44 | lambda> (/ 22 7.0)
45 | 3.142857142857143
46 |
47 | lambda> (defvar pi 3.14159265359)
48 | 3.14159265359
49 |
50 | lambda> (defvar r 10)
51 | 10
52 |
53 | lambda> (* pi (* r r))
54 | 314.159265359
55 |
56 | lambda> (/ 1 0)
57 | Error: Divide by zero
58 |
59 | lambda> (defun add-sq (x y) (+ (* x x) (* y y)))
60 |
61 |
62 | lambda> (add-sq 3 4)
63 | 25
64 |
65 | lambda> (defun fact (x) (cond ((= x 0) 1) (true (* x (fact (- x 1))))))
66 |
67 |
68 | lambda> (fact 30)
69 | 265252859812191058636308480000000
70 |
71 | lambda> (defun add-one (x) (+ x 1))
72 |
73 |
74 | lambda> (defun twice (f x) (f (f x)))
75 |
76 |
77 | lambda> (twice add-one 2)
78 | 4
79 |
80 | lambda> ^D
81 | Goodbye!
82 | ```
83 | lambda can also read from files and execute them. You can try it out with the `-f` option, like:
84 |
85 | ```
86 | ./lambda -f ~/path/to/my/script.l
87 | ```
88 |
89 | ### Inspiration
90 | * [Peter Norvig's post about writing a Lisp-like language](http://norvig.com/lispy.html)
91 | * [Build Your Own Lisp](http://www.buildyourownlisp.com/)
92 |
--------------------------------------------------------------------------------
/lang/ast.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "strings"
7 | )
8 |
9 | // An AstNode either has a value, or has children.
10 | // isValue = 1, if its a value, otherwise false.
11 | type ASTNode struct {
12 | isValue bool
13 | value string
14 | children []*ASTNode
15 | }
16 |
17 | const (
18 | openBracket string = "("
19 | closedBracket string = ")"
20 | )
21 |
22 | func errStr(expected, found string) error {
23 | return errors.New(fmt.Sprintf("Expected %s, got %s.", expected, found))
24 | }
25 |
26 | // This method gets you the AST of a given expression.
27 | func getAST(exp string) (*ASTNode, []string, error) {
28 | tokens := tokenize(exp)
29 | if len(tokens) == 0 {
30 | return nil, nil, errors.New("Nothing to evaluate")
31 | }
32 | return buildAST(tokens)
33 | }
34 |
35 | func tokenize(exp string) []string {
36 | return strings.Fields(
37 | strings.Replace(strings.Replace(exp, "(", " ( ", -1), ")", " ) ", -1),
38 | )
39 | }
40 |
41 | // This method does the heavy-lifting of building an AST, once an expression
42 | // is tokenized.
43 | func buildAST(tokens []string) (*ASTNode, []string, error) {
44 | var token = ""
45 | tokensLen := len(tokens)
46 | // If it is an empty list of tokens, the AST is a nil node
47 | if tokensLen == 0 {
48 | return nil, tokens, nil
49 | } else if tokensLen == 1 {
50 | // TODO Check that this token is a value.
51 | // A proxy for now is checking if this is not a ( or )
52 | token, tokens = pop(tokens)
53 | if token == openBracket || token == closedBracket {
54 | return nil, tokens, errStr("value", token)
55 | }
56 |
57 | node := new(ASTNode)
58 | node.isValue = true
59 | node.value = token
60 | node.children = nil
61 | return node, tokens, nil
62 | } else {
63 | token, tokens = pop(tokens)
64 | if token != openBracket {
65 | return nil, tokens, errStr(openBracket, token)
66 | }
67 |
68 | node := new(ASTNode)
69 | node.isValue = false
70 | // Create a slice with 0 length initially.
71 | node.children = make([]*ASTNode, 0)
72 |
73 | tokensLen = len(tokens)
74 | for len(tokens) != 0 && tokens[0] != closedBracket {
75 | var childNode *ASTNode = nil
76 | var err error = nil
77 | // If this is not an open brace, this is a single value
78 | if tokens[0] != openBracket {
79 | token, tokens = pop(tokens)
80 | childNode, _, err = buildAST([]string{token})
81 | } else {
82 | childNode, tokens, err = buildAST(tokens)
83 | }
84 | if err != nil {
85 | return nil, tokens, err
86 | }
87 | node.children = append(node.children, childNode)
88 | }
89 | if len(tokens) == 0 {
90 | return nil, tokens, errStr(closedBracket, "nil")
91 | }
92 |
93 | token, tokens = pop(tokens)
94 | if token != closedBracket {
95 | return nil, tokens, errStr(token, closedBracket)
96 | }
97 | return node, tokens, nil
98 | }
99 | }
100 |
101 | func StringifyAST(node *ASTNode) string {
102 | if node == nil {
103 | return ""
104 | }
105 | if node.isValue {
106 | return node.value
107 | }
108 | r := "("
109 | if node.children != nil {
110 | for i := 0; i < len(node.children); i++ {
111 | r += StringifyAST(node.children[i])
112 | if i+1 < len(node.children) {
113 | r += " "
114 | }
115 | }
116 | }
117 | r += ")"
118 | return r
119 | }
120 |
--------------------------------------------------------------------------------
/lang/util.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | )
7 |
8 | func checkArgTypes(operatorName string, operands *[]Atom, allowedTypes []valueType) (map[valueType]int, error) {
9 | typesFound := make(map[valueType]int, 0)
10 | for _, operand := range *operands {
11 | exists := false
12 | typesFound[operand.Val.getValueType()]++
13 | for _, t := range allowedTypes {
14 | if operand.Val.getValueType() == t {
15 | exists = true
16 | break
17 | }
18 | }
19 | if !exists {
20 | return nil, errors.New(
21 | fmt.Sprintf("For operator %s, operand %s is of unexpected type: %s.",
22 | operatorName, operand.Val.Str(), operand.Val.getValueType()))
23 | }
24 | }
25 | return typesFound, nil
26 | }
27 |
28 | func pop(tokens []string) (string, []string) {
29 | if len(tokens) == 0 {
30 | return "", tokens
31 | }
32 | return tokens[0], tokens[1:]
33 | }
34 |
35 | // This method force casts all operands to the given value type.
36 | func tryTypeCastTo(operands *[]Atom, finalType valueType) error {
37 | for i := 0; i < len(*operands); i++ {
38 | if (*operands)[i].Val.getValueType() != finalType {
39 | var err error
40 | (*operands)[i].Val, err = (*operands)[i].Val.to(finalType)
41 |
42 | if err != nil {
43 | return err
44 | }
45 | }
46 | }
47 | return nil
48 | }
49 |
50 | // Algorithm:
51 | // 1. We get the type -> count mapping.
52 | // 2. If there is only one type, there is nothing to do.
53 | // 3. If there are multiple, pick the one with the highest precedence.
54 | // 4. Try and cast all operand values to that type. Error out if any of them
55 | // resists. Because, resistance is futile.
56 | func typeCoerce(operatorName string, operands *[]Atom, typePrecendenceMap map[valueType]int) (valueType, error) {
57 | var err error
58 | var typesCountMap map[valueType]int
59 | allowedTypes := make([]valueType, len(typePrecendenceMap))
60 | typeIdx := 0
61 | for vtype, _ := range typePrecendenceMap {
62 | allowedTypes[typeIdx] = vtype
63 | typeIdx++
64 | }
65 |
66 | typesCountMap, err = checkArgTypes(operatorName, operands, allowedTypes)
67 | if err != nil {
68 | return "", err
69 | }
70 |
71 | if len(typesCountMap) == 1 {
72 | valType := (*operands)[0].Val.getValueType()
73 | return valType, nil
74 | }
75 |
76 | var finalType valueType
77 | finalTypePrecedence := -1
78 | for t, c := range typesCountMap {
79 | if c <= 0 {
80 | continue
81 | }
82 | precedence := typePrecendenceMap[t]
83 | if precedence > finalTypePrecedence {
84 | finalType = t
85 | finalTypePrecedence = precedence
86 | }
87 | }
88 |
89 | err = tryTypeCastTo(operands, finalType)
90 | if err != nil {
91 | return "", err
92 | }
93 | return finalType, nil
94 | }
95 |
96 | // This is used when a group of value types can be used with the operator.
97 | // This method processes the operands in order of the precendence maps.
98 | func chainedTypeCoerce(operatorName string, operands *[]Atom, typePrecendenceMapList []map[valueType]int) (valueType, error) {
99 | var vtype valueType
100 | var err error
101 | for _, pMap := range typePrecendenceMapList {
102 | vtype, err = typeCoerce(operatorName, operands, pMap)
103 | if err == nil {
104 | return vtype, nil
105 | }
106 | }
107 | return "", err
108 | }
109 |
110 | func getASTStr(node *ASTNode) string {
111 | if node != nil {
112 | if node.isValue {
113 | return node.value
114 | }
115 |
116 | if node.children != nil {
117 | astStr := ""
118 | for _, child := range node.children {
119 | astStr = astStr + getASTStr(child) + " "
120 | }
121 | return astStr
122 | }
123 | }
124 | return ""
125 | }
126 |
127 | func printVarMap(varMap map[string]Value) {
128 | fmt.Println("varMap values: ")
129 | for k, v := range varMap {
130 | fmt.Printf("%s = %s\n", k, v.Str())
131 | }
132 | }
133 |
--------------------------------------------------------------------------------
/lang/lang.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "strings"
8 | )
9 |
10 | // An Atom is either a value, or an error
11 | type Atom struct {
12 | Err error
13 | Val Value
14 | }
15 |
16 | type EvalResult struct {
17 | ValStr string
18 | ErrStr string
19 | RemainingTokens string
20 | }
21 |
22 | func Eval(exp string, env *LangEnv) *EvalResult {
23 | exp = strings.TrimSpace(exp)
24 | evalResult := new(EvalResult)
25 | astNode, tokens, err := getAST(exp)
26 | if err != nil {
27 | evalResult.ErrStr = err.Error()
28 | return evalResult
29 | }
30 |
31 | // This round-about way is not necessary. Gomobile trips if you return
32 | // structs by value.
33 | // TODO
34 | // Remove this hack, pending: https://github.com/golang/go/issues/11318
35 | result := evalASTHelper(env, astNode)
36 |
37 | if result.Val != nil && result.Val.getValueType() == varType {
38 | result.Val, result.Err = getVarValue(env, result.Val)
39 | }
40 |
41 | if result.Err != nil {
42 | evalResult.ErrStr = result.Err.Error()
43 | } else if result.Val != nil {
44 | evalResult.ValStr = result.Val.Str()
45 | }
46 |
47 | if tokens != nil && len(tokens) > 0 {
48 | var buffer bytes.Buffer
49 | for _, s := range tokens {
50 | buffer.WriteString(s)
51 | buffer.WriteString(" ")
52 | }
53 | evalResult.RemainingTokens = strings.TrimSpace(buffer.String())
54 | }
55 | return evalResult
56 | }
57 |
58 | func evalASTHelper(env *LangEnv, node *ASTNode) Atom {
59 | result := evalAST(env, node)
60 |
61 | if result.Val != nil && result.Val.getValueType() == varType {
62 | result.Val, result.Err = getVarValue(env, result.Val)
63 | }
64 | return result
65 | }
66 |
67 | func evalAST(env *LangEnv, node *ASTNode) Atom {
68 | // printVarMap(env.varMap)
69 | var retVal Atom
70 | retVal.Err = nil
71 |
72 | if node.isValue {
73 | value, err := getValue(env, node.value)
74 | if err != nil {
75 | retVal.Err = errors.New(fmt.Sprintf("%s %s", errStr("value", node.value), err))
76 | } else {
77 | retVal.Val = value
78 | }
79 | return retVal
80 | }
81 | if len(node.children) == 0 {
82 | retVal.Err = errors.New("Cannot evaluate an empty expression")
83 | return retVal
84 | }
85 | if len(node.children) == 1 {
86 | return evalAST(env, node.children[0])
87 | }
88 |
89 | // Assuming that the first child is an operand
90 | symbol := node.children[0].value
91 | operator := env.getOperator(symbol)
92 | if operator == nil {
93 | retVal.Err = errors.New(fmt.Sprintf("Unknown operator '%s'", symbol))
94 | return retVal
95 | }
96 |
97 | if operator.minArgCount == operator.maxArgCount {
98 | argCount := operator.minArgCount
99 | if len(node.children)-1 != argCount {
100 | retVal.Err = errors.New(
101 | fmt.Sprintf("Received %d arguments for operator %s, expected: %d",
102 | len(node.children)-1, symbol, argCount))
103 | return retVal
104 | }
105 | } else {
106 | if len(node.children)-1 < operator.minArgCount {
107 | retVal.Err = errors.New(
108 | fmt.Sprintf("Received %d arguments for operator %s, minimum expected arguments: %d",
109 | len(node.children)-1, symbol, operator.minArgCount))
110 | return retVal
111 | } else if len(node.children)-1 > operator.maxArgCount {
112 | retVal.Err = errors.New(
113 | fmt.Sprintf("Received %d arguments for operator %s, maximum expected arguments: %d",
114 | len(node.children)-1, symbol, operator.maxArgCount))
115 | return retVal
116 | }
117 | }
118 |
119 | operands := make([]Atom, 0)
120 | if operator.passRawAST {
121 | var o Atom
122 | o.Val = newASTValue(node)
123 | operands = append(operands, o)
124 | } else {
125 | for i := 1; i < len(node.children); i++ {
126 | v := evalAST(env, node.children[i])
127 | if v.Err != nil {
128 | return v
129 | }
130 | if !operator.doNotResolveVars && v.Val.getValueType() == varType {
131 | v.Val, v.Err = getVarValue(env, v.Val)
132 | if v.Err != nil {
133 | return v
134 | }
135 | }
136 | operands = append(operands, v)
137 | }
138 | }
139 | v := operator.handler(env, operands)
140 | if v.Err != nil {
141 | return v
142 | }
143 | retVal.Val = v.Val
144 | return retVal
145 | }
146 |
--------------------------------------------------------------------------------
/lang/value_test.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "fmt"
5 | "testing"
6 | )
7 |
8 | type TestPair struct {
9 | a, b interface{}
10 | }
11 |
12 | func doTypeChecks(v Value, cases []TestPair, t *testing.T) {
13 | vtype := v.getValueType()
14 | for _, p := range cases {
15 | str, ok := p.a.(string)
16 | if !ok {
17 | t.Errorf("Error setting up the test cases.")
18 | return
19 | }
20 |
21 | check := v.ofType(str)
22 | if check != p.b {
23 | t.Errorf(fmt.Sprintf("%s.ofType(%s), expected: %t, actual: %t",
24 | vtype, p.a, p.b, check))
25 | }
26 | }
27 | }
28 |
29 | func doChecks(v Value, cases []TestPair, t *testing.T) {
30 | vtype := v.getValueType()
31 | for _, p := range cases {
32 | _, ok := p.a.(string)
33 | if !ok {
34 | t.Errorf("Error setting up the test cases.")
35 | return
36 | }
37 |
38 | if p.a != p.b {
39 | t.Errorf(fmt.Sprintf("%s.Str(), expected: %s, actual: %s",
40 | vtype, p.b, p.a))
41 | }
42 | }
43 | }
44 |
45 | func TestintValue(t *testing.T) {
46 | iv := new(intValue)
47 | cases := make([]TestPair, 0)
48 | cases = append(cases, TestPair{"1.2", false})
49 | cases = append(cases, TestPair{"1", true})
50 | cases = append(cases, TestPair{"-1", true})
51 | cases = append(cases, TestPair{"foobar", false})
52 | doTypeChecks(iv, cases, t)
53 |
54 | strCases := make([]TestPair, 0)
55 | iv.value = 1
56 | strCases = append(strCases, TestPair{iv.Str(), "1"})
57 | iv.value = 0
58 | strCases = append(strCases, TestPair{iv.Str(), "0"})
59 |
60 | doChecks(iv, strCases, t)
61 |
62 | iv.value = 2
63 | conv, err := iv.to(intType)
64 | if err != nil || conv.getValueType() != intType || conv.Str() != iv.Str() {
65 | t.Errorf("Could not convert from intType to intType")
66 | }
67 |
68 | conv, err = iv.to(floatType)
69 | if err != nil || conv.getValueType() != floatType || conv.Str() != iv.Str() {
70 | t.Errorf("Could not convert from intType to floatType")
71 | }
72 | }
73 |
74 | func TestfloatValue(t *testing.T) {
75 | fv := new(floatValue)
76 | cases := make([]TestPair, 0)
77 | cases = append(cases, TestPair{"1.2", true})
78 | cases = append(cases, TestPair{"1", true})
79 | cases = append(cases, TestPair{"-1", true})
80 | cases = append(cases, TestPair{"foobar", false})
81 | doTypeChecks(fv, cases, t)
82 |
83 | strCases := make([]TestPair, 0)
84 | fv.value = 1.0
85 | strCases = append(strCases, TestPair{fv.Str(), "1"})
86 | fv.value = -1.0
87 | strCases = append(strCases, TestPair{fv.Str(), "-1"})
88 | fv.value = -1.1
89 | strCases = append(strCases, TestPair{fv.Str(), "-1.1"})
90 | fv.value = -1.23456789
91 | strCases = append(strCases, TestPair{fv.Str(), "-1.23456789"})
92 | doChecks(fv, strCases, t)
93 | }
94 |
95 | func TeststringValue(t *testing.T) {
96 | sv := new(stringValue)
97 | cases := make([]TestPair, 0)
98 | cases = append(cases, TestPair{"", false})
99 | cases = append(cases, TestPair{"''", true})
100 | cases = append(cases, TestPair{"'abc'", true})
101 | cases = append(cases, TestPair{"\"\"", true})
102 | cases = append(cases, TestPair{"\"abc\"", true})
103 | cases = append(cases, TestPair{"1.2", false})
104 | doTypeChecks(sv, cases, t)
105 |
106 | strCases := make([]TestPair, 0)
107 | sv.value = "\"abc\""
108 | strCases = append(strCases, TestPair{sv.Str(), sv.value})
109 |
110 | doChecks(sv, strCases, t)
111 | }
112 |
113 | func TestTypeInfer(t *testing.T) {
114 | env := new(LangEnv)
115 | env.Init()
116 | v, e := getValue(env, "1")
117 | if v == nil || v.getValueType() != intType || e != nil {
118 | t.Errorf("Could not correctly getValue(1)")
119 | }
120 |
121 | v, e = getValue(env, "1.2")
122 | if v == nil || v.getValueType() != floatType || e != nil {
123 | t.Errorf("Could not correctly getValue(1)")
124 | }
125 |
126 | v, e = getValue(env, "'xyz'")
127 | if v == nil || v.getValueType() != stringType || e != nil {
128 | t.Errorf("Could not correctly getValue(1)")
129 | }
130 |
131 | v, e = getValue(env, "true")
132 | if v == nil || v.getValueType() != boolType || e != nil {
133 | t.Errorf("Could not correctly getValue(1)")
134 | }
135 |
136 | v, e = getValue(env, "false")
137 | if v == nil || v.getValueType() != boolType || e != nil {
138 | t.Errorf("Could not correctly getValue(1)")
139 | }
140 | }
141 |
--------------------------------------------------------------------------------
/lang/value.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "errors"
5 | "fmt"
6 | "math/big"
7 | "regexp"
8 | "strconv"
9 | )
10 |
11 | // Different types of values supported
12 | type valueType interface{}
13 |
14 | const (
15 | // Value type
16 | stringType = "stringType"
17 | intType = "intType"
18 | bigIntType = "bigIntType"
19 | floatType = "floatType"
20 | varType = "varType"
21 | boolType = "boolType"
22 | astType = "astType"
23 | )
24 |
25 | type Value interface {
26 | Str() string
27 | getValueType() valueType
28 | to(valueType) (Value, error)
29 | ofType(string) bool
30 | newValue(string) Value
31 | }
32 |
33 | func getVarValue(env *LangEnv, varVal Value) (Value, error) {
34 | if varVal != nil && varVal.getValueType() == varType {
35 | varTypeVal, _ := varVal.(varValue)
36 | varName := varTypeVal.varName
37 |
38 | val := env.varMap[varName]
39 | if val != nil {
40 | return val, nil
41 | }
42 | opVal := env.opMap[varName]
43 | if opVal != nil {
44 | return varVal, nil
45 | }
46 | return nil, errors.New(fmt.Sprintf("Undefined variable: %s", varName))
47 | }
48 | return nil, errors.New(fmt.Sprintf("Error while resolving variable."))
49 | }
50 |
51 | // Algorithm
52 | // 1. Go through all the value types, in order.
53 | // 2. Pick the highest value type that complies.
54 | // 3. Return that value type.
55 | func getValue(env *LangEnv, token string) (Value, error) {
56 | types := builtinTypes()
57 | for _, t := range types {
58 | if t.ofType(token) {
59 | return t.newValue(token), nil
60 | }
61 | }
62 | return nil, errors.New(fmt.Sprintf("Could not get type for token: %s", token))
63 | }
64 |
65 | /*
66 | Types in lambda:
67 | > 1 + 1
68 | 2
69 | > 1 + 1.0
70 | 2.0
71 | > 1 * 1.0
72 | 2.0
73 | > 1 / 0
74 | NaN
75 | >
76 | */
77 |
78 | func typeConvError(from, to valueType) error {
79 | return errors.New(fmt.Sprintf("Cannot convert %s to %s", from, to))
80 | }
81 |
82 | type stringValue struct {
83 | value string
84 | }
85 |
86 | func (v stringValue) getValueType() valueType {
87 | return stringType
88 | }
89 |
90 | func (v stringValue) to(targetType valueType) (Value, error) {
91 | switch targetType {
92 | case stringType:
93 | return v, nil
94 | }
95 | return nil, typeConvError(v.getValueType(), targetType)
96 | }
97 |
98 | func (v stringValue) Str() string {
99 | return v.value
100 | }
101 |
102 | func (v stringValue) ofType(targetValue string) bool {
103 | valLen := len(targetValue)
104 | if valLen < 2 {
105 | return false
106 | }
107 | // TODO
108 | // Stricter checks for quotes inside strings, like ''' should not be valid.
109 | f, l := targetValue[0], targetValue[valLen-1]
110 | if (f == '\'' && l == '\'') || (f == '"' && l == '"') {
111 | return true
112 | }
113 | return false
114 | }
115 |
116 | func (v stringValue) newValue(str string) Value {
117 | var val stringValue
118 | val.value = str
119 | return val
120 | }
121 |
122 | type intValue struct {
123 | value int64
124 | }
125 |
126 | func (v intValue) getValueType() valueType {
127 | return intType
128 | }
129 |
130 | func (v intValue) to(targetType valueType) (Value, error) {
131 | switch targetType {
132 | case intType:
133 | return v, nil
134 | case bigIntType:
135 | var val bigIntValue
136 | val.value = new(big.Int)
137 | val.value.SetInt64(v.value)
138 | return val, nil
139 | case floatType:
140 | var val floatValue
141 | val.value = float64(v.value)
142 | return val, nil
143 | }
144 | return nil, typeConvError(v.getValueType(), targetType)
145 | }
146 |
147 | func (v intValue) ofType(targetValue string) bool {
148 | _, err := strconv.ParseInt(targetValue, 0, 64)
149 | if err != nil {
150 | // fmt.Printf("Error processing %s: %s", targetValue, err)
151 | return false
152 | }
153 | return true
154 | }
155 |
156 | func (v intValue) Str() string {
157 | return strconv.FormatInt(v.value, 10)
158 | }
159 |
160 | func (v intValue) newValue(str string) Value {
161 | intVal, err := strconv.ParseInt(str, 0, 64)
162 | if err != nil {
163 | return nil
164 | }
165 | var val intValue
166 | val.value = intVal
167 | return val
168 | }
169 |
170 | type bigIntValue struct {
171 | value *big.Int
172 | }
173 |
174 | func (v bigIntValue) getValueType() valueType {
175 | return bigIntType
176 | }
177 |
178 | func (v bigIntValue) to(targetType valueType) (Value, error) {
179 | switch targetType {
180 | case intType:
181 | // Get the int64 representation, and
182 | // try creating an big.Int out of it.
183 | intRep := v.value.Int64()
184 | if v.value.Cmp(new(big.Int).SetInt64(intRep)) == 0 {
185 | var val intValue
186 | val.value = intRep
187 | return val, nil
188 | }
189 | // An alternate way would be to check if the bigInt is either smaller than
190 | // the smallest value of int64, or larger than the largest value of int64.
191 | }
192 | return nil, typeConvError(v.getValueType(), targetType)
193 | }
194 |
195 | func (v bigIntValue) ofType(targetValue string) bool {
196 | // Here we are creating an extra copy of the big int.
197 | bigIntVal := new(big.Int)
198 | var ok bool
199 | bigIntVal, ok = bigIntVal.SetString(targetValue, 0)
200 | return ok
201 | }
202 |
203 | func (v bigIntValue) Str() string {
204 | return v.value.String()
205 | }
206 |
207 | func (v bigIntValue) newValue(str string) Value {
208 | bigIntVal := new(big.Int)
209 | var ok bool
210 | bigIntVal, ok = bigIntVal.SetString(str, 0)
211 | if !ok {
212 | fmt.Printf("There was an error!\n")
213 | return nil
214 | }
215 |
216 | var val bigIntValue
217 | val.value = bigIntVal
218 | return val
219 | }
220 |
221 | type floatValue struct {
222 | value float64
223 | }
224 |
225 | func (v floatValue) getValueType() valueType {
226 | return floatType
227 | }
228 |
229 | func (v floatValue) to(targetType valueType) (Value, error) {
230 | switch targetType {
231 | case floatType:
232 | return v, nil
233 | }
234 | return nil, typeConvError(v.getValueType(), targetType)
235 | }
236 |
237 | func (v floatValue) ofType(targetValue string) bool {
238 | _, err := strconv.ParseFloat(targetValue, 64)
239 | if err != nil {
240 | // fmt.Printf("Error processing %s: %s", targetValue, err)
241 | return false
242 | }
243 | return true
244 | }
245 |
246 | func (v floatValue) Str() string {
247 | return strconv.FormatFloat(v.value, 'g', -1, 64)
248 | }
249 |
250 | func (v floatValue) newValue(str string) Value {
251 | floatVal, err := strconv.ParseFloat(str, 64)
252 | if err != nil {
253 | return nil
254 | }
255 | var val floatValue
256 | val.value = floatVal
257 | return val
258 | }
259 |
260 | type varValue struct {
261 | value string
262 | varName string
263 | }
264 |
265 | func (v varValue) getValueType() valueType {
266 | return varType
267 | }
268 |
269 | func (v varValue) to(targetType valueType) (Value, error) {
270 | return nil, typeConvError(v.getValueType(), targetType)
271 | }
272 |
273 | func (v varValue) ofType(targetValue string) bool {
274 | matched, err := regexp.MatchString("[a-zA-Z]+[a-zA-Z0-9]*", targetValue)
275 | if matched == false || err != nil {
276 | return false
277 | }
278 | return true
279 | }
280 |
281 | func (v varValue) Str() string {
282 | return v.value
283 | }
284 |
285 | func (v varValue) newValue(str string) Value {
286 | var val varValue
287 | val.value = str
288 | val.varName = str
289 | return val
290 | }
291 |
292 | type boolValue struct {
293 | value bool
294 | }
295 |
296 | func (v boolValue) getValueType() valueType {
297 | return boolType
298 | }
299 |
300 | func (v boolValue) to(targetType valueType) (Value, error) {
301 | return nil, typeConvError(v.getValueType(), targetType)
302 | }
303 |
304 | func (v boolValue) ofType(targetValue string) bool {
305 | if targetValue == "true" || targetValue == "false" {
306 | return true
307 | }
308 | return false
309 | }
310 |
311 | func (v boolValue) Str() string {
312 | if v.value == true {
313 | return "true"
314 | } else {
315 | return "false"
316 | }
317 | }
318 |
319 | func (v boolValue) newValue(str string) Value {
320 | var val boolValue
321 | if str == "true" {
322 | val.value = true
323 | } else {
324 | val.value = false
325 | }
326 | return val
327 | }
328 |
329 | func newBoolValue(b bool) Value {
330 | var val boolValue
331 | val.value = b
332 | return val
333 | }
334 |
335 | type astValue struct {
336 | astNodes []*ASTNode
337 | parentASTNode *ASTNode
338 | }
339 |
340 | func (v astValue) getValueType() valueType {
341 | return astType
342 | }
343 |
344 | func (v astValue) to(targetType valueType) (Value, error) {
345 | return nil, typeConvError(v.getValueType(), targetType)
346 | }
347 |
348 | func (v astValue) ofType(targetValue string) bool {
349 | return false
350 | }
351 |
352 | func (v astValue) Str() string {
353 | return getASTStr(v.parentASTNode)
354 | }
355 |
356 | func (v astValue) newValue(str string) Value {
357 | return nil
358 | }
359 |
360 | func newASTValue(parentNode *ASTNode) Value {
361 | var val astValue
362 | val.parentASTNode = parentNode
363 | val.astNodes = parentNode.children[1:]
364 | return val
365 | }
366 |
367 | type method struct {
368 | methodName string
369 | params []string
370 | ast *ASTNode
371 | }
372 |
--------------------------------------------------------------------------------
/lang/lang_test.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "fmt"
5 | "math/rand"
6 | "testing"
7 | "time"
8 | )
9 |
10 | func saneExprTest(query string, t *testing.T, env *LangEnv) {
11 | val := Eval(query, env)
12 | if len(val.ValStr) == 0 || len(val.ErrStr) > 0 {
13 | t.Errorf("Expected %s to not be nil. Err: %s", query, val.ErrStr)
14 | }
15 | }
16 |
17 | func checkExprResultTest(query, expected string, t *testing.T, env *LangEnv) {
18 | val := Eval(query, env)
19 | if len(val.ValStr) == 0 || len(val.ErrStr) > 0 {
20 | t.Errorf("Expected %s to not be nil. Err: %s", query, val.ErrStr)
21 | } else {
22 | if val.ValStr != expected {
23 | t.Errorf("Expected %s to be %s, but was %s", query, expected, val.ValStr)
24 | }
25 | }
26 | }
27 |
28 | func malformedExprTest(query string, t *testing.T, env *LangEnv) {
29 | val := Eval(query, env)
30 | if len(val.ValStr) != 0 {
31 | t.Errorf("Expected value of %s to be nil, was %s", val.ValStr)
32 | } else if len(val.ErrStr) == 0 {
33 | t.Errorf("Expected %s to return a non-nil error")
34 | }
35 | }
36 |
37 | // Generates an s-expression out of the basic {+, -, *, /} operators.
38 | func genSimpleOperatorsExpr(terminationProb float32, r *rand.Rand, depth int) string {
39 | p := r.Float32()
40 | // fmt.Printf("Got %f, terminationProb was %f, depth: %d\n", p, terminationProb, depth)
41 | if p < terminationProb || depth > 1000 {
42 | return fmt.Sprintf("%f", r.Float32())
43 | }
44 | operands := []string{"+", "-", "*", "/"}
45 | return fmt.Sprintf("(%s %s %s)", operands[r.Intn(len(operands))], genSimpleOperatorsExpr(terminationProb, r, depth+1), genSimpleOperatorsExpr(terminationProb, r, depth+1))
46 | }
47 |
48 | // This will run a lot of random smoke tests with simple operators.
49 | func runRandomSmokeTests(t *testing.T, env *LangEnv) {
50 | seed := time.Now().Unix()
51 | // fmt.Printf("Using the seed %d. Use it to reproduce test failures.\n", seed)
52 | r := rand.New(rand.NewSource(seed))
53 | for i := 0; i < 100; i++ {
54 | expr := genSimpleOperatorsExpr(0.5, r, 0)
55 | saneExprTest(expr, t, env)
56 | }
57 | }
58 |
59 | func TestBasicLang(t *testing.T) {
60 | env := new(LangEnv)
61 | env.Init()
62 |
63 | checkExprResultTest("1", "1", t, env)
64 | checkExprResultTest("(+ 1 2)", "3", t, env)
65 | checkExprResultTest("(+ 1 2 3 4 5)", "15", t, env)
66 | checkExprResultTest("(+ 111111111111111111111111111111111111111111111111 1)",
67 | "111111111111111111111111111111111111111111111112", t, env)
68 | checkExprResultTest("(+ \"Hello\" \",\" \"World!\")", "\"Hello,World!\"", t, env)
69 | checkExprResultTest("(- 1 2)", "-1", t, env)
70 | checkExprResultTest("(- 111111111111111111111111111111111111111111111111 1)",
71 | "111111111111111111111111111111111111111111111110", t, env)
72 | checkExprResultTest("(* 1 2)", "2", t, env)
73 | checkExprResultTest("(* 1 2 3 4 5)", "120", t, env)
74 | checkExprResultTest("(* 111111111111111111111111111111111111111111111111 2)",
75 | "222222222222222222222222222222222222222222222222", t, env)
76 | checkExprResultTest("(/ 1 2)", "0", t, env)
77 | checkExprResultTest("(/ 111111111111111111111111111111111111111111111111 1)", "111111111111111111111111111111111111111111111111", t, env)
78 |
79 | checkExprResultTest("(+ 1.1 2.1)", "3.2", t, env)
80 | checkExprResultTest("(- 1.3 2.1)", "-0.8", t, env)
81 | checkExprResultTest("(* 1.3 2)", "2.6", t, env)
82 | checkExprResultTest("(/ 1.3 2)", "0.65", t, env)
83 |
84 | checkExprResultTest("(- 1 (/ 0.5509 0.5698))", "0.033169533169533194", t, env)
85 | checkExprResultTest("(- 1 (/ 6 3))", "-1", t, env)
86 |
87 | // The following checks are for overflows/underflows.
88 | // MaxInt64 = 9223372036854775807
89 | // MinInt64 = -9223372036854775808
90 | checkExprResultTest("(+ 9223372036854775807 1)", "9223372036854775808", t, env)
91 | checkExprResultTest("(+ -9223372036854775808 -1)", "-9223372036854775809", t, env)
92 | checkExprResultTest("(- -9223372036854775808 1)", "-9223372036854775809", t, env)
93 | checkExprResultTest("(- 9223372036854775807 -1)", "9223372036854775808", t, env)
94 | checkExprResultTest("(/ -9223372036854775808 -1)", "9223372036854775808", t, env)
95 | checkExprResultTest("(* -9223372036854775808 -1)", "9223372036854775808", t, env)
96 | checkExprResultTest("(* 9223372036854775807 2)", "18446744073709551614", t, env)
97 |
98 | checkExprResultTest("(defvar x 2.0)", "2", t, env)
99 | checkExprResultTest("(+ x 2.0)", "4", t, env)
100 | checkExprResultTest("(defvar y 1.9)", "1.9", t, env)
101 | checkExprResultTest("(* x y)", "3.8", t, env)
102 | checkExprResultTest("(defvar i 5.0)", "5", t, env)
103 | checkExprResultTest("(defvar i 6.0)", "6", t, env)
104 | malformedExprTest("(+ i j)", t, env)
105 | checkExprResultTest("(defvar j 3.1)", "3.1", t, env)
106 | checkExprResultTest("(+ i j)", "9.1", t, env)
107 |
108 | checkExprResultTest("(> 3 2)", "true", t, env)
109 | checkExprResultTest("(> 2 3)", "false", t, env)
110 | checkExprResultTest("(< 3 2)", "false", t, env)
111 | checkExprResultTest("(< 2 3)", "true", t, env)
112 | checkExprResultTest("(>= 3 2)", "true", t, env)
113 | checkExprResultTest("(>= 3 3)", "true", t, env)
114 | checkExprResultTest("(<= 3 3)", "true", t, env)
115 | checkExprResultTest("(<= 3 2)", "false", t, env)
116 |
117 | checkExprResultTest("(> \"a\" \"b\")", "false", t, env)
118 | checkExprResultTest("(> \"b\" \"a\")", "true", t, env)
119 | checkExprResultTest("(< \"a\" \"b\")", "true", t, env)
120 | checkExprResultTest("(< \"b\" \"a\")", "false", t, env)
121 |
122 | checkExprResultTest("(and true false)", "false", t, env)
123 | checkExprResultTest("(and true true)", "true", t, env)
124 | checkExprResultTest("(and false false)", "false", t, env)
125 | checkExprResultTest("(and true true true true)", "true", t, env)
126 | checkExprResultTest("(and true true true false)", "false", t, env)
127 |
128 | checkExprResultTest("(or true false)", "true", t, env)
129 | checkExprResultTest("(or true true)", "true", t, env)
130 | checkExprResultTest("(or false false)", "false", t, env)
131 | checkExprResultTest("(or true true true true)", "true", t, env)
132 | checkExprResultTest("(or false false false true)", "true", t, env)
133 |
134 | checkExprResultTest("(cond (true 1) (false 2))", "1", t, env)
135 | checkExprResultTest("(cond (false 1) (true 2))", "2", t, env)
136 | checkExprResultTest("(cond (false 1) (true 2) (false 3))", "2", t, env)
137 | checkExprResultTest("(cond ((> 2 3) 1) ((= 3 3) 2))", "2", t, env)
138 |
139 | malformedExprTest("()", t, env)
140 | malformedExprTest(")(", t, env)
141 | malformedExprTest(")", t, env)
142 | malformedExprTest("(", t, env)
143 | malformedExprTest("]]]", t, env)
144 | malformedExprTest("zephyr", t, env)
145 |
146 | malformedExprTest("(/ 1 0)", t, env)
147 |
148 | malformedExprTest("(cond (1 2))", t, env)
149 | malformedExprTest("(cond (false 1) (false 2))", t, env)
150 |
151 | runRandomSmokeTests(t, env)
152 | }
153 |
154 | func TestMethods(t *testing.T) {
155 | env := new(LangEnv)
156 | env.Init()
157 |
158 | saneExprTest("(defun foo (x) (+ 1 x))", t, env)
159 | checkExprResultTest("(foo 4)", "5", t, env)
160 | // TODO: This should fail
161 | // See https://github.com/reddragon/lambda/issues/10
162 | // malformedExprTest("(foo)", t, env)
163 | malformedExprTest("(foo 4 5)", t, env)
164 |
165 | saneExprTest("(defvar p 1)", t, env)
166 | saneExprTest("(defun hello (p) (p))", t, env)
167 | checkExprResultTest("(hello 3)", "3", t, env)
168 | saneExprTest("(defun fact (x) (cond ((= x 0) 1) (true (* x (fact (- x 1))))))", t, env)
169 | checkExprResultTest("(fact 10)", "3628800", t, env)
170 | saneExprTest("(defun fib (x) (cond ((= x 0) 0) (true (+ x (fib (- x 1))))))", t, env)
171 | checkExprResultTest("(fib 10)", "55", t, env)
172 |
173 | saneExprTest("(defvar varDefinedOutside 10)", t, env)
174 | saneExprTest("(defun add (formalArg) (+ varDefinedOutside formalArg))", t, env)
175 | checkExprResultTest("(add 11)", "21", t, env)
176 |
177 | // Checking that we correctly override previously defined vars of same name
178 | saneExprTest("(defvar formalArg -10)", t, env)
179 | checkExprResultTest("(add 11)", "21", t, env)
180 |
181 | // Check the magic number method.
182 | saneExprTest("(defun magic (x) (cond ((<= x 0) 1) (true (+ (magic (- x 1)) (* 2 (magic (- x 3)))))))", t, env)
183 | checkExprResultTest("(magic -10)", "1", t, env)
184 | checkExprResultTest("(magic 0)", "1", t, env)
185 | checkExprResultTest("(magic 10)", "309", t, env)
186 |
187 | // Check the ability to pass functions as params.
188 | saneExprTest("(defun add-one (x) (+ x 1))", t, env)
189 | saneExprTest("(defun twice (f x) (f (f x)))", t, env)
190 | checkExprResultTest("(twice add-one 2)", "4", t, env)
191 | }
192 |
193 | func checkOfType(value string, expectedType Value, t *testing.T) {
194 | if !expectedType.ofType(value) {
195 | t.Errorf("Expected %s to be of type %s, but was not.", value,
196 | expectedType.getValueType())
197 | }
198 | }
199 |
200 | func checkNotOfType(value string, unexpectedType Value, t *testing.T) {
201 | if unexpectedType.ofType(value) {
202 | t.Errorf("Expected %s to NOT be of type %s, but was.", value,
203 | unexpectedType.getValueType())
204 | }
205 | }
206 |
207 | // Adding some simple type checks.
208 | func TestTypes(t *testing.T) {
209 | checkOfType("'hello'", new(stringValue), t)
210 | checkOfType("\"hello\"", new(stringValue), t)
211 | checkNotOfType("123", new(stringValue), t)
212 | checkNotOfType("1.23", new(stringValue), t)
213 | checkNotOfType("true", new(stringValue), t)
214 | checkNotOfType("false", new(stringValue), t)
215 |
216 | checkOfType("123", new(intValue), t)
217 | checkNotOfType("1.23", new(intValue), t)
218 | checkNotOfType("foobar", new(intValue), t)
219 | checkNotOfType("true", new(intValue), t)
220 | checkNotOfType("false", new(intValue), t)
221 |
222 | // 123 is a valid float value too. We just would prefer it to be an int.
223 | checkOfType("123", new(floatValue), t)
224 | checkOfType("1.23", new(floatValue), t)
225 | checkNotOfType("true", new(floatValue), t)
226 |
227 | checkOfType("true", new(boolValue), t)
228 | checkOfType("false", new(boolValue), t)
229 | checkNotOfType("123", new(boolValue), t)
230 | checkNotOfType("1.23", new(boolValue), t)
231 |
232 | checkOfType("123", new(bigIntValue), t)
233 | checkOfType("123456789012345789", new(bigIntValue), t)
234 | checkNotOfType("1.23", new(bigIntValue), t)
235 | checkNotOfType("foobar", new(bigIntValue), t)
236 | checkNotOfType("deadbeef", new(bigIntValue), t)
237 | checkNotOfType("true", new(bigIntValue), t)
238 | checkNotOfType("false", new(bigIntValue), t)
239 | }
240 |
241 | func checkTypeInitUsingStrMatches(vType Value, targetStr string, t *testing.T) {
242 | actualStr := vType.newValue(targetStr).Str()
243 | if actualStr != targetStr {
244 | t.Errorf("Expected %s to be correctly represented in type %s, but was %s.",
245 | targetStr, vType.getValueType(), actualStr)
246 | }
247 | }
248 |
249 | func TestValueInitializations(t *testing.T) {
250 | // This test checks if the value we used to initialize certain types, is
251 | // actually getting set.
252 | // Basically, what we can check is,
253 | // newType.newValue(targetStr).Str() == targetStr
254 |
255 | var bv bigIntValue
256 | checkTypeInitUsingStrMatches(bv, "1234567891234512345678912345123456789", t)
257 | checkTypeInitUsingStrMatches(bv, "-1234567891234512345678912345123456789", t)
258 |
259 | var iv intValue
260 | checkTypeInitUsingStrMatches(iv, "12345678912345", t)
261 | checkTypeInitUsingStrMatches(iv, "-12345678912345", t)
262 | }
263 |
--------------------------------------------------------------------------------
/lang/operator.go:
--------------------------------------------------------------------------------
1 | package lang
2 |
3 | import (
4 | "bytes"
5 | "errors"
6 | "fmt"
7 | "math"
8 | "math/big"
9 | "strings"
10 | )
11 |
12 | type Operator struct {
13 | symbol string
14 | minArgCount int
15 | maxArgCount int
16 | doNotResolveVars bool
17 | passRawAST bool
18 | handler (func(*LangEnv, []Atom) Atom)
19 | }
20 |
21 | const (
22 | // Operators
23 | add string = "+"
24 | sub string = "-"
25 | mul string = "*"
26 | div string = "/"
27 | def string = "defvar"
28 | eq string = "="
29 | gt string = ">"
30 | geq string = ">="
31 | lt string = "<"
32 | leq string = "<="
33 | and string = "and"
34 | or string = "or"
35 | defun string = "defun"
36 | cond string = "cond"
37 | )
38 |
39 | func addOperator(opMap map[string]*Operator, op *Operator) {
40 | opMap[op.symbol] = op
41 | }
42 |
43 | func addBuiltinOperators(opMap map[string]*Operator) {
44 | numValPrecedenceMap := map[valueType]int{intType: 1, bigIntType: 2, floatType: 3}
45 | strValPrecedenceMap := map[valueType]int{stringType: 1}
46 | boolValPrecedenceMap := map[valueType]int{boolType: 1}
47 |
48 | addOperator(opMap,
49 | &Operator{
50 | symbol: add,
51 | minArgCount: 2,
52 | maxArgCount: 100,
53 | handler: func(env *LangEnv, operands []Atom) Atom {
54 | var retVal Atom
55 | var finalType valueType
56 | finalType, retVal.Err = chainedTypeCoerce(add, &operands, []map[valueType]int{numValPrecedenceMap, strValPrecedenceMap})
57 | if retVal.Err != nil {
58 | return retVal
59 | }
60 |
61 | performOp:
62 | switch finalType {
63 | case intType:
64 | var finalVal intValue
65 | finalVal.value = 0
66 | for _, o := range operands {
67 | v, ok := o.Val.(intValue)
68 | if ok {
69 | // Check for overflow/underflow here.
70 | if (v.value > 0 && (finalVal.value > math.MaxInt64-v.value)) ||
71 | (v.value <= 0 && finalVal.value < math.MinInt64-v.value) {
72 | // There will be an overflow, so better cast to bigIntType here.
73 | err := tryTypeCastTo(&operands, bigIntType)
74 | if err != nil {
75 | fmt.Printf("Problem while avoiding overflow in operand %s: %s.\n", add, err)
76 | } else {
77 | finalType = bigIntType
78 | goto performOp
79 | }
80 | }
81 |
82 | finalVal.value = finalVal.value + v.value
83 | } else {
84 | fmt.Errorf("Error while converting %s to intValue\n", o.Val.Str())
85 | }
86 | }
87 | retVal.Val = finalVal
88 | break
89 |
90 | case bigIntType:
91 | var finalVal bigIntValue
92 | finalVal.value = new(big.Int)
93 | for _, o := range operands {
94 | v, ok := o.Val.(bigIntValue)
95 | if ok {
96 | finalVal.value = finalVal.value.Add(finalVal.value, v.value)
97 | } else {
98 | fmt.Errorf("Error while converting %s to bigIntValue\n", o.Val.Str())
99 | }
100 | }
101 | retVal.Val = finalVal
102 | break
103 |
104 | case floatType:
105 | var finalVal floatValue
106 | finalVal.value = 0
107 | for _, o := range operands {
108 | v, ok := o.Val.(floatValue)
109 | if ok {
110 | finalVal.value = finalVal.value + v.value
111 | } else {
112 | fmt.Errorf("Error while converting %s to floatValue\n", o.Val.Str())
113 | }
114 | }
115 | retVal.Val = finalVal
116 | break
117 |
118 | case stringType:
119 | var buffer bytes.Buffer
120 | var finalVal stringValue
121 | for _, o := range operands {
122 | v, ok := o.Val.(stringValue)
123 | if ok {
124 | buffer.WriteString(strings.Split(v.value, "\"")[1])
125 | }
126 | }
127 |
128 | retVal.Val = finalVal.newValue(fmt.Sprintf("\"%s\"", buffer.String()))
129 | break
130 | }
131 | return retVal
132 | },
133 | },
134 | )
135 |
136 | addOperator(opMap,
137 | &Operator{
138 | symbol: sub,
139 | minArgCount: 2,
140 | maxArgCount: 2,
141 | handler: func(env *LangEnv, operands []Atom) Atom {
142 | var retVal Atom
143 | var finalType valueType
144 |
145 | finalType, retVal.Err = typeCoerce(sub, &operands, numValPrecedenceMap)
146 | if retVal.Err != nil {
147 | return retVal
148 | }
149 |
150 | performOp:
151 | switch finalType {
152 | case intType:
153 | var finalVal intValue
154 | var val1, val2 intValue
155 | finalVal.value = 0
156 |
157 | var ok bool
158 | val1, ok = operands[0].Val.(intValue)
159 | if !ok {
160 | fmt.Errorf("Error while converting %s to intValue\n", operands[0].Val.Str())
161 | }
162 |
163 | val2, ok = operands[1].Val.(intValue)
164 | if !ok {
165 | fmt.Errorf("Error while converting %s to intValue\n", operands[1].Val.Str())
166 | }
167 |
168 | // Check for overflow/underflow here.
169 | if (val2.value > 0 && val1.value < math.MinInt64+val2.value) || (val2.value <= 0 && val1.value > math.MaxInt64+val2.value) {
170 | err := tryTypeCastTo(&operands, bigIntType)
171 | if err != nil {
172 | fmt.Printf("Problem while avoiding overflow in operand %s: %s.\n", add, err)
173 | } else {
174 | finalType = bigIntType
175 | goto performOp
176 | }
177 | }
178 |
179 | finalVal.value = val1.value - val2.value
180 | retVal.Val = finalVal
181 | break
182 |
183 | case bigIntType:
184 | var finalVal bigIntValue
185 | var val1, val2 bigIntValue
186 | finalVal.value = new(big.Int)
187 |
188 | var ok bool
189 | val1, ok = operands[0].Val.(bigIntValue)
190 | if !ok {
191 | fmt.Errorf("Error while converting %s to bigIntValue\n", operands[0].Val.Str())
192 | }
193 |
194 | val2, ok = operands[1].Val.(bigIntValue)
195 | if !ok {
196 | fmt.Errorf("Error while converting %s to bigIntValue\n", operands[1].Val.Str())
197 | }
198 | finalVal.value.Sub(val1.value, val2.value)
199 | retVal.Val = finalVal
200 | break
201 |
202 | case floatType:
203 | var finalVal floatValue
204 | var val1, val2 floatValue
205 | finalVal.value = 0
206 |
207 | var ok bool
208 | val1, ok = operands[0].Val.(floatValue)
209 | if !ok {
210 | fmt.Errorf("Error while converting %s to floatValue\n", operands[0].Val.Str())
211 | }
212 |
213 | val2, ok = operands[1].Val.(floatValue)
214 | if !ok {
215 | fmt.Printf("It was not ok!, type: %s, rawtype: %T\n",
216 | operands[1].Val.getValueType(), operands[1].Val)
217 | fmt.Errorf("Error while converting %s to floatValue\n", operands[1].Val.Str())
218 | }
219 |
220 | finalVal.value = val1.value - val2.value
221 | retVal.Val = finalVal
222 | break
223 | }
224 | return retVal
225 | },
226 | },
227 | )
228 |
229 | addOperator(opMap,
230 | &Operator{
231 | symbol: mul,
232 | minArgCount: 2,
233 | maxArgCount: 100,
234 | handler: func(env *LangEnv, operands []Atom) Atom {
235 | var retVal Atom
236 | var finalType valueType
237 | finalType, retVal.Err = typeCoerce(mul, &operands, numValPrecedenceMap)
238 | if retVal.Err != nil {
239 | return retVal
240 | }
241 |
242 | performOp:
243 | switch finalType {
244 | case intType:
245 | var finalVal intValue
246 | finalVal.value = 1
247 |
248 | bigIntOp1 := new(big.Int)
249 | bigIntOp2 := new(big.Int)
250 | for _, o := range operands {
251 | v, ok := o.Val.(intValue)
252 | if ok {
253 | // Check for overflow/underflow here.
254 | // The check for multiply turns out to be quite complex.
255 | // Refer to this document for the suggested implementation:
256 | // https://www.securecoding.cert.org/confluence/display/java/NUM00-J.+Detect+or+prevent+integer+overflow
257 | // In order to preserve readability, I would just check if
258 | // big.Int's Mul method returns the same value.
259 | // This would obviously make multiplications a little slow.
260 | bigIntOp1.SetInt64(finalVal.value)
261 | bigIntOp2.SetInt64(v.value)
262 | bigIntOp1.Mul(bigIntOp1, bigIntOp2)
263 |
264 | finalVal.value = finalVal.value * v.value
265 | if bigIntOp1.String() != finalVal.Str() {
266 | err := tryTypeCastTo(&operands, bigIntType)
267 | if err != nil {
268 | fmt.Printf("Problem while avoiding overflow in operand %s: %s.\n", add, err)
269 | } else {
270 | finalType = bigIntType
271 | goto performOp
272 | }
273 | }
274 | } else {
275 | fmt.Errorf("Error while converting %s to intValue\n", o.Val.Str())
276 | }
277 | }
278 | retVal.Val = finalVal
279 | break
280 |
281 | case bigIntType:
282 | var finalVal bigIntValue
283 | finalVal.value = new(big.Int)
284 | finalVal.value.SetInt64(1)
285 | for _, o := range operands {
286 | v, ok := o.Val.(bigIntValue)
287 | if ok {
288 | finalVal.value = finalVal.value.Mul(finalVal.value, v.value)
289 | } else {
290 | fmt.Errorf("Error while converting %s to bigIntValue\n", o.Val.Str())
291 | }
292 | }
293 | retVal.Val = finalVal
294 | break
295 |
296 | case floatType:
297 | var finalVal floatValue
298 | finalVal.value = 1
299 | for _, o := range operands {
300 | v, ok := o.Val.(floatValue)
301 | if ok {
302 | finalVal.value = finalVal.value * v.value
303 | } else {
304 | fmt.Errorf("Error while converting %s to floatValue\n", o.Val.Str())
305 | }
306 | }
307 | retVal.Val = finalVal
308 | break
309 | }
310 | return retVal
311 | },
312 | },
313 | )
314 |
315 | addOperator(opMap,
316 | &Operator{
317 | symbol: div,
318 | minArgCount: 2,
319 | maxArgCount: 2,
320 | handler: func(env *LangEnv, operands []Atom) Atom {
321 | var retVal Atom
322 | var finalType valueType
323 | finalType, retVal.Err = typeCoerce(div, &operands, numValPrecedenceMap)
324 | if retVal.Err != nil {
325 | return retVal
326 | }
327 |
328 | performOp:
329 | switch finalType {
330 | case intType:
331 | var finalVal intValue
332 | var val1, val2 intValue
333 | finalVal.value = 1
334 |
335 | var ok bool
336 | val1, ok = operands[0].Val.(intValue)
337 | if !ok {
338 | fmt.Errorf("Error while converting %s to intValue\n", operands[0].Val.Str())
339 | }
340 |
341 | val2, ok = operands[1].Val.(intValue)
342 | if !ok {
343 | fmt.Errorf("Error while converting %s to intValue\n", operands[1].Val.Str())
344 | }
345 | if val2.value != 0 {
346 | // Check for overflow/underflow here.
347 | if val1.value == math.MinInt64 && val2.value == -1 {
348 | err := tryTypeCastTo(&operands, bigIntType)
349 | if err != nil {
350 | fmt.Printf("Problem while avoiding overflow in operand %s: %s.\n", add, err)
351 | } else {
352 | finalType = bigIntType
353 | goto performOp
354 | }
355 | }
356 |
357 | finalVal.value = val1.value / val2.value
358 | retVal.Val = finalVal
359 | } else {
360 | retVal.Err = errors.New(fmt.Sprintf("divide by zero"))
361 | }
362 | break
363 |
364 | case bigIntType:
365 | var finalVal bigIntValue
366 | var val1, val2 bigIntValue
367 | finalVal.value = new(big.Int)
368 | finalVal.value.SetInt64(1)
369 |
370 | var ok bool
371 | val1, ok = operands[0].Val.(bigIntValue)
372 | if !ok {
373 | fmt.Errorf("Error while converting %s to bigIntValue\n", operands[0].Val.Str())
374 | }
375 |
376 | val2, ok = operands[1].Val.(bigIntValue)
377 | if !ok {
378 | fmt.Errorf("Error while converting %s to bigIntValue\n", operands[1].Val.Str())
379 | }
380 | if val2.value.Cmp(new(big.Int).SetInt64(0)) != 0 {
381 | finalVal.value.Div(val1.value, val2.value)
382 | retVal.Val = finalVal
383 | } else {
384 | retVal.Err = errors.New(fmt.Sprintf("divide by zero"))
385 | }
386 | break
387 |
388 | case floatType:
389 | var finalVal floatValue
390 | var val1, val2 floatValue
391 | finalVal.value = 0
392 |
393 | var ok bool
394 | val1, ok = operands[0].Val.(floatValue)
395 | if !ok {
396 | fmt.Printf("Error while converting %s to floatValue\n", operands[0].Val.Str())
397 | }
398 |
399 | val2, ok = operands[1].Val.(floatValue)
400 | if !ok {
401 | fmt.Printf("Error while converting %s to floatValue\n", operands[1].Val.Str())
402 | }
403 | if val2.value != 0 {
404 | finalVal.value = val1.value / val2.value
405 | retVal.Val = finalVal
406 | } else {
407 | retVal.Err = errors.New(fmt.Sprintf("divide by zero"))
408 | }
409 | break
410 | }
411 | return retVal
412 | },
413 | },
414 | )
415 |
416 | addOperator(opMap,
417 | &Operator{
418 | symbol: def,
419 | minArgCount: 2,
420 | maxArgCount: 2,
421 | doNotResolveVars: true,
422 | handler: func(env *LangEnv, operands []Atom) Atom {
423 | var retVal Atom
424 | vtype1 := operands[0].Val.getValueType()
425 | vtype2 := operands[1].Val.getValueType()
426 |
427 | if vtype1 != varType {
428 | retVal.Err = errors.New(fmt.Sprintf("For %s, expected %s to be %s, but was %s", def, operands[0].Val.Str(), varType, vtype1))
429 | return retVal
430 | }
431 |
432 | if vtype2 == varType {
433 | retVal.Err = errors.New(fmt.Sprintf("For %s, expected %s to not be %s, but was.", def, operands[1].Val.Str(), varType))
434 | return retVal
435 | }
436 |
437 | sym := operands[0].Val.Str()
438 | if env.getOperator(sym) != nil {
439 | retVal.Err = errors.New(fmt.Sprintf("Cannot use %s as a variable, as it is defined as an operator.", sym))
440 | return retVal
441 | }
442 |
443 | env.varMap[sym] = operands[1].Val
444 | retVal.Val = operands[1].Val
445 | return retVal
446 | },
447 | },
448 | )
449 |
450 | addOperator(opMap,
451 | &Operator{
452 | symbol: eq,
453 | minArgCount: 2,
454 | maxArgCount: 2,
455 | handler: func(env *LangEnv, operands []Atom) Atom {
456 | var retVal Atom
457 | vtype1 := operands[0].Val.getValueType()
458 | vtype2 := operands[1].Val.getValueType()
459 |
460 | if vtype1 != vtype2 {
461 | retVal.Err = errors.New(fmt.Sprintf("Cannot use %s operator for two different types %s and %s", eq, vtype1, vtype2))
462 | return retVal
463 | }
464 |
465 | retVal.Val = newBoolValue(operands[0].Val.Str() == operands[1].Val.Str())
466 | return retVal
467 | },
468 | },
469 | )
470 |
471 | addOperator(opMap,
472 | &Operator{
473 | symbol: gt,
474 | minArgCount: 2,
475 | maxArgCount: 2,
476 | handler: func(env *LangEnv, operands []Atom) Atom {
477 | var retVal Atom
478 | var finalType valueType
479 | finalType, retVal.Err = chainedTypeCoerce(gt, &operands, []map[valueType]int{numValPrecedenceMap, strValPrecedenceMap})
480 | if retVal.Err != nil {
481 | return retVal
482 | }
483 |
484 | switch finalType {
485 | case intType:
486 | var val1, val2 intValue
487 | val1, _ = operands[0].Val.(intValue)
488 | val2, _ = operands[1].Val.(intValue)
489 | retVal.Val = newBoolValue(val1.value > val2.value)
490 | break
491 |
492 | case floatType:
493 | var val1, val2 floatValue
494 | val1, _ = operands[0].Val.(floatValue)
495 | val2, _ = operands[1].Val.(floatValue)
496 | retVal.Val = newBoolValue(val1.value > val2.value)
497 | break
498 |
499 | case stringType:
500 | var val1, val2 stringValue
501 | val1, _ = operands[0].Val.(stringValue)
502 | val2, _ = operands[1].Val.(stringValue)
503 | retVal.Val = newBoolValue(val1.value > val2.value)
504 | break
505 | }
506 | return retVal
507 | },
508 | },
509 | )
510 |
511 | addOperator(opMap,
512 | &Operator{
513 | symbol: geq,
514 | minArgCount: 2,
515 | maxArgCount: 2,
516 | handler: func(env *LangEnv, operands []Atom) Atom {
517 | var retVal Atom
518 | var finalType valueType
519 | finalType, retVal.Err = chainedTypeCoerce(gt, &operands, []map[valueType]int{numValPrecedenceMap, strValPrecedenceMap})
520 | if retVal.Err != nil {
521 | return retVal
522 | }
523 |
524 | switch finalType {
525 | case intType:
526 | var val1, val2 intValue
527 | val1, _ = operands[0].Val.(intValue)
528 | val2, _ = operands[1].Val.(intValue)
529 | retVal.Val = newBoolValue(val1.value >= val2.value)
530 | break
531 |
532 | case floatType:
533 | var val1, val2 floatValue
534 | val1, _ = operands[0].Val.(floatValue)
535 | val2, _ = operands[1].Val.(floatValue)
536 | retVal.Val = newBoolValue(val1.value >= val2.value)
537 | break
538 |
539 | case stringType:
540 | var val1, val2 stringValue
541 | val1, _ = operands[0].Val.(stringValue)
542 | val2, _ = operands[1].Val.(stringValue)
543 | retVal.Val = newBoolValue(val1.value >= val2.value)
544 | break
545 | }
546 | return retVal
547 | },
548 | },
549 | )
550 |
551 | addOperator(opMap,
552 | &Operator{
553 | symbol: lt,
554 | minArgCount: 2,
555 | maxArgCount: 2,
556 | handler: func(env *LangEnv, operands []Atom) Atom {
557 | var retVal Atom
558 | var finalType valueType
559 | finalType, retVal.Err = chainedTypeCoerce(gt, &operands, []map[valueType]int{numValPrecedenceMap, strValPrecedenceMap})
560 | if retVal.Err != nil {
561 | return retVal
562 | }
563 |
564 | switch finalType {
565 | case intType:
566 | var val1, val2 intValue
567 | val1, _ = operands[0].Val.(intValue)
568 | val2, _ = operands[1].Val.(intValue)
569 | retVal.Val = newBoolValue(val1.value < val2.value)
570 | break
571 |
572 | case floatType:
573 | var val1, val2 floatValue
574 | val1, _ = operands[0].Val.(floatValue)
575 | val2, _ = operands[1].Val.(floatValue)
576 | retVal.Val = newBoolValue(val1.value < val2.value)
577 | break
578 |
579 | case stringType:
580 | var val1, val2 stringValue
581 | val1, _ = operands[0].Val.(stringValue)
582 | val2, _ = operands[1].Val.(stringValue)
583 | retVal.Val = newBoolValue(val1.value < val2.value)
584 | break
585 | }
586 | return retVal
587 | },
588 | },
589 | )
590 |
591 | addOperator(opMap,
592 | &Operator{
593 | symbol: leq,
594 | minArgCount: 2,
595 | maxArgCount: 2,
596 | handler: func(env *LangEnv, operands []Atom) Atom {
597 | var retVal Atom
598 | var finalType valueType
599 | finalType, retVal.Err = chainedTypeCoerce(gt, &operands, []map[valueType]int{numValPrecedenceMap, strValPrecedenceMap})
600 | if retVal.Err != nil {
601 | return retVal
602 | }
603 |
604 | switch finalType {
605 | case intType:
606 | var val1, val2 intValue
607 | val1, _ = operands[0].Val.(intValue)
608 | val2, _ = operands[1].Val.(intValue)
609 | retVal.Val = newBoolValue(val1.value <= val2.value)
610 | break
611 |
612 | case floatType:
613 | var val1, val2 floatValue
614 | val1, _ = operands[0].Val.(floatValue)
615 | val2, _ = operands[1].Val.(floatValue)
616 | retVal.Val = newBoolValue(val1.value <= val2.value)
617 | break
618 |
619 | case stringType:
620 | var val1, val2 stringValue
621 | val1, _ = operands[0].Val.(stringValue)
622 | val2, _ = operands[1].Val.(stringValue)
623 | retVal.Val = newBoolValue(val1.value <= val2.value)
624 | break
625 | }
626 | return retVal
627 | },
628 | },
629 | )
630 |
631 | addOperator(opMap,
632 | &Operator{
633 | symbol: defun,
634 | minArgCount: 3,
635 | maxArgCount: 3,
636 | passRawAST: true,
637 | handler: func(env *LangEnv, operands []Atom) Atom {
638 | var retVal Atom
639 | astVal, ok := operands[0].Val.(astValue)
640 | if !ok {
641 | retVal.Err = errors.New(fmt.Sprintf("operand[0] has to be astValue"))
642 | return retVal
643 | }
644 | // Check astNode[0] is of varType, and is not registered in varMap
645 | if !astVal.astNodes[0].isValue {
646 | retVal.Err = errors.New(fmt.Sprintf("Method name not defined correctly."))
647 | return retVal
648 | }
649 | methodNameVal, err := getValue(env, astVal.astNodes[0].value)
650 | if err != nil || methodNameVal.getValueType() != varType {
651 | retVal.Err = errors.New(fmt.Sprintf("Expecting method name, got %s", astVal.astNodes[0].value))
652 | return retVal
653 | }
654 |
655 | methodName := methodNameVal.Str()
656 | if _, ok := env.varMap[methodNameVal.Str()]; ok {
657 | retVal.Err = errors.New(fmt.Sprintf("Method %s already defined as a variable", methodName))
658 | return retVal
659 | }
660 |
661 | if _, ok := env.opMap[methodNameVal.Str()]; ok {
662 | retVal.Err = errors.New(fmt.Sprintf("Method %s already defined as an operator", methodName))
663 | return retVal
664 | }
665 |
666 | if astVal.astNodes[1].isValue {
667 | retVal.Err = errors.New(fmt.Sprintf("Missing list of parameters for method %s", methodName))
668 | return retVal
669 | }
670 |
671 | // Check astNode[1] is a list of varTypes.
672 | params := make([]string, 0)
673 | for i, node := range astVal.astNodes[1].children {
674 | if !node.isValue {
675 | retVal.Err = errors.New(fmt.Sprintf("Malformed parameter %d in method %s.", i, methodName))
676 | return retVal
677 | }
678 | paramName := node.value
679 | val, err := getValue(env, paramName)
680 | if err != nil || val.getValueType() != varType {
681 | retVal.Err = errors.New(fmt.Sprintf("Malformed parameter %s in method %s.", paramName, methodName))
682 | return retVal
683 | }
684 | params = append(params, paramName)
685 | }
686 |
687 | addOperator(opMap,
688 | &Operator{
689 | symbol: methodName,
690 | minArgCount: len(params),
691 | maxArgCount: len(params),
692 | handler: func(env *LangEnv, operands []Atom) Atom {
693 | var retVal Atom
694 | maxRecursionLimit := 100000
695 | newEnv := NewEnv()
696 |
697 | // Copy all the operators of the parent env.
698 | newEnv.opMap = make(map[string]*Operator, 0)
699 | for k, v := range env.opMap {
700 | newEnv.opMap[k] = v
701 | }
702 |
703 | // Copy all the variable values of the parent env.
704 | // We will favor formal arguments over previously defined variables.
705 | newEnv.varMap = make(map[string]Value, 0)
706 | for k, v := range env.varMap {
707 | newEnv.varMap[k] = v
708 | }
709 |
710 | // fmt.Printf("Executing the method %s with values: \n", methodName)
711 | for i, p := range params {
712 | // Check here whether operands[i] is a variable / operator.
713 | if op, ok := opMap[operands[i].Val.Str()]; ok {
714 | newEnv.opMap[p] = op
715 | // fmt.Printf("%s = %s (op)\n", p, operands[i].Val.Str())
716 | } else {
717 | newEnv.varMap[p] = operands[i].Val
718 | // fmt.Printf("%s = %s (op)\n", p, operands[i].Val.Str())
719 | }
720 | }
721 |
722 | // fmt.Printf(", d: %d\n", env.recursionDepth)
723 | newEnv.recursionDepth = env.recursionDepth + 1
724 | if newEnv.recursionDepth > maxRecursionLimit {
725 | retVal.Err = errors.New(fmt.Sprintf("Reached the recursion limit of %d. Terminating.", maxRecursionLimit))
726 | return retVal
727 | }
728 |
729 | // fmt.Printf("AST structure: %s\n", getASTStr(astVal.astNodes[2]))
730 | retVal = evalASTHelper(newEnv, astVal.astNodes[2])
731 | // fmt.Printf("evalASTHelper returned with %s\n", retVal.Val.Str())
732 | return retVal
733 | },
734 | },
735 | )
736 | var val varValue
737 | val.value = fmt.Sprintf("", methodName)
738 | val.varName = methodName
739 | retVal.Val = val
740 | return retVal
741 | },
742 | },
743 | )
744 |
745 | addOperator(opMap,
746 | &Operator{
747 | symbol: and,
748 | minArgCount: 2,
749 | maxArgCount: 100,
750 | handler: func(env *LangEnv, operands []Atom) Atom {
751 | var retVal Atom
752 | _, retVal.Err = typeCoerce(and, &operands, boolValPrecedenceMap)
753 | if retVal.Err != nil {
754 | return retVal
755 | }
756 |
757 | result := true
758 | for _, o := range operands {
759 | v, ok := o.Val.(boolValue)
760 | if ok {
761 | result = result && v.value
762 | }
763 | }
764 | retVal.Val = newBoolValue(result)
765 | return retVal
766 | },
767 | },
768 | )
769 |
770 | addOperator(opMap,
771 | &Operator{
772 | symbol: or,
773 | minArgCount: 2,
774 | maxArgCount: 100,
775 | handler: func(env *LangEnv, operands []Atom) Atom {
776 | var retVal Atom
777 | _, retVal.Err = typeCoerce(or, &operands, boolValPrecedenceMap)
778 | if retVal.Err != nil {
779 | return retVal
780 | }
781 |
782 | result := false
783 | for _, o := range operands {
784 | v, ok := o.Val.(boolValue)
785 | if ok {
786 | result = result || v.value
787 | }
788 | }
789 | retVal.Val = newBoolValue(result)
790 | return retVal
791 | },
792 | },
793 | )
794 |
795 | addOperator(opMap,
796 | &Operator{
797 | symbol: cond,
798 | minArgCount: 1,
799 | maxArgCount: 100,
800 | passRawAST: true,
801 | handler: func(env *LangEnv, operands []Atom) Atom {
802 | var retVal Atom
803 | astNodeVal, _ := operands[0].Val.(astValue)
804 | for i, astNode := range astNodeVal.astNodes {
805 | if len(astNode.children) != 2 {
806 | retVal.Err = errors.New(fmt.Sprintf(
807 | "Arguments for %s should be of the format `(condition value)`.",
808 | cond))
809 | return retVal
810 | }
811 | condValue := evalAST(env, astNode.children[0])
812 | if condValue.Err != nil {
813 | return condValue
814 | }
815 | if condValue.Val.getValueType() != boolType {
816 | retVal.Err = errors.New(fmt.Sprintf(
817 | "Arguments for operand %d for %s was of type %s instead of %s.",
818 | i+1, cond, condValue.Val.getValueType(), boolType))
819 | }
820 | condBoolValue, _ := condValue.Val.(boolValue)
821 | if condBoolValue.value {
822 | return evalAST(env, astNode.children[1])
823 | }
824 |
825 | retVal.Err = errors.New(fmt.Sprintf(
826 | "None of the arguments for %s evaluated to true.",
827 | cond))
828 | }
829 | return retVal
830 | },
831 | },
832 | )
833 | }
834 |
--------------------------------------------------------------------------------