├── .travis.yml ├── LICENSE ├── README.md ├── ast ├── apply.go ├── begin.go ├── block.go ├── call.go ├── define.go ├── delay.go ├── float.go ├── force.go ├── function.go ├── go.go ├── if.go ├── int.go ├── lambda.go ├── let.go ├── letrec.go ├── letstar.go ├── name.go ├── node.go ├── pair.go ├── quasiquote.go ├── quote.go ├── select.go ├── set.go ├── string.go ├── tuple.go ├── unquote.go └── unquote_splicing.go ├── binder └── binder.go ├── constants └── constants.go ├── converter └── converter.go ├── lexer └── lexer.go ├── main.go ├── parser ├── expand.go ├── parser.go └── preparser.go ├── repl └── repl.go ├── scope └── scope.go ├── stdlib.ss ├── tests ├── binding_test.ss ├── define_test.ss ├── if_test.ss ├── lambda_test.ss ├── ping_pong_test.out ├── ping_pong_test.ss ├── prim_test.ss ├── promise_test.ss ├── quasiquote_test.ss ├── select_ping_pong_test.out ├── select_ping_pong_test.ss ├── select_test.ss ├── stdlib_test.ss ├── tests.go └── tests_test.go └── value ├── boolvalue.go ├── channel.go ├── closure.go ├── floatvalue.go ├── intvalue.go ├── pairvalue.go ├── primfunc.go ├── primitives ├── add.go ├── and.go ├── car.go ├── cdr.go ├── chan_recv.go ├── chan_send.go ├── close_chan.go ├── cons.go ├── display.go ├── div.go ├── eq.go ├── gt.go ├── gte.go ├── is_equal.go ├── is_eqv.go ├── lt.go ├── lte.go ├── make_chan.go ├── mod.go ├── mult.go ├── newline.go ├── or.go ├── random.go ├── sleep.go ├── sub.go └── typeof.go ├── promise.go ├── stringvalue.go ├── symbol.go └── value.go /.travis.yml: -------------------------------------------------------------------------------- 1 | language: go 2 | 3 | go: 4 | - 1.2 5 | - 1.3 6 | - tip 7 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Wei Sun 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 | LispEx [![Build Status](https://travis-ci.org/kedebug/LispEx.svg?branch=master)](https://travis-ci.org/kedebug/LispEx) 2 | ====== 3 | A dialect of Lisp extended to support concurrent programming. 4 | 5 | 6 | ### Overview 7 | LispEx is another *Lisp Interpreter* implemented with *Go*. The syntax, semantics and library procedures are a subset of [R5RS](http://www.schemers.org/Documents/Standards/R5RS/): 8 | 9 | ```ss 10 | LispEx 0.1.0 (Saturday, 19-Jul-14 12:52:45 CST) 11 | 12 | ;; lambda expression 13 | >>> ((lambda (x y . z) (+ x y (car z))) 1 2 5 11) 14 | 8 15 | 16 | ;; currying 17 | >>> (define (curry func arg1) (lambda (arg) (apply func arg1 (list arg)))) 18 | >>> (map (curry + 2) '(1 2 3 4)) 19 | (3 4 5 6) 20 | 21 | ;; apply 22 | >>> (apply + 1 2 '(3 4)) 23 | 10 24 | 25 | ;; composite function 26 | >>> (define ((compose f g) x) (f (g x))) 27 | >>> (define caar (compose car car)) 28 | >>> (caar '((1 2) 3 4)) 29 | 1 30 | 31 | ;; tail recursion 32 | >>> (letrec 33 | ((even? (lambda (n) (if (= 0 n) #t (odd? (- n 1))))) 34 | (odd? (lambda (n) (if (= 0 n) #f (even? (- n 1)))))) 35 | (even? 88)) 36 | #t 37 | 38 | ;; multiple nestings of quasiquote 39 | ;; (challenging to have a right implementation) 40 | >>> `(1 `,(+ 1 ,(+ 2 3)) 4) 41 | (1 `,(+ 1 5) 4) 42 | >>> `(1 ```,,@,,@(list (+ 1 2)) 4) 43 | (1 ```,,@,3 4) 44 | 45 | ;; lazy evaluation 46 | >>> (define f (delay (+ 1))) 47 | >>> (force f) 48 | 1 49 | ``` 50 | 51 | What's new, the *Go*-like concurrency features are introduced in LispEx. You can start new coroutines with `go` statements, and use `<-chan` or `chan<-` connecting them. A ping-pong example is shown below: 52 | 53 | ```ss 54 | ; define channels 55 | (define ping-chan (make-chan)) 56 | (define pong-chan (make-chan)) 57 | ; define a buffered channel 58 | (define sem (make-chan 2)) 59 | 60 | (define (ping n) 61 | (if (> n 0) 62 | (begin 63 | (display (<-chan ping-chan)) 64 | (newline) 65 | (chan<- pong-chan 'pong) 66 | (ping (- n 1))) 67 | (chan<- sem 'exit-ping))) 68 | 69 | (define (pong n) 70 | (if (> n 0) 71 | (begin 72 | (chan<- ping-chan 'ping) 73 | (display (<-chan pong-chan)) 74 | (newline) 75 | (pong (- n 1))) 76 | (chan<- sem 'exit-pong))) 77 | 78 | (go (ping 6)) ; start ping-routine 79 | (go (pong 6)) ; start pong-routine 80 | 81 | ; implement semaphore with channel, waiting for ping-pong finishing 82 | (<-chan sem) (newline) 83 | (<-chan sem) (newline) 84 | 85 | ; should close channels if you don't need it 86 | (close-chan sem) 87 | (close-chan pong-chan) 88 | (close-chan ping-chan) 89 | 90 | ; the output will be: ping pong ping pong ... exit-ping exit-pong 91 | ``` 92 | 93 | Furthermore, `select` statement is also supported, which is necessary for you to select between multiple channels that working with concurrent routines. Just like *Go*, the code can be written like this: 94 | 95 | ```ss 96 | (define chan-1 (make-chan)) 97 | (define chan-2 (make-chan)) 98 | 99 | (go (chan<- chan-1 'hello-chan-1)) 100 | (go (chan<- chan-2 'hello-chan-2)) 101 | 102 | (select 103 | ((<-chan chan-1)) 104 | ((<-chan chan-2)) 105 | (default 'hello-default)) 106 | 107 | (close-chan chan-1) 108 | (close-chan chan-2) 109 | 110 | ; the output will be: hello-default, as it will cost some CPU times when a coroutine is lanuched. 111 | ``` 112 | 113 | In this scenario, `default` case is chosen since there is no ready data in `chan-1` or `chan-2` when `select` statement is intepretered. But such scenario will be changed if we `sleep` the main thread for a while: 114 | 115 | ```ss 116 | (define chan-1 (make-chan)) 117 | (define chan-2 (make-chan)) 118 | 119 | (go (chan<- chan-1 'hello-chan-1)) 120 | (go (chan<- chan-2 'hello-chan-2)) 121 | 122 | ; sleep for 20 millisecond 123 | (sleep 20) 124 | 125 | (select 126 | ((<-chan chan-1)) 127 | ((<-chan chan-2)) 128 | (default 'hello-default)) 129 | 130 | (close-chan chan-1) 131 | (close-chan chan-2) 132 | 133 | ; the output will be randomized: hello-chan-1 or hello-chan-2 134 | ``` 135 | 136 | For more interesting examples, please see files under [tests](/tests) folder. 137 | 138 | 139 | ### Features 140 | - Clean designed code, very easy for you to understand the principle, inspired from [yin](https://github.com/yinwang0/yin) 141 | - A concurrent design for lexical scanning, inspired from [Rob Pike](http://cuddle.googlecode.com/hg/talk/lex.html#title-slide) 142 | - Builtin routines, channels and other necessary components for concurrent programming 143 | - Give you a REPL 144 | 145 | ### In developing 146 | - `loop` in R5RS 147 | - tail call optimization 148 | - type checker 149 | 150 | 151 | ### Have a try 152 | ``` 153 | git clone https://github.com/kedebug/LispEx.git 154 | cd LispEx 155 | go build && ./LispEx 156 | LispEx 0.1.0 (Saturday, 19-Jul-14 12:52:45 CST) 157 | >>> 158 | ``` 159 | From here you can type in forms and you'll get the evaluated expressions back. To interpreter a file: 160 | ``` 161 | ./LispEx filename.ss 162 | ``` 163 | Lisp is fun, go is fun, concurrency is fun. Hope you will have an extraordinary programming experience with LispEx. 164 | 165 | ### License 166 | MIT 167 | -------------------------------------------------------------------------------- /ast/apply.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/converter" 6 | "github.com/kedebug/LispEx/scope" 7 | . "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type Apply struct { 11 | Proc Node 12 | Args []Node 13 | } 14 | 15 | func NewApply(proc Node, args []Node) *Apply { 16 | return &Apply{Proc: proc, Args: args} 17 | } 18 | 19 | func (self *Apply) Eval(s *scope.Scope) Value { 20 | // (apply proc arg1 ... args) 21 | // Proc must be a procedure and args must be a list. 22 | // Calls proc with the elements of the list 23 | // (append (list arg1 ...) args) as the actual arguments. 24 | 25 | proc := self.Proc.Eval(s) 26 | args := ExpandApplyArgs(EvalList(self.Args, s)) 27 | 28 | switch proc.(type) { 29 | case *Closure: 30 | curry := proc.(*Closure) 31 | lambda, ok := curry.Body.(*Lambda) 32 | if !ok { 33 | panic(fmt.Sprint("unexpected type: ", curry.Body)) 34 | } 35 | env := curry.Env.(*scope.Scope) 36 | BindArguments(env, lambda.Params, args) 37 | return lambda.Body.Eval(env) 38 | case PrimFunc: 39 | return proc.(PrimFunc).Apply(converter.PairsToSlice(args)) 40 | default: 41 | panic(fmt.Sprintf("apply: expected a procedure, given: %s", self.Proc)) 42 | } 43 | } 44 | 45 | func (self *Apply) String() string { 46 | return fmt.Sprintf("(apply %s %s)", self.Proc, self.Args) 47 | } 48 | 49 | // (1 2 '(3)) => (1 2 3) 50 | func ExpandApplyArgs(args []Value) Value { 51 | prev := NewPairValue(nil, nil) 52 | curr := NewPairValue(nil, nil) 53 | front := prev 54 | expectlist := false 55 | 56 | for i, arg := range args { 57 | switch arg.(type) { 58 | case *PairValue: 59 | prev.Second = arg.(*PairValue) 60 | for { 61 | if _, ok := arg.(*PairValue); ok { 62 | arg = arg.(*PairValue).Second 63 | } else if _, ok := arg.(*EmptyPairValue); ok { 64 | break 65 | } else { 66 | panic(fmt.Sprint("apply: expected list, given: ", arg)) 67 | } 68 | } 69 | expectlist = false 70 | if i != len(args)-1 { 71 | panic(fmt.Sprint("apply: expected list, given: ", arg)) 72 | } 73 | case *EmptyPairValue: 74 | expectlist = false 75 | if i != len(args)-1 { 76 | panic(fmt.Sprint("apply: expected list, given: ", arg)) 77 | } 78 | default: 79 | expectlist = true 80 | curr.First = arg 81 | prev.Second = curr 82 | prev = curr 83 | curr = NewPairValue(nil, nil) 84 | } 85 | } 86 | if expectlist { 87 | panic(fmt.Sprint("apply: expected list, given: ", args[len(args)-1])) 88 | } 89 | return front.Second 90 | } 91 | -------------------------------------------------------------------------------- /ast/begin.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Begin struct { 10 | Body Node 11 | } 12 | 13 | func NewBegin(body Node) *Begin { 14 | return &Begin{Body: body} 15 | } 16 | 17 | func (self *Begin) Eval(env *scope.Scope) value.Value { 18 | // The s are evaluated sequentially from left to right, 19 | // and the value(s) of the last is(are) returned. 20 | 21 | return self.Body.Eval(env) 22 | } 23 | 24 | func (self *Begin) String() string { 25 | if self.Body.String() == "" { 26 | return "(begin)" 27 | } else { 28 | return fmt.Sprintf("(begin %s)", self.Body) 29 | } 30 | } 31 | -------------------------------------------------------------------------------- /ast/block.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Block struct { 10 | Exprs []Node 11 | } 12 | 13 | func NewBlock(exprs []Node) *Block { 14 | return &Block{Exprs: exprs} 15 | } 16 | 17 | func (self *Block) Eval(env *scope.Scope) value.Value { 18 | length := len(self.Exprs) 19 | if length == 0 { 20 | return nil 21 | } 22 | for i := 0; i < length-1; i++ { 23 | self.Exprs[i].Eval(env) 24 | } 25 | return self.Exprs[length-1].Eval(env) 26 | } 27 | 28 | func (self *Block) String() string { 29 | var s string 30 | for i, expr := range self.Exprs { 31 | if i == 0 { 32 | s += fmt.Sprintf("%s", expr) 33 | } else { 34 | s += fmt.Sprintf(" %s", expr) 35 | } 36 | } 37 | return s 38 | } 39 | -------------------------------------------------------------------------------- /ast/call.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/converter" 6 | "github.com/kedebug/LispEx/scope" 7 | . "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type Call struct { 11 | Callee Node 12 | Args []Node 13 | } 14 | 15 | func NewCall(callee Node, args []Node) *Call { 16 | return &Call{Callee: callee, Args: args} 17 | } 18 | 19 | func (self *Call) Eval(s *scope.Scope) Value { 20 | callee := self.Callee.Eval(s) 21 | // we will handle (+ . (1)) latter 22 | args := EvalList(self.Args, s) 23 | 24 | switch callee.(type) { 25 | case *Closure: 26 | curry := callee.(*Closure) 27 | env := scope.NewScope(curry.Env.(*scope.Scope)) 28 | lambda, ok := curry.Body.(*Lambda) 29 | if !ok { 30 | panic(fmt.Sprint("unexpected type: ", curry.Body)) 31 | } 32 | // bind call arguments to parameters 33 | // these nodes should be in Lisp pair structure 34 | BindArguments(env, lambda.Params, converter.SliceToPairValues(args)) 35 | return lambda.Body.Eval(env) 36 | case PrimFunc: 37 | return callee.(PrimFunc).Apply(args) 38 | default: 39 | panic(fmt.Sprintf("%s: not allowed in a call context, args: %s", callee, self.Args[0])) 40 | } 41 | } 42 | 43 | func (self *Call) String() string { 44 | var s string 45 | for _, arg := range self.Args { 46 | s += fmt.Sprintf(" %s", arg) 47 | } 48 | return fmt.Sprintf("(%s%s)", self.Callee, s) 49 | } 50 | 51 | func BindArguments(env *scope.Scope, params Node, args Value) { 52 | if name, ok := params.(*Name); ok && args == NilPairValue { 53 | // ((lambda x ) '()) 54 | env.Put(name.Identifier, args) 55 | return 56 | } 57 | for { 58 | if params == NilPair && args == NilPairValue { 59 | return 60 | } else if params == NilPair && args != NilPairValue { 61 | panic(fmt.Sprint("too many arguments")) 62 | } else if params != NilPair && args == NilPairValue { 63 | panic(fmt.Sprint("missing arguments")) 64 | } 65 | switch params.(type) { 66 | case *Pair: 67 | // R5RS declare first element must be a *Name* 68 | name, _ := params.(*Pair).First.(*Name) 69 | pair, ok := args.(*PairValue) 70 | if !ok { 71 | panic(fmt.Sprint("arguments does not match given number")) 72 | } 73 | env.Put(name.Identifier, pair.First) 74 | params = params.(*Pair).Second 75 | args = pair.Second 76 | case *Name: 77 | env.Put(params.(*Name).Identifier, args) 78 | return 79 | } 80 | } 81 | } 82 | -------------------------------------------------------------------------------- /ast/define.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/binder" 6 | "github.com/kedebug/LispEx/scope" 7 | "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type Define struct { 11 | Pattern *Name 12 | Value Node 13 | } 14 | 15 | func NewDefine(pattern *Name, val Node) *Define { 16 | return &Define{Pattern: pattern, Value: val} 17 | } 18 | 19 | func (self *Define) Eval(env *scope.Scope) value.Value { 20 | binder.Define(env, self.Pattern.Identifier, self.Value.Eval(env)) 21 | return nil 22 | } 23 | 24 | func (self *Define) String() string { 25 | return fmt.Sprintf("(define %s %s)", self.Pattern, self.Value) 26 | } 27 | -------------------------------------------------------------------------------- /ast/delay.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Delay struct { 10 | Expr Node 11 | } 12 | 13 | func NewDelay(expr Node) *Delay { 14 | return &Delay{Expr: expr} 15 | } 16 | 17 | func (self *Delay) Eval(env *scope.Scope) value.Value { 18 | return value.NewPromise(env, self.Expr) 19 | } 20 | 21 | func (self *Delay) String() string { 22 | return fmt.Sprintf("(delay %s)", self.Expr) 23 | } 24 | -------------------------------------------------------------------------------- /ast/float.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | "strconv" 8 | ) 9 | 10 | type Float struct { 11 | Value float64 12 | } 13 | 14 | func NewFloat(s string) *Float { 15 | val, err := strconv.ParseFloat(s, 64) 16 | if err != nil { 17 | panic(fmt.Sprintf("%s is not float format", s)) 18 | } 19 | return &Float{Value: val} 20 | } 21 | 22 | func (self *Float) Eval(s *scope.Scope) value.Value { 23 | return value.NewFloatValue(self.Value) 24 | } 25 | 26 | func (self *Float) String() string { 27 | return strconv.FormatFloat(self.Value, 'f', -1, 64) 28 | } 29 | -------------------------------------------------------------------------------- /ast/force.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | . "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Force struct { 10 | Promise Node 11 | } 12 | 13 | func NewForce(promise Node) *Force { 14 | return &Force{Promise: promise} 15 | } 16 | 17 | func (self *Force) Eval(s *scope.Scope) Value { 18 | val := self.Promise.Eval(s) 19 | if promise, ok := val.(*Promise); ok { 20 | if promise.IsVal == false { 21 | return nil 22 | } else { 23 | promise.IsVal = false 24 | env := promise.Env.(*scope.Scope) 25 | lazy := promise.Lazy.(Node) 26 | return lazy.Eval(env) 27 | } 28 | } else { 29 | panic(fmt.Sprintf("force: expected argument of type , given: %s", val)) 30 | } 31 | } 32 | 33 | func (self *Force) String() string { 34 | return fmt.Sprintf("(force %s)", self.Promise) 35 | } 36 | -------------------------------------------------------------------------------- /ast/function.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | // ((foo 1) 2) 10 | type Function struct { 11 | Caller *Name 12 | Body Node 13 | } 14 | 15 | func NewFunction(caller *Name, body Node) *Function { 16 | return &Function{Caller: caller, Body: body} 17 | } 18 | 19 | func (self *Function) Eval(env *scope.Scope) value.Value { 20 | return self.Body.Eval(env) 21 | } 22 | 23 | func (self *Function) String() string { 24 | return fmt.Sprintf("%s", self.Body) 25 | } 26 | -------------------------------------------------------------------------------- /ast/go.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | . "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Go struct { 10 | Expr Node 11 | } 12 | 13 | func NewGo(expr Node) *Go { 14 | return &Go{Expr: expr} 15 | } 16 | 17 | func (self *Go) Eval(env *scope.Scope) Value { 18 | // We need to recover the panic message of goroutine 19 | go func() { 20 | defer func() { 21 | if err := recover(); err != nil { 22 | fmt.Println(err) 23 | } 24 | }() 25 | self.Expr.Eval(scope.NewScope(env)) 26 | }() 27 | return nil 28 | } 29 | 30 | func (self *Go) String() string { 31 | return fmt.Sprintf("(go %s)", self.Expr) 32 | } 33 | -------------------------------------------------------------------------------- /ast/if.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/constants" 6 | "github.com/kedebug/LispEx/scope" 7 | "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type If struct { 11 | Test Node 12 | Then Node 13 | Else Node 14 | } 15 | 16 | func NewIf(test, then, else_ Node) *If { 17 | return &If{Test: test, Then: then, Else: else_} 18 | } 19 | 20 | func (self *If) Eval(env *scope.Scope) value.Value { 21 | tv := self.Test.Eval(env) 22 | if bv, ok := tv.(*value.BoolValue); ok { 23 | if bv.Value == false { 24 | if self.Else == nil { 25 | return nil 26 | } else { 27 | return self.Else.Eval(env) 28 | } 29 | } 30 | } 31 | return self.Then.Eval(env) 32 | } 33 | 34 | func (self *If) String() string { 35 | return fmt.Sprintf("(%s %s %s %s)", constants.IF, self.Test, self.Then, self.Else) 36 | } 37 | -------------------------------------------------------------------------------- /ast/int.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | "strconv" 8 | "strings" 9 | ) 10 | 11 | type Int struct { 12 | Value int64 13 | Base int 14 | } 15 | 16 | func NewInt(s string) *Int { 17 | var val int64 18 | var base int 19 | var err error 20 | var missed string 21 | 22 | if strings.HasPrefix(s, "0x") || strings.HasPrefix(s, "0X") { 23 | missed = s[:2] 24 | s = s[2:] 25 | base = 16 26 | } else { 27 | base = 10 28 | } 29 | val, err = strconv.ParseInt(s, base, 64) 30 | if err != nil { 31 | panic(fmt.Sprintf("%s is not integer format", missed+s)) 32 | } 33 | return &Int{Value: val, Base: base} 34 | } 35 | 36 | func (self *Int) Eval(env *scope.Scope) value.Value { 37 | return value.NewIntValue(self.Value) 38 | } 39 | 40 | func (self *Int) String() string { 41 | return strconv.FormatInt(self.Value, self.Base) 42 | } 43 | -------------------------------------------------------------------------------- /ast/lambda.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | // http://docs.racket-lang.org/guide/lambda.html 10 | // (lambda x body) 11 | // (lambda (x) body) 12 | type Lambda struct { 13 | Params Node 14 | Body Node 15 | } 16 | 17 | func NewLambda(params Node, body Node) *Lambda { 18 | if params == nil { 19 | params = NilPair 20 | } 21 | return &Lambda{Params: params, Body: body} 22 | } 23 | 24 | func (self *Lambda) Eval(env *scope.Scope) value.Value { 25 | return value.NewClosure(env, self) 26 | } 27 | 28 | func (self *Lambda) String() string { 29 | return fmt.Sprintf("(lambda %s %s)", self.Params, self.Body) 30 | } 31 | -------------------------------------------------------------------------------- /ast/let.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/binder" 6 | "github.com/kedebug/LispEx/scope" 7 | "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type Let struct { 11 | Patterns []*Name 12 | Exprs []Node 13 | Body Node 14 | } 15 | 16 | func NewLet(patterns []*Name, exprs []Node, body Node) *Let { 17 | return &Let{Patterns: patterns, Exprs: exprs, Body: body} 18 | } 19 | 20 | func (self *Let) Eval(s *scope.Scope) value.Value { 21 | // The s are evaluated in the current environment 22 | // (in some unspecified order), the s are bound 23 | // to fresh locations holding the results, the is 24 | // evaluated in the extended environment, and the value(s) 25 | // of the last expression of is(are) returned. 26 | 27 | env := scope.NewScope(s) 28 | extended := scope.NewScope(s) 29 | for i := 0; i < len(self.Patterns); i++ { 30 | binder.Define(extended, self.Patterns[i].Identifier, self.Exprs[i].Eval(env)) 31 | } 32 | return self.Body.Eval(extended) 33 | } 34 | 35 | func (self *Let) String() string { 36 | var bindings string 37 | for i := 0; i < len(self.Patterns); i++ { 38 | if i == 0 { 39 | bindings += fmt.Sprintf("(%s %s)", self.Patterns[i], self.Exprs[i]) 40 | } else { 41 | bindings += fmt.Sprintf(" (%s %s)", self.Patterns[i], self.Exprs[i]) 42 | } 43 | } 44 | return fmt.Sprintf("(let (%s) %s)", bindings, self.Body) 45 | } 46 | -------------------------------------------------------------------------------- /ast/letrec.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/binder" 6 | "github.com/kedebug/LispEx/scope" 7 | "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type LetRec struct { 11 | Patterns []*Name 12 | Exprs []Node 13 | Body Node 14 | } 15 | 16 | func NewLetRec(patterns []*Name, exprs []Node, body Node) *LetRec { 17 | return &LetRec{Patterns: patterns, Exprs: exprs, Body: body} 18 | } 19 | 20 | func (self *LetRec) Eval(s *scope.Scope) value.Value { 21 | // The s are bound to fresh locations holding undefined values, 22 | // the s are evaluated in the resulting environment 23 | // (in some unspecified order), each is assigned to the result of 24 | // the corresponding , the is evaluated in the resulting 25 | // environment, and the value(s) of the last expression in is(are) 26 | // returned. Each binding of a has the entire letrec expression 27 | // as its region, making it possible to define mutually recursive procedures. 28 | 29 | env := scope.NewScope(s) 30 | extended := make([]*scope.Scope, len(self.Patterns)) 31 | for i := 0; i < len(self.Patterns); i++ { 32 | extended[i] = scope.NewScope(env) 33 | binder.Define(extended[i], self.Patterns[i].Identifier, self.Exprs[i].Eval(env)) 34 | } 35 | for i := 0; i < len(extended); i++ { 36 | env.PutAll(extended[i]) 37 | } 38 | return self.Body.Eval(env) 39 | } 40 | 41 | func (self *LetRec) String() string { 42 | var bindings string 43 | for i := 0; i < len(self.Patterns); i++ { 44 | if i == 0 { 45 | bindings += fmt.Sprintf("(%s %s)", self.Patterns[i], self.Exprs[i]) 46 | } else { 47 | bindings += fmt.Sprintf(" (%s %s)", self.Patterns[i], self.Exprs[i]) 48 | } 49 | } 50 | return fmt.Sprintf("(letrec (%s) %s)", bindings, self.Body) 51 | } 52 | -------------------------------------------------------------------------------- /ast/letstar.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/binder" 6 | "github.com/kedebug/LispEx/scope" 7 | "github.com/kedebug/LispEx/value" 8 | ) 9 | 10 | type LetStar struct { 11 | Patterns []*Name 12 | Exprs []Node 13 | Body Node 14 | } 15 | 16 | func NewLetStar(patterns []*Name, exprs []Node, body Node) *LetStar { 17 | return &LetStar{Patterns: patterns, Exprs: exprs, Body: body} 18 | } 19 | 20 | func (self *LetStar) Eval(env *scope.Scope) value.Value { 21 | // Let* is similar to let, but the bindings are performed sequentially 22 | // from left to right, and the region of a binding indicated by 23 | // ( ) is that part of the let* expression to the right 24 | // of the binding. Thus the second binding is done in an environment in 25 | // which the first binding is visible, and so on. 26 | 27 | for i := 0; i < len(self.Patterns); i++ { 28 | env = scope.NewScope(env) 29 | binder.Define(env, self.Patterns[i].Identifier, self.Exprs[i].Eval(env)) 30 | } 31 | return self.Body.Eval(env) 32 | } 33 | 34 | func (self *LetStar) String() string { 35 | var bindings string 36 | for i := 0; i < len(self.Patterns); i++ { 37 | if i == 0 { 38 | bindings += fmt.Sprintf("(%s %s)", self.Patterns[i], self.Exprs[i]) 39 | } else { 40 | bindings += fmt.Sprintf(" (%s %s)", self.Patterns[i], self.Exprs[i]) 41 | } 42 | } 43 | return fmt.Sprintf("(let* (%s) %s)", bindings, self.Body) 44 | } 45 | -------------------------------------------------------------------------------- /ast/name.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | . "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Name struct { 10 | Identifier string 11 | } 12 | 13 | func NewName(identifier string) *Name { 14 | return &Name{Identifier: identifier} 15 | } 16 | 17 | func (self *Name) Eval(env *scope.Scope) Value { 18 | if val := env.Lookup(self.Identifier); val != nil { 19 | return val.(Value) 20 | } else { 21 | panic(fmt.Sprintf("%s: undefined identifier", self.Identifier)) 22 | } 23 | } 24 | 25 | func (self *Name) String() string { 26 | return self.Identifier 27 | } 28 | -------------------------------------------------------------------------------- /ast/node.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "github.com/kedebug/LispEx/scope" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | // Node can be seen as Value => 9 | // ' : The single quote character is used to indicate literal data 10 | // ` : The backquote character is used to indicate almost-constant data 11 | type Node interface { 12 | value.Value 13 | Eval(s *scope.Scope) value.Value 14 | } 15 | 16 | func EvalList(nodes []Node, s *scope.Scope) []value.Value { 17 | values := make([]value.Value, 0, len(nodes)) 18 | for _, node := range nodes { 19 | values = append(values, node.Eval(s)) 20 | } 21 | return values 22 | } 23 | -------------------------------------------------------------------------------- /ast/pair.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Pair struct { 10 | First Node 11 | Second Node 12 | } 13 | 14 | var NilPair = NewEmptyPair() 15 | 16 | type EmptyPair struct { 17 | } 18 | 19 | func NewEmptyPair() *EmptyPair { 20 | return &EmptyPair{} 21 | } 22 | 23 | func (self *EmptyPair) Eval(s *scope.Scope) value.Value { 24 | return value.NilPairValue 25 | } 26 | 27 | func (self *EmptyPair) String() string { 28 | return "()" 29 | } 30 | 31 | func NewPair(first, second Node) *Pair { 32 | if second == nil { 33 | second = NilPair 34 | } 35 | return &Pair{First: first, Second: second} 36 | } 37 | 38 | func (self *Pair) Eval(env *scope.Scope) value.Value { 39 | var first value.Value 40 | var second value.Value 41 | 42 | if self.Second == NilPair { 43 | second = value.NilPairValue 44 | } else { 45 | switch self.Second.(type) { 46 | case *Name: 47 | second = value.NewSymbol(self.Second.(*Name).Identifier) 48 | default: 49 | second = self.Second.Eval(env) 50 | } 51 | } 52 | 53 | if name, ok := self.First.(*Name); ok { 54 | // treat Name as Symbol 55 | first = value.NewSymbol(name.Identifier) 56 | } else if _, ok := self.First.(*UnquoteSplicing); ok { 57 | // our parser garantees unquote-splicing only appears in quasiquote 58 | // and unquote-splicing will be evaluated to a list 59 | first = self.First.Eval(env) 60 | // () empty list must be handled 61 | if first == value.NilPairValue { 62 | return second 63 | } 64 | // seek for the last element 65 | var last value.Value = first 66 | for { 67 | switch last.(type) { 68 | case *value.PairValue: 69 | pair := last.(*value.PairValue) 70 | if pair.Second == value.NilPairValue { 71 | pair.Second = second 72 | return first 73 | } 74 | last = pair.Second 75 | default: 76 | if second == value.NilPairValue { 77 | return first 78 | } else { 79 | // `(,@(cdr '(1 . 2) 3)) 80 | panic(fmt.Sprintf("unquote-splicing: expected list?, given: %s", first)) 81 | } 82 | } 83 | } 84 | } else { 85 | first = self.First.Eval(env) 86 | } 87 | return value.NewPairValue(first, second) 88 | } 89 | 90 | func (self *Pair) String() string { 91 | if self.Second == NilPair { 92 | return fmt.Sprintf("(%s)", self.First) 93 | } 94 | switch self.Second.(type) { 95 | case *Pair: 96 | return fmt.Sprintf("(%s %s", self.First, self.Second.String()[1:]) 97 | default: 98 | return fmt.Sprintf("(%s . %s)", self.First, self.Second) 99 | } 100 | } 101 | -------------------------------------------------------------------------------- /ast/quasiquote.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Quasiquote struct { 10 | Body Node 11 | } 12 | 13 | func NewQuasiquote(body Node) *Quasiquote { 14 | return &Quasiquote{Body: body} 15 | } 16 | 17 | func (self *Quasiquote) Eval(env *scope.Scope) value.Value { 18 | if name, ok := self.Body.(*Name); ok { 19 | return value.NewSymbol(name.Identifier) 20 | } else { 21 | return self.Body.Eval(env) 22 | } 23 | } 24 | 25 | func (self *Quasiquote) String() string { 26 | return fmt.Sprintf("`%s", self.Body) 27 | } 28 | -------------------------------------------------------------------------------- /ast/quote.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | // Literal expressions 10 | // Body is represented as Pairs (list) 11 | // the list only contains Pair, Name or literal nodes 12 | // the Name would be treated as Symbol (see Pair.Eval) 13 | type Quote struct { 14 | Body Node 15 | } 16 | 17 | func NewQuote(body Node) *Quote { 18 | return &Quote{Body: body} 19 | } 20 | 21 | func (self *Quote) Eval(env *scope.Scope) value.Value { 22 | if name, ok := self.Body.(*Name); ok { 23 | return value.NewSymbol(name.Identifier) 24 | } else { 25 | return self.Body.Eval(env) 26 | } 27 | } 28 | 29 | func (self *Quote) String() string { 30 | return fmt.Sprintf("'%s", self.Body) 31 | } 32 | -------------------------------------------------------------------------------- /ast/select.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/constants" 6 | "github.com/kedebug/LispEx/scope" 7 | . "github.com/kedebug/LispEx/value" 8 | "reflect" 9 | ) 10 | 11 | type Select struct { 12 | Clauses [][]Node 13 | } 14 | 15 | func NewSelect(clauses [][]Node) *Select { 16 | return &Select{Clauses: clauses} 17 | } 18 | 19 | func (self *Select) Eval(env *scope.Scope) Value { 20 | cases := make([]reflect.SelectCase, len(self.Clauses)) 21 | for i, clause := range self.Clauses { 22 | // parser guarantee the test case is a Call or Name 23 | // Call.Callee is either `<-chan' or `chan<-' 24 | // Name.Identifier must be `default' 25 | var test *Call 26 | var name *Name 27 | var args []Value 28 | var channel *Channel 29 | 30 | switch clause[0].(type) { 31 | case *Call: 32 | test, _ = clause[0].(*Call) 33 | name, _ = test.Callee.(*Name) 34 | args = EvalList(test.Args, env) 35 | _, ok := args[0].(*Channel) 36 | if ok { 37 | channel, _ = args[0].(*Channel) 38 | } else { 39 | panic(fmt.Sprintf("incorrect argument type for `%s', expected: channel, given: %s", name, args[0])) 40 | } 41 | case *Name: 42 | name, _ = clause[0].(*Name) 43 | } 44 | 45 | if name.Identifier == constants.CHAN_SEND { 46 | // send to chan `chan<-' 47 | cases[i].Send = reflect.ValueOf(args[1]) 48 | cases[i].Dir = reflect.SelectSend 49 | cases[i].Chan = reflect.ValueOf(channel.Value) 50 | } else if name.Identifier == constants.CHAN_RECV { 51 | // receive from chan `<-chan' 52 | cases[i].Dir = reflect.SelectRecv 53 | cases[i].Chan = reflect.ValueOf(channel.Value) 54 | } else if name.Identifier == constants.DEFAULT { 55 | // default 56 | cases[i].Dir = reflect.SelectDefault 57 | } 58 | } 59 | 60 | chosen, recv, ok := reflect.Select(cases) 61 | exprs := self.Clauses[chosen] 62 | 63 | if len(exprs) == 1 { 64 | if ok { 65 | return recv.Interface().(Value) 66 | } else { 67 | return nil 68 | } 69 | } else { 70 | exprs = exprs[1:] 71 | for i := 0; i < len(exprs)-1; i++ { 72 | exprs[i].Eval(env) 73 | } 74 | return exprs[len(exprs)-1].Eval(env) 75 | } 76 | } 77 | 78 | func (self *Select) String() string { 79 | var result string 80 | for _, clause := range self.Clauses { 81 | var s string 82 | for i, expr := range clause { 83 | if i == 0 { 84 | s += fmt.Sprint(expr) 85 | } else { 86 | s += fmt.Sprintf(" %s", expr) 87 | } 88 | } 89 | result += fmt.Sprintf(" (%s)", s) 90 | } 91 | return fmt.Sprintf("(select %s)", result) 92 | } 93 | -------------------------------------------------------------------------------- /ast/set.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/binder" 6 | "github.com/kedebug/LispEx/constants" 7 | "github.com/kedebug/LispEx/scope" 8 | . "github.com/kedebug/LispEx/value" 9 | ) 10 | 11 | type Set struct { 12 | Pattern *Name 13 | Value Node 14 | } 15 | 16 | func NewSet(pattern *Name, val Node) *Set { 17 | return &Set{Pattern: pattern, Value: val} 18 | } 19 | 20 | func (self *Set) Eval(env *scope.Scope) Value { 21 | val := self.Value.Eval(env) 22 | binder.Assign(env, self.Pattern.Identifier, val) 23 | return nil 24 | } 25 | 26 | func (self *Set) String() string { 27 | return fmt.Sprintf("(%s %s %s)", constants.SET, self.Pattern, self.Value) 28 | } 29 | -------------------------------------------------------------------------------- /ast/string.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | . "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type String struct { 10 | Value string 11 | } 12 | 13 | func NewString(val string) *String { 14 | return &String{Value: val[1 : len(val)-1]} 15 | } 16 | 17 | func (self *String) Eval(env *scope.Scope) Value { 18 | return NewStringValue(self.Value) 19 | } 20 | 21 | func (self *String) String() string { 22 | return fmt.Sprintf("\"%s\"", self.Value) 23 | } 24 | -------------------------------------------------------------------------------- /ast/tuple.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Tuple struct { 10 | Elements []Node 11 | } 12 | 13 | func NewTuple(elements []Node) *Tuple { 14 | return &Tuple{Elements: elements} 15 | } 16 | 17 | func (self *Tuple) Eval(env *scope.Scope) value.Value { 18 | panic("unsupported tuple evaluation") 19 | return nil 20 | } 21 | 22 | func (self *Tuple) String() string { 23 | var s string 24 | for i, e := range self.Elements { 25 | if i == 0 { 26 | s += e.String() 27 | } else { 28 | s += fmt.Sprintf(" %s", e) 29 | } 30 | } 31 | return fmt.Sprintf("(%s)", s) 32 | } 33 | -------------------------------------------------------------------------------- /ast/unquote.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type Unquote struct { 10 | Body Node 11 | } 12 | 13 | func NewUnquote(body Node) *Unquote { 14 | return &Unquote{Body: body} 15 | } 16 | 17 | func (self *Unquote) Eval(env *scope.Scope) value.Value { 18 | return self.Body.Eval(env) 19 | } 20 | 21 | func (self *Unquote) String() string { 22 | return fmt.Sprintf(",%s", self.Body) 23 | } 24 | -------------------------------------------------------------------------------- /ast/unquote_splicing.go: -------------------------------------------------------------------------------- 1 | package ast 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | type UnquoteSplicing struct { 10 | Body Node 11 | } 12 | 13 | func NewUnquoteSplicing(body Node) *UnquoteSplicing { 14 | return &UnquoteSplicing{Body: body} 15 | } 16 | 17 | func (self *UnquoteSplicing) Eval(env *scope.Scope) value.Value { 18 | return self.Body.Eval(env) 19 | } 20 | 21 | func (self *UnquoteSplicing) String() string { 22 | return fmt.Sprintf(",@%s", self.Body) 23 | } 24 | -------------------------------------------------------------------------------- /binder/binder.go: -------------------------------------------------------------------------------- 1 | package binder 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/scope" 6 | ) 7 | 8 | func Define(env *scope.Scope, pattern string, value interface{}) { 9 | env.Put(pattern, value) 10 | } 11 | 12 | func Assign(s *scope.Scope, pattern string, value interface{}) { 13 | if env := s.FindScope(pattern); env != nil { 14 | env.Put(pattern, value) 15 | } else { 16 | panic(fmt.Sprintf("%s was not defined", pattern)) 17 | } 18 | } 19 | -------------------------------------------------------------------------------- /constants/constants.go: -------------------------------------------------------------------------------- 1 | package constants 2 | 3 | const ( 4 | DEFINE = "define" 5 | BEGIN = "begin" 6 | SET = "set!" 7 | LAMBDA = "lambda" 8 | LET = "let" 9 | LET_STAR = "let*" 10 | LET_REC = "letrec" 11 | OPEN_PARANT = "(" 12 | CLOSE_PARANT = ")" 13 | ADD = "+" 14 | SUB = "-" 15 | MULT = "*" 16 | DIV = "/" 17 | DOT = "." 18 | QUOTE = "quote" 19 | QUASIQUOTE = "quasiquote" 20 | UNQUOTE = "unquote" 21 | UNQUOTE_SPLICING = "unquote-splicing" 22 | APPLY = "apply" 23 | IF = "if" 24 | COND = "cond" 25 | DELAY = "delay" 26 | FORCE = "force" 27 | GO = "go" 28 | CHAN_SEND = "chan<-" 29 | CHAN_RECV = "<-chan" 30 | SELECT = "select" 31 | DEFAULT = "default" 32 | SLEEP = "sleep" 33 | RANDOM = "random" 34 | ) 35 | -------------------------------------------------------------------------------- /converter/converter.go: -------------------------------------------------------------------------------- 1 | package converter 2 | 3 | import ( 4 | . "github.com/kedebug/LispEx/value" 5 | ) 6 | 7 | // convert golang slice to lisp pairs 8 | func SliceToPairValues(slice []Value) Value { 9 | var front Value = NilPairValue 10 | for i := len(slice) - 1; i >= 0; i-- { 11 | front = NewPairValue(slice[i], front) 12 | } 13 | return front 14 | } 15 | 16 | // convert lisp pairs to golang slice 17 | func PairsToSlice(pairs Value) []Value { 18 | slice := make([]Value, 0) 19 | for { 20 | if pairs == nil || pairs == NilPairValue { 21 | break 22 | } 23 | switch pairs.(type) { 24 | case *PairValue: 25 | pair := pairs.(*PairValue) 26 | slice = append(slice, pair.First) 27 | pairs = pair.Second 28 | default: 29 | panic("can not convert pairs to slice") 30 | } 31 | } 32 | return slice 33 | } 34 | -------------------------------------------------------------------------------- /lexer/lexer.go: -------------------------------------------------------------------------------- 1 | package lexer 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "unicode" 7 | "unicode/utf8" 8 | ) 9 | 10 | type TokenType int 11 | 12 | const EOF = -1 13 | 14 | const ( 15 | TokenError TokenType = iota 16 | TokenEOF 17 | 18 | TokenIdentifier 19 | 20 | TokenStringLiteral 21 | TokenIntegerLiteral 22 | TokenFloatLiteral 23 | TokenBooleanLiteral 24 | 25 | TokenQuote 26 | TokenQuasiquote 27 | TokenUnquote 28 | TokenUnquoteSplicing 29 | 30 | TokenOpenParen 31 | TokenCloseParen 32 | TokenOpenSquare 33 | TokenCloseSquare 34 | ) 35 | 36 | type Token struct { 37 | Type TokenType 38 | Value string 39 | } 40 | 41 | type stateFn func(*Lexer) stateFn 42 | 43 | type Lexer struct { 44 | name string 45 | input string 46 | state stateFn 47 | start int 48 | pos int 49 | width int 50 | tokens chan Token 51 | } 52 | 53 | func NewLexer(name, input string) *Lexer { 54 | l := &Lexer{ 55 | name: name, 56 | input: input, 57 | tokens: make(chan Token), 58 | } 59 | go l.run() 60 | return l 61 | } 62 | 63 | func (l *Lexer) NextToken() Token { 64 | return <-l.tokens 65 | } 66 | 67 | func (l *Lexer) run() { 68 | for l.state = lexWhiteSpace; l.state != nil; { 69 | l.state = l.state(l) 70 | } 71 | close(l.tokens) 72 | } 73 | 74 | func (l *Lexer) emit(t TokenType) { 75 | l.tokens <- Token{t, l.input[l.start:l.pos]} 76 | l.start = l.pos 77 | } 78 | 79 | func (l *Lexer) next() rune { 80 | if len(l.input) <= l.pos { 81 | l.width = 0 82 | return EOF 83 | } 84 | 85 | r, size := utf8.DecodeRuneInString(l.input[l.pos:]) 86 | l.width = size 87 | l.pos += l.width 88 | 89 | return r 90 | } 91 | 92 | func (l *Lexer) backup() { 93 | l.pos -= l.width 94 | } 95 | 96 | func (l *Lexer) ignore() { 97 | l.start = l.pos 98 | } 99 | 100 | func (l *Lexer) peek() rune { 101 | r := l.next() 102 | l.backup() 103 | return r 104 | } 105 | 106 | func (l *Lexer) accept(valid string) bool { 107 | if strings.IndexRune(valid, l.next()) >= 0 { 108 | return true 109 | } 110 | l.backup() 111 | return false 112 | } 113 | 114 | func (l *Lexer) acceptRun(valid string) { 115 | for strings.IndexRune(valid, l.next()) >= 0 { 116 | } 117 | l.backup() 118 | } 119 | 120 | func (l *Lexer) errorf(format string, args ...interface{}) stateFn { 121 | l.tokens <- Token{TokenError, fmt.Sprintf(format, args...)} 122 | return nil 123 | } 124 | 125 | func lexWhiteSpace(l *Lexer) stateFn { 126 | for r := l.next(); r == ' ' || r == '\t' || r == '\n' || r == '\r'; r = l.next() { 127 | } 128 | l.backup() 129 | l.ignore() 130 | 131 | switch r := l.next(); { 132 | case r == EOF: 133 | return lexEOF 134 | case r == ';': 135 | return lexComment 136 | case r == '(': 137 | return lexOpenParen 138 | case r == ')': 139 | return lexCloseParen 140 | case r == '"': 141 | return lexString 142 | case r == '\'': 143 | return lexQuote 144 | case r == '`': 145 | return lexQuasiquote 146 | case r == ',': 147 | return lexUnquote 148 | case r == '+' || r == '-' || ('0' <= r && r <= '9'): 149 | l.backup() 150 | return lexNumber 151 | case isAlphaNumeric(r): 152 | // begin with non-numberic character 153 | return lexIdentifier 154 | default: 155 | return l.errorf("Unexpected character: %q", r) 156 | } 157 | } 158 | 159 | func lexEOF(l *Lexer) stateFn { 160 | l.emit(TokenEOF) 161 | return nil 162 | } 163 | 164 | func lexQuote(l *Lexer) stateFn { 165 | l.emit(TokenQuote) 166 | return lexWhiteSpace 167 | } 168 | 169 | func lexQuasiquote(l *Lexer) stateFn { 170 | l.emit(TokenQuasiquote) 171 | return lexWhiteSpace 172 | } 173 | 174 | func lexUnquote(l *Lexer) stateFn { 175 | hasAt := l.accept("@") 176 | if !hasAt { 177 | l.emit(TokenUnquote) 178 | } else { 179 | l.emit(TokenUnquoteSplicing) 180 | } 181 | return lexWhiteSpace 182 | } 183 | 184 | func lexUnquoteSplicing(l *Lexer) stateFn { 185 | l.emit(TokenUnquoteSplicing) 186 | return lexWhiteSpace 187 | } 188 | 189 | func lexString(l *Lexer) stateFn { 190 | for r := l.next(); r != '"'; r = l.next() { 191 | if r == '\\' { 192 | r = l.next() 193 | } 194 | if r == EOF { 195 | return l.errorf("read: expected a closing `\"'") 196 | } 197 | } 198 | l.emit(TokenStringLiteral) 199 | return lexWhiteSpace 200 | } 201 | 202 | func lexOpenParen(l *Lexer) stateFn { 203 | l.emit(TokenOpenParen) 204 | return lexWhiteSpace 205 | } 206 | 207 | func lexCloseParen(l *Lexer) stateFn { 208 | l.emit(TokenCloseParen) 209 | return lexWhiteSpace 210 | } 211 | 212 | func lexIdentifier(l *Lexer) stateFn { 213 | for r := l.next(); isAlphaNumeric(r); r = l.next() { 214 | } 215 | l.backup() 216 | 217 | l.emit(TokenIdentifier) 218 | return lexWhiteSpace 219 | } 220 | 221 | func lexComment(l *Lexer) stateFn { 222 | for r := l.next(); r != '\n'; r = l.next() { 223 | } 224 | return lexWhiteSpace 225 | } 226 | 227 | func lexNumber(l *Lexer) stateFn { 228 | isFloat := false 229 | 230 | hasFlag := l.accept("+-") 231 | digits := "0123456789" 232 | if l.accept("0") && l.accept("xX") { 233 | digits = "0123456789abcdefABCDEF" 234 | } 235 | l.acceptRun(digits) 236 | 237 | if l.accept(".") { 238 | isFloat = true 239 | l.acceptRun(digits) 240 | } 241 | 242 | if l.accept("eE") { 243 | l.accept("+-") 244 | l.acceptRun("0123456789") 245 | } 246 | 247 | if r := l.peek(); isAlphaNumeric(r) { 248 | l.next() 249 | return l.errorf("bad number syntax: %q", l.input[l.start:l.pos]) 250 | } 251 | 252 | if hasFlag && l.start+1 == l.pos { 253 | return lexIdentifier 254 | } 255 | 256 | if isFloat { 257 | l.emit(TokenFloatLiteral) 258 | } else { 259 | l.emit(TokenIntegerLiteral) 260 | } 261 | return lexWhiteSpace 262 | } 263 | 264 | func isAlphaNumeric(r rune) bool { 265 | if strings.IndexRune("!#$%&|*+-/:<=>?@^_~", r) >= 0 { 266 | return true 267 | } 268 | return r == '.' || unicode.IsLetter(r) || unicode.IsDigit(r) 269 | } 270 | -------------------------------------------------------------------------------- /main.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "bufio" 5 | "fmt" 6 | "github.com/kedebug/LispEx/repl" 7 | "github.com/kedebug/LispEx/scope" 8 | "io/ioutil" 9 | "os" 10 | "time" 11 | ) 12 | 13 | const version = "LispEx 0.1.0" 14 | 15 | func LoadStdlib() (string, error) { 16 | lib, err := ioutil.ReadFile("stdlib.ss") 17 | if err != nil { 18 | return "", err 19 | } 20 | return string(lib), nil 21 | } 22 | 23 | func EvalFile(filename string) error { 24 | lib, err := LoadStdlib() 25 | if err != nil { 26 | return err 27 | } 28 | exprs, err := ioutil.ReadFile(filename) 29 | if err != nil { 30 | return err 31 | } 32 | fmt.Println(repl.REPL(string(lib)+string(exprs), scope.NewRootScope())) 33 | return nil 34 | } 35 | 36 | func try(body func(), handler func(interface{})) { 37 | defer func() { 38 | if err := recover(); err != nil { 39 | handler(err) 40 | } 41 | }() 42 | body() 43 | } 44 | 45 | func main() { 46 | if len(os.Args) > 1 { 47 | if err := EvalFile(os.Args[1]); err != nil { 48 | fmt.Println(err) 49 | } 50 | return 51 | } 52 | 53 | lib, err := LoadStdlib() 54 | if err != nil { 55 | fmt.Println(err) 56 | return 57 | } 58 | env := scope.NewRootScope() 59 | repl.REPL(lib, env) 60 | reader := bufio.NewReader(os.Stdin) 61 | 62 | fmt.Printf("%s (%v)\n", version, time.Now().Format(time.RFC850)) 63 | 64 | for { 65 | fmt.Print(">>> ") 66 | line, _, _ := reader.ReadLine() 67 | try( 68 | func() { 69 | r := repl.REPL(string(line), env) 70 | if len(r) > 0 { 71 | fmt.Println(r) 72 | } 73 | }, 74 | func(e interface{}) { fmt.Println(e) }, 75 | ) 76 | } 77 | } 78 | -------------------------------------------------------------------------------- /parser/expand.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/ast" 6 | ) 7 | 8 | // expand definition formals to pairs 9 | func ExpandFormals(nodes []ast.Node) ast.Node { 10 | //(1). 11 | // (define ( ) ) equivalent to 12 | // (define 13 | // (lambda () )) 14 | //(2). 15 | // (define ( . ) ) equivalent to 16 | // should be a single variable 17 | // (define 18 | // (lambda )) 19 | 20 | prev := ast.NewPair(nil, nil) 21 | curr := ast.NewPair(nil, nil) 22 | 23 | front := prev 24 | dotted := false 25 | 26 | exists := make(map[string]bool) 27 | 28 | for i, node := range nodes { 29 | switch node.(type) { 30 | case *ast.Name: 31 | id := node.(*ast.Name).Identifier 32 | if id == "." { 33 | dotted = true 34 | if i+1 == len(nodes) { 35 | panic(fmt.Sprint("unexpected `)' after dot")) 36 | } 37 | } else { 38 | if _, ok := exists[id]; ok { 39 | panic(fmt.Sprint("duplicate argument identifier: ", node)) 40 | } else { 41 | exists[id] = true 42 | } 43 | if dotted { 44 | prev.Second = node 45 | // should be the last element 46 | if i+1 < len(nodes) { 47 | panic(fmt.Sprint("illegal use of `.'")) 48 | } 49 | } else { 50 | curr.First = node 51 | prev.Second = curr 52 | prev = curr 53 | curr = ast.NewPair(nil, nil) 54 | } 55 | } 56 | default: 57 | panic(fmt.Sprint("illegal argument type: ", node)) 58 | } 59 | } 60 | return front.Second 61 | } 62 | 63 | func ExpandList(nodes []ast.Node) ast.Node { 64 | prev := ast.NewPair(nil, nil) 65 | curr := ast.NewPair(nil, nil) 66 | 67 | front := prev 68 | dotted := false 69 | 70 | for i, node := range nodes { 71 | isdot := false 72 | expanded := node 73 | 74 | switch node.(type) { 75 | case *ast.Name: 76 | id := node.(*ast.Name).Identifier 77 | if id == "." { 78 | isdot = true 79 | dotted = true 80 | if i == 0 || i+2 != len(nodes) { 81 | panic(fmt.Sprint("illegal use of `.'")) 82 | } 83 | } 84 | case *ast.Tuple: 85 | elements := node.(*ast.Tuple).Elements 86 | expanded = ExpandList(elements) 87 | default: 88 | } 89 | if !isdot { 90 | if dotted { 91 | prev.Second = expanded 92 | } else { 93 | prev.Second = curr 94 | curr.First = expanded 95 | prev = curr 96 | curr = ast.NewPair(nil, nil) 97 | } 98 | } 99 | } 100 | return front.Second 101 | } 102 | -------------------------------------------------------------------------------- /parser/parser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/ast" 6 | "github.com/kedebug/LispEx/constants" 7 | "github.com/kedebug/LispEx/lexer" 8 | ) 9 | 10 | func ParseFromString(name, program string) []ast.Node { 11 | return Parse(lexer.NewLexer(name, program)) 12 | } 13 | 14 | func Parse(l *lexer.Lexer) []ast.Node { 15 | elements := PreParser(l, make([]ast.Node, 0), " ") 16 | return ParseList(elements) 17 | } 18 | 19 | func ParseNode(node ast.Node) ast.Node { 20 | tuple, ok := node.(*ast.Tuple) 21 | if !ok { 22 | return node 23 | } 24 | elements := tuple.Elements 25 | if len(elements) == 0 { 26 | panic(fmt.Errorf("syntax error, empty list")) 27 | } 28 | switch elements[0].(type) { 29 | case *ast.Name: 30 | name := elements[0].(*ast.Name) 31 | switch name.Identifier { 32 | case constants.DEFINE: 33 | return ParseDefine(tuple) 34 | case constants.BEGIN: 35 | return ParseBegin(tuple) 36 | case constants.LAMBDA: 37 | return ParseLambda(tuple) 38 | case constants.LET: 39 | fallthrough 40 | case constants.LET_STAR: 41 | fallthrough 42 | case constants.LET_REC: 43 | return ParseLetFamily(tuple) 44 | case constants.GO: 45 | return ParseGo(tuple) 46 | case constants.SELECT: 47 | return ParseSelect(tuple) 48 | case constants.IF: 49 | return ParseIf(tuple) 50 | case constants.SET: 51 | return ParseSet(tuple) 52 | case constants.APPLY: 53 | return ParseApply(tuple) 54 | case constants.QUOTE: 55 | return ParseQuote(tuple) 56 | case constants.QUASIQUOTE: 57 | // parse "unquote" and "unquote-splicing" in "quasiquote" 58 | // so they never go through ParseNode 59 | return ParseQuasiquote(tuple, 1) 60 | case constants.UNQUOTE: 61 | panic(fmt.Sprint("unquote: not in quasiquote")) 62 | case constants.UNQUOTE_SPLICING: 63 | panic(fmt.Sprint("unquote-splicing: not in quasiquote")) 64 | case constants.DELAY: 65 | return ParseDelay(tuple) 66 | case constants.FORCE: 67 | return ParseForce(tuple) 68 | default: 69 | return ParseCall(tuple) 70 | } 71 | case *ast.Tuple: 72 | //(1). currying 73 | // ((foo ) ) 74 | //(2). lambda 75 | // ((lambda ) ) 76 | return ParseCall(tuple) 77 | default: 78 | panic(fmt.Sprintf("%s: not a procedure", tuple)) 79 | } 80 | } 81 | 82 | func ParseList(nodes []ast.Node) []ast.Node { 83 | var parsed []ast.Node 84 | for _, node := range nodes { 85 | parsed = append(parsed, ParseNode(node)) 86 | } 87 | return parsed 88 | } 89 | 90 | func ParseBlock(tuple *ast.Tuple) *ast.Block { 91 | elements := tuple.Elements 92 | exprs := ParseList(elements) 93 | return ast.NewBlock(exprs) 94 | } 95 | 96 | func ParseBegin(tuple *ast.Tuple) *ast.Begin { 97 | // (begin ...) 98 | 99 | elements := tuple.Elements 100 | exprs := ParseList(elements[1:]) 101 | return ast.NewBegin(ast.NewBlock(exprs)) 102 | } 103 | 104 | func ParseGo(tuple *ast.Tuple) *ast.Go { 105 | // (go ...) 106 | 107 | elements := tuple.Elements 108 | if len(elements) != 2 { 109 | panic(fmt.Sprint("go: bad syntax, only expected 1 expression")) 110 | } 111 | expr := ParseNode(elements[1]) 112 | return ast.NewGo(expr) 113 | } 114 | 115 | func ParseApply(tuple *ast.Tuple) *ast.Apply { 116 | // (apply proc arg1 ... args) 117 | // Proc must be a procedure and args must be a list 118 | 119 | elements := tuple.Elements 120 | if len(elements) < 3 { 121 | panic(fmt.Sprint("apply: bad syntax (missing expressions), expected at least 3")) 122 | } 123 | proc := ParseNode(elements[1]) 124 | args := ParseList(elements[2:]) 125 | return ast.NewApply(proc, args) 126 | } 127 | 128 | func ParseSelect(tuple *ast.Tuple) *ast.Select { 129 | // (select ...) 130 | // = ( ) 131 | // = ( | | ) 132 | 133 | elements := tuple.Elements 134 | if len(elements) < 2 { 135 | panic(fmt.Sprint("select: bad syntax (missing clauses), expected at least 1")) 136 | } 137 | elements = elements[1:] 138 | clauses := make([][]ast.Node, len(elements)) 139 | for i, clause := range elements { 140 | if _, ok := clause.(*ast.Tuple); ok { 141 | exprs := clause.(*ast.Tuple).Elements 142 | if len(exprs) == 0 { 143 | panic(fmt.Sprint("select: bad syntax (missing select cases), given: ()")) 144 | } 145 | clauses[i] = ParseList(exprs) 146 | if call, ok := clauses[i][0].(*ast.Call); ok { 147 | if name, ok := call.Callee.(*ast.Name); ok { 148 | if name.Identifier == constants.CHAN_SEND { 149 | if len(call.Args) != 2 { 150 | panic(fmt.Sprintf("%s: arguments mismatch, expected 2", constants.CHAN_SEND)) 151 | } 152 | continue 153 | } else if name.Identifier == constants.CHAN_RECV { 154 | if len(call.Args) != 1 { 155 | panic(fmt.Sprintf("%s: arguments mismatch, expected 1", constants.CHAN_SEND)) 156 | } 157 | continue 158 | } 159 | } 160 | } else if name, ok := clauses[i][0].(*ast.Name); ok { 161 | if name.Identifier == constants.DEFAULT { 162 | continue 163 | } 164 | } 165 | } 166 | panic(fmt.Sprint("select: bad syntax, given: ", clause)) 167 | } 168 | return ast.NewSelect(clauses) 169 | } 170 | 171 | func ParseLetFamily(tuple *ast.Tuple) ast.Node { 172 | // (let_ ) 173 | // should have the form -> 174 | // (( ) ...) 175 | // where each is an expression 176 | 177 | elements := tuple.Elements 178 | if len(elements) < 3 { 179 | panic(fmt.Sprintf("%s: bad syntax, no expression in body", elements[0])) 180 | } 181 | 182 | if _, ok := elements[1].(*ast.Tuple); !ok { 183 | panic(fmt.Sprintf("%s: bad syntax, expected bindings, given: %s", elements[0], elements[1])) 184 | } 185 | 186 | bindings := elements[1].(*ast.Tuple).Elements 187 | patterns := make([]*ast.Name, len(bindings)) 188 | exprs := make([]ast.Node, len(bindings)) 189 | for i, binding := range bindings { 190 | if tuple, ok := binding.(*ast.Tuple); ok { 191 | if len(tuple.Elements) == 2 { 192 | if name, ok := tuple.Elements[0].(*ast.Name); ok { 193 | patterns[i] = name 194 | exprs[i] = ParseNode(tuple.Elements[1]) 195 | continue 196 | } 197 | } 198 | } 199 | panic(fmt.Sprintf("%s: bad syntax, not an identifer and expression for a binding %s", elements[0], binding)) 200 | } 201 | 202 | body := ast.NewBlock(ParseList(elements[2:])) 203 | name, _ := elements[0].(*ast.Name) 204 | switch name.Identifier { 205 | case constants.LET: 206 | return ast.NewLet(patterns, exprs, body) 207 | case constants.LET_STAR: 208 | return ast.NewLetStar(patterns, exprs, body) 209 | case constants.LET_REC: 210 | return ast.NewLetRec(patterns, exprs, body) 211 | default: 212 | panic(fmt.Sprintf("%s: should not be here", elements[0])) 213 | } 214 | } 215 | 216 | func ParseQuote(tuple *ast.Tuple) *ast.Quote { 217 | // (quote ) 218 | // ' 219 | 220 | elements := tuple.Elements 221 | if len(elements) != 2 { 222 | panic(fmt.Sprint("quote: wrong number of parts")) 223 | } 224 | switch elements[1].(type) { 225 | case *ast.Tuple: 226 | slice := elements[1].(*ast.Tuple).Elements 227 | return ast.NewQuote(ExpandList(slice)) 228 | default: 229 | return ast.NewQuote(elements[1]) 230 | } 231 | } 232 | 233 | func ParseQuasiquote(tuple *ast.Tuple, level int) ast.Node { 234 | // (quasiquote ) 235 | // ` 236 | 237 | // http://docs.racket-lang.org/reference/quasiquote.html 238 | // A quasiquote form within the original datum increments 239 | // the level of quasiquotation: within the quasiquote form, 240 | // each unquote or unquote-splicing is preserved, 241 | // but a further nested unquote or unquote-splicing escapes. 242 | // Multiple nestings of quasiquote require multiple nestings 243 | // of unquote or unquote-splicing to escape. 244 | 245 | elements := tuple.Elements 246 | if len(elements) != 2 { 247 | panic(fmt.Sprint("quasiquote: wrong number of parts")) 248 | } 249 | switch elements[1].(type) { 250 | case *ast.Tuple: 251 | // `(()) `((x y)) `((1 2)) are all legal 252 | // `(()) will be expanded correctly latter 253 | node := ParseNestedQuasiquote(elements[1].(*ast.Tuple), level) 254 | if level > 1 { 255 | // only level 1 will be parsed as Quasiquote Node 256 | // others will be treated as almost constants (tuple) 257 | elements[1] = node 258 | return tuple 259 | } else { 260 | if tuple1, ok := node.(*ast.Tuple); ok { 261 | return ast.NewQuasiquote(ExpandList(tuple1.Elements)) 262 | } else { 263 | return ast.NewQuasiquote(node) 264 | } 265 | } 266 | default: 267 | return ast.NewQuasiquote(elements[1]) 268 | } 269 | } 270 | 271 | func ParseNestedQuasiquote(tuple *ast.Tuple, level int) ast.Node { 272 | // tuple can be: 273 | // (unquote ) 274 | // (unquote-splicing ) 275 | // (quasiquote ) 276 | // also can be: 277 | // (var1 var2 (unquote )) etc. 278 | // We should handle these scenarios carefully. 279 | 280 | elements := tuple.Elements 281 | if len(elements) == 0 { 282 | return tuple 283 | } 284 | if name, ok := elements[0].(*ast.Name); ok { 285 | switch name.Identifier { 286 | case constants.UNQUOTE: 287 | return ParseUnquote(tuple, level-1) 288 | case constants.UNQUOTE_SPLICING: 289 | return ParseUnquoteSplicing(tuple, level-1) 290 | case constants.QUASIQUOTE: 291 | return ParseQuasiquote(tuple, level+1) 292 | } 293 | } 294 | slice := make([]ast.Node, 0, len(elements)) 295 | for _, node := range elements { 296 | if _, ok := node.(*ast.Tuple); ok { 297 | node = ParseNestedQuasiquote(node.(*ast.Tuple), level) 298 | } 299 | slice = append(slice, node) 300 | } 301 | return ast.NewTuple(slice) 302 | } 303 | 304 | func ParseUnquote(tuple *ast.Tuple, level int) ast.Node { 305 | elements := tuple.Elements 306 | if len(elements) != 2 { 307 | panic(fmt.Sprint("unquote: wrong number of parts")) 308 | } 309 | if level == 0 { 310 | return ast.NewUnquote(ParseNode(elements[1])) 311 | } else { 312 | if _, ok := elements[1].(*ast.Tuple); ok { 313 | elements[1] = ParseNestedQuasiquote(elements[1].(*ast.Tuple), level) 314 | } 315 | return tuple 316 | } 317 | } 318 | 319 | func ParseUnquoteSplicing(tuple *ast.Tuple, level int) ast.Node { 320 | elements := tuple.Elements 321 | if len(elements) != 2 { 322 | panic(fmt.Sprint("unquote-splicing: wrong number of parts")) 323 | } 324 | if level == 0 { 325 | return ast.NewUnquoteSplicing(ParseNode(elements[1])) 326 | } else { 327 | if _, ok := elements[1].(*ast.Tuple); ok { 328 | elements[1] = ParseNestedQuasiquote(elements[1].(*ast.Tuple), level) 329 | } 330 | return tuple 331 | } 332 | } 333 | 334 | func ParseDefine(tuple *ast.Tuple) *ast.Define { 335 | elements := tuple.Elements 336 | if len(elements) < 3 { 337 | panic(fmt.Sprint("define: bad syntax (missing expressions) ", tuple)) 338 | } 339 | 340 | switch elements[1].(type) { 341 | case *ast.Name: 342 | // (define ) 343 | if len(elements) > 3 { 344 | panic(fmt.Sprint("define: bad syntax (multiple expressions) ", tuple)) 345 | } 346 | pattern := elements[1].(*ast.Name) 347 | value := ParseNode(elements[2]) 348 | return ast.NewDefine(pattern, value) 349 | 350 | case *ast.Tuple: 351 | // (define ( ) ) 352 | // (define ( . ) ) 353 | tail := ast.NewBlock(ParseList(elements[2:])) 354 | function := ParseFunction(elements[1].(*ast.Tuple), tail) 355 | return ast.NewDefine(function.Caller, function) 356 | 357 | default: 358 | panic(fmt.Sprint("unsupported parser type ", elements[1])) 359 | } 360 | } 361 | 362 | func ParseFunction(tuple *ast.Tuple, tail ast.Node) *ast.Function { 363 | // expand definition: e.g. 364 | // ((f x) y) => 365 | // (lambda (x) 366 | // (lambda (y) )) 367 | 368 | lambda := ast.NewLambda(nil, tail) 369 | for { 370 | elements := tuple.Elements 371 | lambda.Params = ExpandFormals(elements[1:]) 372 | 373 | // len(elements) must be greater than 0 374 | switch elements[0].(type) { 375 | case *ast.Name: 376 | return ast.NewFunction(elements[0].(*ast.Name), lambda) 377 | case *ast.Tuple: 378 | tuple = elements[0].(*ast.Tuple) 379 | lambda = ast.NewLambda(nil, lambda) 380 | default: 381 | panic(fmt.Sprint("unsupported parser type ", elements[0])) 382 | } 383 | } 384 | } 385 | 386 | func ParseCall(tuple *ast.Tuple) *ast.Call { 387 | elements := tuple.Elements 388 | if len(elements) == 0 { 389 | panic(fmt.Sprint("missing procedure expression")) 390 | } 391 | callee := ParseNode(elements[0]) 392 | args := ParseList(elements[1:]) 393 | return ast.NewCall(callee, args) 394 | } 395 | 396 | func ParseLambda(tuple *ast.Tuple) *ast.Lambda { 397 | // (lambda ) 398 | // switch : 399 | // ( ...) 400 | // 401 | // ( ... . ) 402 | 403 | elements := tuple.Elements 404 | if len(elements) < 3 { 405 | panic(fmt.Sprint("lambda: bad syntax: ", tuple)) 406 | } 407 | pattern := elements[1] 408 | body := ast.NewBlock(ParseList(elements[2:])) 409 | 410 | switch pattern.(type) { 411 | case *ast.Name: 412 | return ast.NewLambda(pattern, body) 413 | case *ast.Tuple: 414 | formals := ExpandFormals(pattern.(*ast.Tuple).Elements) 415 | _, ok := formals.(*ast.Pair) 416 | if ok || formals == ast.NilPair { 417 | return ast.NewLambda(formals, body) 418 | } else { 419 | // (. ) is not allowed 420 | panic(fmt.Sprint("lambda: illegal use of `.'")) 421 | } 422 | default: 423 | panic(fmt.Sprint("unsupported parser type ", pattern)) 424 | } 425 | } 426 | 427 | func ParseIf(tuple *ast.Tuple) *ast.If { 428 | elements := tuple.Elements 429 | length := len(elements) 430 | if length != 3 && length != 4 { 431 | panic(fmt.Sprintf("incorrect format of if: %s", tuple)) 432 | } 433 | test := ParseNode(elements[1]) 434 | then := ParseNode(elements[2]) 435 | if length == 3 { 436 | return ast.NewIf(test, then, nil) 437 | } else { 438 | return ast.NewIf(test, then, ParseNode(elements[3])) 439 | } 440 | } 441 | 442 | func ParseSet(tuple *ast.Tuple) *ast.Set { 443 | elements := tuple.Elements 444 | if len(elements) != 3 { 445 | panic(fmt.Sprintf("incorrect format of set!: %s", tuple)) 446 | } 447 | pattern := ParseNode(elements[1]) 448 | if _, ok := pattern.(*ast.Name); !ok { 449 | panic(fmt.Sprintf("set!: not an indentifier in %s", tuple)) 450 | } 451 | value := ParseNode(elements[2]) 452 | return ast.NewSet(pattern.(*ast.Name), value) 453 | } 454 | 455 | func ParseDelay(tuple *ast.Tuple) *ast.Delay { 456 | elements := tuple.Elements 457 | if len(elements) != 2 { 458 | panic(fmt.Sprintf("delay: bad syntax in: %s", tuple)) 459 | } 460 | return ast.NewDelay(ParseNode(elements[1])) 461 | } 462 | 463 | func ParseForce(tuple *ast.Tuple) *ast.Force { 464 | elements := tuple.Elements 465 | if len(elements) != 2 { 466 | panic(fmt.Sprint("force: argument number mismatch, expected 1")) 467 | } 468 | return ast.NewForce(ParseNode(elements[1])) 469 | } 470 | -------------------------------------------------------------------------------- /parser/preparser.go: -------------------------------------------------------------------------------- 1 | package parser 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/ast" 6 | "github.com/kedebug/LispEx/constants" 7 | "github.com/kedebug/LispEx/lexer" 8 | ) 9 | 10 | func PreParser(l *lexer.Lexer, elements []ast.Node, delimiter string) []ast.Node { 11 | 12 | for token := l.NextToken(); token.Type != lexer.TokenEOF; token = l.NextToken() { 13 | switch token.Type { 14 | case lexer.TokenIdentifier: 15 | elements = append(elements, ast.NewName(token.Value)) 16 | 17 | case lexer.TokenIntegerLiteral: 18 | elements = append(elements, ast.NewInt(token.Value)) 19 | case lexer.TokenFloatLiteral: 20 | elements = append(elements, ast.NewFloat(token.Value)) 21 | case lexer.TokenStringLiteral: 22 | elements = append(elements, ast.NewString(token.Value)) 23 | 24 | case lexer.TokenOpenParen: 25 | tuple := ast.NewTuple(PreParser(l, make([]ast.Node, 0), "(")) 26 | elements = append(elements, tuple) 27 | case lexer.TokenCloseParen: 28 | if delimiter != "(" { 29 | panic(fmt.Sprint("read: unexpected `)'")) 30 | } 31 | return elements 32 | 33 | case lexer.TokenQuote: 34 | quote := []ast.Node{ast.NewName(constants.QUOTE)} 35 | quote = append(quote, PreParser(l, make([]ast.Node, 0), "'")...) 36 | elements = append(elements, ast.NewTuple(quote)) 37 | case lexer.TokenQuasiquote: 38 | quasiquote := []ast.Node{ast.NewName(constants.QUASIQUOTE)} 39 | quasiquote = append(quasiquote, PreParser(l, make([]ast.Node, 0), "`")...) 40 | elements = append(elements, ast.NewTuple(quasiquote)) 41 | case lexer.TokenUnquote: 42 | unquote := []ast.Node{ast.NewName(constants.UNQUOTE)} 43 | unquote = append(unquote, PreParser(l, make([]ast.Node, 0), ",")...) 44 | elements = append(elements, ast.NewTuple(unquote)) 45 | case lexer.TokenUnquoteSplicing: 46 | unquoteSplicing := []ast.Node{ast.NewName(constants.UNQUOTE_SPLICING)} 47 | unquoteSplicing = append(unquoteSplicing, PreParser(l, make([]ast.Node, 0), ",@")...) 48 | elements = append(elements, ast.NewTuple(unquoteSplicing)) 49 | 50 | case lexer.TokenError: 51 | panic(fmt.Errorf("token error: %s", token.Value)) 52 | default: 53 | panic(fmt.Errorf("unexpected token type: %v", token.Type)) 54 | } 55 | switch delimiter { 56 | case "'", "`", ",", ",@": 57 | return elements 58 | } 59 | } 60 | if delimiter != " " { 61 | panic(fmt.Errorf("unclosed delimeter, expected: `%s'", delimiter)) 62 | } 63 | return elements 64 | } 65 | -------------------------------------------------------------------------------- /repl/repl.go: -------------------------------------------------------------------------------- 1 | package repl 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/ast" 6 | "github.com/kedebug/LispEx/parser" 7 | "github.com/kedebug/LispEx/scope" 8 | ) 9 | 10 | // read-eval-print loop 11 | func REPL(exprs string, env *scope.Scope) string { 12 | result := "" 13 | first := true 14 | 15 | sexprs := parser.ParseFromString("", exprs) 16 | values := ast.EvalList(sexprs, env) 17 | 18 | for _, val := range values { 19 | if val != nil { 20 | if first { 21 | first = false 22 | result += fmt.Sprint(val) 23 | } else { 24 | result += fmt.Sprintf("\n%s", val) 25 | } 26 | } 27 | } 28 | return result 29 | } 30 | -------------------------------------------------------------------------------- /scope/scope.go: -------------------------------------------------------------------------------- 1 | package scope 2 | 3 | import ( 4 | "github.com/kedebug/LispEx/value" 5 | "github.com/kedebug/LispEx/value/primitives" 6 | ) 7 | 8 | type Scope struct { 9 | parent *Scope 10 | env map[string]interface{} 11 | } 12 | 13 | func NewScope(parent *Scope) *Scope { 14 | return &Scope{ 15 | parent: parent, 16 | env: make(map[string]interface{}), 17 | } 18 | } 19 | 20 | func NewRootScope() *Scope { 21 | root := NewScope(nil) 22 | root.Put("+", primitives.NewAdd()) 23 | root.Put("-", primitives.NewSub()) 24 | root.Put("*", primitives.NewMult()) 25 | root.Put("/", primitives.NewDiv()) 26 | root.Put("=", primitives.NewEq()) 27 | root.Put(">", primitives.NewGt()) 28 | root.Put(">=", primitives.NewGtE()) 29 | root.Put("<", primitives.NewLt()) 30 | root.Put("<=", primitives.NewLtE()) 31 | root.Put("%", primitives.NewMod()) 32 | root.Put("and", primitives.NewAnd()) 33 | root.Put("or", primitives.NewOr()) 34 | root.Put("eqv?", primitives.NewIsEqv()) 35 | root.Put("type-of", primitives.NewTypeOf()) 36 | root.Put("display", primitives.NewDisplay()) 37 | root.Put("newline", primitives.NewNewline()) 38 | root.Put("car", primitives.NewCar()) 39 | root.Put("cdr", primitives.NewCdr()) 40 | root.Put("cons", primitives.NewCons()) 41 | root.Put("make-chan", primitives.NewMakeChan()) 42 | root.Put("close-chan", primitives.NewCloseChan()) 43 | root.Put("<-chan", primitives.NewChanRecv()) 44 | root.Put("chan<-", primitives.NewChanSend()) 45 | root.Put("sleep", primitives.NewSleep()) 46 | root.Put("random", primitives.NewRandom()) 47 | root.Put("#t", value.NewBoolValue(true)) 48 | root.Put("#f", value.NewBoolValue(false)) 49 | return root 50 | } 51 | 52 | func (self *Scope) Put(name string, value interface{}) { 53 | self.env[name] = value 54 | } 55 | 56 | func (self *Scope) PutAll(other *Scope) { 57 | for name, value := range other.env { 58 | self.env[name] = value 59 | } 60 | } 61 | 62 | func (self *Scope) Lookup(name string) interface{} { 63 | value := self.LookupLocal(name) 64 | if value != nil { 65 | return value 66 | } else if self.parent != nil { 67 | return self.parent.Lookup(name) 68 | } else { 69 | return nil 70 | } 71 | } 72 | 73 | func (self *Scope) LookupLocal(name string) interface{} { 74 | if v, ok := self.env[name]; ok { 75 | return v 76 | } 77 | return nil 78 | } 79 | 80 | func (self *Scope) FindScope(name string) *Scope { 81 | if v := self.LookupLocal(name); v != nil { 82 | return self 83 | } else if self.parent != nil { 84 | return self.parent.FindScope(name) 85 | } else { 86 | return nil 87 | } 88 | } 89 | -------------------------------------------------------------------------------- /stdlib.ss: -------------------------------------------------------------------------------- 1 | ;; primitive type predicates 2 | (define (is? x t) (eqv? (type-of x) t)) 3 | (define (bool? x) (is? x 'bool)) 4 | (define (integer? x) (is? x 'integer)) 5 | (define (float? x) (is? x 'float)) 6 | (define (number? x) (if (integer? x) #t (if (float? x) #t #f))) 7 | (define (string? x) (is? x 'string)) 8 | (define (pair? x) (is? x 'pair)) 9 | (define (procedure? x) (is? x 'procedure)) 10 | 11 | (define (null? obj) (if (eqv? obj '()) #t #f)) 12 | (define ((compose f g) x) (f (g x))) 13 | 14 | ;; list accessors 15 | (define caar (compose car car)) 16 | (define cadr (compose car cdr)) 17 | (define cdar (compose cdr car)) 18 | (define cddr (compose cdr cdr)) 19 | (define caaar (compose car caar)) 20 | (define caadr (compose car cadr)) 21 | (define cadar (compose car cdar)) 22 | (define caddr (compose car cddr)) 23 | (define cdaar (compose cdr caar)) 24 | (define cdadr (compose cdr cadr)) 25 | (define cddar (compose cdr cdar)) 26 | (define cdddr (compose cdr cddr)) 27 | (define caaaar (compose car caaar)) 28 | (define caaadr (compose car caadr)) 29 | (define caadar (compose car cadar)) 30 | (define caaddr (compose car caddr)) 31 | (define cadaar (compose car cdaar)) 32 | (define cadadr (compose car cdadr)) 33 | (define caddar (compose car cddar)) 34 | (define cadddr (compose car cdddr)) 35 | (define cdaaar (compose cdr caaar)) 36 | (define cdaadr (compose cdr caadr)) 37 | (define cdadar (compose cdr cadar)) 38 | (define cdaddr (compose cdr caddr)) 39 | (define cddaar (compose cdr cdaar)) 40 | (define cddadr (compose cdr cdadr)) 41 | (define cdddar (compose cdr cddar)) 42 | (define cddddr (compose cdr cdddr)) 43 | 44 | (define (not x) (if x #f #t)) 45 | 46 | (define (list . objs) objs) 47 | 48 | (define (flip func) (lambda (arg1 arg2) (func arg2 arg1))) 49 | (define (curry func arg1) (lambda (arg) (apply func (cons arg1 (list arg))))) 50 | 51 | (define zero? (curry = 0)) 52 | (define positive? (curry < 0)) 53 | (define negative? (curry > 0)) 54 | (define (abs num) (if (< num 0) (- num) num)) 55 | (define (even? num) (= (% num 2) 0)) 56 | (define (odd? num) (not (even? num))) 57 | 58 | ; from tinyscheme 59 | (define (list-tail x k) 60 | (if (zero? k) 61 | x 62 | (list-tail (cdr x) (- k 1)))) 63 | (define (list-ref x k) 64 | (car (list-tail x k))) 65 | 66 | ; from tinyscheme 67 | (define gcd 68 | (lambda a 69 | (if (null? a) 70 | 0 71 | (let ((aa (abs (car a))) 72 | (bb (abs (cadr a)))) 73 | (if (= bb 0) 74 | aa 75 | (gcd bb (% aa bb))))))) 76 | 77 | (define lcm 78 | (lambda a 79 | (if (null? a) 80 | 1 81 | (let ((aa (abs (car a))) 82 | (bb (abs (cadr a)))) 83 | (if (or (= aa 0) (= bb 0)) 84 | 0 85 | (abs (* (/ aa (gcd aa bb)) bb))))))) 86 | 87 | (define (foldr func end lst) 88 | (if (null? lst) 89 | end 90 | (func (car lst) (foldr func end (cdr lst))))) 91 | 92 | (define (foldl func accum lst) 93 | (if (null? lst) 94 | accum 95 | (foldl func (func accum (car lst)) (cdr lst)))) 96 | 97 | (define fold foldl) 98 | (define reduce fold) 99 | (define (unfold func init pred) 100 | (if (pred init) 101 | (cons init '()) 102 | (cons init (unfold func (func init) pred)))) 103 | (define (sum . lst) (fold + 0 lst)) 104 | 105 | (define (max first . rest) 106 | (fold (lambda (old new) (if (> old new) old new)) first rest)) 107 | 108 | (define (min first . rest) 109 | (fold (lambda (old new) (if (< old new) old new)) first rest)) 110 | 111 | (define (map func lst) 112 | (foldr (lambda (x y) (cons (func x) y)) '() lst)) 113 | 114 | (define (filter pred lst) 115 | (foldr (lambda (x y) (if (pred x) (cons x y) y)) '() lst)) 116 | 117 | (define (length lst) (fold (lambda (x y) (+ x 1)) 0 lst)) 118 | (define (reverse lst) (fold (flip cons) '() lst)) -------------------------------------------------------------------------------- /tests/binding_test.ss: -------------------------------------------------------------------------------- 1 | (let ((x 2) (y 3)) (* x y)) 2 | 3 | (let ((x 2) (y 3)) 4 | (let ((x 7) (z (+ x y))) 5 | (* z x))) 6 | 7 | (begin 8 | (define a 5) 9 | (let ((a 10) (b a)) 10 | (- a b))) 11 | 12 | (letrec ((x 2) (y 3)) (* x y)) 13 | 14 | (letrec ((x 2) (y 3)) 15 | (letrec ((x 7) (z (+ x y))) 16 | (* z x))) 17 | 18 | (define x 5) (letrec ((x 3) (y 5)) (+ x y)) x 19 | 20 | (begin (define a 5) (letrec ((a 10) (b a)) (- a b))) 21 | 22 | (letrec ((even? 23 | (lambda (n) 24 | (if (= 0 n) 25 | #t 26 | (odd? (- n 1))))) 27 | (odd? 28 | (lambda (n) 29 | (if (= 0 n) 30 | #f 31 | (even? (- n 1)))))) 32 | (even? 88)) 33 | 34 | (let* ((x 2) (y 3)) 35 | (* x y)) 36 | 37 | (let* ((x 2) (y 3)) 38 | (let ((x 7) (z (+ x y))) 39 | (* z x))) 40 | 41 | (let* ((x 2) (y 3)) 42 | (let* ((x 7) (z (+ x y))) 43 | (* z x))) 44 | 45 | (begin 46 | (define a 5) 47 | (let* ((a 10) (b a)) 48 | (- a b))) -------------------------------------------------------------------------------- /tests/define_test.ss: -------------------------------------------------------------------------------- 1 | (define x 3) x (+ x x) 2 | (define x 1) x (define x (+ x 1)) x 3 | (define y 2) ((lambda (x) (define y 1) (+ x y)) 3) y 4 | (define f (lambda () (+ 1 2))) (f) 5 | (define add3 (lambda (x) (+ x 3))) (add3 3) 6 | (define first car) (first '(1 2)) 7 | (define (x y . z) (cons y z)) (x 1 2 3) 8 | (define (f x) (+ x y)) (define y 1) (f 1) 9 | (define plus (lambda (x) (+ x y))) (define y 1) (plus 3) 10 | (define x 0) (define z 1) (define (f x y) (set! z 2) (+ x y)) (f 1 2) x z 11 | (define x -2) x (set! x (* x x)) x 12 | 13 | (apply + '(1 2 3)) 14 | (define compose 15 | (lambda (f g) 16 | (lambda args 17 | (f (apply g args))))) 18 | ((compose + *) 12 75) -------------------------------------------------------------------------------- /tests/if_test.ss: -------------------------------------------------------------------------------- 1 | (if 1 2 invalid) 2 | (if #f invalid 'ok) 3 | (if #t 1) 4 | (if #f 1) -------------------------------------------------------------------------------- /tests/lambda_test.ss: -------------------------------------------------------------------------------- 1 | (lambda x 1 2 3) 2 | (lambda (x) 1 2 3) 3 | (lambda (x y) 1 2 3) 4 | (lambda (x . y) 1 2 3) 5 | 6 | ((lambda (x) x) 'a) 7 | ((lambda x x) 'a) 8 | ((lambda x x) 'a 'b) 9 | ((lambda (x y) (+ x y)) 3 5) 10 | ((lambda (x . y) (+ x (car y))) 1 2 5) 11 | ((lambda (x y . z) (+ x y (car z))) 1 2 5 11) 12 | (define x 10) ((lambda (x) x) 5) x -------------------------------------------------------------------------------- /tests/ping_pong_test.out: -------------------------------------------------------------------------------- 1 | ping 2 | pong 3 | ping 4 | pong 5 | ping 6 | pong 7 | ping 8 | pong 9 | ping 10 | pong 11 | ping 12 | pong 13 | exit-ping 14 | exit-pong -------------------------------------------------------------------------------- /tests/ping_pong_test.ss: -------------------------------------------------------------------------------- 1 | ;; ping-pong test 2 | (define ping-chan (make-chan)) 3 | (define pong-chan (make-chan)) 4 | (define sem (make-chan 2)) 5 | 6 | (define (ping n) 7 | (if (> n 0) 8 | (begin 9 | (display (<-chan ping-chan)) 10 | (newline) 11 | (chan<- pong-chan 'pong) 12 | (ping (- n 1))) 13 | (chan<- sem 'exit-ping))) 14 | 15 | (define (pong n) 16 | (if (> n 0) 17 | (begin 18 | (chan<- ping-chan 'ping) 19 | (display (<-chan pong-chan)) 20 | (newline) 21 | (pong (- n 1))) 22 | (chan<- sem 'exit-pong))) 23 | 24 | (go (ping 6)) 25 | (go (pong 6)) 26 | 27 | (display (<-chan sem)) (newline) 28 | (display (<-chan sem)) (newline) 29 | 30 | (close-chan sem) 31 | (close-chan pong-chan) 32 | (close-chan ping-chan) 33 | -------------------------------------------------------------------------------- /tests/prim_test.ss: -------------------------------------------------------------------------------- 1 | 1 2 | (+) (*) 3 | (+ 1 1) 4 | (+ 1 2 -3) 5 | (+ (* 3 4) (- -4 5) (/ 2 -1)) 6 | 7 | (= 1 2) (= 1 1) 8 | (< 1 2) (< 1 1) 9 | (> 1 1) (> 2 1) 10 | (>= 1 1) (>= 1 2) 11 | (<= 1 1) (<= 2 1) 12 | 13 | 'abc 14 | (quote abc) 15 | '() 16 | '(compose f g) 17 | 18 | (car '(a b c)) 19 | (car '(a)) 20 | (car '(a b . c)) 21 | (cdr '(a b c)) 22 | (cdr '(a b)) 23 | (cdr '(a)) 24 | (cdr '(a . b)) 25 | (cdr '(a b . c)) 26 | (cons 'a '(b c)) 27 | (cons 'a '()) 28 | (cons 'a '(b . c)) 29 | (cons 'a 'b) 30 | (cons '() '()) 31 | 32 | (apply * '(1 2 4)) 33 | (apply + 1 2 '()) 34 | (apply + 1 2 '(3 4)) 35 | (apply min '(6 8 3 2 5)) 36 | (apply min 5 1 3 '(6 8 3 2 5)) -------------------------------------------------------------------------------- /tests/promise_test.ss: -------------------------------------------------------------------------------- 1 | (define f (delay (+ 1 1))) f (force f) f 2 | (define f (delay (+ 1))) (+ 2) (force f) 3 | (define f (delay (+ 1))) (force f) (force f) -------------------------------------------------------------------------------- /tests/quasiquote_test.ss: -------------------------------------------------------------------------------- 1 | `() 2 | `(()) 3 | `(+ 2 3) 4 | `(+ 2 ,(+ 3 4)) 5 | `(a b (,(+ 2 3) c) d) 6 | '`,(cons 'a 'b) 7 | `',(cons 'a 'b) 8 | `(+ ,@(cdr '(* 2 3))) 9 | `(1 2 `(3 4 ,@(5 6 8 9 10) 11 12) 13 14) 10 | `(1 2 `(3 4 ,@(5 6 ,@(cdr '(6 7 8)) 9 10) 11 12) 13 14) 11 | ``(+ ,,(+ 1 2) 2 3) 12 | `(1 2 `(10 ,',(+ 2 3))) 13 | `(+ 2 `(10 ,(+ 2 3))) 14 | `(1 2 `(10 ,,(+ 2 3))) 15 | `(1 `,(+ 1 ,(+ 2 3)) 4) 16 | 17 | (let ((a 1) (b 2)) `(,a . ,b)) 18 | `,(let () (define x 1) x) 19 | (let ((a 1) (b 2)) `(,a ,@b)) 20 | -------------------------------------------------------------------------------- /tests/select_ping_pong_test.out: -------------------------------------------------------------------------------- 1 | ping 2 | pong 3 | ping 4 | pong 5 | ping 6 | pong 7 | ping 8 | pong 9 | ping 10 | pong 11 | ping 12 | pong 13 | exit-ping 14 | exit-pong 15 | -------------------------------------------------------------------------------- /tests/select_ping_pong_test.ss: -------------------------------------------------------------------------------- 1 | (define ping-chan-0 (make-chan)) 2 | (define ping-chan-1 (make-chan)) 3 | (define ping-chan-2 (make-chan)) 4 | (define ping-chan-3 (make-chan)) 5 | (define pong-chan-0 (make-chan)) 6 | (define pong-chan-1 (make-chan)) 7 | (define pong-chan-2 (make-chan)) 8 | (define pong-chan-3 (make-chan)) 9 | (define sem (make-chan 2)) 10 | 11 | (define (select-receive chan-0 chan-1 chan-2 chan-3) 12 | (select 13 | ((<-chan chan-0)) 14 | ((<-chan chan-1)) 15 | ((<-chan chan-2)) 16 | ((<-chan chan-3)))) 17 | 18 | (define (random-send x . chan-list) 19 | (let* ((n (length chan-list)) 20 | (i (random n))) 21 | (chan<- (list-ref chan-list i) x))) 22 | 23 | (define (ping n) 24 | (if (> n 0) 25 | (begin 26 | (display (select-receive ping-chan-0 ping-chan-1 ping-chan-2 ping-chan-3)) 27 | (newline) 28 | (random-send 'pong pong-chan-0 pong-chan-1 pong-chan-2 pong-chan-3) 29 | (ping (- n 1))) 30 | (chan<- sem 'exit-ping))) 31 | 32 | (define (pong n) 33 | (if (> n 0) 34 | (begin 35 | (random-send 'ping ping-chan-0 ping-chan-1 ping-chan-2 ping-chan-3) 36 | (display (select-receive pong-chan-0 pong-chan-1 pong-chan-2 pong-chan-3)) 37 | (newline) 38 | (pong (- n 1))) 39 | (chan<- sem 'exit-pong))) 40 | 41 | (go (ping 6)) 42 | (go (pong 6)) 43 | 44 | (display (<-chan sem)) (newline) 45 | (display (<-chan sem)) (newline) -------------------------------------------------------------------------------- /tests/select_test.ss: -------------------------------------------------------------------------------- 1 | (define ch0 (make-chan)) 2 | (go (chan<- ch0 "hello world")) 3 | (select ((<-chan ch0))) 4 | 5 | (define ch1 (make-chan)) 6 | (select 7 | ((<-chan ch1) 1) 8 | ((chan<- ch1 2) 2) 9 | (default 3)) 10 | 11 | (define ch3 (make-chan)) 12 | (go (chan<- ch3 42)) 13 | (sleep 20) 14 | (select 15 | ((<-chan ch3) 1) 16 | ((chan<- ch3 2) 2) 17 | (default 3)) 18 | 19 | (define ch4 (make-chan)) 20 | (go (chan<- ch4 42)) 21 | (sleep 20) 22 | (select 23 | ((<-chan ch4)) 24 | ((chan<- ch4 2) 2) 25 | (default 3)) 26 | 27 | (define ch5 (make-chan)) 28 | (go (<-chan ch5)) 29 | (sleep 20) 30 | (select 31 | ((<-chan ch5) 1) 32 | ((chan<- ch5 2) 2) 33 | (default 3)) 34 | 35 | 36 | (define ch6 (make-chan)) 37 | (go (chan<- ch6 42)) 38 | (select 39 | ((chan<- ch6 42)) 40 | ((<-chan ch6))) -------------------------------------------------------------------------------- /tests/stdlib_test.ss: -------------------------------------------------------------------------------- 1 | (bool? #t) (bool? #f) (bool? 12) 2 | (integer? 1) (integer? 2.0) 3 | (float? 1) (float? 2.0) 4 | (string? 1) (string? "abc") 5 | (number? 1) (number? 2.0) 6 | (null? '()) (null? '(1 2 3)) 7 | (define f (lambda () (+ 1 2))) (procedure? f) 8 | (define (f x) (+ x)) (procedure? f) 9 | (caar '((1 2) 3 4)) (cadr '((1 2) 3 4)) 10 | (cdar '((1 2) 3 4)) (cddr '((1 2) 3 4)) 11 | (caaar '(((1 2) 3) 5 6)) 12 | 13 | (zero? 1) (zero? 0) 14 | (positive? 1) (positive? 0) (positive? -1) 15 | (negative? -1) (negative? 0) (negative? 1) 16 | 17 | (sum 1 2 3) 18 | (gcd 32 -36) (gcd) 19 | (lcm 32 -36) (lcm) 20 | (map (curry + 2) '(1 2 3 4)) 21 | (filter even? '(1 2 3 4)) -------------------------------------------------------------------------------- /tests/tests.go: -------------------------------------------------------------------------------- 1 | package tests 2 | -------------------------------------------------------------------------------- /tests/tests_test.go: -------------------------------------------------------------------------------- 1 | package tests 2 | 3 | import ( 4 | "github.com/kedebug/LispEx/repl" 5 | "github.com/kedebug/LispEx/scope" 6 | "io/ioutil" 7 | "testing" 8 | ) 9 | 10 | func testFile(filename string, t *testing.T) string { 11 | lib, err := ioutil.ReadFile("../stdlib.ss") 12 | if err != nil { 13 | t.Error(err) 14 | } 15 | exprs, err := ioutil.ReadFile(filename) 16 | if err != nil { 17 | t.Error(err) 18 | } 19 | return repl.REPL(string(lib)+string(exprs), scope.NewRootScope()) 20 | } 21 | 22 | func TestIf(t *testing.T) { 23 | result := testFile("if_test.ss", t) 24 | expected := "2\nok\n1" 25 | 26 | if expected != result { 27 | t.Error("expected: ", expected, " evaluated: ", result) 28 | } 29 | } 30 | 31 | func TestPrimitives(t *testing.T) { 32 | result := testFile("prim_test.ss", t) 33 | 34 | expected := "1\n0\n1\n2\n0\n1" 35 | expected += "\n#f\n#t\n#t\n#f\n#f\n#t\n#t\n#f\n#t\n#f" 36 | expected += "\nabc\nabc\n()\n(compose f g)" 37 | expected += "\na\na\na\n(b c)\n(b)\n()\nb\n(b . c)\n(a b c)\n(a)\n(a b . c)\n(a . b)\n(())" 38 | expected += "\n8\n3\n10\n2\n1" 39 | 40 | if expected != result { 41 | t.Error("expected: ", expected, " evaluated: ", result) 42 | } 43 | } 44 | 45 | func TestDefine(t *testing.T) { 46 | result := testFile("define_test.ss", t) 47 | 48 | expected := "3\n6\n1\n2\n4\n2\n3\n6\n1\n(1 2 3)\n2\n4\n3\n0\n2\n-2\n4" 49 | expected += "\n6\n900" 50 | 51 | if expected != result { 52 | t.Error("expected: ", expected, " evaluated: ", result) 53 | } 54 | } 55 | 56 | func TestLambda(t *testing.T) { 57 | result := testFile("lambda_test.ss", t) 58 | 59 | expected := "#\n#\n#\n#" 60 | expected += "\na\n(a)\n(a b)\n8\n3\n8\n5\n10" 61 | 62 | if expected != result { 63 | t.Error("expected: ", expected, " evaluated: ", result) 64 | } 65 | } 66 | 67 | func TestQuasiquote(t *testing.T) { 68 | result := testFile("quasiquote_test.ss", t) 69 | 70 | expected := "()" 71 | expected += "\n(())" 72 | expected += "\n(+ 2 3)" 73 | expected += "\n(+ 2 7)" 74 | expected += "\n(a b (5 c) d)" 75 | expected += "\n`,(cons 'a 'b)" 76 | expected += "\n'(a . b)" 77 | expected += "\n(+ 2 3)" 78 | expected += "\n(1 2 `(3 4 ,@(5 6 8 9 10) 11 12) 13 14)" 79 | expected += "\n(1 2 `(3 4 ,@(5 6 7 8 9 10) 11 12) 13 14)" 80 | expected += "\n`(+ ,3 2 3)" 81 | expected += "\n(1 2 `(10 ,'5))" 82 | expected += "\n(+ 2 `(10 ,(+ 2 3)))" 83 | expected += "\n(1 2 `(10 ,5))" 84 | expected += "\n(1 `,(+ 1 5) 4)" 85 | expected += "\n(1 . 2)\n1\n(1 . 2)" 86 | 87 | if expected != result { 88 | t.Error("expected: ", expected, " evaluated: ", result) 89 | } 90 | } 91 | 92 | func TestStdlib(t *testing.T) { 93 | result := testFile("stdlib_test.ss", t) 94 | 95 | expected := "#t\n#t\n#f\n#t\n#f\n#f\n#t\n#f\n#t\n#t\n#t\n#t\n#f\n#t\n#t" 96 | expected += "\n1\n3\n(2)\n(4)\n1" 97 | expected += "\n#f\n#t\n#t\n#f\n#f\n#t\n#f\n#f" 98 | expected += "\n6\n4\n0\n288\n1\n(3 4 5 6)\n(2 4)" 99 | 100 | if expected != result { 101 | t.Error("expected: ", expected, " evaluated: ", result) 102 | } 103 | } 104 | 105 | func TestSelect(t *testing.T) { 106 | result := testFile("select_test.ss", t) 107 | expected := "\"hello world\"\n3\n1\n42\n2\n42" 108 | 109 | if expected != result { 110 | t.Error("expected: ", expected, " evaluated: ", result) 111 | } 112 | } 113 | 114 | func TestBinding(t *testing.T) { 115 | result := testFile("binding_test.ss", t) 116 | 117 | expected := "6\n35\n5" 118 | expected += "\n6\n35\n8\n5\n5\n#t" 119 | expected += "\n6\n35\n70\n0" 120 | 121 | if expected != result { 122 | t.Error("expected: ", expected, " evaluated: ", result) 123 | } 124 | } 125 | 126 | func TestPingPong(t *testing.T) { 127 | result := testFile("ping_pong_test.ss", t) 128 | expected := "" 129 | 130 | if expected != result { 131 | t.Error("expected: ", expected, " evaluated: ", result) 132 | } 133 | } 134 | 135 | func TestSelectPingPong(t *testing.T) { 136 | result := testFile("select_ping_pong_test.ss", t) 137 | expected := "" 138 | 139 | if expected != result { 140 | t.Error("expected: ", expected, " evaluated: ", result) 141 | } 142 | } 143 | 144 | func TestPromise(t *testing.T) { 145 | result := testFile("promise_test.ss", t) 146 | expected := "#\n2\n#\n2\n1\n1" 147 | 148 | if expected != result { 149 | t.Error("expected: ", expected, " evaluated: ", result) 150 | } 151 | } 152 | -------------------------------------------------------------------------------- /value/boolvalue.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type BoolValue struct { 4 | Value bool 5 | } 6 | 7 | func NewBoolValue(val bool) *BoolValue { 8 | return &BoolValue{Value: val} 9 | } 10 | 11 | func (self *BoolValue) String() string { 12 | if self.Value { 13 | return "#t" 14 | } else { 15 | return "#f" 16 | } 17 | } 18 | -------------------------------------------------------------------------------- /value/channel.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import "fmt" 4 | 5 | type Channel struct { 6 | Value chan Value 7 | } 8 | 9 | func NewChannel(size int) *Channel { 10 | return &Channel{Value: make(chan Value, size)} 11 | } 12 | 13 | func (self *Channel) String() string { 14 | return fmt.Sprint(self.Value) 15 | } 16 | -------------------------------------------------------------------------------- /value/closure.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type Closure struct { 4 | Env interface{} 5 | Body interface{} 6 | } 7 | 8 | func NewClosure(env, body interface{}) *Closure { 9 | return &Closure{Env: env, Body: body} 10 | } 11 | 12 | func (self *Closure) String() string { 13 | return "#" 14 | } 15 | -------------------------------------------------------------------------------- /value/floatvalue.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import "strconv" 4 | 5 | type FloatValue struct { 6 | Value float64 7 | } 8 | 9 | func NewFloatValue(val float64) *FloatValue { 10 | return &FloatValue{Value: val} 11 | } 12 | 13 | func (self *FloatValue) String() string { 14 | return strconv.FormatFloat(self.Value, 'f', -1, 64) 15 | } 16 | -------------------------------------------------------------------------------- /value/intvalue.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import "strconv" 4 | 5 | type IntValue struct { 6 | Value int64 7 | } 8 | 9 | func NewIntValue(val int64) *IntValue { 10 | return &IntValue{Value: val} 11 | } 12 | 13 | func (v *IntValue) String() string { 14 | return strconv.FormatInt(v.Value, 10) 15 | } 16 | -------------------------------------------------------------------------------- /value/pairvalue.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type PairValue struct { 8 | First Value 9 | Second Value 10 | } 11 | 12 | var NilPairValue = NewEmptyPairValue() 13 | 14 | type EmptyPairValue struct { 15 | } 16 | 17 | func NewEmptyPairValue() *EmptyPairValue { 18 | return &EmptyPairValue{} 19 | } 20 | 21 | func (self *EmptyPairValue) String() string { 22 | return "()" 23 | } 24 | 25 | func NewPairValue(first, second Value) *PairValue { 26 | if second == nil { 27 | second = NilPairValue 28 | } 29 | return &PairValue{First: first, Second: second} 30 | } 31 | 32 | func (self *PairValue) String() string { 33 | if self.Second == NilPairValue { 34 | return fmt.Sprintf("(%s)", self.First) 35 | } 36 | length := len(self.Second.String()) 37 | switch self.First.String() { 38 | case "quote": 39 | return fmt.Sprintf("'%s", self.Second.String()[1:length-1]) 40 | case "unquote": 41 | return fmt.Sprintf(",%s", self.Second.String()[1:length-1]) 42 | case "quasiquote": 43 | return fmt.Sprintf("`%s", self.Second.String()[1:length-1]) 44 | case "unquote-splicing": 45 | return fmt.Sprintf(",@%s", self.Second.String()[1:length-1]) 46 | } 47 | switch self.Second.(type) { 48 | case *PairValue: 49 | return fmt.Sprintf("(%s %s", self.First, self.Second.String()[1:]) 50 | default: 51 | return fmt.Sprintf("(%s . %s)", self.First, self.Second) 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /value/primfunc.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type PrimFunc interface { 4 | Value 5 | Apply(args []Value) Value 6 | } 7 | 8 | type Primitive struct { 9 | Name string 10 | } 11 | 12 | func (self *Primitive) String() string { 13 | return self.Name 14 | } 15 | -------------------------------------------------------------------------------- /value/primitives/add.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Add struct { 9 | value.Primitive 10 | } 11 | 12 | func NewAdd() *Add { 13 | return &Add{value.Primitive{"+"}} 14 | } 15 | 16 | func (self *Add) Apply(args []value.Value) value.Value { 17 | var val1 int64 18 | var val2 float64 19 | isfloat := false 20 | 21 | for _, arg := range args { 22 | switch arg.(type) { 23 | case *value.IntValue: 24 | val1 += arg.(*value.IntValue).Value 25 | case *value.FloatValue: 26 | isfloat = true 27 | val2 += arg.(*value.FloatValue).Value 28 | default: 29 | panic(fmt.Sprint("incorrect argument type for '+' : ", arg)) 30 | } 31 | } 32 | if !isfloat { 33 | return value.NewIntValue(val1) 34 | } else { 35 | return value.NewFloatValue(float64(val1) + val2) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /value/primitives/and.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type And struct { 9 | Primitive 10 | } 11 | 12 | func NewAnd() *And { 13 | return &And{Primitive{"and"}} 14 | } 15 | 16 | func (self *And) Apply(args []Value) Value { 17 | result := true 18 | for _, arg := range args { 19 | if val, ok := arg.(*BoolValue); ok { 20 | result = result && val.Value 21 | } else { 22 | panic(fmt.Sprint("incorrect argument type for `and', expected bool?")) 23 | } 24 | } 25 | return NewBoolValue(result) 26 | } 27 | -------------------------------------------------------------------------------- /value/primitives/car.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Car struct { 9 | value.Primitive 10 | } 11 | 12 | func NewCar() *Car { 13 | return &Car{value.Primitive{"car"}} 14 | } 15 | 16 | func (self *Car) Apply(args []value.Value) value.Value { 17 | if len(args) != 1 { 18 | panic(fmt.Sprint("car: arguments mismatch, expect 1")) 19 | } 20 | pairs := args[0] 21 | switch pairs.(type) { 22 | case *value.PairValue: 23 | return pairs.(*value.PairValue).First 24 | default: 25 | panic(fmt.Sprint("car: expected pair, given: ", pairs)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /value/primitives/cdr.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Cdr struct { 9 | value.Primitive 10 | } 11 | 12 | func NewCdr() *Cdr { 13 | return &Cdr{value.Primitive{"cdr"}} 14 | } 15 | 16 | func (self *Cdr) Apply(args []value.Value) value.Value { 17 | if len(args) != 1 { 18 | panic(fmt.Sprint("cdr: arguments mismatch, expect 1")) 19 | } 20 | pairs := args[0] 21 | switch pairs.(type) { 22 | case *value.PairValue: 23 | return pairs.(*value.PairValue).Second 24 | default: 25 | panic(fmt.Sprint("cdr: expected pair, given: ", pairs)) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /value/primitives/chan_recv.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/constants" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | // receive from channel 10 | type ChanRecv struct { 11 | value.Primitive 12 | } 13 | 14 | func NewChanRecv() *ChanRecv { 15 | return &ChanRecv{value.Primitive{constants.CHAN_RECV}} 16 | } 17 | 18 | func (self *ChanRecv) Apply(args []value.Value) value.Value { 19 | if len(args) != 1 { 20 | panic(fmt.Sprintf("%s: arguments mismatch, expected 1", constants.CHAN_RECV)) 21 | } 22 | if channel, ok := args[0].(*value.Channel); ok { 23 | return <-channel.Value 24 | } else { 25 | panic(fmt.Sprintf("incorrect argument type for `%s', expected: channel, given: %s", constants.CHAN_RECV, args[0])) 26 | } 27 | } 28 | -------------------------------------------------------------------------------- /value/primitives/chan_send.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/constants" 6 | "github.com/kedebug/LispEx/value" 7 | ) 8 | 9 | // send to channel 10 | type ChanSend struct { 11 | value.Primitive 12 | } 13 | 14 | func NewChanSend() *ChanSend { 15 | return &ChanSend{value.Primitive{constants.CHAN_SEND}} 16 | } 17 | 18 | func (self *ChanSend) Apply(args []value.Value) value.Value { 19 | if len(args) != 2 { 20 | panic(fmt.Sprintf("%s: arguments mismatch, expected 2", constants.CHAN_SEND)) 21 | } 22 | if channel, ok := args[0].(*value.Channel); ok { 23 | channel.Value <- args[1] 24 | } else { 25 | panic(fmt.Sprintf("incorrect argument type for `%s', expected: channel, given: %s", constants.CHAN_SEND, args[0])) 26 | } 27 | return nil 28 | } 29 | -------------------------------------------------------------------------------- /value/primitives/close_chan.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type CloseChan struct { 9 | value.Primitive 10 | } 11 | 12 | func NewCloseChan() *CloseChan { 13 | return &CloseChan{value.Primitive{"close-chan"}} 14 | } 15 | 16 | func (self *CloseChan) Apply(args []value.Value) value.Value { 17 | if len(args) != 1 { 18 | panic(fmt.Sprint("close-chan: arguments mismatch, expected 1")) 19 | } 20 | if channel, ok := args[0].(*value.Channel); ok { 21 | close(channel.Value) 22 | return nil 23 | } else { 24 | panic(fmt.Sprint("incorrect argument type for `close-chan', expected: channel, given: ", args[0])) 25 | } 26 | } 27 | -------------------------------------------------------------------------------- /value/primitives/cons.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Cons struct { 9 | value.Primitive 10 | } 11 | 12 | func NewCons() *Cons { 13 | return &Cons{value.Primitive{"cons"}} 14 | } 15 | 16 | func (self *Cons) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprintf("cons: arguments mismatch, expected: 2, given: ", args)) 19 | } 20 | return value.NewPairValue(args[0], args[1]) 21 | } 22 | -------------------------------------------------------------------------------- /value/primitives/display.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Display struct { 9 | Primitive 10 | } 11 | 12 | func NewDisplay() *Display { 13 | return &Display{Primitive{"display"}} 14 | } 15 | 16 | func (self *Display) Apply(args []Value) Value { 17 | if len(args) != 1 { 18 | panic(fmt.Sprint("display: argument mismatch, expected 1")) 19 | } 20 | fmt.Print(args[0]) 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /value/primitives/div.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Div struct { 9 | value.Primitive 10 | } 11 | 12 | func NewDiv() *Div { 13 | return &Div{value.Primitive{"/"}} 14 | } 15 | 16 | func (self *Div) Apply(args []value.Value) value.Value { 17 | var val float64 = 1 18 | 19 | if len(args) == 0 { 20 | panic(fmt.Sprint("`/' argument unmatch: expected at least 1")) 21 | } else if len(args) > 1 { 22 | switch args[0].(type) { 23 | case *value.IntValue: 24 | val = float64(args[0].(*value.IntValue).Value) 25 | case *value.FloatValue: 26 | val = args[0].(*value.FloatValue).Value 27 | default: 28 | panic(fmt.Sprint("incorrect argument type for `/' : ", args[0])) 29 | } 30 | args = args[1:] 31 | } 32 | if len(args) == 0 && val == 0 { 33 | // (/ 0) 34 | panic(fmt.Sprint("`/' division by zero")) 35 | } 36 | 37 | for _, arg := range args { 38 | switch arg.(type) { 39 | case *value.IntValue: 40 | divisor := arg.(*value.IntValue).Value 41 | if divisor == 0 { 42 | panic(fmt.Sprint("`/' division by zero")) 43 | } 44 | val /= float64(divisor) 45 | case *value.FloatValue: 46 | divisor := arg.(*value.FloatValue).Value 47 | if divisor == 0 { 48 | panic(fmt.Sprint("`/' division by zero")) 49 | } 50 | val /= divisor 51 | default: 52 | panic(fmt.Sprint("incorrect argument type for `/' : ", arg)) 53 | } 54 | 55 | } 56 | return value.NewFloatValue(val) 57 | } 58 | -------------------------------------------------------------------------------- /value/primitives/eq.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Eq struct { 9 | value.Primitive 10 | } 11 | 12 | func NewEq() *Eq { 13 | return &Eq{value.Primitive{"="}} 14 | } 15 | 16 | func (self *Eq) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `=', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*value.IntValue); ok { 21 | if v2, ok := args[1].(*value.IntValue); ok { 22 | return value.NewBoolValue(v1.Value == v2.Value) 23 | } else if v2, ok := args[1].(*value.FloatValue); ok { 24 | return value.NewBoolValue(float64(v1.Value) == v2.Value) 25 | } 26 | } else if v1, ok := args[0].(*value.FloatValue); ok { 27 | if v2, ok := args[1].(*value.IntValue); ok { 28 | return value.NewBoolValue(v1.Value == float64(v2.Value)) 29 | } else if v2, ok := args[1].(*value.FloatValue); ok { 30 | return value.NewBoolValue(v1.Value == v2.Value) 31 | } 32 | } 33 | panic(fmt.Sprint("incorrect argument type for `=', expected number?")) 34 | } 35 | -------------------------------------------------------------------------------- /value/primitives/gt.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Gt struct { 9 | value.Primitive 10 | } 11 | 12 | func NewGt() *Gt { 13 | return &Gt{value.Primitive{">"}} 14 | } 15 | 16 | func (self *Gt) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `>', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*value.IntValue); ok { 21 | if v2, ok := args[1].(*value.IntValue); ok { 22 | return value.NewBoolValue(v1.Value > v2.Value) 23 | } else if v2, ok := args[1].(*value.FloatValue); ok { 24 | return value.NewBoolValue(float64(v1.Value) > v2.Value) 25 | } 26 | } else if v1, ok := args[0].(*value.FloatValue); ok { 27 | if v2, ok := args[1].(*value.IntValue); ok { 28 | return value.NewBoolValue(v1.Value > float64(v2.Value)) 29 | } else if v2, ok := args[1].(*value.FloatValue); ok { 30 | return value.NewBoolValue(v1.Value > v2.Value) 31 | } 32 | } 33 | panic(fmt.Sprint("incorrect argument type for `>', expected number?")) 34 | } 35 | -------------------------------------------------------------------------------- /value/primitives/gte.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type GtE struct { 9 | value.Primitive 10 | } 11 | 12 | func NewGtE() *GtE { 13 | return &GtE{value.Primitive{">="}} 14 | } 15 | 16 | func (self *GtE) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `>=', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*value.IntValue); ok { 21 | if v2, ok := args[1].(*value.IntValue); ok { 22 | return value.NewBoolValue(v1.Value >= v2.Value) 23 | } else if v2, ok := args[1].(*value.FloatValue); ok { 24 | return value.NewBoolValue(float64(v1.Value) >= v2.Value) 25 | } 26 | } else if v1, ok := args[0].(*value.FloatValue); ok { 27 | if v2, ok := args[1].(*value.IntValue); ok { 28 | return value.NewBoolValue(v1.Value >= float64(v2.Value)) 29 | } else if v2, ok := args[1].(*value.FloatValue); ok { 30 | return value.NewBoolValue(v1.Value >= v2.Value) 31 | } 32 | } 33 | panic(fmt.Sprint("incorrect argument type for `>=', expected number?")) 34 | } 35 | -------------------------------------------------------------------------------- /value/primitives/is_equal.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "github.com/kedebug/LispEx/value" 5 | ) 6 | 7 | type IsEqual struct { 8 | value.Primitive 9 | } 10 | 11 | func NewIsEqual() *IsEqual { 12 | return &IsEqual{value.Primitive{"equal?"}} 13 | } 14 | -------------------------------------------------------------------------------- /value/primitives/is_eqv.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type IsEqv struct { 9 | value.Primitive 10 | } 11 | 12 | func NewIsEqv() *IsEqv { 13 | return &IsEqv{value.Primitive{"eqv?"}} 14 | } 15 | 16 | func (self *IsEqv) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `eqv?', expected 2, given: ", len(args))) 19 | } 20 | typeof := NewTypeOf() 21 | symbol1 := typeof.Apply(args[0:1]).(*value.Symbol) 22 | symbol2 := typeof.Apply(args[1:2]).(*value.Symbol) 23 | 24 | if symbol1.Value != symbol2.Value { 25 | return value.NewBoolValue(false) 26 | } 27 | iseqv := false 28 | switch args[0].(type) { 29 | case *value.EmptyPairValue: 30 | iseqv = true 31 | case *value.BoolValue: 32 | val1 := args[0].(*value.BoolValue) 33 | val2 := args[1].(*value.BoolValue) 34 | iseqv = val1.Value == val2.Value 35 | case *value.IntValue: 36 | val1 := args[0].(*value.IntValue) 37 | val2 := args[1].(*value.IntValue) 38 | iseqv = val1.Value == val2.Value 39 | case *value.FloatValue: 40 | val1 := args[0].(*value.FloatValue) 41 | val2 := args[1].(*value.FloatValue) 42 | iseqv = val1.Value == val2.Value 43 | case *value.StringValue: 44 | val1 := args[0].(*value.StringValue) 45 | val2 := args[1].(*value.StringValue) 46 | iseqv = val1.Value == val2.Value 47 | case *value.Symbol: 48 | val1 := args[0].(*value.Symbol) 49 | val2 := args[1].(*value.Symbol) 50 | iseqv = val1.Value == val2.Value 51 | } 52 | return value.NewBoolValue(iseqv) 53 | } 54 | -------------------------------------------------------------------------------- /value/primitives/lt.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Lt struct { 9 | value.Primitive 10 | } 11 | 12 | func NewLt() *Lt { 13 | return &Lt{value.Primitive{"<"}} 14 | } 15 | 16 | func (self *Lt) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `<', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*value.IntValue); ok { 21 | if v2, ok := args[1].(*value.IntValue); ok { 22 | return value.NewBoolValue(v1.Value < v2.Value) 23 | } else if v2, ok := args[1].(*value.FloatValue); ok { 24 | return value.NewBoolValue(float64(v1.Value) < v2.Value) 25 | } 26 | } else if v1, ok := args[0].(*value.FloatValue); ok { 27 | if v2, ok := args[1].(*value.IntValue); ok { 28 | return value.NewBoolValue(v1.Value < float64(v2.Value)) 29 | } else if v2, ok := args[1].(*value.FloatValue); ok { 30 | return value.NewBoolValue(v1.Value < v2.Value) 31 | } 32 | } 33 | panic(fmt.Sprint("incorrect argument type for `<', expected number?")) 34 | } 35 | -------------------------------------------------------------------------------- /value/primitives/lte.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type LtE struct { 9 | value.Primitive 10 | } 11 | 12 | func NewLtE() *LtE { 13 | return &LtE{value.Primitive{"<="}} 14 | } 15 | 16 | func (self *LtE) Apply(args []value.Value) value.Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `<=', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*value.IntValue); ok { 21 | if v2, ok := args[1].(*value.IntValue); ok { 22 | return value.NewBoolValue(v1.Value <= v2.Value) 23 | } else if v2, ok := args[1].(*value.FloatValue); ok { 24 | return value.NewBoolValue(float64(v1.Value) <= v2.Value) 25 | } 26 | } else if v1, ok := args[0].(*value.FloatValue); ok { 27 | if v2, ok := args[1].(*value.IntValue); ok { 28 | return value.NewBoolValue(v1.Value <= float64(v2.Value)) 29 | } else if v2, ok := args[1].(*value.FloatValue); ok { 30 | return value.NewBoolValue(v1.Value <= v2.Value) 31 | } 32 | } 33 | panic(fmt.Sprint("incorrect argument type for `<=', expected number?")) 34 | } 35 | -------------------------------------------------------------------------------- /value/primitives/make_chan.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type MakeChan struct { 9 | value.Primitive 10 | } 11 | 12 | func NewMakeChan() *MakeChan { 13 | return &MakeChan{value.Primitive{"make-chan"}} 14 | } 15 | 16 | func (self *MakeChan) Apply(args []value.Value) value.Value { 17 | if len(args) > 1 { 18 | panic(fmt.Sprint("make-chan: arguments mismatch, expected at most 1")) 19 | } 20 | var size int 21 | if len(args) == 1 { 22 | if val, ok := args[0].(*value.IntValue); ok { 23 | size = int(val.Value) 24 | if size < 0 { 25 | panic(fmt.Sprint("make-chan: expected nonnegative number, given: ", size)) 26 | } 27 | } else { 28 | panic(fmt.Sprint("make-chan: expected integer, given: ", args[0])) 29 | } 30 | } 31 | return value.NewChannel(size) 32 | } 33 | -------------------------------------------------------------------------------- /value/primitives/mod.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Mod struct { 9 | Primitive 10 | } 11 | 12 | func NewMod() *Mod { 13 | return &Mod{Primitive{"%"}} 14 | } 15 | 16 | func (self *Mod) Apply(args []Value) Value { 17 | if len(args) != 2 { 18 | panic(fmt.Sprint("argument mismatch for `%', expected 2, given: ", len(args))) 19 | } 20 | if v1, ok := args[0].(*IntValue); ok { 21 | if v2, ok := args[1].(*IntValue); ok { 22 | if v2.Value == 0 { 23 | panic(fmt.Sprint("remainder: undefined for 0")) 24 | } 25 | return NewIntValue(v1.Value % v2.Value) 26 | } 27 | } 28 | panic(fmt.Sprint("incorrect argument type for `%', expected integer?")) 29 | } 30 | -------------------------------------------------------------------------------- /value/primitives/mult.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Mult struct { 9 | value.Primitive 10 | } 11 | 12 | func NewMult() *Mult { 13 | return &Mult{value.Primitive{"*"}} 14 | } 15 | 16 | func (self *Mult) Apply(args []value.Value) value.Value { 17 | var val1 int64 = 1 18 | var val2 float64 = 1 19 | isfloat := false 20 | 21 | for _, arg := range args { 22 | switch arg.(type) { 23 | case *value.IntValue: 24 | val1 *= arg.(*value.IntValue).Value 25 | case *value.FloatValue: 26 | isfloat = true 27 | val2 *= arg.(*value.FloatValue).Value 28 | default: 29 | panic(fmt.Sprint("incorrect argument type for '*' : ", arg)) 30 | } 31 | } 32 | if !isfloat { 33 | return value.NewIntValue(val1) 34 | } else { 35 | return value.NewFloatValue(float64(val1) * val2) 36 | } 37 | } 38 | -------------------------------------------------------------------------------- /value/primitives/newline.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Newline struct { 9 | Primitive 10 | } 11 | 12 | func NewNewline() *Newline { 13 | return &Newline{Primitive{"newline"}} 14 | } 15 | 16 | func (self *Newline) Apply(args []Value) Value { 17 | if len(args) != 0 { 18 | panic(fmt.Sprint("newline: argument mismatch, expected 0")) 19 | } 20 | fmt.Print("\n") 21 | return nil 22 | } 23 | -------------------------------------------------------------------------------- /value/primitives/or.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Or struct { 9 | Primitive 10 | } 11 | 12 | func NewOr() *Or { 13 | return &Or{Primitive{"or"}} 14 | } 15 | 16 | func (self *Or) Apply(args []Value) Value { 17 | result := false 18 | for _, arg := range args { 19 | if val, ok := arg.(*BoolValue); ok { 20 | result = result || val.Value 21 | } else { 22 | panic(fmt.Sprint("incorrect argument type for `or', expected bool?")) 23 | } 24 | } 25 | return NewBoolValue(result) 26 | } 27 | -------------------------------------------------------------------------------- /value/primitives/random.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | . "github.com/kedebug/LispEx/value" 6 | "math/rand" 7 | "time" 8 | ) 9 | 10 | type Random struct { 11 | Primitive 12 | rand *rand.Rand 13 | } 14 | 15 | func NewRandom() *Random { 16 | r := rand.New(rand.NewSource(time.Now().UnixNano())) 17 | return &Random{Primitive: Primitive{"random"}, rand: r} 18 | } 19 | 20 | func (self *Random) Apply(args []Value) Value { 21 | if len(args) != 1 { 22 | panic(fmt.Sprint("random: argument mismatch, expected 1")) 23 | } 24 | if val, ok := args[0].(*IntValue); ok { 25 | if val.Value <= 0 { 26 | panic(fmt.Sprint("random: expected positive integer, given: ", val)) 27 | } 28 | return NewIntValue(self.rand.Int63n(val.Value)) 29 | } 30 | panic(fmt.Sprint("random: expected integer?, given: ", args[0])) 31 | } 32 | -------------------------------------------------------------------------------- /value/primitives/sleep.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/constants" 6 | . "github.com/kedebug/LispEx/value" 7 | "time" 8 | ) 9 | 10 | type Sleep struct { 11 | Primitive 12 | } 13 | 14 | func NewSleep() *Sleep { 15 | return &Sleep{Primitive{constants.SLEEP}} 16 | } 17 | 18 | func (self *Sleep) Apply(args []Value) Value { 19 | if len(args) != 1 { 20 | panic(fmt.Sprintf("%s: arguments mismatch, expected 1", constants.SLEEP)) 21 | } 22 | if val, ok := args[0].(*IntValue); ok { 23 | time.Sleep(time.Duration(val.Value) * time.Millisecond) 24 | return nil 25 | } else { 26 | panic(fmt.Sprintf("incorrect argument type for `%s', expected: integer?, given: %s", constants.SLEEP, args[0])) 27 | } 28 | } 29 | -------------------------------------------------------------------------------- /value/primitives/sub.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type Sub struct { 9 | value.Primitive 10 | } 11 | 12 | func NewSub() *Sub { 13 | return &Sub{value.Primitive{"-"}} 14 | } 15 | 16 | func (self *Sub) Apply(args []value.Value) value.Value { 17 | var val float64 18 | isfloat := false 19 | 20 | if len(args) == 0 { 21 | panic(fmt.Sprint("`-' argument unmatch: expected at least 1")) 22 | } else if len(args) > 1 { 23 | switch args[0].(type) { 24 | case *value.IntValue: 25 | val = float64(args[0].(*value.IntValue).Value) 26 | case *value.FloatValue: 27 | val = args[0].(*value.FloatValue).Value 28 | default: 29 | panic(fmt.Sprint("incorrect argument type for `-' : ", args[0])) 30 | } 31 | args = args[1:] 32 | } 33 | 34 | for _, arg := range args { 35 | switch arg.(type) { 36 | case *value.IntValue: 37 | val -= float64(arg.(*value.IntValue).Value) 38 | case *value.FloatValue: 39 | isfloat = true 40 | val -= arg.(*value.FloatValue).Value 41 | default: 42 | panic(fmt.Sprint("incorrect argument type for `-' : ", arg)) 43 | } 44 | 45 | } 46 | if isfloat { 47 | return value.NewFloatValue(val) 48 | } else { 49 | return value.NewIntValue(int64(val)) 50 | } 51 | } 52 | -------------------------------------------------------------------------------- /value/primitives/typeof.go: -------------------------------------------------------------------------------- 1 | package primitives 2 | 3 | import ( 4 | "fmt" 5 | "github.com/kedebug/LispEx/value" 6 | ) 7 | 8 | type TypeOf struct { 9 | value.Primitive 10 | } 11 | 12 | func NewTypeOf() *TypeOf { 13 | return &TypeOf{value.Primitive{"type-of"}} 14 | } 15 | 16 | func (self *TypeOf) Apply(args []value.Value) value.Value { 17 | if len(args) != 1 { 18 | panic(fmt.Sprint("argument mismatch for `type-of', expected 1, given: ", len(args))) 19 | } 20 | symbol := "unknown" 21 | switch args[0].(type) { 22 | case *value.IntValue: 23 | symbol = "integer" 24 | case *value.FloatValue: 25 | symbol = "float" 26 | case *value.BoolValue: 27 | symbol = "bool" 28 | case *value.StringValue: 29 | symbol = "string" 30 | case *value.Channel: 31 | symbol = "channel" 32 | case *value.EmptyPairValue: 33 | symbol = "nilpair" 34 | case *value.PairValue: 35 | symbol = "pair" 36 | case *value.Closure: 37 | symbol = "procedure" 38 | case value.PrimFunc: 39 | symbol = "procedure" 40 | case *value.Symbol: 41 | symbol = args[0].(*value.Symbol).Value 42 | } 43 | return value.NewSymbol(symbol) 44 | } 45 | -------------------------------------------------------------------------------- /value/promise.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type Promise struct { 4 | IsVal bool 5 | Env interface{} 6 | Lazy interface{} 7 | } 8 | 9 | func NewPromise(env, lazy interface{}) *Promise { 10 | return &Promise{IsVal: true, Env: env, Lazy: lazy} 11 | } 12 | 13 | func (self *Promise) String() string { 14 | return "#" 15 | } 16 | -------------------------------------------------------------------------------- /value/stringvalue.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | import "fmt" 4 | 5 | type StringValue struct { 6 | Value string 7 | } 8 | 9 | func NewStringValue(val string) *StringValue { 10 | return &StringValue{Value: val} 11 | } 12 | 13 | func (self *StringValue) String() string { 14 | return fmt.Sprintf("\"%s\"", self.Value) 15 | } 16 | -------------------------------------------------------------------------------- /value/symbol.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type Symbol struct { 4 | Value string 5 | } 6 | 7 | func NewSymbol(value string) *Symbol { 8 | return &Symbol{Value: value} 9 | } 10 | 11 | func (self *Symbol) String() string { 12 | return self.Value 13 | } 14 | -------------------------------------------------------------------------------- /value/value.go: -------------------------------------------------------------------------------- 1 | package value 2 | 3 | type Value interface { 4 | String() string 5 | } 6 | --------------------------------------------------------------------------------