├── .gitignore ├── LICENSE ├── README.md ├── apply.go ├── atom.go ├── buildin.go ├── environment.go ├── environment_test.go ├── eval.go ├── eval_test.go ├── expression.go ├── expression_test.go ├── list.go ├── list_test.go ├── main.go ├── pair.go ├── parser.go ├── parser_test.go ├── repl.go ├── tokenizer.go ├── tokenizer_test.go └── util.go /.gitignore: -------------------------------------------------------------------------------- 1 | /Flang 2 | /.DS_Store 3 | /GPATH 4 | /GRTAGS 5 | /GTAGS 6 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2019 Guo Fei 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 | # Flang 2 | 3 | A Scheme dialect written in Golang 4 | 5 | ## Concept 6 | 7 | [The eval-apply cycle](http://sarabander.github.io/sicp/html/4_002e1.xhtml#g_t4_002e1_002e4) 8 | 9 | ## Hello World 10 | 11 | ``` lisp 12 | (p "hello world") 13 | "hello world" 14 | ``` 15 | 16 | ## Basic types 17 | 18 | Number, Symbol, String, Boolean, Lambda, List, Pair 19 | 20 | ### Number 21 | 22 | ```lisp 23 | (+ 1 2) 24 | 3 25 | (- 10 2) 26 | 8 27 | (* 1.5 2) 28 | 3 29 | ``` 30 | 31 | ### String 32 | 33 | Example 34 | 35 | ```lisp 36 | (string? "hello world") 37 | #t 38 | ``` 39 | 40 | ### Boolean 41 | 42 | Example 43 | 44 | ```lisp 45 | (eq? 1 1) 46 | #t 47 | (eq? 1 2) 48 | #f 49 | ``` 50 | 51 | ### Lambda 52 | 53 | Example 54 | 55 | ```lisp 56 | ((lambda (x) (* x x)) 2) 57 | 4 58 | ``` 59 | 60 | ### List 61 | 62 | Example 63 | 64 | ```lisp 65 | (list? (list 1 2 3)) 66 | #t 67 | ``` 68 | 69 | ### Pair 70 | 71 | Example 72 | 73 | ```lisp 74 | (pair? (cons 1 2)) 75 | #t 76 | ``` 77 | 78 | 79 | ## Definition 80 | 81 | ### Define a varable 82 | 83 | ``` lisp 84 | (define ) 85 | ``` 86 | 87 | Example 88 | 89 | ``` lisp 90 | (define x 1) 91 | ``` 92 | 93 | ### Define a function 94 | 95 | ``` lisp 96 | (define ( ... ) ) 97 | ``` 98 | 99 | Example 100 | 101 | ``` lisp 102 | 103 | (define (factorial n) 104 | (if (eq? n 1) 105 | 1 106 | (* (factorial (- n 1)) n))) 107 | (factorial 5) 108 | 109 | ;; result: 120 110 | ``` 111 | 112 | ## Assignment 113 | 114 | ``` lisp 115 | (set! ) 116 | ``` 117 | 118 | Example 119 | 120 | ``` lisp 121 | (define x 1) 122 | (set! x 10) 123 | (p x) 124 | 125 | ;; result: 10 126 | ``` 127 | 128 | ## Conditions 129 | 130 | if, cond 131 | 132 | Example 133 | 134 | ``` lisp 135 | (define x 1) 136 | (if (> x 5) 137 | (p "greater than 5") 138 | (p "less than 5")) 139 | ``` 140 | 141 | ## Function 142 | 143 | ### Lambda 144 | ``` lisp 145 | ((lambda (x) (* x x)) 2) 146 | 4 147 | ``` 148 | 149 | ### Standard Library 150 | 151 | cons, car, cdr, list, pair ... 152 | 153 | ## TODOS 154 | 155 | - [x] REPL 156 | - [ ] Macro 157 | - [ ] Comment 158 | - [ ] Go Channel 159 | - [ ] Bootstrapping 160 | 161 | -------------------------------------------------------------------------------- /apply.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Apply ... 8 | func Apply(procedure Expression, args Expression) (Expression, error) { 9 | switch p := procedure.(type) { 10 | case Primitive: 11 | return p(args) 12 | case Procedure: 13 | childEnv := p.Env.NewChild() 14 | childEnv.Extend(p.Parameters, args) 15 | return EvalSequence(p.Body, childEnv) 16 | default: 17 | return nil, fmt.Errorf("unknown procedure type %v", procedure) 18 | } 19 | } 20 | -------------------------------------------------------------------------------- /atom.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // String ... 8 | func (n Number) String() string { 9 | return fmt.Sprintf("%v", float64(n)) 10 | } 11 | 12 | // String ... 13 | func (s Symbol) String() string { 14 | return string(s) 15 | } 16 | 17 | // String ... 18 | func (s String) String() string { 19 | return fmt.Sprintf("\"%v\"", string(s)) 20 | } 21 | 22 | // String ... 23 | func (b Boolean) String() string { 24 | if b { 25 | return "#t" 26 | } 27 | return "#f" 28 | } 29 | 30 | // IsAtom ... 31 | func (n Number) IsAtom() bool { 32 | return true 33 | } 34 | 35 | // IsAtom ... 36 | func (s Symbol) IsAtom() bool { 37 | return true 38 | } 39 | 40 | // IsAtom ... 41 | func (s String) IsAtom() bool { 42 | return true 43 | } 44 | 45 | // IsAtom ... 46 | func (b Boolean) IsAtom() bool { 47 | return true 48 | } 49 | 50 | // IsList ... 51 | func (n Number) IsList() bool { 52 | return false 53 | } 54 | 55 | // IsList ... 56 | func (s Symbol) IsList() bool { 57 | return false 58 | } 59 | 60 | // IsList ... 61 | func (s String) IsList() bool { 62 | return false 63 | } 64 | 65 | // IsList ... 66 | func (b Boolean) IsList() bool { 67 | return false 68 | } 69 | -------------------------------------------------------------------------------- /buildin.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Number ... 8 | type Number float64 9 | 10 | // Symbol ... 11 | type Symbol string 12 | 13 | // String ... 14 | type String string 15 | 16 | // Boolean ... 17 | type Boolean bool 18 | 19 | // Primitive ... 20 | type Primitive func(Expression) (Expression, error) 21 | 22 | // IsAtom ... 23 | func (p Primitive) IsAtom() bool { 24 | return false 25 | } 26 | 27 | // IsList ... 28 | func (p Primitive) IsList() bool { 29 | return false 30 | } 31 | 32 | // String ... 33 | func (p Primitive) String() string { 34 | return "primitive" 35 | } 36 | 37 | // Procedure ... 38 | type Procedure struct { 39 | Parameters Expression 40 | Body *List 41 | Env *Environment 42 | } 43 | 44 | // IsAtom ... 45 | func (p Procedure) IsAtom() bool { 46 | return false 47 | } 48 | 49 | // IsList ... 50 | func (p Procedure) IsList() bool { 51 | return false 52 | } 53 | 54 | // String ... 55 | func (p Procedure) String() string { 56 | return fmt.Sprintf("(lambda (%v) (%v))", p.Parameters, p.Body) 57 | } 58 | 59 | // ParseToken ... 60 | func ParseToken(token Token) (Expression, error) { 61 | switch token.Name { 62 | case NUMBER: 63 | return Number(ToFloat(token.Value)), nil 64 | case SYMBOL: 65 | return Symbol(token.Value), nil 66 | case STRING: 67 | return String(token.Value[1 : len(token.Value)-1]), nil 68 | case BOOLEAN: 69 | return Boolean(ToBool(token.Value)), nil 70 | default: 71 | return nil, fmt.Errorf("Can't parse token %v", token) 72 | } 73 | } 74 | 75 | func add(args Expression) (Expression, error) { 76 | argsList, ok := args.(*List) 77 | if !ok { 78 | return nil, fmt.Errorf("add error %v", args) 79 | } 80 | if argsList.IsNull() { 81 | return Number(0), nil 82 | } 83 | car, _ := argsList.Car() 84 | cdr, _ := argsList.Cdr() 85 | n, ok := car.(Number) 86 | if !ok { 87 | return nil, fmt.Errorf("add error %v", args) 88 | } 89 | rest, err := add(cdr) 90 | if err != nil { 91 | return nil, fmt.Errorf("add error %v", args) 92 | } 93 | return n + rest.(Number), nil 94 | } 95 | 96 | func subtract(args Expression) (Expression, error) { 97 | argsList, ok := args.(*List) 98 | if !ok { 99 | return nil, fmt.Errorf("subtract error %v", args) 100 | } 101 | if argsList.IsNull() { 102 | return nil, fmt.Errorf("subtract error %v", args) 103 | } 104 | car, _ := argsList.Car() 105 | n, ok := car.(Number) 106 | if !ok { 107 | return nil, fmt.Errorf("subtract error %v", args) 108 | } 109 | cdr, _ := argsList.Cdr() 110 | sum, err := add(cdr) 111 | if err != nil { 112 | return nil, fmt.Errorf("subtract error %v", args) 113 | } 114 | return n - sum.(Number), nil 115 | } 116 | 117 | func multiply(args Expression) (Expression, error) { 118 | argsList, ok := args.(*List) 119 | if !ok { 120 | return nil, fmt.Errorf("multiply error %v", args) 121 | } 122 | if argsList.IsNull() { 123 | return Number(1), nil 124 | } 125 | car, _ := argsList.Car() 126 | cdr, _ := argsList.Cdr() 127 | n, ok := car.(Number) 128 | if !ok { 129 | return nil, fmt.Errorf("multiply error %v", args) 130 | } 131 | rest, err := multiply(cdr) 132 | if err != nil { 133 | return nil, fmt.Errorf("multiply error %v", args) 134 | } 135 | return n * rest.(Number), nil 136 | } 137 | 138 | func comparable(args Expression) bool { 139 | list, ok := args.(*List) 140 | if !ok { 141 | return false 142 | } 143 | car, err := list.Car() 144 | if err != nil { 145 | return false 146 | } 147 | cadr, err := list.Cadr() 148 | if err != nil { 149 | return false 150 | } 151 | _, ok = car.(Number) 152 | if !ok { 153 | return false 154 | } 155 | _, ok = cadr.(Number) 156 | return ok 157 | } 158 | 159 | func divide(args Expression) (Expression, error) { 160 | ok := comparable(args) 161 | if !ok { 162 | return nil, fmt.Errorf("divide error %v", args) 163 | } 164 | list, _ := args.(*List) 165 | n1, _ := list.Car() 166 | n2, _ := list.Cadr() 167 | if !ok { 168 | return nil, fmt.Errorf("divide error %v", args) 169 | } 170 | return n1.(Number) / n2.(Number), nil 171 | } 172 | 173 | func greaterThan(args Expression) (Expression, error) { 174 | ok := comparable(args) 175 | if !ok { 176 | return nil, fmt.Errorf("> error %v", args) 177 | } 178 | list, _ := args.(*List) 179 | n1, _ := list.Car() 180 | n2, _ := list.Cadr() 181 | if !ok { 182 | return nil, fmt.Errorf("> error %v", args) 183 | } 184 | return Boolean(n1.(Number) > n2.(Number)), nil 185 | } 186 | 187 | func lessThan(args Expression) (Expression, error) { 188 | ok := comparable(args) 189 | if !ok { 190 | return nil, fmt.Errorf("< error %v", args) 191 | } 192 | list, _ := args.(*List) 193 | n1, _ := list.Car() 194 | n2, _ := list.Cadr() 195 | if !ok { 196 | return nil, fmt.Errorf("< error %v", args) 197 | } 198 | return Boolean(n1.(Number) < n2.(Number)), nil 199 | } 200 | 201 | func greaterEqThan(args Expression) (Expression, error) { 202 | ok := comparable(args) 203 | if !ok { 204 | return nil, fmt.Errorf(">= error %v", args) 205 | } 206 | list, _ := args.(*List) 207 | n1, _ := list.Car() 208 | n2, _ := list.Cadr() 209 | if !ok { 210 | return nil, fmt.Errorf(">= error %v", args) 211 | } 212 | return Boolean(n1.(Number) >= n2.(Number)), nil 213 | } 214 | 215 | func lessEqThan(args Expression) (Expression, error) { 216 | ok := comparable(args) 217 | if !ok { 218 | return nil, fmt.Errorf("<= error %v", args) 219 | } 220 | list, _ := args.(*List) 221 | n1, _ := list.Car() 222 | n2, _ := list.Cadr() 223 | if !ok { 224 | return nil, fmt.Errorf("<= error %v", args) 225 | } 226 | return Boolean(n1.(Number) <= n2.(Number)), nil 227 | } 228 | 229 | func firstArg(args Expression) (Expression, error) { 230 | list, ok := args.(*List) 231 | if !ok { 232 | return nil, fmt.Errorf("args error %v", args) 233 | } 234 | return list.Car() 235 | } 236 | 237 | func firstArgAsList(args Expression) (*List, error) { 238 | list, ok := args.(*List) 239 | if !ok { 240 | return nil, fmt.Errorf("args error %v", args) 241 | } 242 | first, err := list.Car() 243 | if err != nil { 244 | return nil, fmt.Errorf("args error %v", args) 245 | } 246 | firstList, ok := first.(*List) 247 | if !ok { 248 | return nil, fmt.Errorf("args must be a list: %v", args) 249 | } 250 | return firstList, nil 251 | } 252 | 253 | func car(args Expression) (Expression, error) { 254 | list, err := firstArgAsList(args) 255 | if err != nil { 256 | return nil, err 257 | } 258 | return list.Car() 259 | } 260 | 261 | func cdr(args Expression) (Expression, error) { 262 | list, err := firstArgAsList(args) 263 | if err != nil { 264 | return nil, err 265 | } 266 | return list.Cdr() 267 | } 268 | 269 | func cons(args Expression) (Expression, error) { 270 | return args.(*List).Copy(), nil 271 | } 272 | 273 | func list(args Expression) (Expression, error) { 274 | return args.(*List).Copy(), nil 275 | } 276 | 277 | func isEqual(args Expression) (Expression, error) { 278 | list, ok := args.(*List) 279 | if !ok { 280 | return Boolean(false), nil 281 | } 282 | car, err := list.Car() 283 | if err != nil { 284 | return nil, err 285 | } 286 | cadr, err := list.Cadr() 287 | if err != nil { 288 | return nil, err 289 | } 290 | return Boolean(car == cadr), nil 291 | } 292 | 293 | func isNull(args Expression) (Expression, error) { 294 | list, err := firstArgAsList(args) 295 | if err != nil { 296 | return nil, err 297 | } 298 | return Boolean(list.IsNull()), nil 299 | } 300 | 301 | func isList(args Expression) (Expression, error) { 302 | first, err := firstArg(args) 303 | if err != nil { 304 | return nil, err 305 | } 306 | firstList, ok := first.(*List) 307 | if ok { 308 | return Boolean(firstList.IsList()), nil 309 | } 310 | return Boolean(false), nil 311 | } 312 | 313 | func isPair(args Expression) (Expression, error) { 314 | first, err := firstArg(args) 315 | if err != nil { 316 | return nil, err 317 | } 318 | firstList, ok := first.(*List) 319 | if ok { 320 | return Boolean(firstList.IsPair()), nil 321 | } 322 | return Boolean(false), nil 323 | } 324 | 325 | func isSymbol(args Expression) (Expression, error) { 326 | first, err := firstArg(args) 327 | if err != nil { 328 | return nil, err 329 | } 330 | _, ok := first.(Symbol) 331 | return Boolean(ok), nil 332 | } 333 | 334 | func isString(args Expression) (Expression, error) { 335 | first, err := firstArg(args) 336 | if err != nil { 337 | return nil, err 338 | } 339 | _, ok := first.(String) 340 | return Boolean(ok), nil 341 | } 342 | 343 | func printExp(args Expression) (Expression, error) { 344 | first, err := firstArg(args) 345 | if err != nil { 346 | return nil, err 347 | } 348 | fmt.Println(first) 349 | return Symbol("OK"), nil 350 | } 351 | -------------------------------------------------------------------------------- /environment.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type frame map[Symbol]Expression 8 | 9 | // Environment ... 10 | type Environment struct { 11 | parent *Environment 12 | f frame 13 | } 14 | 15 | // Lookup ... 16 | func (env *Environment) Lookup(s Symbol) (Expression, error) { 17 | v, ok := env.f[s] 18 | if ok { 19 | return v, nil 20 | } 21 | if env.parent == nil { 22 | return nil, fmt.Errorf("unbound variable %v", s) 23 | } 24 | return env.parent.Lookup(s) 25 | } 26 | 27 | // Set ... 28 | func (env *Environment) Set(k Symbol, v Expression) { 29 | env.f[k] = v 30 | } 31 | 32 | // Extend ... 33 | func (env *Environment) Extend(varsExp Expression, valsExp Expression) bool { 34 | vars, ok := varsExp.(*List) 35 | if !ok { 36 | return false 37 | } 38 | vals, ok := valsExp.(*List) 39 | if !ok { 40 | return false 41 | } 42 | if vars.Len() != vals.Len() { 43 | return false 44 | } 45 | varsCar, err := vars.Car() 46 | if err != nil { 47 | return true 48 | } 49 | valsCar, err := vals.Car() 50 | if err != nil { 51 | return true 52 | } 53 | env.Set(varsCar.(Symbol), valsCar) 54 | varsCdr, err := vars.Cdr() 55 | if err != nil { 56 | return true 57 | } 58 | valsCdr, err := vals.Cdr() 59 | if err != nil { 60 | return true 61 | } 62 | return env.Extend(varsCdr.(*List), valsCdr.(*List)) 63 | } 64 | 65 | // BaseEnv ... 66 | func BaseEnv() *Environment { 67 | env := &Environment{nil, make(frame)} 68 | f := frame{ 69 | "car": Primitive(car), 70 | "cdr": Primitive(cdr), 71 | "cons": Primitive(cons), 72 | "list": Primitive(list), 73 | "eq?": Primitive(isEqual), 74 | "null?": Primitive(isNull), 75 | "list?": Primitive(isList), 76 | "pair?": Primitive(isPair), 77 | "symbol?": Primitive(isSymbol), 78 | "string?": Primitive(isString), 79 | "p": Primitive(printExp), 80 | "+": Primitive(add), 81 | "-": Primitive(subtract), 82 | "*": Primitive(multiply), 83 | "/": Primitive(divide), 84 | ">": Primitive(greaterThan), 85 | ">=": Primitive(greaterEqThan), 86 | "<": Primitive(lessThan), 87 | "<=": Primitive(lessEqThan), 88 | "#t": Boolean(true), 89 | "#f": Boolean(false), 90 | } 91 | for s, exp := range f { 92 | env.Set(s, exp) 93 | } 94 | return env 95 | } 96 | 97 | // NewChild ... 98 | func (env *Environment) NewChild() *Environment { 99 | return &Environment{env, make(frame)} 100 | } 101 | -------------------------------------------------------------------------------- /environment_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestBuildInCons(t *testing.T) { 9 | env := BaseEnv() 10 | code := `(cons 1 (list 2 3))` 11 | exp, _ := Parse(code) 12 | res, _ := Eval(exp, env) 13 | except := `(1 (2 3))` 14 | if fmt.Sprintf("%v", res) != except { 15 | t.Error("Expression Error: buildin cons") 16 | t.Log("cons: ", res, "except: ", except) 17 | } 18 | } 19 | 20 | func TestBuildInList(t *testing.T) { 21 | env := BaseEnv() 22 | code := `(list 1 2 3)` 23 | exp, _ := Parse(code) 24 | res, _ := Eval(exp, env) 25 | except := `(1 2 3)` 26 | if fmt.Sprintf("%v", res) != except { 27 | t.Error("Expression Error: buildin list") 28 | t.Log("list: ", res, "except: ", except) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /eval.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Eval ... 8 | func Eval(exp Expression, env *Environment) (Expression, error) { 9 | switch { 10 | case isSelfEvaluating(exp): 11 | return exp, nil 12 | case isVariable(exp): 13 | return env.Lookup(exp.(Symbol)) 14 | case isSpecialForm(exp): 15 | return evalSpecialForm(exp.(*List), env) 16 | case isApplication(exp): 17 | list := exp.(*List) 18 | operator, _ := list.Car() 19 | procedure, err := Eval(operator, env) 20 | if err != nil { 21 | return nil, err 22 | } 23 | operands, _ := list.Cdr() 24 | args, err := listOfValues(operands, env) 25 | if err != nil { 26 | return nil, err 27 | } 28 | return Apply(procedure, args) 29 | default: 30 | return nil, fmt.Errorf("unknown expression type %v", exp) 31 | } 32 | } 33 | 34 | func isSelfEvaluating(exp Expression) bool { 35 | switch exp.(type) { 36 | case Number, String, Boolean: 37 | return true 38 | default: 39 | return false 40 | } 41 | } 42 | 43 | func isVariable(exp Expression) bool { 44 | switch exp.(type) { 45 | case Symbol: 46 | return true 47 | default: 48 | return false 49 | } 50 | } 51 | 52 | func isApplication(exp Expression) bool { 53 | switch t := exp.(type) { 54 | case *List: 55 | return t.IsPair() 56 | default: 57 | return false 58 | } 59 | } 60 | 61 | func isSpecialForm(exp Expression) bool { 62 | switch t := exp.(type) { 63 | case *List: 64 | car, err := t.Car() 65 | if err != nil { 66 | return false 67 | } 68 | switch ct := car.(type) { 69 | case Symbol: 70 | _, ok := formFunc(ct) 71 | return ok 72 | default: 73 | return false 74 | } 75 | default: 76 | return false 77 | } 78 | } 79 | 80 | func listOfValues(exps Expression, env *Environment) (Expression, error) { 81 | if exps.(*List).IsNull() { 82 | return EmptyList(), nil 83 | } 84 | car, _ := exps.(*List).Car() 85 | firstValue, err := Eval(car, env) 86 | if err != nil { 87 | return firstValue, err 88 | } 89 | cdr, _ := exps.(*List).Cdr() 90 | restValues, err := listOfValues(cdr, env) 91 | if err != nil { 92 | return restValues, err 93 | } 94 | return Cons(firstValue, restValues), nil 95 | } 96 | 97 | func formFunc(f Symbol) (func(*List, *Environment) (Expression, error), bool) { 98 | dict := map[Symbol]func(*List, *Environment) (Expression, error){ 99 | "quote": textOfQuotation, 100 | "set!": evalAssignment, 101 | "define": evalDefinition, 102 | "if": evalIf, 103 | "lambda": evalLambda, 104 | "begin": evalBegin, 105 | "cond": evalCond, 106 | } 107 | v, ok := dict[f] 108 | return v, ok 109 | } 110 | 111 | func evalSpecialForm(exp *List, env *Environment) (Expression, error) { 112 | form, _ := exp.Car() 113 | fun, _ := formFunc(form.(Symbol)) 114 | return fun(exp, env) 115 | } 116 | 117 | func textOfQuotation(exp *List, env *Environment) (Expression, error) { 118 | return exp.Cadr() 119 | } 120 | 121 | func evalAssignment(exp *List, env *Environment) (Expression, error) { 122 | variable, err := exp.Cadr() 123 | if err != nil { 124 | return nil, fmt.Errorf("assignment error %v", exp) 125 | } 126 | valueBody, err := exp.Caddr() 127 | if err != nil { 128 | return nil, fmt.Errorf("assignment error %v", exp) 129 | } 130 | value, err := Eval(valueBody, env) 131 | if err != nil { 132 | return nil, fmt.Errorf("assignment error %v", exp) 133 | } 134 | k, ok := variable.(Symbol) 135 | if !ok { 136 | return nil, fmt.Errorf("assignment error %v", exp) 137 | } 138 | env.Set(k, value) 139 | return Symbol("ok"), nil 140 | } 141 | 142 | func definitionVariable(exp *List) (Symbol, error) { 143 | variableExp, err := exp.Cadr() 144 | if err != nil { 145 | return "", fmt.Errorf("definition error %v", exp) 146 | } 147 | switch t := variableExp.(type) { 148 | case Symbol: 149 | return t, nil 150 | default: 151 | v, err := variableExp.(*List).Car() 152 | if err != nil { 153 | return "", fmt.Errorf("definition error %v", variableExp) 154 | } 155 | s, ok := v.(Symbol) 156 | if !ok { 157 | return "", fmt.Errorf("definition error %v", variableExp) 158 | } 159 | return s, nil 160 | } 161 | } 162 | 163 | func makeLambda(params Expression, body Expression) *List { 164 | return Cons(Symbol("lambda"), Cons(params, body)) 165 | } 166 | 167 | func definitionValue(exp *List) (Expression, error) { 168 | variableExp, err := exp.Cadr() 169 | if err != nil { 170 | return nil, fmt.Errorf("definition error %v", exp) 171 | } 172 | switch t := variableExp.(type) { 173 | case Symbol: 174 | value, err := exp.Caddr() 175 | if err != nil { 176 | return nil, fmt.Errorf("definition error %v", exp) 177 | } 178 | return value, err 179 | default: 180 | params, err := t.(*List).Cdr() 181 | if err != nil { 182 | return nil, fmt.Errorf("definition error %v", exp) 183 | } 184 | body, err := exp.Cddr() 185 | if err != nil { 186 | return nil, fmt.Errorf("definition error %v", exp) 187 | } 188 | return makeLambda(params, body), nil 189 | } 190 | } 191 | 192 | func evalDefinition(exp *List, env *Environment) (Expression, error) { 193 | variable, err := definitionVariable(exp) 194 | if err != nil { 195 | return nil, fmt.Errorf("definition error %v", exp) 196 | } 197 | value, err := definitionValue(exp) 198 | if err != nil { 199 | return nil, fmt.Errorf("definition error %v", exp) 200 | } 201 | v, err := Eval(value, env) 202 | if err != nil { 203 | return nil, fmt.Errorf("definition error %v", exp) 204 | } 205 | env.Set(variable, v) 206 | return Symbol("ok"), nil 207 | } 208 | 209 | func ifAlternative(exp *List) (Expression, error) { 210 | cdddr, err := exp.Cdddr() 211 | if err != nil { 212 | return nil, fmt.Errorf("if error %v", exp) 213 | } 214 | switch t := cdddr.(type) { 215 | case *List: 216 | if t.IsNull() { 217 | return Boolean(false), nil 218 | } 219 | return t.Car() 220 | default: 221 | return nil, fmt.Errorf("if error %v", exp) 222 | } 223 | } 224 | 225 | func evalIf(exp *List, env *Environment) (Expression, error) { 226 | predicateExp, err := exp.Cadr() 227 | if err != nil { 228 | return nil, fmt.Errorf("if error %v", exp) 229 | } 230 | predicate, err := Eval(predicateExp, env) 231 | if err != nil { 232 | return nil, fmt.Errorf("if error %v", exp) 233 | } 234 | if IsTrue(predicate) { 235 | consequent, e := exp.Caddr() 236 | if e != nil { 237 | return nil, fmt.Errorf("if error %v", exp) 238 | } 239 | return Eval(consequent, env) 240 | } 241 | alternative, err := ifAlternative(exp) 242 | if err != nil { 243 | return nil, fmt.Errorf("if error %v", exp) 244 | } 245 | return Eval(alternative, env) 246 | } 247 | 248 | func evalLambda(exp *List, env *Environment) (Expression, error) { 249 | params, err := exp.Cadr() 250 | if err != nil { 251 | return nil, fmt.Errorf("lambda error %v", exp) 252 | } 253 | body, err := exp.Cddr() 254 | if err != nil { 255 | return nil, fmt.Errorf("lambda error %v", exp) 256 | } 257 | return Procedure{params, body.(*List), env}, nil 258 | } 259 | 260 | func evalBegin(exps *List, env *Environment) (Expression, error) { 261 | cdr, err := exps.Cdr() 262 | if err != nil { 263 | return nil, fmt.Errorf("begin error %v", exps) 264 | } 265 | return EvalSequence(cdr.(*List), env) 266 | } 267 | 268 | // EvalSequence ... 269 | func EvalSequence(exps *List, env *Environment) (Expression, error) { 270 | car, err := exps.Car() 271 | if err != nil { 272 | return nil, fmt.Errorf("sequence error 1 %v", exps) 273 | } 274 | cdr, err := exps.Cdr() 275 | if err != nil { 276 | return nil, fmt.Errorf("sequence error 2 %v", exps) 277 | } 278 | if cdr.(*List).IsNull() { 279 | return Eval(car, env) 280 | } 281 | _, err = Eval(car, env) 282 | if err != nil { 283 | return nil, fmt.Errorf("sequence error 3 %v", car) 284 | } 285 | return EvalSequence(cdr.(*List), env) 286 | } 287 | 288 | // TODO 289 | func evalCond(exp *List, env *Environment) (Expression, error) { 290 | return nil, nil 291 | } 292 | -------------------------------------------------------------------------------- /eval_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestEvalSelfEval(t *testing.T) { 9 | exp := String("abc") 10 | res, _ := Eval(exp, nil) 11 | if fmt.Sprintf("%v", res) != `"abc"` { 12 | t.Error("Eval Error") 13 | } 14 | } 15 | 16 | func TestEvalPrimitiveAdd(t *testing.T) { 17 | env := BaseEnv() 18 | code := `(+ 1 2)` 19 | ats, _ := Parse(code) 20 | res, _ := Eval(ats, env) 21 | if res != Number(3) { 22 | t.Error("Eval Error") 23 | } 24 | } 25 | 26 | func TestEvalPrimitivePrint(t *testing.T) { 27 | env := BaseEnv() 28 | code := `(p "hello world")` 29 | ats, _ := Parse(code) 30 | res, _ := Eval(ats, env) 31 | if res != Symbol("OK") { 32 | t.Error("Eval Error") 33 | } 34 | } 35 | 36 | func TestEvalPrimitiveMultiply(t *testing.T) { 37 | env := BaseEnv() 38 | code := `(* 2 2)` 39 | ats, _ := Parse(code) 40 | res, _ := Eval(ats, env) 41 | if res != Number(4) { 42 | t.Error("Eval Error") 43 | } 44 | } 45 | 46 | func TestEvalDefinition(t *testing.T) { 47 | env := BaseEnv() 48 | code := `(define x 1)` 49 | ats, _ := Parse(code) 50 | res, _ := Eval(ats, env) 51 | if res != Symbol("ok") { 52 | t.Error("Eval Error") 53 | } 54 | } 55 | 56 | func TestEvalDefinitionfunction(t *testing.T) { 57 | env := BaseEnv() 58 | code := `(define (double x) (+ x x))` 59 | ats, _ := Parse(code) 60 | res, _ := Eval(ats, env) 61 | if res != Symbol("ok") { 62 | t.Error("Eval Error") 63 | } 64 | } 65 | 66 | func TestEvalBegin(t *testing.T) { 67 | env := BaseEnv() 68 | code := `(begin 69 | (define a 3) 70 | (define b 5) 71 | (+ a b))` 72 | ats, _ := Parse(code) 73 | res, _ := Eval(ats, env) 74 | if res != Number(8) { 75 | t.Error("Eval Error") 76 | } 77 | } 78 | 79 | func TestEvalApplication(t *testing.T) { 80 | env := BaseEnv() 81 | code := `(begin 82 | (define (double x) (+ x x)) 83 | (define b 5) 84 | (+ (double 2) b))` 85 | ats, _ := Parse(code) 86 | res, _ := Eval(ats, env) 87 | if res != Number(9) { 88 | t.Error("Eval Error") 89 | } 90 | } 91 | 92 | func TestEvalIf(t *testing.T) { 93 | env := BaseEnv() 94 | code := `(if (eq? 1 1) 95 | (+ 1 1) 96 | (- 1 1))` 97 | ats, _ := Parse(code) 98 | res, _ := Eval(ats, env) 99 | if res != Number(2) { 100 | t.Error("Eval Error") 101 | } 102 | } 103 | 104 | func TestEvalElse(t *testing.T) { 105 | env := BaseEnv() 106 | code := `(if (eq? 2 1) 107 | (+ 1 1) 108 | (- 1 1))` 109 | ats, _ := Parse(code) 110 | res, _ := Eval(ats, env) 111 | if res != Number(0) { 112 | t.Error("Eval Error") 113 | } 114 | } 115 | 116 | func TestEvalIfAndDefine(t *testing.T) { 117 | env := BaseEnv() 118 | code := `(begin 119 | (define (f n) 120 | (if (eq? n 1) 121 | 1 122 | (* 2 n))) 123 | (f 5))` 124 | ats, _ := Parse(code) 125 | res, _ := Eval(ats, env) 126 | if res != Number(10) { 127 | t.Error("Eval Error") 128 | } 129 | } 130 | 131 | func TestEvalRecursion(t *testing.T) { 132 | env := BaseEnv() 133 | code := `(begin 134 | (define (factorial n) 135 | (if (eq? n 1) 136 | 1 137 | (* (factorial (- n 1)) n))) 138 | (factorial 5))` 139 | ats, _ := Parse(code) 140 | res, _ := Eval(ats, env) 141 | if res != Number(120) { 142 | t.Error("Eval Error") 143 | } 144 | } 145 | -------------------------------------------------------------------------------- /expression.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Expression ... 4 | type Expression interface { 5 | IsAtom() bool 6 | IsList() bool 7 | String() string 8 | } 9 | 10 | // IsTrue ... 11 | func IsTrue(exp Expression) bool { 12 | switch t := exp.(type) { 13 | case Boolean: 14 | return bool(t) 15 | default: 16 | return true 17 | } 18 | } 19 | 20 | // Cons ... 21 | func Cons(args ...Expression) *List { 22 | res := &List{} 23 | c1, ok := args[0].(*List) 24 | if ok { 25 | res.car = c1 26 | } else { 27 | res.car = args[0] 28 | } 29 | c2, ok := args[1].(*List) 30 | if ok { 31 | res.cdr = c2 32 | } else { 33 | res.cdr = &List{args[1], &List{}} 34 | } 35 | return res 36 | } 37 | -------------------------------------------------------------------------------- /expression_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestCons(t *testing.T) { 9 | exp1 := Cons(Number(1), Number(2)) 10 | except1 := `(1 2)` 11 | if fmt.Sprintf("%v", exp1) != except1 { 12 | t.Error("Cons Error") 13 | t.Log("exp: ", exp1, "except: ", except1) 14 | } 15 | exp2 := Cons(Number(1), EmptyList()) 16 | except2 := `(1)` 17 | if fmt.Sprintf("%v", exp2) != except2 { 18 | t.Error("Cons Error") 19 | t.Log("exp: ", exp2, "except: ", except2) 20 | } 21 | exp3 := Cons(Number(1), Cons(Number(2), EmptyList())) 22 | except3 := `(1 2)` 23 | if fmt.Sprintf("%v", exp3) != except3 { 24 | t.Error("Cons Error") 25 | t.Log("exp: ", exp3, "except: ", except3) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /list.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // TODO: Change rescursion to for loop 8 | 9 | // List ... 10 | type List Pair 11 | 12 | // IsAtom ... 13 | func (list *List) IsAtom() bool { 14 | return false 15 | } 16 | 17 | // String ... 18 | func (list *List) String() string { 19 | if list.Len() <= 0 { 20 | return "()" 21 | } 22 | return fmt.Sprintf("(%v)", list.childToString()) 23 | } 24 | 25 | // childToString ... 26 | func (list *List) childToString() string { 27 | if list.Len() == 0 { 28 | return "" 29 | } 30 | if list.Len() == 1 { 31 | return fmt.Sprintf("%v", list.car) 32 | } 33 | 34 | switch t := list.cdr.(type) { 35 | case *List: 36 | return fmt.Sprintf("%v %v", list.car, t.childToString()) 37 | default: 38 | return fmt.Sprintf("%v %v", list.car, t) 39 | } 40 | } 41 | 42 | // Car ... 43 | func (list *List) Car() (Expression, error) { 44 | if list.IsNull() { 45 | return nil, fmt.Errorf("attempt to apply car on nil %v", list) 46 | } 47 | return list.car, nil 48 | } 49 | 50 | // Cdr ... 51 | func (list *List) Cdr() (Expression, error) { 52 | if list.IsNull() { 53 | return nil, fmt.Errorf("attempt to apply cdr on nil %v", list) 54 | } 55 | return list.cdr, nil 56 | } 57 | 58 | // Cadr second 59 | func (list *List) Cadr() (Expression, error) { 60 | cdr, err := list.Cdr() 61 | if err != nil { 62 | return nil, err 63 | } 64 | return cdr.(*List).Car() 65 | } 66 | 67 | // Cddr ... 68 | func (list *List) Cddr() (Expression, error) { 69 | cdr, err := list.Cdr() 70 | if err != nil { 71 | return nil, err 72 | } 73 | return cdr.(*List).Cdr() 74 | } 75 | 76 | // Cdddr ... 77 | func (list *List) Cdddr() (Expression, error) { 78 | cdr, err := list.Cddr() 79 | if err != nil { 80 | return nil, err 81 | } 82 | return cdr.(*List).Cdr() 83 | } 84 | 85 | // Caddr third 86 | func (list *List) Caddr() (Expression, error) { 87 | cddr, err := list.Cddr() 88 | if err != nil { 89 | return nil, err 90 | } 91 | return cddr.(*List).Car() 92 | } 93 | 94 | // IsNull ... 95 | func (list *List) IsNull() bool { 96 | return list.car == nil && list.cdr == nil 97 | } 98 | 99 | // IsList ... 100 | func (list *List) IsList() bool { 101 | if list.IsNull() { 102 | return true 103 | } 104 | switch list.cdr.(type) { 105 | case *List: 106 | return list.cdr.(*List).IsList() 107 | default: 108 | return false 109 | } 110 | } 111 | 112 | // IsPair ... 113 | func (list *List) IsPair() bool { 114 | return !list.IsNull() 115 | } 116 | 117 | // Len ... 118 | func (list *List) Len() int { 119 | if list.IsNull() { 120 | return 0 121 | } 122 | return 1 + list.cdr.(*List).Len() 123 | } 124 | 125 | // PushBack ... 126 | func (list *List) PushBack(exp Expression) { 127 | if list.IsNull() { 128 | list.car = exp 129 | list.cdr = &List{} 130 | } else { 131 | list.cdr.(*List).PushBack(exp) 132 | } 133 | } 134 | 135 | // Copy ... 136 | func (list *List) Copy() *List { 137 | res := &List{} 138 | if list.IsNull() { 139 | return res 140 | } 141 | car, ok := list.car.(*List) 142 | if ok { 143 | res.car = car.Copy() 144 | } else { 145 | res.car = list.car 146 | } 147 | cdr, ok := list.cdr.(*List) 148 | if ok { 149 | res.cdr = cdr.Copy() 150 | } else { 151 | res.cdr = list.cdr 152 | } 153 | return res 154 | } 155 | 156 | // EmptyList ... 157 | func EmptyList() *List { 158 | return &List{} 159 | } 160 | -------------------------------------------------------------------------------- /list_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestCopy(t *testing.T) { 9 | code := `(+ (* 1 2) (- 2 1))` 10 | exp, _ := Parse(code) 11 | copyed := exp.(*List).Copy() 12 | except := `(+ (* 1 2) (- 2 1))` 13 | if fmt.Sprintf("%v", copyed) != except { 14 | t.Error("Expression Error: copy") 15 | t.Log("exp: ", copyed, "except: ", except) 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | func main() { 4 | REPL() 5 | } 6 | -------------------------------------------------------------------------------- /pair.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // Pair ... 4 | type Pair struct { 5 | car Expression 6 | cdr Expression 7 | } 8 | -------------------------------------------------------------------------------- /parser.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | // Pipe ... 8 | type Pipe interface { 9 | IsEmpty() bool 10 | In(string) 11 | Out() (bool, Token) 12 | Peek() (bool, Token) 13 | } 14 | 15 | // TextPipe ... 16 | type TextPipe struct { 17 | tokens []Token 18 | } 19 | 20 | // NewTextPipe ... 21 | func NewTextPipe() *TextPipe { 22 | return &TextPipe{tokens: []Token{}} 23 | } 24 | 25 | // IsEmpty ... 26 | func (p *TextPipe) IsEmpty() bool { 27 | return len(p.tokens) <= 0 28 | } 29 | 30 | // In ... 31 | func (p *TextPipe) In(code string) { 32 | tokens := Tokenization(code) 33 | p.tokens = append(p.tokens, tokens...) 34 | } 35 | 36 | // Peek ... 37 | func (p *TextPipe) Peek() (bool, Token) { 38 | if p.IsEmpty() { 39 | return false, Token{} 40 | } 41 | return true, p.tokens[0] 42 | } 43 | 44 | // Out ... 45 | func (p *TextPipe) Out() (bool, Token) { 46 | if p.IsEmpty() { 47 | return false, Token{} 48 | } 49 | header := p.tokens[0] 50 | p.tokens = p.tokens[1:] 51 | return true, header 52 | } 53 | 54 | // Parse ... 55 | func Parse(code string) (Expression, error) { 56 | p := NewTextPipe() 57 | p.In(code) 58 | ats, err := parse(p) 59 | if err != nil { 60 | return nil, fmt.Errorf("parse error %v", code) 61 | } 62 | return ats, nil 63 | } 64 | 65 | func parse(p Pipe) (Expression, error) { 66 | exp := &List{} 67 | ok, lp := p.Out() 68 | if !ok { 69 | return nil, fmt.Errorf("unable to find token") 70 | } 71 | if lp.Name != LPARENTHESE { 72 | return nil, fmt.Errorf("unable to find LPARENTHESE") 73 | } 74 | for { 75 | ok, token := p.Peek() 76 | if !ok { 77 | return nil, fmt.Errorf("unable to find token") 78 | } 79 | switch token.Name { 80 | case LPARENTHESE: 81 | child, err := parse(p) 82 | if err != nil { 83 | return child, err 84 | } 85 | exp.PushBack(child) 86 | case RPARENTHESE: 87 | return exp, nil 88 | default: 89 | v, err := ParseToken(token) 90 | if err != nil { 91 | return v, err 92 | } 93 | exp.PushBack(v) 94 | } 95 | _, _ = p.Out() 96 | } 97 | } 98 | -------------------------------------------------------------------------------- /parser_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "testing" 6 | ) 7 | 8 | func TestParse(t *testing.T) { 9 | code := `(+ (* 1 1) (f "xx" "yy"))` 10 | ats, _ := Parse(code) 11 | except := `(+ (* 1 1) (f "xx" "yy"))` 12 | if fmt.Sprintf("%v", ats) != except { 13 | t.Error("Parse Error") 14 | t.Log("ats: ", ats, "except: ", except) 15 | } 16 | } 17 | 18 | func TestParseDefinition(t *testing.T) { 19 | code := `(begin 20 | (define (double x) (+ x x)) 21 | (define b 5) 22 | (+ (double 2) b))` 23 | ats, _ := Parse(code) 24 | except := `(begin (define (double x) (+ x x)) (define b 5) (+ (double 2) b))` 25 | if fmt.Sprintf("%v", ats) != except { 26 | t.Error("Parse Error") 27 | t.Log("ats: ", ats, "except: ", except) 28 | } 29 | } 30 | -------------------------------------------------------------------------------- /repl.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "os" 7 | ) 8 | 9 | // IOPipe ... 10 | type IOPipe struct { 11 | tokens []Token 12 | s *bufio.Scanner 13 | } 14 | 15 | // NewIOPipe ... 16 | func NewIOPipe() *IOPipe { 17 | return &IOPipe{tokens: []Token{}, s: bufio.NewScanner(os.Stdin)} 18 | } 19 | 20 | // IsEmpty ... 21 | func (p *IOPipe) IsEmpty() bool { 22 | return len(p.tokens) <= 0 23 | } 24 | 25 | // In ... 26 | func (p *IOPipe) In(code string) { 27 | tokens := Tokenization(code) 28 | p.tokens = append(p.tokens, tokens...) 29 | } 30 | 31 | // Peek ... 32 | func (p *IOPipe) Peek() (bool, Token) { 33 | if p.IsEmpty() { 34 | fmt.Print(">> ") 35 | p.s.Scan() 36 | p.In(p.s.Text()) 37 | } 38 | return true, p.tokens[0] 39 | } 40 | 41 | // Out ... 42 | func (p *IOPipe) Out() (bool, Token) { 43 | if p.IsEmpty() { 44 | fmt.Print(">> ") 45 | p.s.Scan() 46 | p.In(p.s.Text()) 47 | } 48 | header := p.tokens[0] 49 | p.tokens = p.tokens[1:] 50 | return true, header 51 | } 52 | 53 | // REPL ... 54 | func REPL() { 55 | p := NewIOPipe() 56 | fmt.Println("Welcome to Flang") 57 | env := BaseEnv() 58 | for { 59 | ats, err := parse(p) 60 | if err != nil { 61 | continue 62 | } 63 | res, err := Eval(ats, env) 64 | if err != nil { 65 | fmt.Println(err) 66 | continue 67 | } 68 | _, _ = printExp(res) 69 | } 70 | } 71 | -------------------------------------------------------------------------------- /tokenizer.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | // TokenName ... 4 | type TokenName int 5 | 6 | // Token ... 7 | type Token struct { 8 | Value string 9 | Name TokenName 10 | } 11 | 12 | // Tokens ... 13 | const ( 14 | LPARENTHESE TokenName = iota 15 | RPARENTHESE 16 | SYMBOL 17 | BOOLEAN 18 | NUMBER 19 | STRING 20 | ) 21 | 22 | // Tokenization ... 23 | func Tokenization(code string) []Token { 24 | tokens := []Token{} 25 | i := 0 26 | for { 27 | i = trimLeft(code, i) 28 | if i < 0 { 29 | break 30 | } 31 | item, nextIndex := nextItem(code, i) 32 | if nextIndex < 0 { 33 | break 34 | } 35 | tokens = append(tokens, Token{item, getType(item)}) 36 | i = nextIndex 37 | } 38 | return tokens 39 | } 40 | 41 | func isSpliter(r byte) bool { 42 | return r == ' ' || r == '\t' || r == '\r' || r == '\n' 43 | } 44 | 45 | func getString(code string, i int) (string, int) { 46 | if i+1 >= len(code) { 47 | return "", -1 48 | } 49 | item := code[i : i+1] 50 | next := i + 1 51 | for { 52 | if next >= len(code) { 53 | return "", -1 54 | } 55 | item += code[next : next+1] 56 | if code[next] == '"' { 57 | next++ 58 | break 59 | } 60 | next++ 61 | } 62 | return item, next 63 | } 64 | 65 | func trimLeft(code string, i int) int { 66 | if i >= len(code) { 67 | return -1 68 | } 69 | next := i 70 | for isSpliter(code[next]) { 71 | next++ 72 | if next >= len(code) { 73 | return -1 74 | } 75 | } 76 | return next 77 | } 78 | 79 | func nextItem(code string, i int) (string, int) { 80 | if i >= len(code) { 81 | return "", -1 82 | } 83 | if code[i] == '(' || code[i] == ')' { 84 | return code[i : i+1], i + 1 85 | } 86 | if code[i] == '"' { 87 | return getString(code, i) 88 | } 89 | var item string 90 | next := i 91 | for { 92 | if isSpliter(code[next]) || code[next] == '(' || code[next] == ')' { 93 | break 94 | } 95 | item += code[next : next+1] 96 | next++ 97 | if next == len(code) { 98 | break 99 | } 100 | if next > len(code) { 101 | return "", -1 102 | } 103 | } 104 | return item, next 105 | } 106 | 107 | func getType(item string) TokenName { 108 | switch { 109 | case item == "(": 110 | return LPARENTHESE 111 | case item == ")": 112 | return RPARENTHESE 113 | case IsBool(item): 114 | return BOOLEAN 115 | case IsString(item): 116 | return STRING 117 | case IsNumeric(item): 118 | return NUMBER 119 | default: 120 | return SYMBOL 121 | } 122 | } 123 | -------------------------------------------------------------------------------- /tokenizer_test.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | func TestTokenization(t *testing.T) { 9 | code := ` 10 | (define x 11 | ( lambda (p1) 12 | (y "hello world" p1 99))) 13 | ` 14 | tokens := Tokenization(code) 15 | except := []Token{ 16 | Token{"(", LPARENTHESE}, 17 | Token{"define", SYMBOL}, 18 | Token{"x", SYMBOL}, 19 | Token{"(", LPARENTHESE}, 20 | Token{"lambda", SYMBOL}, 21 | Token{"(", LPARENTHESE}, 22 | Token{"p1", SYMBOL}, 23 | Token{")", RPARENTHESE}, 24 | Token{"(", LPARENTHESE}, 25 | Token{"y", SYMBOL}, 26 | Token{"\"hello world\"", STRING}, 27 | Token{"p1", SYMBOL}, 28 | Token{"99", NUMBER}, 29 | Token{")", RPARENTHESE}, 30 | Token{")", RPARENTHESE}, 31 | Token{")", RPARENTHESE}, 32 | } 33 | if !reflect.DeepEqual(tokens, except) { 34 | t.Error("Tokenization Error") 35 | t.Log("tokens: ", tokens, "except: ", except) 36 | } 37 | } 38 | 39 | func TestTokenizationOneValue(t *testing.T) { 40 | code := `1` 41 | tokens := Tokenization(code) 42 | except := []Token{ 43 | Token{"1", NUMBER}, 44 | } 45 | if !reflect.DeepEqual(tokens, except) { 46 | t.Error("Tokenization Error") 47 | t.Log("tokens: ", tokens, "except: ", except) 48 | } 49 | } 50 | 51 | func TestTokenizationTowValue(t *testing.T) { 52 | code := `1 "abc"` 53 | tokens := Tokenization(code) 54 | except := []Token{ 55 | Token{"1", NUMBER}, 56 | Token{"\"abc\"", STRING}, 57 | } 58 | if !reflect.DeepEqual(tokens, except) { 59 | t.Error("Tokenization Error") 60 | t.Log("tokens: ", tokens, "except: ", except) 61 | } 62 | } 63 | -------------------------------------------------------------------------------- /util.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "strconv" 5 | ) 6 | 7 | // IsNumeric ... 8 | func IsNumeric(s string) bool { 9 | _, err := strconv.ParseFloat(s, 64) 10 | return err == nil 11 | } 12 | 13 | // IsString ... 14 | func IsString(s string) bool { 15 | return s[0] == '"' && s[len(s)-1] == '"' 16 | } 17 | 18 | // IsBool ... 19 | func IsBool(s string) bool { 20 | return s == "#t" || s == "#f" 21 | } 22 | 23 | // ToFloat ... 24 | func ToFloat(s string) float64 { 25 | f, _ := strconv.ParseFloat(s, 64) 26 | return f 27 | } 28 | 29 | // ToBool ... 30 | func ToBool(s string) bool { 31 | return s == "#t" 32 | } 33 | --------------------------------------------------------------------------------