├── .gitignore ├── .vscode └── launch.json ├── LICENSE ├── README.md ├── ast.go ├── errors.go ├── interpreter.go ├── interpreter_test.go ├── lexer.go ├── lexer_test.go ├── main.go ├── parser.go ├── parser_test.go ├── run.sh └── stack.go /.gitignore: -------------------------------------------------------------------------------- 1 | # Binaries for programs and plugins 2 | *.exe 3 | *.exe~ 4 | *.dll 5 | *.so 6 | *.dylib 7 | 8 | # Test binary, build with `go test -c` 9 | *.test 10 | 11 | # Output of the go coverage tool, specifically when used with LiteIDE 12 | *.out 13 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "Launch", 6 | "type": "go", 7 | "request": "launch", 8 | "mode": "auto", 9 | "program": "${fileDirname}", 10 | "env": {}, 11 | "args": [] 12 | } 13 | ] 14 | } -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # go-simple-expression-eval 2 | Simple expression evaluator in golang for learning lexers, parsers, ast's and interpreters. 3 | 4 | This project doesn't support unary operators, so expression "-1+2" will report us "Missing operand". 5 | For the unary operator support a modified version of Shunting-yard algorithm is needed ([See this project](https://github.com/MacTee/Shunting-Yard-Algorithm/blob/master/ShuntingYard/InfixToPostfixConverter.cs)) 6 | 7 | # Running 8 | **go build** 9 | **./go-simple-expression-eval "1+2+3*5-(5-8)"** 10 | 11 | // quote your expression as it might contain shell operator: 12 | 13 | # Program flow 14 | 1. Tokenize (Lexer) 15 | 2. Convert **infix form** to **postfix form** using Shunting-yard algorithm (Parser) 16 | 3. Construct **Abstract syntax tree** - AST (Parser) 17 | 4. Evaluate AST (Interpreter) 18 | 19 | # Lexer 20 | _lexer.go : Lex()_ 21 | 22 | Purpose of lexer is to turn input into sequence of tokens (lex items) and determine their type while thrashing whitespaces and reporting unrecognized characters. 23 | 24 | Lexer on its own doen't know grammar of parsed language so it does't report syntactic errors or semantic errors. 25 | 26 | I heavily recommend watching [Youtube - Lexical Scanning in Go - Rob Pike](https://www.youtube.com/watch?v=HxaD_trXwRE) as I use these principles. 27 | 28 | Given well-formed expression input: "1+2+3", lexer correctly produces sequence "1", "+", "2", "+", "3". 29 | 30 | Given not well-formed expression, such as "1)+3(((" lexer **doesn't report any errors**, but produces sequence "1", ")", "+", "3", "(", "(", "(". Errors will be reported in parsing phase not in lexing phase. 31 | 32 | Gived input with unrecognized characters "*12+死+3" lexer produces error: 33 | 34 | **Lexing error at 4: Invalid symbol: '死'** 35 | 36 | Our lexer also determines type and position of lexed items, which will come handy later. 37 | Given expression "1+1*(5-5)" lexer produces: 38 | 39 | Type: INUMBER, Val: "1", Pos: 1 40 | Type: IADD, Val: "+", Pos: 2 41 | Type: INUMBER, Val: "1", Pos: 3 42 | Type: IMUL, Val: "*", Pos: 4 43 | Type: ILPAR, Val: "(", Pos: 5 44 | Type: INUMBER, Val: "5", Pos: 6 45 | Type: ISUB, Val: "-", Pos: 7 46 | Type: INUMBER, Val: "5", Pos: 8 47 | Type: IRPAR, Val: ")", Pos: 9 48 | Type: EOF, Val: "", Pos: 9 49 | 50 | 51 | 52 | # Form conversion 53 | _parser.go : toPostfix()_ 54 | 55 | The classical format of expressions "<operand> <operator> <operand>" is called **infix form**. This form is somewhat troublesome when constructing AST so first we will convert **Infox form** to **Postfix form** using Shunting yard algorithm. Postfix form uses rather weird syntax "<operand> <operand> <operator>". The advantage is that we get rid of the need for parentheses (those are required in infix form). 56 | 57 | This algorithm goes trough out lexer tokens and using **stack data structure** and **list** converts the form. Stack is used as stacked operator store and list for postfix output. 58 | 59 | The algorithm is following: 60 | 1. If current token is number (INUMBER) then put it to output 61 | 2. If current token is left parenthesis then Push it to stack 62 | 3. If current token is right parenthesis then Pop stack until we hit left parenthesis and Pop it out 63 | 4. If current token is operator +,-,*,/ Pop the stack until we find operator with higher or equal precedence (see below) and Push current operator 64 | 5. When we go trough all tokens, Pop all items from stack to output 65 | 66 | **Operator precedence:** 67 | Add and Subtract = 1 68 | Multiply and Divide = 2 69 | 70 | For more about Postfix form see: 71 | [Wikipedia - Reverse Polish notation](https://en.wikipedia.org/wiki/Reverse_Polish_notation) 72 | [Expression binary trees and forms](http://www.cim.mcgill.ca/~langer/250/19-binarytrees-slides.pdf) 73 | 74 | # Constructing Abstract syntax tree - AST 75 | _parser.go : constructAst()_ 76 | 77 | AST for expressions is a simple binary tree. Once we have our expression in postfix form, creating AST is trivial. For construction we will go trough our postfix formed lex items and using stack we build a tree. 78 | 79 | In the tree we distinguish types of nodes we will use later in interpretation. Nodes containing operator (+-*/) hold no value, just left ,right operand children. Nodes containing numbers have no children (leaves). 80 | 81 | Algorithm for AST from postfix form is following: 82 | Go trough all postfix items 83 | 1. If current item is number, create node with this value and Push it to stack. 84 | 2. If its operator, create node for it, then Pop the stack and put it to right child, Pop stack again and put it to left child (order is important). Then push new operator node to stack. 85 | 86 | When there are no more postfix items, Pop the stack last time, this is your AST root node. 87 | 88 | # Interpreting (Evaluating AST) 89 | _interpreter.go : Interpret(), postOrderTraversal()_ 90 | 91 | 92 | Once we have AST the interpreting is childs play. All we need to do is do a recursive **Post order traversal** of our AST binary tree and execute corresponding operation. 93 | 94 | Post order traversal interpretation algorithm: 95 | 1. Start at root 96 | 2. If current node is leaf node return its value 97 | 3. Recurse to left child and store return value 98 | 4. Recurse to right child and store return value 99 | 5. Perform arithmetic operation on stored left and right values 100 | 6. Return result of operation 101 | 102 | Result of traversal is calculated value. 103 | 104 | # Sources 105 | [Youtube - Lexical Scanning in Go - Rob Pike](https://www.youtube.com/watch?v=HxaD_trXwRE) 106 | [Lexer theory](http://www.cse.chalmers.se/edu/year/2010/course/TIN321/lectures/proglang-04.html) 107 | [Binary and expression trees](http://www.cim.mcgill.ca/~langer/250/19-binarytrees-slides.pdf) 108 | [Minimal expression evaluator in Go](https://rosettacode.org/wiki/Arithmetic_Evaluator/Go) 109 | [More complex lexer for template engine](https://golang.org/src/text/template/parse/lex.go) 110 | [AST creation](https://softwareengineering.stackexchange.com/questions/254074/how-exactly-is-an-abstract-syntax-tree-created) 111 | 112 | 113 | 114 | 115 | 116 | -------------------------------------------------------------------------------- /ast.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | type AstNodeType int 6 | 7 | const ( 8 | ASTNODE_LEAF AstNodeType = iota 9 | ASTNODE_ADD 10 | ASTNODE_SUB 11 | ASTNODE_MUL 12 | ASTNODE_DIV 13 | ) 14 | 15 | func (t AstNodeType) String() string { 16 | switch t { 17 | case ASTNODE_LEAF: 18 | return "ASTNODE_LEAF" 19 | case ASTNODE_ADD: 20 | return "ASTNODE_ADD" 21 | case ASTNODE_SUB: 22 | return "ASTNODE_SUB" 23 | case ASTNODE_MUL: 24 | return "ASTNODE_MUL" 25 | case ASTNODE_DIV: 26 | return "ASTNODE_DIV" 27 | default: 28 | return "Unknown" 29 | } 30 | } 31 | 32 | type AstNode struct { 33 | Typ AstNodeType 34 | Value *string 35 | 36 | Left *AstNode 37 | Right *AstNode 38 | } 39 | 40 | func (node AstNode) String() string { 41 | printableVal := "" 42 | 43 | if node.Value != nil { 44 | printableVal = *node.Value 45 | } 46 | 47 | return fmt.Sprintf("AstNode: %s -> %s", node.Typ, printableVal) 48 | } 49 | 50 | func NewAstNode(typ AstNodeType, value *string) *AstNode { 51 | return &AstNode{ 52 | Typ: typ, 53 | Value: value, 54 | } 55 | } 56 | -------------------------------------------------------------------------------- /errors.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import "fmt" 4 | 5 | // EvalErrorType error code type 6 | type EvalErrorType int 7 | 8 | const ( 9 | // ErrParser error code raised by parser 10 | ErrParser EvalErrorType = iota 11 | 12 | // ErrLexer error code raised by lexer 13 | ErrLexer 14 | 15 | // ErrInterpreter error code raised by interpreter 16 | ErrInterpreter 17 | ) 18 | 19 | func (err EvalErrorType) String() string { 20 | switch err { 21 | case ErrParser: 22 | return "Parser error" 23 | case ErrLexer: 24 | return "Lexer error" 25 | case ErrInterpreter: 26 | return "Interpreter error" 27 | default: 28 | return "UNDEFINED" 29 | } 30 | } 31 | 32 | // EvalError represents error during lexing, parsing or interpreting 33 | type EvalError struct { 34 | // message 35 | s string 36 | 37 | // type of error 38 | code EvalErrorType 39 | } 40 | 41 | func (err EvalError) String() string { 42 | return fmt.Sprintf("%s: %s", err.code, err.s) 43 | } 44 | 45 | // NewLexerError intantiates new Lexer error 46 | func NewLexerError(msg string, args ...interface{}) *EvalError { 47 | return &EvalError{ 48 | s: fmt.Sprintf(msg, args...), 49 | code: ErrLexer, 50 | } 51 | } 52 | 53 | // NewParserError intantiates new Parser error 54 | func NewParserError(msg string, args ...interface{}) *EvalError { 55 | return &EvalError{ 56 | s: fmt.Sprintf(msg, args...), 57 | code: ErrParser, 58 | } 59 | } 60 | 61 | // NewInterpreterError intantiates new Interpreter error 62 | func NewInterpreterError(msg string, args ...interface{}) *EvalError { 63 | return &EvalError{ 64 | s: fmt.Sprintf(msg, args...), 65 | code: ErrInterpreter, 66 | } 67 | } 68 | -------------------------------------------------------------------------------- /interpreter.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // Implementation of ADD operation 8 | func add(a int, b int) int { 9 | return a + b 10 | } 11 | 12 | // Implementation of SUBTRACT operation 13 | func sub(a int, b int) int { 14 | return a - b 15 | } 16 | 17 | // Implementation of MULTIPLY operation 18 | func mul(a int, b int) int { 19 | return a * b 20 | } 21 | 22 | // Implementation of DIVIDE operation 23 | func div(a int, b int) int { 24 | return a / b 25 | } 26 | 27 | // Type of arithmetic function taking two ints and returning ints 28 | type arithmeticFunc func(int, int) int 29 | 30 | // type for mapping AST node types to corresponding arithmetic operation 31 | type interpretMap map[AstNodeType]arithmeticFunc 32 | 33 | // Recursive post order traversal that evaluates AST 34 | // 1. Visit left 35 | // 2. Visit right 36 | // 3. Visit self 37 | func postOrderTraversal(node *AstNode, functions interpretMap) (int, *EvalError) { 38 | if node == nil { 39 | return 0, NewInterpreterError("Expected evaluatable node, got nil") 40 | } 41 | 42 | // If we are on number node 43 | if node.Typ == ASTNODE_LEAF { 44 | if node.Value == nil { 45 | return 0, NewInterpreterError("Expected value, got nil") 46 | } 47 | 48 | // Parse string val to integer 49 | number, err := strconv.Atoi(*node.Value) 50 | 51 | if err != nil { 52 | return 0, NewInterpreterError("Unable to parse number %s", err) 53 | } 54 | 55 | // return it to higher stack frame (numbers should occur only in leaf nodes) 56 | return number, nil 57 | } 58 | 59 | // pick correct computation function 60 | aritFunc := functions[node.Typ] 61 | 62 | // recursively evaluate left subtree 63 | left, err := postOrderTraversal(node.Left, functions) 64 | 65 | if err != nil { 66 | return 0, err 67 | } 68 | 69 | // recursively evaluate right subtree 70 | right, err := postOrderTraversal(node.Right, functions) 71 | 72 | if err != nil { 73 | return 0, err 74 | } 75 | 76 | // use its value to do computation 77 | return aritFunc(left, right), nil 78 | } 79 | 80 | // Interpret is function that evaluates AST and returns corresponding result 81 | func Interpret(ast *AstNode) (int, *EvalError) { 82 | var astInterpretMap = interpretMap{ 83 | ASTNODE_ADD: add, 84 | ASTNODE_SUB: sub, 85 | ASTNODE_MUL: mul, 86 | ASTNODE_DIV: div, 87 | } 88 | 89 | return postOrderTraversal(ast, astInterpretMap) 90 | } 91 | -------------------------------------------------------------------------------- /interpreter_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestInterpreter(t *testing.T) { 8 | ast, _ := Parse("1+2") 9 | 10 | res, err := Interpret(ast) 11 | 12 | if err != nil { 13 | t.Errorf("err = %s; want nil", err) 14 | } 15 | 16 | if res != 3 { 17 | t.Errorf("res = %d; want %d", res, 3) 18 | } 19 | } 20 | 21 | func TestSingleValue(t *testing.T) { 22 | ast, _ := Parse("1") 23 | 24 | res, err := Interpret(ast) 25 | 26 | if err != nil { 27 | t.Errorf("err = %s; want nil", err) 28 | } 29 | 30 | if res != 1 { 31 | t.Errorf("res = %d; want %d", res, 1) 32 | } 33 | } 34 | 35 | func TestOperator(t *testing.T) { 36 | 37 | ast := NewAstNode(ASTNODE_ADD, nil) 38 | 39 | _, err := Interpret(ast) 40 | 41 | if err == nil { 42 | t.Error("err = nil; want 'Expected evaluatable node, got nil'") 43 | } 44 | } 45 | -------------------------------------------------------------------------------- /lexer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "unicode/utf8" 7 | ) 8 | 9 | // ItemType is type of lexer items 10 | type ItemType int 11 | 12 | func (typ ItemType) String() string { 13 | strType := "UNDEFINED" 14 | 15 | switch typ { 16 | case IERR: 17 | strType = "IERR" 18 | case INUMBER: 19 | strType = "INUMBER" 20 | case ILPAR: 21 | strType = "ILPAR" 22 | case IRPAR: 23 | strType = "IRPAR" 24 | case IADD: 25 | strType = "IADD" 26 | case ISUB: 27 | strType = "ISUB" 28 | case IMUL: 29 | strType = "IMUL" 30 | case IDIV: 31 | strType = "IDIV" 32 | case EOF: 33 | strType = "EOF" 34 | } 35 | return strType 36 | } 37 | 38 | // Item types from lexer items 39 | const ( 40 | // EOF Item type denoting end of input 41 | EOF ItemType = -1 42 | 43 | // lexing error occured 44 | IERR ItemType = iota 45 | 46 | // positive integer 47 | INUMBER 48 | 49 | // left parenthesis 50 | ILPAR 51 | 52 | // right parenthesis 53 | IRPAR 54 | 55 | // plus + symbol 56 | IADD 57 | 58 | // minus - symbol 59 | ISUB 60 | 61 | // multiply * symbol 62 | IMUL 63 | 64 | // divide / symbol 65 | IDIV 66 | ) 67 | 68 | // Tokens understood by lexer 69 | const ( 70 | numbers = "0123456789" 71 | operators = "+-*/" 72 | white = " \n\r\t" 73 | lpar = "(" 74 | rpar = ")" 75 | ) 76 | 77 | // Type for lexing state machine, function returns another function which represents state transition 78 | type stateFn func(*Lexer) stateFn 79 | 80 | // LexItem are items emitted by lexer 81 | type LexItem struct { 82 | Typ ItemType 83 | Pos int 84 | Val string 85 | } 86 | 87 | // Debug stringify lex item 88 | func (li LexItem) String() string { 89 | return fmt.Sprintf("Type: %s, Val: %q, Pos: %d", li.Typ, li.Val, li.Pos) 90 | } 91 | 92 | // Lexer class containing state of lexer 93 | type Lexer struct { 94 | // input text 95 | text string 96 | 97 | // start index (of input text) of currently lexing token 98 | start int 99 | 100 | // current position (of input text) of lexer 101 | Pos int 102 | 103 | // width of last read rune 104 | width int 105 | 106 | // output channel of lexer 107 | items chan LexItem 108 | } 109 | 110 | func (l *Lexer) dumpState() { 111 | fmt.Printf("%#v\n", l) 112 | } 113 | 114 | // Move to next ASCII or UTF-8 character/rune 115 | func (l *Lexer) next() rune { 116 | if l.Pos >= len(l.text) { 117 | l.width = 0 118 | return -1 // EOF 119 | } 120 | 121 | r, w := utf8.DecodeRuneInString(l.text[l.Pos:]) 122 | 123 | l.width = w 124 | l.Pos += w 125 | 126 | return r 127 | } 128 | 129 | // Go back one character/rune 130 | func (l *Lexer) backup() { 131 | l.Pos -= l.width 132 | _, w := utf8.DecodeLastRuneInString(l.text[:l.Pos]) 133 | l.width = w 134 | } 135 | 136 | // Check what rune is next in input 137 | func (l *Lexer) peek() rune { 138 | r := l.next() 139 | l.backup() 140 | return r 141 | } 142 | 143 | // Consume next rune if its one of 'runes' parameter, otherwise no effect 144 | func (l *Lexer) consume(runes string) bool { 145 | if strings.ContainsRune(runes, l.next()) { 146 | return true 147 | } 148 | l.backup() 149 | return false 150 | } 151 | 152 | // Consume all following runes that are one of 'runes' 153 | func (l *Lexer) consumeAll(runes string) { 154 | for l.consume(runes) { 155 | } 156 | } 157 | 158 | // Ignores all un-emitted characters (moves start to current pos) 159 | func (l *Lexer) ignore() { 160 | l.start = l.Pos 161 | l.width = 0 162 | } 163 | 164 | // Emits lexer item to output channel 165 | func (l *Lexer) emit(typ ItemType) { 166 | l.items <- LexItem{ 167 | Typ: typ, 168 | Pos: l.Pos, 169 | Val: l.text[l.start:l.Pos], 170 | } 171 | l.start = l.Pos 172 | } 173 | 174 | // Helper func that emits error item IERR with message 175 | func (l *Lexer) errorf(format string, args ...interface{}) stateFn { 176 | l.items <- LexItem{ 177 | Pos: l.Pos, 178 | Typ: IERR, 179 | Val: fmt.Sprintf(format, args...), 180 | } 181 | return nil 182 | } 183 | 184 | // Starting state of state machine, peeks forward and decides what lexing function should be used 185 | func lexFn(l *Lexer) stateFn { 186 | r := l.peek() 187 | switch { 188 | case r == -1: 189 | l.emit(EOF) 190 | return nil 191 | 192 | case strings.ContainsRune(white, r): 193 | return lexWhite 194 | case strings.ContainsRune(operators, r): 195 | return lexOperator 196 | case strings.ContainsRune(numbers, r): 197 | return lexNumber 198 | case strings.ContainsRune(lpar, r): 199 | return lexLpar 200 | case strings.ContainsRune(rpar, r): 201 | return lexRpar 202 | default: 203 | return l.errorf("Invalid symbol: %q", r) 204 | } 205 | } 206 | 207 | // Lexes operators 208 | func lexOperator(l *Lexer) stateFn { 209 | op := l.next() 210 | switch op { 211 | case '+': 212 | l.emit(IADD) 213 | case '-': 214 | l.emit(ISUB) 215 | case '*': 216 | l.emit(IMUL) 217 | case '/': 218 | l.emit(IDIV) 219 | default: 220 | return l.errorf("lexOperator: inValid operator: %q", op) 221 | } 222 | 223 | return lexFn 224 | } 225 | 226 | // Lexes left parenthesis 227 | func lexLpar(l *Lexer) stateFn { 228 | l.consume(lpar) 229 | l.emit(ILPAR) 230 | return lexFn 231 | } 232 | 233 | // Lexes right parenthesis 234 | func lexRpar(l *Lexer) stateFn { 235 | l.consume(rpar) 236 | l.emit(IRPAR) 237 | return lexFn 238 | } 239 | 240 | // lexes numbers 241 | func lexNumber(l *Lexer) stateFn { 242 | l.consumeAll(numbers) 243 | l.emit(INUMBER) 244 | return lexFn 245 | } 246 | 247 | // Lexes whitespaces and thrashes them (no emitting) 248 | func lexWhite(l *Lexer) stateFn { 249 | l.consumeAll(white) 250 | l.ignore() 251 | return lexFn 252 | } 253 | 254 | // Items method gets channel of lex items 255 | func (l *Lexer) Items() chan LexItem { 256 | return l.items 257 | } 258 | 259 | // Run method will is the core part of this lexer 260 | // It fires off the state machine, starting with lexFn and takes return function as next state 261 | // once some function returns nil (e.g. EOF), it stops lexing and returns 262 | func (l *Lexer) Run() { 263 | defer close(l.items) 264 | 265 | for fun := lexFn; fun != nil; { 266 | fun = fun(l) 267 | } 268 | 269 | } 270 | 271 | // Lex is constructor for lexer 272 | func Lex(text string) *Lexer { 273 | return &Lexer{ 274 | items: make(chan LexItem), 275 | text: text, 276 | } 277 | } 278 | -------------------------------------------------------------------------------- /lexer_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func runLexing(expr string) (*Lexer, []LexItem) { 8 | lexer := Lex(expr) 9 | items := make([]LexItem, 0) 10 | 11 | go lexer.Run() 12 | 13 | for item := range lexer.Items() { 14 | items = append(items, item) 15 | } 16 | 17 | return lexer, items 18 | } 19 | 20 | func TestSimpleExprLen(t *testing.T) { 21 | _, items := runLexing("1+2") 22 | 23 | // should be 4 with EOF 24 | itemCount := len(items) 25 | 26 | if itemCount != 4 { 27 | t.Errorf("Len = %d; want 4", itemCount) 28 | } 29 | } 30 | 31 | func TestSimpleExprItems(t *testing.T) { 32 | _, items := runLexing("11+2") 33 | 34 | tested := items[0] 35 | if tested.Typ != INUMBER { 36 | t.Errorf("Typ is %s; want %s", tested.Typ, INUMBER) 37 | } 38 | 39 | if tested.Val != "11" { 40 | t.Errorf("Val is %s; want %s", tested.Val, "11") 41 | } 42 | 43 | tested = items[1] 44 | if tested.Typ != IADD { 45 | t.Errorf("Typ is %s; want %s", tested.Typ, IADD) 46 | } 47 | 48 | tested = items[2] 49 | if tested.Typ != INUMBER { 50 | t.Errorf("Typ is %s; want %s", tested.Typ, INUMBER) 51 | } 52 | 53 | if tested.Val != "2" { 54 | t.Errorf("Val is %s; want %s", tested.Val, "2") 55 | } 56 | 57 | tested = items[3] 58 | if tested.Typ != EOF { 59 | t.Errorf("Typ is %s; want %s", tested.Typ, EOF) 60 | } 61 | } 62 | 63 | func TestEmptyExprItems(t *testing.T) { 64 | _, items := runLexing("") 65 | 66 | itemCount := len(items) 67 | 68 | if itemCount != 1 { 69 | t.Errorf("Len = %d; want 4", itemCount) 70 | } 71 | 72 | tested := items[0] 73 | 74 | if tested.Typ != EOF { 75 | t.Errorf("Typ is %s; want %s", tested.Typ, EOF) 76 | } 77 | } 78 | 79 | func TestInvalidRune(t *testing.T) { 80 | _, items := runLexing("1+W+3+5") 81 | 82 | itemCount := len(items) 83 | 84 | // stopped at 'W' rune 85 | if itemCount != 3 { 86 | t.Errorf("Len = %d; want 4", itemCount) 87 | } 88 | 89 | tested := items[2] 90 | 91 | if tested.Typ != IERR { 92 | t.Errorf("Typ is %s; want %s", tested.Typ, IERR) 93 | } 94 | } 95 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "os" 6 | ) 7 | 8 | func main() { 9 | 10 | args := os.Args 11 | 12 | if len(args) < 2 { 13 | fmt.Println("Specify expressions to evaluate...\ne.g.: 1+2*(6-8)") 14 | return 15 | } 16 | 17 | // parse given expression into AST 18 | ast, err := Parse(args[1]) 19 | 20 | if err != nil { 21 | fmt.Println(err) 22 | return 23 | } 24 | 25 | // Interpret AST 26 | result, err := Interpret(ast) 27 | 28 | if err != nil { 29 | fmt.Println(err) 30 | return 31 | } 32 | 33 | fmt.Println(result) 34 | } 35 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "container/list" 5 | "fmt" 6 | ) 7 | 8 | // Determines precedence of operator given 9 | func precedence(typ ItemType) int { 10 | switch typ { 11 | case IADD: 12 | fallthrough 13 | case ISUB: 14 | return 1 15 | 16 | case IMUL: 17 | fallthrough 18 | case IDIV: 19 | return 2 20 | 21 | default: 22 | return -1 23 | } 24 | } 25 | 26 | // Converts infix output form of Lexer to postfix form 27 | func toPostfix(lx *Lexer) (*list.List, *EvalError) { 28 | 29 | opStack := NewStack() 30 | postFix := list.New() 31 | 32 | for item := range lx.Items() { 33 | // end of tok stream 34 | if item.Typ == EOF { 35 | continue 36 | } 37 | 38 | // lexing error 39 | if item.Typ == IERR { 40 | return nil, NewLexerError("at %d: %s", item.Pos, item.Val) 41 | } 42 | 43 | // if its number put to output 44 | if item.Typ == INUMBER { 45 | postFix.PushBack(item) 46 | continue 47 | } 48 | 49 | // if left parenth put to output 50 | if item.Typ == ILPAR { 51 | opStack.Push(item) 52 | continue 53 | } 54 | 55 | // if right parenth 56 | if item.Typ == IRPAR { 57 | // pop stack to output until we find left parenth in stack 58 | for opStack.Len() > 0 && opStack.Top().(LexItem).Typ != ILPAR { 59 | postFix.PushBack(opStack.Pop()) 60 | } 61 | 62 | // if there is none then there is error in parity 63 | if opStack.Len() > 0 && opStack.Top().(LexItem).Typ != ILPAR { 64 | return nil, NewParserError("at %d: Unmatched paretheses", opStack.Top().(LexItem).Pos) 65 | } 66 | 67 | // we are in rparenth so if there is no lparenh its parity error 68 | if opStack.Len() == 0 { 69 | return nil, NewParserError("at %d: Missing '('", item.Pos) 70 | } 71 | // otherwise just trash it 72 | opStack.Pop() 73 | 74 | } else { 75 | // is any other operator 76 | // check precedence 77 | for opStack.Len() > 0 && precedence(item.Typ) <= precedence(opStack.Top().(LexItem).Typ) { 78 | // just put it to output 79 | postFix.PushBack(opStack.Pop()) 80 | } 81 | // put it to stack 82 | opStack.Push(item) 83 | } 84 | } 85 | 86 | // empty stack to output 87 | for opStack.Len() > 0 { 88 | postFix.PushBack(opStack.Pop()) 89 | } 90 | 91 | return postFix, nil 92 | } 93 | 94 | // helper method that translates Lexer item types to AST node types 95 | func translateLexToAstType(typ ItemType) (AstNodeType, *EvalError) { 96 | switch typ { 97 | case IADD: 98 | return ASTNODE_ADD, nil 99 | case ISUB: 100 | return ASTNODE_SUB, nil 101 | case IMUL: 102 | return ASTNODE_MUL, nil 103 | case IDIV: 104 | return ASTNODE_DIV, nil 105 | default: 106 | return 0, NewParserError("Unexpected item type occured during parsing %q", typ) 107 | } 108 | } 109 | 110 | // Takes list of postfix formed lexer items and builds binary expression tree 111 | func constructAst(postfixList *list.List) (*AstNode, *EvalError) { 112 | // stack for storing nodes for later computation 113 | stack := NewStack() 114 | 115 | // go trough all items 116 | for item := postfixList.Front(); item != nil; item = item.Next() { 117 | lexItem := item.Value.(LexItem) 118 | // if its number, create node and push it to stack 119 | if lexItem.Typ == INUMBER { 120 | stack.Push(NewAstNode(ASTNODE_LEAF, &lexItem.Val)) 121 | } else { 122 | // otherwise convert type 123 | nodeType, err := translateLexToAstType(lexItem.Typ) 124 | if err != nil { 125 | return nil, NewParserError("at %d: Missing ')'", lexItem.Pos) 126 | } 127 | // create new note 128 | node := NewAstNode(nodeType, nil) 129 | 130 | // validate we have at least two items in stack 131 | if stack.Len() < 2 { 132 | return nil, NewParserError("at %d: Missing operand", lexItem.Pos) 133 | } 134 | 135 | // order important, otherwise we switch operands 136 | // Pop first time to Right operand 137 | node.Right = stack.Pop().(*AstNode) 138 | // Pop second time to Left operand 139 | node.Left = stack.Pop().(*AstNode) 140 | 141 | // push new node to stack 142 | stack.Push(node) 143 | } 144 | } 145 | 146 | // might occur when user inputs "()" expression, no root node 147 | if stack.Len() < 1 { 148 | return nil, NewParserError("Expression without root") 149 | } 150 | 151 | // pop last item from stack, its the root node of AST 152 | return stack.Pop().(*AstNode), nil 153 | } 154 | 155 | // helper debug func 156 | func traversePreorder(root *AstNode) { 157 | if root == nil { 158 | return 159 | } 160 | 161 | fmt.Println(root) 162 | traversePreorder(root.Left) 163 | traversePreorder(root.Right) 164 | } 165 | 166 | // helper debug func 167 | func traverseInorder(root *AstNode) { 168 | if root == nil { 169 | return 170 | } 171 | 172 | traversePreorder(root.Left) 173 | fmt.Println(root) 174 | traversePreorder(root.Right) 175 | } 176 | 177 | // helper debug func 178 | func traversePostorder(root *AstNode) { 179 | if root == nil { 180 | return 181 | } 182 | 183 | traversePreorder(root.Left) 184 | traversePreorder(root.Right) 185 | fmt.Println(root) 186 | } 187 | 188 | // Parse parses given infix expression and produces Abstract syntax tree 189 | func Parse(expr string) (*AstNode, *EvalError) { 190 | lx := Lex(expr) 191 | go lx.Run() 192 | 193 | postfixNotation, err := toPostfix(lx) 194 | 195 | if err != nil { 196 | return nil, err 197 | } 198 | 199 | abstractSyntaxTree, err := constructAst(postfixNotation) 200 | 201 | if err != nil { 202 | return nil, err 203 | } 204 | 205 | return abstractSyntaxTree, nil 206 | } 207 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "testing" 5 | ) 6 | 7 | func TestParserAst(t *testing.T) { 8 | ast, err := Parse("1+2") 9 | 10 | if err != nil { 11 | t.Errorf("got %s; want nil", err) 12 | } 13 | 14 | plus := ast 15 | one := ast.Left 16 | two := ast.Right 17 | 18 | if plus.Typ != ASTNODE_ADD { 19 | t.Errorf("Typ = %s; want %s", ast.Typ, ASTNODE_ADD) 20 | } 21 | 22 | if plus.Value != nil { 23 | t.Errorf("Value = %s; want %v", *ast.Value, nil) 24 | } 25 | 26 | if one.Typ != ASTNODE_LEAF { 27 | t.Errorf("Typ = %s; want %s", one.Typ, ASTNODE_LEAF) 28 | } 29 | 30 | if one.Value == nil || *one.Value != "1" { 31 | t.Errorf("Value = %v; want %s", one.Value, "1") 32 | } 33 | 34 | if two.Typ != ASTNODE_LEAF { 35 | t.Errorf("Typ = %s; want %s", two.Typ, ASTNODE_LEAF) 36 | } 37 | 38 | if two.Value == nil || *two.Value != "2" { 39 | t.Errorf("Value = %v; want %s", two.Value, "2") 40 | } 41 | } 42 | 43 | func TestParserEmptyExpr(t *testing.T) { 44 | ast, err := Parse("") 45 | 46 | if ast != nil { 47 | t.Error("ast not nil; want nil") 48 | } 49 | 50 | if err.code != ErrParser { 51 | t.Errorf("code = %s; want %s", err.code, ErrParser) 52 | } 53 | } 54 | 55 | func TestParserEmptyParenExpr(t *testing.T) { 56 | ast, err := Parse("(())") 57 | 58 | if ast != nil { 59 | t.Error("ast not nil; want nil") 60 | } 61 | 62 | if err.code != ErrParser { 63 | t.Errorf("code = %s; want %s", err.code, ErrParser) 64 | } 65 | } 66 | 67 | func TestParserPrecedence(t *testing.T) { 68 | ast, err := Parse("3*(1-2)") 69 | 70 | if ast == nil { 71 | t.Error("ast nil; want not nil") 72 | } 73 | 74 | if err != nil { 75 | t.Errorf("err = %s; want nil", err) 76 | } 77 | 78 | mul := ast 79 | three := ast.Left 80 | minus := ast.Right 81 | one := ast.Right.Left 82 | two := ast.Right.Right 83 | 84 | if mul.Typ != ASTNODE_MUL { 85 | t.Errorf("Typ = %s; want %s", mul.Typ, ASTNODE_MUL) 86 | } 87 | 88 | if three.Typ != ASTNODE_LEAF { 89 | t.Errorf("Typ = %s; want %s", three.Typ, ASTNODE_LEAF) 90 | } 91 | 92 | if minus.Typ != ASTNODE_SUB { 93 | t.Errorf("Typ = %s; want %s", minus.Typ, ASTNODE_SUB) 94 | } 95 | 96 | if one.Typ != ASTNODE_LEAF { 97 | t.Errorf("Typ = %s; want %s", one.Typ, ASTNODE_LEAF) 98 | } 99 | 100 | if two.Typ != ASTNODE_LEAF { 101 | t.Errorf("Typ = %s; want %s", two.Typ, ASTNODE_LEAF) 102 | } 103 | } 104 | -------------------------------------------------------------------------------- /run.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | go build -o go-simple-expression-eval 4 | ./go-simple-expression-eval "$1" 5 | rm go-simple-expression-eval 6 | -------------------------------------------------------------------------------- /stack.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | type Stack struct { 4 | buf []interface{} 5 | } 6 | 7 | func NewStack() *Stack { 8 | return &Stack{ 9 | buf: make([]interface{}, 0), 10 | } 11 | } 12 | 13 | func (s *Stack) Push(val interface{}) { 14 | s.buf = append(s.buf, val) 15 | } 16 | 17 | func (s *Stack) Pop() interface{} { 18 | len := len(s.buf) 19 | 20 | if len < 1 { 21 | panic("Attempting to Pop() from empty stack") 22 | } 23 | 24 | val := s.buf[len-1] 25 | 26 | s.buf = s.buf[0 : len-1] 27 | 28 | return val 29 | } 30 | 31 | func (s *Stack) Top() interface{} { 32 | return s.buf[len(s.buf)-1] 33 | } 34 | 35 | func (s *Stack) Len() int { 36 | return len(s.buf) 37 | } 38 | --------------------------------------------------------------------------------