├── .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 | --------------------------------------------------------------------------------