├── LICENSE ├── README.md ├── example ├── parallel.scm └── single.scm ├── gosick.go ├── lib └── builtin.scm ├── project.md └── scheme ├── Makefile ├── actor.go ├── application.go ├── boolean.go ├── builtin.go ├── closure.go ├── interpreter.go ├── interpreter_test.go ├── lexer.go ├── lexer_test.go ├── macro.go ├── misc.go ├── number.go ├── object.go ├── pair.go ├── parser.go ├── parser.go.y ├── parser.output ├── parser_test.go ├── string.go ├── subroutine.go ├── symbol.go ├── syntax.go └── variable.go /LICENSE: -------------------------------------------------------------------------------- 1 | The MIT License (MIT) 2 | 3 | Copyright (c) 2014 Takashi Kokubun 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Gosick 2 | 3 | Scheme interpreter implemented by Golang. 4 | This is started as [a programming project](https://github.com/k0kubun/gosick/blob/master/project.md) for newcomers of my laboratory. 5 | 6 | ## Installation 7 | 8 | ```bash 9 | $ go get github.com/k0kubun/gosick 10 | ``` 11 | 12 | ## Usage 13 | 14 | ```bash 15 | # Invoke interactive shell 16 | $ gosick 17 | 18 | # Excecute scheme source code 19 | $ gosick source.scm 20 | 21 | # One liner 22 | $ gosick -e "(+ 1 2)" 23 | 24 | # Dump AST of input source code 25 | $ gosick -a 26 | 27 | # Show help 28 | $ gosick -h 29 | ``` 30 | 31 | ## Implemented syntax and functions 32 | - +, -, *, /, =, <, <=, >, >= 33 | - cons, car, cdr, list, length, last, append, set-car!, set-cdr! 34 | - if, cond, and, or, not, begin, do 35 | - memq, eq?, neq?, equal? 36 | - null?, number?, boolean?, procedure?, pair?, list?, symbol?, string? 37 | - string-append, symbol->string, string->symbol, string->number, number->string 38 | - let, let*, letrec, lambda, define, set!, quote 39 | - write, print, load 40 | 41 | ## License 42 | 43 | Gosick is released under the [MIT License](http://opensource.org/licenses/MIT). 44 | -------------------------------------------------------------------------------- /example/parallel.scm: -------------------------------------------------------------------------------- 1 | (define concurrency 1) 2 | 3 | (define master 4 | (actor 5 | (("sum-upto" last) 6 | (define result 0) 7 | (define count 0) 8 | (define per-proc (/ last concurrency)) 9 | (do ((i 0)) 10 | ((= i concurrency)) 11 | (sum-range (+ (* i per-proc) 1) (* (+ i 1) per-proc) self) 12 | (set! i (+ i 1)))) 13 | 14 | (("add-result" num) 15 | (set! result (+ result num)) 16 | (set! count (+ count 1)) 17 | (if (= count concurrency) 18 | (begin (print result) (exit)))))) 19 | 20 | (define (sum-range start end master) 21 | (let ((child (generate-child))) 22 | (child start) 23 | (child ! "sum-range" start end master))) 24 | 25 | (define (generate-child) 26 | (actor 27 | (("sum-range" range-start range-end parent) 28 | (do ((sum 0) (i range-start)) 29 | ((> i range-end) 30 | (parent ! "add-result" sum)) 31 | (set! sum (+ sum i)) 32 | (set! i (+ i 1)))))) 33 | 34 | (master start) 35 | (master ! "sum-upto" 1200) 36 | 37 | (do () (#f)) 38 | -------------------------------------------------------------------------------- /example/single.scm: -------------------------------------------------------------------------------- 1 | (do ((sum 0) (i 1) (last 1200)) 2 | ((> i last) 3 | (print sum)) 4 | (set! sum (+ sum i)) 5 | (set! i (+ i 1))) 6 | -------------------------------------------------------------------------------- /gosick.go: -------------------------------------------------------------------------------- 1 | package main 2 | 3 | import ( 4 | "fmt" 5 | "github.com/GeertJohan/go.linenoise" 6 | "github.com/jessevdk/go-flags" 7 | "github.com/k0kubun/gosick/scheme" 8 | "io/ioutil" 9 | "log" 10 | "strings" 11 | ) 12 | 13 | type Options struct { 14 | Expression []string `short:"e" long:"expression" description:"excecute given expression"` 15 | DumpAST bool `short:"a" long:"ast" default:"false" description:"whether leaf nodes are plotted"` 16 | } 17 | 18 | func main() { 19 | options := new(Options) 20 | args, err := flags.Parse(options) 21 | if err != nil { 22 | return 23 | } 24 | 25 | if len(args) > 0 { 26 | executeSourceCode(args[0], options) 27 | } else if len(options.Expression) > 0 { 28 | executeExpression(strings.Join(options.Expression, " "), options.DumpAST) 29 | } else { 30 | invokeInteractiveShell(options) 31 | } 32 | } 33 | 34 | func executeSourceCode(filename string, options *Options) { 35 | buffer, err := ioutil.ReadFile(filename) 36 | if err != nil { 37 | log.Fatal(err) 38 | } 39 | 40 | executeExpression(string(buffer), options.DumpAST) 41 | } 42 | 43 | func executeExpression(expression string, dumpAST bool) { 44 | interpreter := scheme.NewInterpreter(expression) 45 | interpreter.PrintErrors(dumpAST) 46 | } 47 | 48 | func invokeInteractiveShell(options *Options) { 49 | mainInterpreter := scheme.NewInterpreter("") 50 | 51 | for { 52 | indentLevel := 0 53 | expression := "" 54 | 55 | for { 56 | currentLine, err := linenoise.Line(shellPrompt(indentLevel)) 57 | if err != nil { 58 | log.Fatal(err) 59 | return 60 | } 61 | if currentLine == "exit" { 62 | return 63 | } 64 | linenoise.AddHistory(currentLine) 65 | expression += " " 66 | expression += currentLine 67 | 68 | interpreter := scheme.NewInterpreter(expression) 69 | indentLevel = interpreter.IndentLevel() 70 | if indentLevel == 0 { 71 | mainInterpreter.ReloadSourceCode(expression) 72 | mainInterpreter.PrintResults(options.DumpAST) 73 | break 74 | } else if indentLevel < 0 { 75 | fmt.Println("*** ERROR: extra close parentheses") 76 | expression = "" 77 | indentLevel = 0 78 | } 79 | } 80 | } 81 | } 82 | 83 | func shellPrompt(indentLevel int) string { 84 | if indentLevel == 0 { 85 | return "gosick> " 86 | } else if indentLevel > 0 { 87 | return fmt.Sprintf("gosick* %s", strings.Repeat(" ", indentLevel)) 88 | } else { 89 | panic("Negative indent level") 90 | } 91 | } 92 | -------------------------------------------------------------------------------- /lib/builtin.scm: -------------------------------------------------------------------------------- 1 | (define (cadr x) 2 | (car (cdr x)) 3 | ) 4 | (define (cddr x) 5 | (cdr (cdr x)) 6 | ) 7 | 8 | (define (not x) 9 | (eq? x #f) 10 | ) 11 | (define (null? x) 12 | (eq? x ()) 13 | ) 14 | -------------------------------------------------------------------------------- /project.md: -------------------------------------------------------------------------------- 1 | ## コースB: Scheme以外の言語で実装する 2 | ### 要件 3 | - S式の構文解析も含める 4 | - TCOとマクロはオプション 5 | - CLOS風のオブジェクト指向言語機構, アクターモデル風の並行計算機構のいずれかを実装する 6 | - 構文に合致しない入力はエラーとすること. 7 | - 実行時エラーによってインタプリタが停止してしまわないようにすること(できる範囲で). 8 | 9 | ## A. 構文 10 | - 非終端記号 11 | - 大文字`[A-Z]`で始まる名前 12 | 13 | - 終端記号 14 | - 開き括弧 `(` 15 | - 閉じ括弧 `)` 16 | - ピリオド `.` 17 | - 小文字`[a-z]`で構成される名前 18 | - シングルクオート `'` で囲まれた文字列 19 | 20 | - 繰り返し記号 21 | - `X*` 要素Xの0個以上のくりかえし 22 | - `X+` 要素Xの1個以上のくりかえし 23 | - `[X]` 要素Xは高々1個 24 | 25 | ``` 26 | Toplevel ::= Exp 27 | | Define 28 | | (load String) ファイルの内容を読み込む 29 | 30 | Define ::= (define Id Exp) 31 | | (define (Id Id* [. Id]) Body) 32 | 33 | Exp ::= Const 定数 34 | | Id 変数 35 | | (lambda Arg Body) λ抽象 36 | | (Exp Exp*) 関数適用 37 | | (quote S-Exp) クオート (注1) 38 | | (set! Id Exp) 代入 39 | | (let [Id] Bindings Body) let 40 | | (let* Bindings Body) let* (注4) 41 | | (letrec Bindings Body) letrec 42 | | (if Exp Exp [Exp]) 条件式(if) 43 | | (cond (Exp Exp+)* [(else Exp+)]) 条件式(cond) (注5) 44 | | (and Exp*) 条件式(and) 45 | | (or Exp*) 条件式(or) 46 | | (begin Exp*) 逐次実行 47 | | (do ((Id Exp Exp)*) (Exp Exp*) Body) 繰り返し 48 | 49 | Body ::= Define* Exp+ 50 | 51 | Arg ::= Id 52 | | (Id* [Id . Id]) 53 | 54 | Bindings ::= ((Id Exp)*) 55 | 56 | S-Exp ::= Const 57 | | Id 58 | | (S-Exp* [S-Exp . S-Exp]) 59 | 60 | Const ::= Num 61 | | Bool 62 | | String 63 | | () 64 | 65 | Num: 最低限10進整数はサポートすること 66 | 67 | Bool ::= '#t' 68 | | '#f' 69 | 70 | String: ダブルクオートで囲まれた文字列 (注2) 71 | 72 | Id ::= [0-9A-Za-z!$%&*+-./<=>?@^_]+ (注3) 73 | ``` 74 | 75 | - (注1) `(quote X)` だけでなく `'X` という記法もサポートすること. 76 | - (注2) バックスラッシュ \ によるエスケープをサポートすること. 77 | - (注3) 数値となるものをのぞくこと. 78 | - (注4) `let*` は `let` の0個以上の繰り返しではなく `let*` という名前である. 79 | - (注5) 節は(`else`節を含めて)最低1個はあること. `else`節は高々1個とすること. 80 | 81 | ## B. 関数(最低限) 82 | ### 整数 83 | ``` 84 | number?, +, -, *, /, =, <, <=, >, >= 85 | ``` 86 | 87 | ### リスト 88 | ``` 89 | null?, pair?, list?, symbol?, 90 | car, cdr, cons, list, length, memq, last, append, 91 | set-car!, set-cdr! 92 | ``` 93 | 94 | ### ブール値 95 | ``` 96 | boolean?, not 97 | ``` 98 | 99 | ### 文字列 100 | ``` 101 | string?, string-append, 102 | symbol->string, string->symbol, string->number, number->string 103 | ``` 104 | 105 | ### 関数 106 | ``` 107 | procedure? 108 | ``` 109 | 110 | ### 比較 111 | ``` 112 | eq?, neq?, equal? 113 | ``` 114 | 115 | ### その他 116 | ``` 117 | load 118 | ``` 119 | 120 | ## C. 末尾呼び出しの最適化 121 | 末尾呼び出しの最適化(Tail Call Optimization, TCO)とは, 122 | 関数本体の末尾の位置(tail position)に関数呼び出しがある場合, 123 | コールスタックを消費せずにその呼び出された関数に制御を移すことです. 124 | 125 | 例えば 126 | 127 | ```scheme 128 | (define (even? x) 129 | (if (= x 0) #t (odd? (- x 1)))) 130 | (define (odd? x) 131 | (if (= x 1) #t (even? (- x 1)))) 132 | ``` 133 | 134 | のような関数 `even?`, `odd?` の場合, 135 | 0以上の引数に対しスタックオーバーフローを起こさずに計算できなくてはなりません. 136 | 137 | ## D. マクロ 138 | マクロは以下のように定義します. 139 | 140 | ```scheme 141 | (define-macro (positive x) 142 | (list '> x 0)) 143 | ``` 144 | 145 | プログラム中に `(positive e)` という式が出てきた場合, 146 | これが `(list '> e 0)` の実行結果で置き換えられ(これを「マクロが展開される」 といいます), 147 | その後で式の評価が行われます. 148 | 149 | 例えば 150 | 151 | ```scheme 152 | (if (positive (+ (g x) 1)) (foo) (bar)) 153 | ``` 154 | 155 | という式の場合,まずマクロが展開されて 156 | 157 | ```scheme 158 | (if (> (+ (g x) 1) 0) (foo) (bar)) 159 | ``` 160 | 161 | という形の式が得られて,それからこの式が評価されます. 162 | 163 | 次に, 以下のような関数定義との違いについて説明しておきます. 164 | 165 | ```scheme 166 | (define (positive x) 167 | (> x 0)) 168 | ``` 169 | 170 | 関数定義の場合は, `(positive e)` という式を評価する場合は, 171 | まず`e`を評価して, その結果の値が関数 `positive` の引数になります. 172 | 一方もしこれがマクロなら, e は評価されずに `(> e 0)` という形の式に展開され, 173 | それから展開後の式が評価されます. 174 | 175 | 引数を評価しなくてすむので, 関数定義と違って以下のような定義も可能です. 176 | 177 | ```scheme 178 | (define (let*-expander vars body) 179 | (if (null? vars) 180 | (cons 'begin body) 181 | (list 'let (list (car vars)) (let*-expander (cdr vars) body)))) 182 | 183 | (define-macro (let* vars . body) 184 | (let*-expander vars body)) 185 | ``` 186 | 187 | これは `let` を使って `let*` を定義した例です. 188 | このようにマクロを使うことで, さまざまな構文をユーザが自分で定義することができます. 189 | 190 | Schemeには `define-syntax (let-syntax)` というマクロ定義機構がありますが, 191 | これは展開の前後で変数名がぶつからないようにする hygienic マクロと呼ばれるもので, 192 | ちょっと実装は難しいと思います. 193 | 今回実装してもらうのは Common-Lisp 風のマクロです. 194 | ちなみにCommon-Lispだと上の例は以下のようになります. 195 | 196 | ```scheme 197 | (defun let*-expander (vars body) 198 | (if (null vars) 199 | (cons 'begin body) 200 | (list 'let (list (car vars)) (let*-expander (cdr vars) body)))) 201 | 202 | (defmacro let* (vars &rest body) 203 | (let*-expander vars body)) 204 | ``` 205 | 206 | 以上. 207 | -------------------------------------------------------------------------------- /scheme/Makefile: -------------------------------------------------------------------------------- 1 | parser.go: parser.go.y 2 | go tool yacc -o parser.go -v parser.output parser.go.y 3 | -------------------------------------------------------------------------------- /scheme/actor.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Actor struct { 8 | ObjectBase 9 | functions map[string]func([]Object) 10 | receiver chan []Object 11 | localBinding Binding 12 | } 13 | 14 | func NewActor(parent Object) *Actor { 15 | actor := &Actor{ 16 | ObjectBase: ObjectBase{parent: parent}, 17 | functions: make(map[string]func([]Object)), 18 | receiver: make(chan []Object), 19 | localBinding: make(Binding), 20 | } 21 | actor.localBinding["self"] = actor 22 | return actor 23 | } 24 | 25 | func (a *Actor) Eval() Object { 26 | return a 27 | } 28 | 29 | func (a *Actor) Invoke(argument Object) Object { 30 | assertListMinimum(argument, 1) 31 | elements := argument.(*Pair).Elements() 32 | switch elements[0].(type) { 33 | case *Variable: 34 | switch elements[0].(*Variable).identifier { 35 | case "start": 36 | go a.Start() 37 | case "!": 38 | a.receiver <- elements[1:] 39 | default: 40 | runtimeError("unexpected method for actor: %s", elements[0].(*Variable).identifier) 41 | } 42 | } 43 | return undef 44 | } 45 | 46 | func (a *Actor) Start() { 47 | for { 48 | select { 49 | case received := <-a.receiver: 50 | if len(received) == 0 { 51 | continue 52 | } 53 | assertObjectType(received[0], "string") 54 | 55 | a.functions[received[0].(*String).text](received[1:]) 56 | } 57 | } 58 | } 59 | 60 | func (a *Actor) String() string { 61 | if a.Bounder() == nil { 62 | return "#" 63 | } 64 | return fmt.Sprintf("#", a.Bounder()) 65 | } 66 | 67 | func (a *Actor) tryDefine(variable Object, object Object) { 68 | if variable.isVariable() { 69 | a.localBinding[variable.(*Variable).identifier] = object 70 | } 71 | } 72 | 73 | func (a *Actor) define(identifier string, object Object) { 74 | a.localBinding[identifier] = object 75 | } 76 | 77 | func (a *Actor) set(identifier string, object Object) { 78 | if a.localBinding[identifier] == nil { 79 | if a.parent == nil { 80 | runtimeError("symbol not defined") 81 | } else { 82 | a.parent.set(identifier, object) 83 | } 84 | } else { 85 | a.localBinding[identifier] = object 86 | } 87 | } 88 | 89 | func (a *Actor) binding() Binding { 90 | return a.localBinding 91 | } 92 | -------------------------------------------------------------------------------- /scheme/application.go: -------------------------------------------------------------------------------- 1 | // Application is a type to express scheme procedure application. 2 | // Application has a procedure and its argument as list which consists 3 | // of Pair. 4 | 5 | package scheme 6 | 7 | type Application struct { 8 | ObjectBase 9 | procedure Object 10 | arguments Object 11 | } 12 | 13 | type Invoker interface { 14 | Invoke(Object) Object 15 | } 16 | 17 | func NewApplication(parent Object) *Application { 18 | return &Application{ 19 | ObjectBase: ObjectBase{parent: parent}, 20 | } 21 | } 22 | 23 | func (a *Application) Eval() Object { 24 | evaledObject := a.procedure.Eval() 25 | 26 | switch evaledObject.(type) { 27 | case Invoker: 28 | return evaledObject.(Invoker).Invoke(a.arguments) 29 | default: 30 | runtimeError("invalid application") 31 | return nil 32 | } 33 | } 34 | 35 | func (a *Application) String() string { 36 | // Exceptional handling for special form: quote 37 | list := a.toList() 38 | firstObject := list.ElementAt(0) 39 | if firstObject.isVariable() && firstObject.(*Variable).content() == builtinSyntaxes["quote"] { 40 | if a.arguments.isNull() { 41 | return "(quote)" 42 | } else { 43 | return "'" + a.arguments.(*Pair).ElementAt(0).String() 44 | } 45 | } 46 | 47 | return list.String() 48 | } 49 | 50 | func (a *Application) toList() *Pair { 51 | list := NewPair(a.Parent()) 52 | list.Car = a.procedure 53 | list.Car.setParent(list) 54 | list.Cdr = a.arguments 55 | list.Cdr.setParent(list) 56 | return list 57 | } 58 | 59 | func (a *Application) isApplication() bool { 60 | return true 61 | } 62 | -------------------------------------------------------------------------------- /scheme/boolean.go: -------------------------------------------------------------------------------- 1 | // Boolean is a type for scheme bool objects, such as #f, #t. 2 | 3 | package scheme 4 | 5 | type Boolean struct { 6 | ObjectBase 7 | value bool 8 | } 9 | 10 | func NewBoolean(value interface{}, options ...Object) (boolean *Boolean) { 11 | switch value.(type) { 12 | case bool: 13 | boolean = &Boolean{value: value.(bool)} 14 | case string: 15 | if value.(string) == "#t" { 16 | boolean = &Boolean{value: true} 17 | } else if value.(string) == "#f" { 18 | boolean = &Boolean{value: false} 19 | } else { 20 | compileError("Unexpected value for NewBoolean") 21 | } 22 | default: 23 | return nil 24 | } 25 | 26 | if len(options) > 0 { 27 | boolean.parent = options[0] 28 | } 29 | return 30 | } 31 | 32 | func (b *Boolean) Eval() Object { 33 | return b 34 | } 35 | 36 | func (b *Boolean) String() string { 37 | if b.value == true { 38 | return "#t" 39 | } else { 40 | return "#f" 41 | } 42 | } 43 | 44 | func (b *Boolean) isBoolean() bool { 45 | return true 46 | } 47 | -------------------------------------------------------------------------------- /scheme/builtin.go: -------------------------------------------------------------------------------- 1 | // This file defines built-in procedures for TopLevel environment. 2 | 3 | package scheme 4 | 5 | import ( 6 | "fmt" 7 | "io/ioutil" 8 | "os" 9 | "strings" 10 | ) 11 | 12 | var ( 13 | builtinProcedures = Binding{ 14 | "+": NewSubroutine(plusSubr), 15 | "-": NewSubroutine(minusSubr), 16 | "*": NewSubroutine(multiplySubr), 17 | "/": NewSubroutine(divideSubr), 18 | "=": NewSubroutine(equalSubr), 19 | "<": NewSubroutine(lessThanSubr), 20 | "<=": NewSubroutine(lessEqualSubr), 21 | ">": NewSubroutine(greaterThanSubr), 22 | ">=": NewSubroutine(greaterEqualSubr), 23 | "append": NewSubroutine(appendSubr), 24 | "boolean?": NewSubroutine(isBooleanSubr), 25 | "car": NewSubroutine(carSubr), 26 | "cdr": NewSubroutine(cdrSubr), 27 | "cons": NewSubroutine(consSubr), 28 | "dump": NewSubroutine(dumpSubr), 29 | "eq?": NewSubroutine(isEqSubr), 30 | "equal?": NewSubroutine(isEqualSubr), 31 | "exit": NewSubroutine(exitSubr), 32 | "last": NewSubroutine(lastSubr), 33 | "length": NewSubroutine(lengthSubr), 34 | "list": NewSubroutine(listSubr), 35 | "list?": NewSubroutine(isListSubr), 36 | "load": NewSubroutine(loadSubr), 37 | "memq": NewSubroutine(memqSubr), 38 | "neq?": NewSubroutine(isNeqSubr), 39 | "number?": NewSubroutine(isNumberSubr), 40 | "number->string": NewSubroutine(numberToStringSubr), 41 | "pair?": NewSubroutine(isPairSubr), 42 | "print": NewSubroutine(printSubr), 43 | "procedure?": NewSubroutine(isProcedureSubr), 44 | "set-car!": NewSubroutine(setCarSubr), 45 | "set-cdr!": NewSubroutine(setCdrSubr), 46 | "string?": NewSubroutine(isStringSubr), 47 | "string-append": NewSubroutine(stringAppendSubr), 48 | "string->number": NewSubroutine(stringToNumberSubr), 49 | "string->symbol": NewSubroutine(stringToSymbolSubr), 50 | "symbol?": NewSubroutine(isSymbolSubr), 51 | "symbol->string": NewSubroutine(symbolToStringSubr), 52 | "write": NewSubroutine(writeSubr), 53 | } 54 | ) 55 | 56 | func carSubr(s *Subroutine, arguments Object) Object { 57 | assertListEqual(arguments, 1) 58 | 59 | object := arguments.(*Pair).ElementAt(0).Eval() 60 | assertObjectType(object, "pair") 61 | return object.(*Pair).Car 62 | } 63 | 64 | func cdrSubr(s *Subroutine, arguments Object) Object { 65 | assertListEqual(arguments, 1) 66 | 67 | object := arguments.(*Pair).ElementAt(0).Eval() 68 | assertObjectType(object, "pair") 69 | return object.(*Pair).Cdr 70 | } 71 | 72 | func consSubr(s *Subroutine, arguments Object) Object { 73 | assertListEqual(arguments, 2) 74 | objects := evaledObjects(arguments.(*Pair).Elements()) 75 | 76 | return &Pair{ 77 | ObjectBase: ObjectBase{parent: arguments.Parent()}, 78 | Car: objects[0], 79 | Cdr: objects[1], 80 | } 81 | } 82 | 83 | func divideSubr(s *Subroutine, arguments Object) Object { 84 | assertListMinimum(arguments, 1) 85 | 86 | numbers := evaledObjects(arguments.(*Pair).Elements()) 87 | assertObjectsType(numbers, "number") 88 | 89 | quotient := numbers[0].(*Number).value 90 | for _, number := range numbers[1:] { 91 | quotient /= number.(*Number).value 92 | } 93 | return NewNumber(quotient) 94 | } 95 | 96 | func dumpSubr(s *Subroutine, arguments Object) Object { 97 | object := arguments.(*Pair).ElementAt(0).Eval() 98 | fmt.Printf("%d\n", object) 99 | return undef 100 | } 101 | 102 | func equalSubr(s *Subroutine, arguments Object) Object { 103 | return s.compareNumbers(arguments, func(a, b int) bool { return a == b }) 104 | } 105 | 106 | func exitSubr(s *Subroutine, arguments Object) Object { 107 | os.Exit(0) 108 | return undef 109 | } 110 | 111 | func greaterThanSubr(s *Subroutine, arguments Object) Object { 112 | return s.compareNumbers(arguments, func(a, b int) bool { return a > b }) 113 | } 114 | 115 | func greaterEqualSubr(s *Subroutine, arguments Object) Object { 116 | return s.compareNumbers(arguments, func(a, b int) bool { return a >= b }) 117 | } 118 | 119 | func lengthSubr(s *Subroutine, arguments Object) Object { 120 | assertListEqual(arguments, 1) 121 | 122 | list := arguments.(*Pair).ElementAt(0).Eval() 123 | assertListMinimum(list, 0) 124 | 125 | return NewNumber(list.(*Pair).ListLength()) 126 | } 127 | 128 | func lessEqualSubr(s *Subroutine, arguments Object) Object { 129 | return s.compareNumbers(arguments, func(a, b int) bool { return a <= b }) 130 | } 131 | 132 | func lessThanSubr(s *Subroutine, arguments Object) Object { 133 | return s.compareNumbers(arguments, func(a, b int) bool { return a < b }) 134 | } 135 | 136 | func listSubr(s *Subroutine, arguments Object) Object { 137 | return arguments 138 | } 139 | 140 | func memqSubr(s *Subroutine, arguments Object) Object { 141 | assertListEqual(arguments, 2) 142 | 143 | searchObject := arguments.(*Pair).ElementAt(0).Eval() 144 | list := arguments.(*Pair).ElementAt(1).Eval() 145 | 146 | for { 147 | switch list.(type) { 148 | case *Pair: 149 | if areIdentical(list.(*Pair).Car, searchObject) { 150 | return list 151 | } 152 | default: 153 | break 154 | } 155 | 156 | if list = list.(*Pair).Cdr; list == nil { 157 | break 158 | } 159 | } 160 | return NewBoolean(false) 161 | } 162 | 163 | func minusSubr(s *Subroutine, arguments Object) Object { 164 | assertListMinimum(arguments, 1) 165 | 166 | numbers := evaledObjects(arguments.(*Pair).Elements()) 167 | assertObjectsType(numbers, "number") 168 | 169 | difference := numbers[0].(*Number).value 170 | for _, number := range numbers[1:] { 171 | difference -= number.(*Number).value 172 | } 173 | return NewNumber(difference) 174 | } 175 | 176 | func multiplySubr(s *Subroutine, arguments Object) Object { 177 | assertListMinimum(arguments, 0) 178 | 179 | numbers := evaledObjects(arguments.(*Pair).Elements()) 180 | assertObjectsType(numbers, "number") 181 | 182 | product := 1 183 | for _, number := range numbers { 184 | product *= number.(*Number).value 185 | } 186 | return NewNumber(product) 187 | } 188 | 189 | func lastSubr(s *Subroutine, arguments Object) Object { 190 | assertListEqual(arguments, 1) 191 | 192 | list := arguments.(*Pair).ElementAt(0).Eval() 193 | if !list.isPair() { 194 | runtimeError("pair required: %s", list) 195 | } 196 | assertListMinimum(list, 1) 197 | 198 | elements := list.(*Pair).Elements() 199 | return elements[len(elements)-1].Eval() 200 | } 201 | 202 | func appendSubr(s *Subroutine, arguments Object) Object { 203 | assertListMinimum(arguments, 0) 204 | elements := evaledObjects(arguments.(*Pair).Elements()) 205 | 206 | appendedList := NewPair(arguments) 207 | for _, element := range elements { 208 | appendedList = appendedList.AppendList(element) 209 | } 210 | 211 | return appendedList 212 | } 213 | 214 | func numberToStringSubr(s *Subroutine, arguments Object) Object { 215 | assertListEqual(arguments, 1) 216 | 217 | object := arguments.(*Pair).ElementAt(0).Eval() 218 | assertObjectType(object, "number") 219 | return NewString(object.(*Number).value) 220 | } 221 | 222 | func isBooleanSubr(s *Subroutine, arguments Object) Object { 223 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isBoolean() }) 224 | } 225 | 226 | func isEqSubr(s *Subroutine, arguments Object) Object { 227 | assertListEqual(arguments, 2) 228 | 229 | objects := evaledObjects(arguments.(*Pair).Elements()) 230 | return NewBoolean(areIdentical(objects[0], objects[1])) 231 | } 232 | 233 | func isEqualSubr(s *Subroutine, arguments Object) Object { 234 | assertListEqual(arguments, 2) 235 | 236 | objects := evaledObjects(arguments.(*Pair).Elements()) 237 | return NewBoolean(areEqual(objects[0], objects[1])) 238 | } 239 | 240 | func isListSubr(s *Subroutine, arguments Object) Object { 241 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isList() }) 242 | } 243 | 244 | func isNeqSubr(s *Subroutine, arguments Object) Object { 245 | return NewBoolean(!isEqSubr(s, arguments).(*Boolean).value) 246 | } 247 | 248 | func isNumberSubr(s *Subroutine, arguments Object) Object { 249 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isNumber() }) 250 | } 251 | 252 | func isPairSubr(s *Subroutine, arguments Object) Object { 253 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isPair() }) 254 | } 255 | 256 | func isProcedureSubr(s *Subroutine, arguments Object) Object { 257 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isProcedure() }) 258 | } 259 | 260 | func isSymbolSubr(s *Subroutine, arguments Object) Object { 261 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isSymbol() }) 262 | } 263 | 264 | func isStringSubr(s *Subroutine, arguments Object) Object { 265 | return s.booleanByFunc(arguments, func(object Object) bool { return object.isString() }) 266 | } 267 | 268 | func loadSubr(s *Subroutine, arguments Object) Object { 269 | assertListEqual(arguments, 1) 270 | 271 | object := arguments.(*Pair).ElementAt(0).Eval() 272 | assertObjectType(object, "string") 273 | 274 | buffer, err := ioutil.ReadFile(object.(*String).text) 275 | if err != nil { 276 | runtimeError("cannot find \"%s\"", object.(*String).text) 277 | return nil 278 | } 279 | 280 | parser := NewParser(string(buffer)) 281 | parser.Peek() 282 | for _, e := range parser.Parse(arguments.Parent()) { 283 | e.Eval() 284 | } 285 | 286 | return NewBoolean(true) 287 | } 288 | 289 | func plusSubr(s *Subroutine, arguments Object) Object { 290 | assertListMinimum(arguments, 0) 291 | 292 | numbers := evaledObjects(arguments.(*Pair).Elements()) 293 | assertObjectsType(numbers, "number") 294 | 295 | sum := 0 296 | for _, number := range numbers { 297 | sum += number.(*Number).value 298 | } 299 | return NewNumber(sum) 300 | } 301 | 302 | func printSubr(s *Subroutine, arguments Object) Object { 303 | assertListEqual(arguments, 1) // TODO: accept output port 304 | 305 | object := arguments.(*Pair).ElementAt(0).Eval() 306 | if object.isString() { 307 | fmt.Printf("%s\n", object.(*String).text) 308 | } else { 309 | fmt.Printf("%s\n", object) 310 | } 311 | return undef 312 | } 313 | 314 | func setCarSubr(s *Subroutine, arguments Object) Object { 315 | assertListEqual(arguments, 2) 316 | 317 | object := arguments.(*Pair).ElementAt(1).Eval() 318 | pair := arguments.(*Pair).ElementAt(0).Eval() 319 | assertObjectType(pair, "pair") 320 | 321 | pair.(*Pair).Car = object 322 | return undef 323 | } 324 | 325 | func setCdrSubr(s *Subroutine, arguments Object) Object { 326 | assertListEqual(arguments, 2) 327 | 328 | object := arguments.(*Pair).ElementAt(1).Eval() 329 | pair := arguments.(*Pair).ElementAt(0).Eval() 330 | assertObjectType(pair, "pair") 331 | 332 | pair.(*Pair).Cdr = object 333 | return undef 334 | } 335 | 336 | func stringAppendSubr(s *Subroutine, arguments Object) Object { 337 | assertListMinimum(arguments, 0) 338 | 339 | stringObjects := evaledObjects(arguments.(*Pair).Elements()) 340 | assertObjectsType(stringObjects, "string") 341 | 342 | texts := []string{} 343 | for _, stringObject := range stringObjects { 344 | texts = append(texts, stringObject.(*String).text) 345 | } 346 | return NewString(strings.Join(texts, "")) 347 | } 348 | 349 | func stringToNumberSubr(s *Subroutine, arguments Object) Object { 350 | assertListEqual(arguments, 1) 351 | 352 | object := arguments.(*Pair).ElementAt(0).Eval() 353 | assertObjectType(object, "string") 354 | return NewNumber(object.(*String).text) 355 | } 356 | 357 | func symbolToStringSubr(s *Subroutine, arguments Object) Object { 358 | assertListEqual(arguments, 1) 359 | 360 | object := arguments.(*Pair).ElementAt(0).Eval() 361 | assertObjectType(object, "symbol") 362 | return NewString(object.(*Symbol).identifier) 363 | } 364 | 365 | func stringToSymbolSubr(s *Subroutine, arguments Object) Object { 366 | assertListEqual(arguments, 1) 367 | 368 | object := arguments.(*Pair).ElementAt(0).Eval() 369 | assertObjectType(object, "string") 370 | return NewSymbol(object.(*String).text) 371 | } 372 | 373 | func writeSubr(s *Subroutine, arguments Object) Object { 374 | assertListEqual(arguments, 1) // TODO: accept output port 375 | 376 | object := arguments.(*Pair).ElementAt(0).Eval() 377 | fmt.Printf("%s", object) 378 | return undef 379 | } 380 | -------------------------------------------------------------------------------- /scheme/closure.go: -------------------------------------------------------------------------------- 1 | // Closure is an object returned by lambda. 2 | // It has references for closures scoped when it was generated. 3 | 4 | package scheme 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type Closure struct { 11 | ObjectBase 12 | localBinding Binding 13 | function func(Object) Object 14 | } 15 | 16 | func NewClosure(parent Object) *Closure { 17 | return &Closure{ObjectBase: ObjectBase{parent: parent}, localBinding: make(Binding)} 18 | } 19 | 20 | // Cover the given object with a new closure. 21 | // Insert this into tree structure between given object and its parent. 22 | func WrapClosure(wrappedObject Object) *Closure { 23 | closure := NewClosure(wrappedObject.Parent()) 24 | wrappedObject.setParent(closure) 25 | return closure 26 | } 27 | 28 | func (c *Closure) String() string { 29 | if c.Bounder() == nil { 30 | return "#" 31 | } 32 | return fmt.Sprintf("#", c.Bounder()) 33 | } 34 | 35 | func (c *Closure) DefineFunction(s *Syntax, variables, body []Object) { 36 | c.function = func(givenArguments Object) Object { 37 | // assert given arguments 38 | givenElements := s.elementsMinimum(givenArguments, 0) 39 | if len(variables) != len(givenElements) { 40 | compileError("wrong number of arguments: requires %d, but got %d", len(variables), len(givenElements)) 41 | } 42 | 43 | // define arguments to local scope 44 | for index, variable := range variables { 45 | c.tryDefine(variable, givenElements[index].Eval()) 46 | } 47 | 48 | return evalAll(body) 49 | } 50 | } 51 | 52 | func (c *Closure) Invoke(argument Object) Object { 53 | return c.function(argument) 54 | } 55 | 56 | func (c *Closure) isClosure() bool { 57 | return true 58 | } 59 | 60 | func (c *Closure) isProcedure() bool { 61 | return true 62 | } 63 | 64 | // This method is for define syntax form. 65 | // Define a local variable in the most inner closure. 66 | func (c *Closure) define(identifier string, object Object) { 67 | c.localBinding[identifier] = object 68 | } 69 | 70 | // If variable is *Variable, define value. 71 | func (c *Closure) tryDefine(variable Object, object Object) { 72 | if variable.isVariable() { 73 | c.localBinding[variable.(*Variable).identifier] = object 74 | } 75 | } 76 | 77 | // This method is for set! syntax form. 78 | // Update most inner scoped closure's binding, otherwise raise error. 79 | func (c *Closure) set(identifier string, object Object) { 80 | if c.localBinding[identifier] == nil { 81 | if c.parent == nil { 82 | runtimeError("symbol not defined") 83 | } else { 84 | c.parent.set(identifier, object) 85 | } 86 | } else { 87 | c.localBinding[identifier] = object 88 | } 89 | } 90 | 91 | func (c *Closure) binding() Binding { 92 | return c.localBinding 93 | } 94 | -------------------------------------------------------------------------------- /scheme/interpreter.go: -------------------------------------------------------------------------------- 1 | // Interpreter is a scheme source code interpreter. 2 | // It owns a role of API for executing scheme program. 3 | // Interpreter embeds Parser and delegates syntactic analysis to it. 4 | 5 | package scheme 6 | 7 | import ( 8 | "fmt" 9 | "io/ioutil" 10 | "log" 11 | "os" 12 | "path/filepath" 13 | "regexp" 14 | "strings" 15 | ) 16 | 17 | type Interpreter struct { 18 | *Parser 19 | closure *Closure 20 | } 21 | 22 | func NewInterpreter(source string) *Interpreter { 23 | i := &Interpreter{ 24 | Parser: NewParser(source), 25 | closure: &Closure{ 26 | ObjectBase: ObjectBase{parent: nil}, 27 | localBinding: defaultBinding(), 28 | }, 29 | } 30 | i.loadBuiltinLibrary("builtin") 31 | return i 32 | } 33 | 34 | func (i *Interpreter) PrintResults(dumpAST bool) { 35 | results := i.EvalResults(dumpAST) 36 | if dumpAST { 37 | fmt.Printf("\n*** Result ***\n") 38 | } 39 | for _, result := range results { 40 | fmt.Println(result) 41 | } 42 | } 43 | 44 | func (i *Interpreter) PrintErrors(dumpAST bool) { 45 | results := i.EvalResults(dumpAST) 46 | if dumpAST { 47 | fmt.Printf("\n*** Result ***\n") 48 | } 49 | 50 | re, err := regexp.Compile("^\\*\\*\\* ERROR:") 51 | if err != nil { 52 | panic(err) 53 | } 54 | 55 | for _, result := range results { 56 | if re.MatchString(result) { 57 | fmt.Println(result) 58 | } 59 | } 60 | } 61 | 62 | func (i *Interpreter) EvalResults(dumpAST bool) (results []string) { 63 | defer func() { 64 | if err := recover(); err != nil { 65 | results = append(results, fmt.Sprintf("*** ERROR: %s", err)) 66 | } 67 | }() 68 | 69 | i.Peek() 70 | for _, e := range i.Parser.Parse(i.closure) { 71 | if dumpAST { 72 | fmt.Printf("\n*** AST ***\n") 73 | i.DumpAST(e, 0) 74 | } 75 | results = append(results, e.Eval().String()) 76 | } 77 | return 78 | } 79 | 80 | // Load new source code with current environment 81 | func (i *Interpreter) ReloadSourceCode(source string) { 82 | i.Parser = NewParser(source) 83 | } 84 | 85 | func (i *Interpreter) DumpAST(object Object, indentLevel int) { 86 | if object == nil { 87 | return 88 | } 89 | switch object.(type) { 90 | case *Application: 91 | i.printWithIndent("Application", indentLevel) 92 | i.DumpAST(object.(*Application).procedure, indentLevel+1) 93 | i.DumpAST(object.(*Application).arguments, indentLevel+1) 94 | case *Pair: 95 | pair := object.(*Pair) 96 | if pair.Car == nil && pair.Cdr == nil { 97 | i.printWithIndent("()", indentLevel) 98 | return 99 | } 100 | i.printWithIndent("Pair", indentLevel) 101 | i.DumpAST(pair.Car, indentLevel+1) 102 | i.DumpAST(pair.Cdr, indentLevel+1) 103 | case *String: 104 | i.printWithIndent(fmt.Sprintf("String(%s)", object), indentLevel) 105 | case *Number: 106 | i.printWithIndent(fmt.Sprintf("Number(%s)", object), indentLevel) 107 | case *Boolean: 108 | i.printWithIndent(fmt.Sprintf("Boolean(%s)", object), indentLevel) 109 | case *Variable: 110 | i.printWithIndent(fmt.Sprintf("Variable(%s)", object.(*Variable).identifier), indentLevel) 111 | } 112 | } 113 | 114 | func (i *Interpreter) printWithIndent(text string, indentLevel int) { 115 | fmt.Printf("%s%s\n", strings.Repeat(" ", indentLevel), text) 116 | } 117 | 118 | func (i *Interpreter) loadBuiltinLibrary(name string) { 119 | originalParser := i.Parser 120 | 121 | buffer, err := ioutil.ReadFile(i.libraryPath(name)) 122 | if err != nil { 123 | log.Fatal(err) 124 | } 125 | i.Parser = NewParser(string(buffer)) 126 | i.EvalResults(false) 127 | 128 | i.Parser = originalParser 129 | } 130 | 131 | func (i *Interpreter) libraryPath(name string) string { 132 | return filepath.Join( 133 | os.Getenv("GOPATH"), 134 | "src", 135 | "github.com", 136 | "k0kubun", 137 | "gosick", 138 | "lib", 139 | name+".scm", 140 | ) 141 | } 142 | -------------------------------------------------------------------------------- /scheme/interpreter_test.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "fmt" 5 | "io/ioutil" 6 | "os" 7 | "testing" 8 | ) 9 | 10 | type interpreterTest struct { 11 | source string 12 | results []string 13 | } 14 | 15 | type evalErrorTest struct { 16 | source string 17 | message string 18 | } 19 | 20 | type loadTest struct { 21 | loadSource string 22 | source string 23 | message string 24 | } 25 | 26 | var interpreterTests = []interpreterTest{ 27 | evalTest("12", "12"), 28 | evalTest("-12", "-12"), 29 | evalTest("()", "()"), 30 | evalTest("#f #t", "#f", "#t"), 31 | evalTest("1234567890", "1234567890"), 32 | 33 | evalTest("'12", "12"), 34 | evalTest("'hello", "hello"), 35 | evalTest("'#f", "#f"), 36 | evalTest("'#t", "#t"), 37 | evalTest("'(1)", "(1)"), 38 | evalTest("'( 1 2 3 )", "(1 2 3)"), 39 | evalTest("'( 1 ( 2 3 ) )", "(1 (2 3))"), 40 | 41 | // evalTest("(quote 12)", "12"), 42 | // evalTest("(quote hello)", "hello"), 43 | // evalTest("(quote 'hello)", "'hello"), 44 | // evalTest("(quote (quote hello))", "'hello"), 45 | // evalTest("(quote (cond ()))", "(cond ())"), 46 | // evalTest("(quote #f)", "#f"), 47 | // evalTest("(quote #t)", "#t"), 48 | // evalTest("(quote ( 1 (3) 4 ))", "(1 (3) 4)"), 49 | 50 | evalTest("\"\"", "\"\""), 51 | evalTest("\"hello\"", "\"hello\""), 52 | 53 | evalTest("(+)", "0"), 54 | evalTest("(- 1)", "1"), 55 | evalTest("(*)", "1"), 56 | evalTest("(/ 1)", "1"), 57 | 58 | evalTest("(+ 1 20 300 4000)", "4321"), 59 | evalTest("(+ -2 -3)", "-5"), 60 | evalTest("(- -2 -3)", "1"), 61 | evalTest("(+ -2 3)", "1"), 62 | evalTest(" ( + 1 2 3 ) ", "6"), 63 | evalTest("(+ 1 (+ 2 3) (+ 3 4))", "13"), 64 | evalTest("(- 3(- 2 3)(+ 3 0))", "1"), 65 | evalTest("(*(* 3 3)3)", "27"), 66 | evalTest("(/ 100(/ 4 2))", "50"), 67 | evalTest("(+ (* 100 3) (/(- 4 2) 2))", "301"), 68 | 69 | evalTest("(= 2 1)", "#f"), 70 | evalTest("(= (* 100 3) 300)", "#t"), 71 | 72 | evalTest("(< 2 1)", "#f"), 73 | evalTest("(< 1 2)", "#t"), 74 | evalTest("(< 1 2 1)", "#f"), 75 | evalTest("(< 1 2 3)", "#t"), 76 | 77 | evalTest("(<= 2 1)", "#f"), 78 | evalTest("(<= 1 2)", "#t"), 79 | evalTest("(<= 2 2)", "#t"), 80 | evalTest("(<= 1 2 1)", "#f"), 81 | evalTest("(<= 1 2 3)", "#t"), 82 | evalTest("(<= 1 1 1)", "#t"), 83 | 84 | evalTest("(> 1 2)", "#f"), 85 | evalTest("(> 2 1)", "#t"), 86 | evalTest("(> 1 2 1)", "#f"), 87 | evalTest("(> 3 2 1)", "#t"), 88 | 89 | evalTest("(>= 1 2)", "#f"), 90 | evalTest("(>= 2 1)", "#t"), 91 | evalTest("(>= 2 2)", "#t"), 92 | evalTest("(>= 1 2 1)", "#f"), 93 | evalTest("(>= 3 2 1)", "#t"), 94 | evalTest("(>= 1 1 1)", "#t"), 95 | 96 | evalTest("(not #f)", "#t"), 97 | evalTest("(not #t)", "#f"), 98 | evalTest("(not (number? ()))", "#t"), 99 | evalTest("(not 1)", "#f"), 100 | evalTest("(not ())", "#f"), 101 | 102 | evalTest("(cons 1 2)", "(1 . 2)"), 103 | evalTest("(car (cons 1 2))", "1"), 104 | evalTest("(cons '(1 2) (list 1 2 3))", "((1 2) 1 2 3)"), 105 | evalTest("(cons (cons 1 2) 3)", "((1 . 2) . 3)"), 106 | evalTest("(car '(1))", "1"), 107 | evalTest("(cdr '(1))", "()"), 108 | evalTest("(car '(1 2))", "1"), 109 | evalTest("(cdr '(1 2))", "(2)"), 110 | evalTest("(cadr (cons 1 (cons 2 3)))", "2"), 111 | evalTest("(cadr '(1 2 3))", "2"), 112 | evalTest("(cddr (cons 1 (cons 2 3)))", "3"), 113 | evalTest("(cddr '(1 2 3))", "(3)"), 114 | 115 | evalTest("(list)", "()"), 116 | evalTest("(list 1 2 3)", "(1 2 3)"), 117 | evalTest("(cdr (list 1 2 3))", "(2 3)"), 118 | 119 | evalTest("(length ())", "0"), 120 | evalTest("(length '(1 2))", "2"), 121 | evalTest("(length (list 1 '(2 3) 4))", "3"), 122 | 123 | // evalTest("(memq (car (cons 'b 'c)) '(a b c))", "(b c)"), 124 | evalTest("(memq 'd '(a b c))", "#f"), 125 | evalTest("(memq 'a (cons 'a 'b))", "(a . b)"), 126 | 127 | evalTest("(last '(1 2 3))", "3"), 128 | evalTest("(last (list 1 (+ 2 3)))", "5"), 129 | 130 | evalTest("(append)", "()"), 131 | evalTest("(append '(1))", "(1)"), 132 | // evalTest("(append '(1 2) '(3 4))", "(1 2 3 4)"), 133 | 134 | evalTest("(string-append)", "\"\""), 135 | evalTest("(string-append \"a\" \" \" \"b\")", "\"a b\""), 136 | 137 | evalTest("(string->symbol \"a\")", "a"), 138 | evalTest("(symbol->string 'a)", "\"a\""), 139 | 140 | evalTest("(string->number \"1\")", "1"), 141 | evalTest("(number->string 1)", "\"1\""), 142 | 143 | evalTest("(number? 100)", "#t"), 144 | evalTest("(number? (+ 3(* 2 8)))", "#t"), 145 | evalTest("(number? #t)", "#f"), 146 | evalTest("(number? ())", "#f"), 147 | 148 | evalTest("(procedure? 1)", "#f"), 149 | evalTest("(procedure? +)", "#t"), 150 | evalTest("(procedure? (lambda (x)))", "#t"), 151 | evalTest("(procedure? define)", "#f"), 152 | 153 | evalTest("(boolean? 1)", "#f"), 154 | evalTest("(boolean? ())", "#f"), 155 | evalTest("(boolean? #t)", "#t"), 156 | evalTest("(boolean? #f)", "#t"), 157 | evalTest("(boolean? (null? 1))", "#t"), 158 | 159 | evalTest("(pair? 1)", "#f"), 160 | evalTest("(pair? ())", "#f"), 161 | evalTest("(pair? '(1 2 3))", "#t"), 162 | 163 | evalTest("(list? 1)", "#f"), 164 | evalTest("(list? ())", "#t"), 165 | evalTest("(list? '(1 2 3))", "#t"), 166 | 167 | evalTest("(symbol? 1)", "#f"), 168 | evalTest("(symbol? 'hello)", "#t"), 169 | 170 | evalTest("(string? 1)", "#f"), 171 | evalTest("(string? \"\")", "#t"), 172 | evalTest("(string? \"hello\")", "#t"), 173 | 174 | evalTest("(null? 1)", "#f"), 175 | evalTest("(null? ())", "#t"), 176 | 177 | evalTest("(eq? 1 1)", "#t"), 178 | evalTest("(eq? 1 2)", "#f"), 179 | evalTest("(eq? 1 #f)", "#f"), 180 | evalTest("(eq? #f #f)", "#t"), 181 | evalTest("(eq? () ())", "#t"), 182 | evalTest("(eq? 'foo 'foo)", "#t"), 183 | evalTest("(eq? \"foo\" \"foo\")", "#f"), 184 | evalTest("(eq? '(1 2) '(1 2))", "#f"), 185 | 186 | evalTest("(neq? 1 1)", "#f"), 187 | evalTest("(neq? 1 2)", "#t"), 188 | evalTest("(neq? 1 #f)", "#t"), 189 | evalTest("(neq? #f #f)", "#f"), 190 | evalTest("(neq? 'foo 'foo)", "#f"), 191 | evalTest("(neq? \"foo\" \"foo\")", "#t"), 192 | evalTest("(neq? '(1 2) '(1 2))", "#t"), 193 | 194 | evalTest("(equal? 1 1)", "#t"), 195 | evalTest("(equal? 1 2)", "#f"), 196 | evalTest("(equal? 1 #f)", "#f"), 197 | evalTest("(equal? #f #f)", "#t"), 198 | evalTest("(equal? 'foo 'foo)", "#t"), 199 | evalTest("(equal? \"foo\" \"foo\")", "#f"), 200 | evalTest("(equal? '(1 1) '(1 2))", "#f"), 201 | evalTest("(equal? '(1 2) '(1 2))", "#t"), 202 | 203 | evalTest("(define x 1) x", "x", "1"), 204 | evalTest("(define x (+ 1 3)) x", "x", "4"), 205 | evalTest("(define x 1) (define y 2) (define z 3) (+ x (* y z))", "x", "y", "z", "7"), 206 | evalTest("(define x 1) (define x 2) x", "x", "x", "2"), 207 | 208 | evalTest("(lambda (x) x)", "#"), 209 | evalTest("((lambda (x) 1) 2)", "1"), 210 | evalTest("((lambda (x y z) (+ 3 4) (- 4 1) ) 2 3 3)", "3"), 211 | evalTest("((lambda (x) (+ x x)) 1)", "2"), 212 | evalTest("(define x 1) ((lambda (y) (+ x y)) 2)", "x", "3"), 213 | evalTest("(define x 1) ((lambda (x) (+ x x)) 2)", "x", "4"), 214 | evalTest("((lambda (x) (define x 3) x) 2)", "3"), 215 | evalTest("((lambda (x y z) (* (+ x y) z)) 1 2 3)", "9"), 216 | evalTest("(define x (lambda (x) y)) (define y 1) (x 2)", "x", "y", "1"), 217 | evalTest("(define x 0) (define y 0) (define z (lambda (z) (set! x y) (set! y z) x)) (z 1) x y (z 2) x y", "x", "y", "z", "0", "0", "1", "1", "1", "2"), 218 | evalTest("(define x (lambda (x) (set! y x))) (define y 3) (x 2) y", "x", "y", "2", "2"), 219 | evalTest("(define x (lambda (a) (* 2 a))) (define y (lambda (a) (* 3 a))) (define z (lambda (a b) (x a) (y b))) (* (x 3) (y 2) (z 4 5))", "x", "y", "z", "540"), 220 | 221 | evalTest("(define x 2) (set! x 3) x", "x", "3", "3"), 222 | evalTest("(define x 4) ((lambda (x) (set! x 3) x) 2) x", "x", "3", "4"), 223 | 224 | evalTest("(define x (cons 3 2)) (set-car! x 1) x", "x", "#", "(1 . 2)"), 225 | evalTest("(define x (cons 3 2)) (set-cdr! x 1) x", "x", "#", "(3 . 1)"), 226 | evalTest("((lambda (x) (set-car! x 1) x) (cons 2 3))", "(1 . 3)"), 227 | evalTest("((lambda (x) (set-cdr! x 1) x) (cons 2 3))", "(2 . 1)"), 228 | 229 | evalTest("(if #t 1 2)", "1"), 230 | evalTest("(if #f 1 2)", "2"), 231 | evalTest("(if 1 2 3)", "2"), 232 | evalTest("(if (null? ()) 1 2)", "1"), 233 | evalTest("(if (null? 3) 1)", "#"), 234 | evalTest("(if (number? 3) 'num)", "num"), 235 | 236 | evalTest("(cond (#t))", "#t"), 237 | evalTest("(cond (()))", "()"), 238 | evalTest("(cond (else))", "#"), 239 | evalTest("(cond (#f 1) (#t 2) (else 3))", "2"), 240 | evalTest("(cond ((number? 3) 'hello) (else 'no))", "hello"), 241 | 242 | evalTest("(and)", "#t"), 243 | evalTest("(and #t 3)", "3"), 244 | evalTest("(and (number? 3) (boolean? #f))", "#t"), 245 | evalTest("(and (number? 3) (boolean? 3))", "#f"), 246 | 247 | evalTest("(or)", "#f"), 248 | evalTest("(or #f 3)", "3"), 249 | evalTest("(or 3)", "3"), 250 | evalTest("(or #f 3 #f)", "3"), 251 | evalTest("(or (number? 3) (boolean? 3))", "#t"), 252 | evalTest("(or (number? #f) (boolean? 3))", "#f"), 253 | 254 | evalTest("(begin)", "#"), 255 | evalTest("(begin 1 2 3)", "3"), 256 | evalTest("(begin (define x 2) (set! x 3) x) x", "3", "3"), 257 | evalTest("(define x 1) (define y x) (set! x 3) y", "x", "y", "3", "1"), 258 | 259 | evalTest("(define (func x) x) (func 1)", "func", "1"), 260 | evalTest("(define (func x y) (+ x y)) (func 1 2)", "func", "3"), 261 | evalTest("(define () x)", "*** ERROR: Compile Error: syntax-error: (define () x)"), 262 | 263 | evalTest("(do () (#t))", "#t"), 264 | evalTest("(do ((x #f)) (x) (set! x #t))", "#t"), 265 | evalTest("(do ((i (+ 1 2))) (#t i))", "3"), 266 | evalTest("(do ((i 1) (j 1)) (#t))", "#t"), 267 | evalTest("(define x \"\") (do ((i 1 (+ i 1)) (j 1 (* j 2))) ((> i 3) x) (begin (set! x (string-append x (number->string i))) (set! x (string-append x (number->string j)))))", "x", "\"112234\""), 268 | 269 | evalTest("(let ((x 1) (y 2)) (+ x y))", "3"), 270 | evalTest("(let ((x 1) (y x)) y)", "*** ERROR: unbound variable: x"), 271 | evalTest("(let ((x (lambda () x))) (x))", "*** ERROR: unbound variable: x"), 272 | 273 | evalTest("(let* ((x 1) (y 2)) (+ x y))", "3"), 274 | evalTest("(let* ((x 1) (y x)) y)", "1"), 275 | evalTest("(let* ((x (lambda () x))) (x))", "*** ERROR: unbound variable: x"), 276 | 277 | evalTest("(letrec ((x 1) (y 2)) (+ x y))", "3"), 278 | evalTest("(letrec ((x 1) (y x)) y)", "*** ERROR: unbound variable: x"), 279 | evalTest("(letrec ((x (lambda () x))) (x))", "#"), 280 | 281 | evalTest("(actor)", "#"), 282 | evalTest("(actor ((\"hello\") \"hello\"))", "#"), 283 | evalTest("(define master (actor)) master", "master", "#"), 284 | 285 | evalTest("(define-macro (positive x) (list '> x 0)) positive", "#", "#"), 286 | 287 | evalTest("actor", "#"), 288 | evalTest("set!", "#"), 289 | evalTest("if", "#"), 290 | evalTest("and", "#"), 291 | evalTest("or", "#"), 292 | evalTest("begin", "#"), 293 | evalTest("quote", "#"), 294 | evalTest("cond", "#"), 295 | evalTest("lambda", "#"), 296 | evalTest("let", "#"), 297 | evalTest("do", "#"), 298 | 299 | evalTest("+", "#"), 300 | 301 | // Tail Call Optimization 302 | evalTest("(letrec ((rec (lambda (x) (if (= x 0) #t (rec (- x 1)))))) (rec 1))", "#t"), 303 | evalTest(`(define (even? x) (if (= x 0) #t (odd? (- x 1)))) (define (odd? x) (if (= x 1) #t (even? (- x 1)))) (even? 10)`, "even?", "odd?"), 304 | } 305 | 306 | var runtimeErrorTests = []interpreterTest{ 307 | evalTest("(1)", "*** ERROR: invalid application"), 308 | evalTest("hello", "*** ERROR: unbound variable: hello"), 309 | evalTest("((lambda (x) (define y 1) 1) 1) y", "1", "*** ERROR: unbound variable: y"), 310 | // evalTest("'1'", "1", "*** ERROR: unterminated quote"), 311 | evalTest("(last ())", "*** ERROR: pair required: ()"), 312 | evalTest("((lambda (x) (set! x 3) x) 2) x", "3", "*** ERROR: unbound variable: x"), 313 | 314 | evalTest("(define set! 0) (set! define 0)", "set!", "*** ERROR: invalid application"), 315 | evalTest("(define if 0) (if #t 0)", "if", "*** ERROR: invalid application"), 316 | // evalTest("(define quote 1) '1", "quote", "*** ERROR: invalid application"), 317 | } 318 | 319 | var compileErrorTests = []interpreterTest{ 320 | evalTest("(quote)", "*** ERROR: Compile Error: syntax-error: malformed quote: (quote)"), 321 | evalTest("(define)", "*** ERROR: Compile Error: syntax-error: malformed define: (define)"), 322 | 323 | evalTest("(-)", "*** ERROR: Compile Error: procedure requires at least 1 argument"), 324 | evalTest("(/)", "*** ERROR: Compile Error: procedure requires at least 1 argument"), 325 | evalTest("(number?)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 326 | evalTest("(null?)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 327 | evalTest("(null? 1 2)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 2"), 328 | evalTest("(not)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 329 | 330 | evalTest("(+ 1 #t)", "*** ERROR: Compile Error: number required, but got #t"), 331 | evalTest("(- #t)", "*** ERROR: Compile Error: number required, but got #t"), 332 | evalTest("(* ())", "*** ERROR: Compile Error: number required, but got ()"), 333 | evalTest("(/ '(1 2 3))", "*** ERROR: Compile Error: number required, but got (1 2 3)"), 334 | 335 | evalTest("(string-append #f)", "*** ERROR: Compile Error: string required, but got #f"), 336 | evalTest("(string-append 1)", "*** ERROR: Compile Error: string required, but got 1"), 337 | 338 | evalTest("(string->symbol)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 339 | evalTest("(string->symbol 'hello)", "*** ERROR: Compile Error: string required, but got hello"), 340 | evalTest("(symbol->string)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 341 | evalTest("(symbol->string \"\")", "*** ERROR: Compile Error: symbol required, but got \"\""), 342 | evalTest("(string->number 1)", "*** ERROR: Compile Error: string required, but got 1"), 343 | evalTest("(number->string \"1\")", "*** ERROR: Compile Error: number required, but got \"1\""), 344 | 345 | evalTest("(car ())", "*** ERROR: Compile Error: pair required, but got ()"), 346 | evalTest("(cdr ())", "*** ERROR: Compile Error: pair required, but got ()"), 347 | evalTest("(car)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 348 | evalTest("(cdr)", "*** ERROR: Compile Error: wrong number of arguments: requires 1, but got 0"), 349 | 350 | evalTest("(length (cons 1 2))", "*** ERROR: Compile Error: proper list required for function application or macro use"), 351 | evalTest("(memq 'a '(a b c) 1)", "*** ERROR: Compile Error: wrong number of arguments: requires 2, but got 3"), 352 | evalTest("(append () 1 ())", "*** ERROR: Compile Error: proper list required for function application or macro use"), 353 | evalTest("(set! x 1 1)", "*** ERROR: Compile Error: syntax-error: malformed set!: (set! x 1 1)"), 354 | 355 | evalTest("(cond)", "*** ERROR: Compile Error: syntax-error: at least one clause is required for cond"), 356 | evalTest("(cond ())", "*** ERROR: Compile Error: syntax-error: bad clause in cond"), 357 | evalTest("(cond (#t) (else) ())", "*** ERROR: Compile Error: syntax-error: 'else' clause followed by more clauses"), 358 | 359 | evalTest("(do () ())", "*** ERROR: Compile Error: syntax-error: malformed do: (do () ())"), 360 | evalTest("(do ((i 1 1 1)) (#t))", "*** ERROR: Compile Error: bad update expr in do: (do ((i 1 1 1)) (#t))"), 361 | 362 | evalTest("(define 1 1)", "*** ERROR: Compile Error: syntax-error: (define 1 1)"), 363 | evalTest("(let ((x 1 1)))", "*** ERROR: Compile Error: syntax-error: malformed let: (let ((x 1 1)))"), 364 | 365 | evalTest("(actor ())", "*** ERROR: Compile Error: syntax-error: malformed actor: (actor ())"), 366 | } 367 | 368 | func evalTest(source string, results ...string) interpreterTest { 369 | return interpreterTest{source: source, results: results} 370 | } 371 | 372 | func runTests(t *testing.T, tests []interpreterTest) { 373 | for _, test := range tests { 374 | i := NewInterpreter(test.source) 375 | evalResults := i.EvalResults(false) 376 | 377 | for i := 0; i < len(test.results); i++ { 378 | expect := test.results[i] 379 | actual := evalResults[i] 380 | if actual != expect { 381 | t.Errorf("%s => %s; want %s", test.source, actual, expect) 382 | } 383 | } 384 | } 385 | } 386 | 387 | func TestInterpreter(t *testing.T) { 388 | runTests(t, interpreterTests) 389 | runTests(t, runtimeErrorTests) 390 | runTests(t, compileErrorTests) 391 | } 392 | 393 | func TestLoad(t *testing.T) { 394 | file, err := ioutil.TempFile(os.TempDir(), "load_test") 395 | if err != nil { 396 | panic(err) 397 | } 398 | fileSource := "(define x 3)" 399 | ioutil.WriteFile(file.Name(), []byte(fileSource), os.ModeAppend) 400 | defer os.Remove(file.Name()) 401 | 402 | source := fmt.Sprintf("(load \"%s\") x (load invalid)", file.Name()) 403 | interpreter := NewInterpreter(source) 404 | expects := []string{"#t", "3", "*** ERROR: unbound variable: invalid"} 405 | actuals := interpreter.EvalResults(false) 406 | 407 | for i := 0; i < len(actuals); i++ { 408 | expect := expects[i] 409 | actual := actuals[i] 410 | if actual != expect { 411 | t.Errorf("%s => %s; want %s", source, actual, expect) 412 | } 413 | } 414 | } 415 | -------------------------------------------------------------------------------- /scheme/lexer.go: -------------------------------------------------------------------------------- 1 | // Lexer is a Lexical Analyzer for scheme. 2 | // It returns each token from source code and analyzes its type. 3 | 4 | package scheme 5 | 6 | import ( 7 | "fmt" 8 | "regexp" 9 | "strings" 10 | "text/scanner" 11 | ) 12 | 13 | type Lexer struct { 14 | scanner.Scanner 15 | results []Object 16 | } 17 | 18 | const ( 19 | EOF = -(iota + 1) 20 | IdentifierToken 21 | IntToken 22 | BooleanToken 23 | StringToken 24 | ) 25 | 26 | var identifierChars = "a-zA-Z?!*/<=>:$%^&_~" 27 | var numberChars = "0-9+-." 28 | var identifierExp = fmt.Sprintf("[%s][%s%s]*", identifierChars, identifierChars, numberChars) 29 | 30 | func NewLexer(source string) *Lexer { 31 | lexer := new(Lexer) 32 | lexer.Init(strings.NewReader(source)) 33 | lexer.Mode &^= scanner.ScanChars 34 | return lexer 35 | } 36 | 37 | func (l *Lexer) Lex(lval *yySymType) int { 38 | token := int(l.TokenType()) 39 | lval.token = l.NextToken() 40 | return token 41 | } 42 | 43 | func (l *Lexer) Error(e string) { 44 | panic(e) 45 | } 46 | 47 | // Non-destructive scanner.Scan(). 48 | // This method returns next token type or unicode character. 49 | func (l Lexer) TokenType() rune { 50 | token := l.PeekToken() 51 | if l.matchRegexp(token, "^[ ]*$") { 52 | return EOF 53 | } else if l.matchRegexp(token, fmt.Sprintf("^(%s|\\+|-)$", identifierExp)) { 54 | return IDENTIFIER 55 | } else if l.matchRegexp(token, "^-?[0-9]+$") { 56 | return NUMBER 57 | } else if l.matchRegexp(token, "^#(f|t)$") { 58 | return BOOLEAN 59 | } else if l.matchRegexp(token, "\"[^\"]*\"") { 60 | return STRING 61 | } else { 62 | runes := []rune(token) 63 | return runes[0] 64 | } 65 | } 66 | 67 | // Non-destructive Lexer.NextToken(). 68 | func (l Lexer) PeekToken() string { 69 | return l.nextToken() 70 | } 71 | 72 | // This function returns next token and moves current token reading 73 | // position to next token position. 74 | func (l *Lexer) NextToken() string { 75 | defer l.ensureAvailability() 76 | return l.nextToken() 77 | } 78 | 79 | func (l Lexer) IndentLevel() int { 80 | tokens := l.AllTokens() 81 | openCount, closedCount := 0, 0 82 | 83 | for _, token := range tokens { 84 | if token == "(" { 85 | openCount++ 86 | } else if token == ")" { 87 | closedCount++ 88 | } 89 | } 90 | return openCount - closedCount 91 | } 92 | 93 | func (l Lexer) AllTokens() []string { 94 | tokens := []string{} 95 | 96 | for { 97 | token := l.NextToken() 98 | if token == "" { 99 | break 100 | } 101 | tokens = append(tokens, token) 102 | } 103 | return tokens 104 | } 105 | 106 | func (l *Lexer) nextToken() string { 107 | // text/scanner scans text which starts with "'" in one token. 108 | if l.Peek() == '\'' { 109 | l.Next() 110 | return "'" 111 | } 112 | 113 | l.Scan() 114 | if l.TokenText() == "#" { 115 | // text/scanner scans '#t' as '#' and 't'. 116 | l.Scan() 117 | switch l.TokenText() { 118 | case "t", "f": 119 | return fmt.Sprintf("#%s", l.TokenText()) 120 | default: 121 | runtimeError("Tokens which start from '#' are not implemented except #f, #t.") 122 | } 123 | } else if l.matchRegexp(l.TokenText(), fmt.Sprintf("^%s$", identifierExp)) { 124 | // text/scanner scans some signs as splitted token from alphabet token. 125 | text := l.TokenText() 126 | for l.isIdentifierChar(l.Peek()) { 127 | l.Scan() 128 | text = fmt.Sprintf("%s%s", text, l.TokenText()) 129 | } 130 | return text 131 | } else if l.TokenText() == "-" && l.matchRegexp(fmt.Sprintf("%c", l.Peek()), "[0-9]") { 132 | text := l.TokenText() 133 | l.Scan() 134 | text = text + l.TokenText() 135 | return text 136 | } 137 | return l.TokenText() 138 | } 139 | 140 | func (l Lexer) isIdentifierChar(char rune) bool { 141 | charString := fmt.Sprintf("%c", char) 142 | return l.matchRegexp(charString, fmt.Sprintf("^[%s%s]$", identifierChars, numberChars)) 143 | } 144 | 145 | func (l *Lexer) matchRegexp(matchString string, expression string) bool { 146 | re, err := regexp.Compile(expression) 147 | if err != nil { 148 | runtimeError(err.Error()) 149 | } 150 | return re.MatchString(matchString) 151 | } 152 | 153 | func (l *Lexer) ensureAvailability() { 154 | // Error message will be printed by interpreter 155 | recover() 156 | } 157 | -------------------------------------------------------------------------------- /scheme/lexer_test.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | "testing" 7 | ) 8 | 9 | type tokenTypeTest struct { 10 | source string 11 | result rune 12 | } 13 | 14 | type tokenizeTest struct { 15 | source string 16 | result []string 17 | } 18 | 19 | var tokenTypeTests = []tokenTypeTest{ 20 | {"(", '('}, 21 | {")", ')'}, 22 | {"'", '\''}, 23 | 24 | {"100", NUMBER}, 25 | {"-1", NUMBER}, 26 | 27 | {"#f", BOOLEAN}, 28 | {"#t", BOOLEAN}, 29 | 30 | {"+", IDENTIFIER}, 31 | {"-", IDENTIFIER}, 32 | {"f2000", IDENTIFIER}, 33 | {"a0?!*/<=>:$%^&_~", IDENTIFIER}, 34 | 35 | {"\"a b\"", STRING}, 36 | } 37 | 38 | var tokenizeTests = []tokenizeTest{ 39 | {"1", makeTokens("1")}, 40 | {"-1", makeTokens("-1")}, 41 | {"#f", makeTokens("#f")}, 42 | {"#t", makeTokens("#t")}, 43 | 44 | {"(+ 1)", makeTokens("(,+,1,)")}, 45 | {"(+ 1 (+ 1))", makeTokens("(,+,1,(,+,1,),)")}, 46 | {"(+ (- 1)2)", makeTokens("(,+,(,-,1,),2,)")}, 47 | {"(* (/ 1)2)", makeTokens("(,*,(,/,1,),2,)")}, 48 | {"(number? 1)", makeTokens("(,number?,1,)")}, 49 | {"(string-append \"\")", makeTokens("(,string-append,\"\",)")}, 50 | 51 | {"((()", makeTokens("(,(,(,)")}, 52 | {"()))", makeTokens("(,),),)")}, 53 | {")))", makeTokens("),),)")}, 54 | {"((()))(()", makeTokens("(,(,(,),),),(,(,)")}, 55 | 56 | {"'2", makeTokens("',2")}, 57 | {"'#f", makeTokens("',#f")}, 58 | {"'#t", makeTokens("',#t")}, 59 | {"'hello", makeTokens("',hello")}, 60 | {"'(1 2 3)", makeTokens("',(,1,2,3,)")}, 61 | {"'(1(2 3))", makeTokens("',(,1,(,2,3,),)")}, 62 | {"\"a b\"", makeTokens("\"a b\"")}, 63 | 64 | {"(set! x 1)", makeTokens("(,set!,x,1,)")}, 65 | } 66 | 67 | func TestTokenType(t *testing.T) { 68 | for _, test := range tokenTypeTests { 69 | l := NewLexer(test.source) 70 | 71 | actual := l.TokenType() 72 | if actual != test.result { 73 | t.Errorf("%s => %s; want %s", test.source, tokenTypeString(actual), tokenTypeString(test.result)) 74 | } 75 | } 76 | } 77 | 78 | func TestAllTokens(t *testing.T) { 79 | for _, test := range tokenizeTests { 80 | l := NewLexer(test.source) 81 | actual := l.AllTokens() 82 | 83 | if !areTheSameStrings(actual, test.result) { 84 | t.Errorf("%s => %s; want %s", test.source, actual, test.result) 85 | } 86 | } 87 | } 88 | 89 | func tokenTypeString(tokenType rune) string { 90 | switch tokenType { 91 | case EOF: 92 | return "EOF" 93 | case IdentifierToken: 94 | return "IdentifierToken" 95 | case IntToken: 96 | return "IntToken" 97 | case StringToken: 98 | return "StringToken" 99 | default: 100 | return fmt.Sprintf("%c", tokenType) 101 | } 102 | } 103 | 104 | func areTheSameStrings(a, b []string) bool { 105 | if len(a) != len(b) { 106 | return false 107 | } 108 | 109 | for i := 0; i < len(a); i++ { 110 | if a[i] != b[i] { 111 | return false 112 | } 113 | } 114 | return true 115 | } 116 | 117 | func makeTokens(text string) []string { 118 | return strings.Split(text, ",") 119 | } 120 | -------------------------------------------------------------------------------- /scheme/macro.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "fmt" 5 | ) 6 | 7 | type Macro struct { 8 | ObjectBase 9 | } 10 | 11 | func NewMacro() *Macro { 12 | return &Macro{} 13 | } 14 | 15 | func (m *Macro) Eval() Object { 16 | return m 17 | } 18 | 19 | func (m *Macro) String() string { 20 | if m.Bounder() == nil { 21 | return "#" 22 | } 23 | return fmt.Sprintf("#", m.Bounder()) 24 | } 25 | -------------------------------------------------------------------------------- /scheme/misc.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "fmt" 5 | "strings" 6 | ) 7 | 8 | func areEqual(a Object, b Object) bool { 9 | if a == nil { 10 | return true 11 | } 12 | if typeName(a) != typeName(b) { 13 | return false 14 | } else if areIdentical(a, b) { 15 | return true 16 | } 17 | 18 | switch a.(type) { 19 | case *Pair: 20 | return areEqual(a.(*Pair).Car, b.(*Pair).Car) && areEqual(a.(*Pair).Cdr, b.(*Pair).Cdr) 21 | default: 22 | return false 23 | } 24 | } 25 | 26 | func areIdentical(a Object, b Object) bool { 27 | if typeName(a) != typeName(b) { 28 | return false 29 | } 30 | 31 | switch a.(type) { 32 | case *Number: 33 | return a.(*Number).value == b.(*Number).value 34 | case *Boolean: 35 | return a.(*Boolean).value == b.(*Boolean).value 36 | default: 37 | return a == b 38 | } 39 | } 40 | 41 | func areSameList(a Object, b Object) bool { 42 | if typeName(a) != typeName(b) { 43 | return false 44 | } 45 | 46 | switch a.(type) { 47 | case *Pair: 48 | return areSameList(a.(*Pair).Car, b.(*Pair).Car) && areSameList(a.(*Pair).Cdr, b.(*Pair).Cdr) 49 | default: 50 | return areIdentical(a, b) 51 | } 52 | } 53 | 54 | func assertListMinimum(arguments Object, minimum int) { 55 | if !arguments.isList() { 56 | compileError("proper list required for function application or macro use") 57 | } else if arguments.(*Pair).ListLength() < minimum { 58 | compileError("procedure requires at least %d argument", minimum) 59 | } 60 | } 61 | 62 | func assertListEqual(arguments Object, length int) { 63 | if !arguments.isList() { 64 | compileError("proper list required for function application or macro use") 65 | } else if arguments.(*Pair).ListLength() != length { 66 | compileError("wrong number of arguments: requires %d, but got %d", 67 | length, arguments.(*Pair).ListLength()) 68 | } 69 | } 70 | 71 | func assertObjectsType(objects []Object, typeName string) { 72 | for _, object := range objects { 73 | assertObjectType(object, typeName) 74 | } 75 | } 76 | 77 | func assertObjectType(object Object, assertType string) { 78 | if assertType != typeName(object) { 79 | compileError("%s required, but got %s", assertType, object) 80 | } 81 | } 82 | 83 | func compileError(format string, a ...interface{}) Object { 84 | return runtimeError("Compile Error: "+format, a...) 85 | } 86 | 87 | func defaultBinding() Binding { 88 | binding := make(Binding) 89 | for key, value := range builtinProcedures { 90 | binding[key] = value 91 | } 92 | for key, value := range builtinSyntaxes { 93 | binding[key] = value 94 | } 95 | return binding 96 | } 97 | 98 | func evaledObjects(objects []Object) []Object { 99 | evaledObjects := []Object{} 100 | 101 | for _, object := range objects { 102 | evaledObjects = append(evaledObjects, object.Eval()) 103 | } 104 | return evaledObjects 105 | } 106 | 107 | func runtimeError(format string, a ...interface{}) Object { 108 | panic(fmt.Sprintf(format, a...)) 109 | return undef 110 | } 111 | 112 | func syntaxError(format string, a ...interface{}) Object { 113 | return compileError("syntax-error: "+format, a...) 114 | } 115 | 116 | func typeName(object Object) string { 117 | switch object.(type) { 118 | case *Pair: 119 | if object.isNull() { 120 | return "null" 121 | } else { 122 | return "pair" 123 | } 124 | default: 125 | rawTypeName := fmt.Sprintf("%T", object) 126 | typeName := strings.Replace(rawTypeName, "*scheme.", "", 1) 127 | return strings.ToLower(typeName) 128 | } 129 | } 130 | -------------------------------------------------------------------------------- /scheme/number.go: -------------------------------------------------------------------------------- 1 | // Number is a scheme number object, which is expressed by number literal. 2 | 3 | package scheme 4 | 5 | import ( 6 | "strconv" 7 | ) 8 | 9 | type Number struct { 10 | ObjectBase 11 | value int 12 | } 13 | 14 | func NewNumber(argument interface{}, options ...Object) *Number { 15 | var value int 16 | var err error 17 | 18 | switch argument.(type) { 19 | case int: 20 | value = argument.(int) 21 | case string: 22 | value, err = strconv.Atoi(argument.(string)) 23 | if err != nil { 24 | runtimeError("String conversion %s to integer failed.", argument.(string)) 25 | } 26 | default: 27 | runtimeError("Unexpected argument type for NewNumber()") 28 | } 29 | 30 | if len(options) > 0 { 31 | return &Number{ObjectBase: ObjectBase{parent: options[0]}, value: value} 32 | } else { 33 | return &Number{value: value} 34 | } 35 | } 36 | 37 | func (n *Number) Eval() Object { 38 | return n 39 | } 40 | 41 | func (n *Number) String() string { 42 | return strconv.Itoa(n.value) 43 | } 44 | 45 | func (n *Number) isNumber() bool { 46 | return true 47 | } 48 | -------------------------------------------------------------------------------- /scheme/object.go: -------------------------------------------------------------------------------- 1 | // Object and ObjectBase is an abstract class for all scheme expressions. 2 | // A return value of a method which returns scheme object is Object. 3 | // And ObjectBase has Object's implementation of String(). 4 | 5 | package scheme 6 | 7 | type Object interface { 8 | Parent() Object 9 | Bounder() *Variable 10 | setParent(Object) 11 | setBounder(*Variable) 12 | Eval() Object 13 | String() string 14 | isNumber() bool 15 | isBoolean() bool 16 | isClosure() bool 17 | isProcedure() bool 18 | isNull() bool 19 | isPair() bool 20 | isList() bool 21 | isSymbol() bool 22 | isSyntax() bool 23 | isString() bool 24 | isVariable() bool 25 | isApplication() bool 26 | define(string, Object) 27 | set(string, Object) 28 | scopedBinding() Binding 29 | binding() Binding 30 | boundedObject(string) Object 31 | } 32 | 33 | type Binding map[string]Object 34 | 35 | type ObjectBase struct { 36 | parent Object 37 | bounder *Variable // Variable.Eval() sets itself into this 38 | } 39 | 40 | func (o *ObjectBase) Eval() Object { 41 | runtimeError("This object's Eval() is not implemented yet.") 42 | return nil 43 | } 44 | 45 | func (o *ObjectBase) String() string { 46 | runtimeError("This object's String() is not implemented yet.") 47 | return "" 48 | } 49 | 50 | func (o *ObjectBase) isNumber() bool { 51 | return false 52 | } 53 | 54 | func (o *ObjectBase) isBoolean() bool { 55 | return false 56 | } 57 | 58 | func (o *ObjectBase) isClosure() bool { 59 | return false 60 | } 61 | 62 | func (o *ObjectBase) isProcedure() bool { 63 | return false 64 | } 65 | 66 | func (o *ObjectBase) isNull() bool { 67 | return false 68 | } 69 | 70 | func (o *ObjectBase) isPair() bool { 71 | return false 72 | } 73 | 74 | func (o *ObjectBase) isList() bool { 75 | return false 76 | } 77 | 78 | func (o *ObjectBase) isSymbol() bool { 79 | return false 80 | } 81 | 82 | func (o *ObjectBase) isSyntax() bool { 83 | return false 84 | } 85 | 86 | func (o *ObjectBase) isString() bool { 87 | return false 88 | } 89 | 90 | func (o *ObjectBase) isVariable() bool { 91 | return false 92 | } 93 | 94 | func (o *ObjectBase) isApplication() bool { 95 | return false 96 | } 97 | 98 | func (o *ObjectBase) binding() Binding { 99 | return Binding{} 100 | } 101 | 102 | func (o *ObjectBase) Parent() Object { 103 | return o.parent 104 | } 105 | 106 | func (o *ObjectBase) Bounder() *Variable { 107 | return o.bounder 108 | } 109 | 110 | func (o *ObjectBase) setParent(parent Object) { 111 | o.parent = parent 112 | } 113 | 114 | func (o *ObjectBase) setBounder(bounder *Variable) { 115 | o.bounder = bounder 116 | } 117 | 118 | func (o *ObjectBase) scopedBinding() (scopedBinding Binding) { 119 | scopedBinding = make(Binding) 120 | parent := o.Parent() 121 | 122 | for parent != nil { 123 | for identifier, object := range parent.binding() { 124 | if scopedBinding[identifier] == nil { 125 | scopedBinding[identifier] = object 126 | } 127 | } 128 | parent = parent.Parent() 129 | } 130 | return 131 | } 132 | 133 | // Define variable in the most inner closure 134 | func (o *ObjectBase) define(identifier string, object Object) { 135 | if o.parent == nil { 136 | runtimeError("Bind called for object whose parent is nil") 137 | } else { 138 | o.parent.define(identifier, object) 139 | } 140 | } 141 | 142 | // This is for set! syntax. 143 | // Update the variable's value when it is defined. 144 | func (o *ObjectBase) set(identifier string, object Object) { 145 | if o.parent == nil { 146 | runtimeError("symbol not defined") 147 | } else { 148 | o.parent.set(identifier, object) 149 | } 150 | } 151 | 152 | func (o *ObjectBase) boundedObject(identifier string) Object { 153 | scopedBinding := make(Binding) 154 | parent := o.Parent() 155 | 156 | for parent != nil { 157 | for identifier, object := range parent.binding() { 158 | if scopedBinding[identifier] == nil { 159 | scopedBinding[identifier] = object 160 | } 161 | } 162 | parent = parent.Parent() 163 | } 164 | 165 | return scopedBinding[identifier] 166 | } 167 | -------------------------------------------------------------------------------- /scheme/pair.go: -------------------------------------------------------------------------------- 1 | // Pair is a type which is generated by cons procedure. 2 | // Pair has two pointers, which are named car and cdr. 3 | // 4 | // List is expressed by linked list of Pair. 5 | // And procedure application has list which consists of Pair 6 | // as its arguments. 7 | 8 | package scheme 9 | 10 | import ( 11 | "fmt" 12 | "strings" 13 | ) 14 | 15 | var ( 16 | Null = &Pair{ObjectBase: ObjectBase{parent: nil}, Car: nil, Cdr: nil} 17 | ) 18 | 19 | type Pair struct { 20 | ObjectBase 21 | Car Object 22 | Cdr Object 23 | } 24 | 25 | func NewPair(parent Object) *Pair { 26 | return &Pair{ObjectBase: ObjectBase{parent: parent}, Car: nil, Cdr: nil} 27 | } 28 | 29 | func NewList(parent Object, objects ...Object) *Pair { 30 | list := NewPair(parent) 31 | for _, object := range objects { 32 | list.Append(object) 33 | } 34 | return list 35 | } 36 | 37 | func (p *Pair) Eval() Object { 38 | return p 39 | } 40 | 41 | func (p *Pair) String() string { 42 | if p.isNull() { 43 | return "()" 44 | } else if p.isList() { 45 | length := p.ListLength() 46 | tokens := []string{} 47 | for i := 0; i < length; i++ { 48 | tokens = append(tokens, p.ElementAt(i).String()) 49 | } 50 | return fmt.Sprintf("(%s)", strings.Join(tokens, " ")) 51 | } else { 52 | return fmt.Sprintf("(%s . %s)", p.Car, p.Cdr) 53 | } 54 | } 55 | 56 | func (p *Pair) isNull() bool { 57 | return p.Car == nil && p.Cdr == nil 58 | } 59 | 60 | func (p *Pair) isPair() bool { 61 | return !p.isNull() 62 | } 63 | 64 | func (p *Pair) isList() bool { 65 | pair := p 66 | 67 | for { 68 | if pair.isNull() { 69 | return true 70 | } 71 | switch pair.Cdr.(type) { 72 | case *Pair: 73 | pair = pair.Cdr.(*Pair) 74 | default: 75 | return false 76 | } 77 | } 78 | return false 79 | } 80 | 81 | func (p *Pair) Elements() []Object { 82 | elements := []Object{} 83 | 84 | pair := p 85 | for { 86 | if pair.Car == nil { 87 | break 88 | } else { 89 | elements = append(elements, pair.Car) 90 | } 91 | pair = pair.Cdr.(*Pair) 92 | } 93 | return elements 94 | } 95 | 96 | func (p *Pair) ElementAt(index int) Object { 97 | return p.Elements()[index] 98 | } 99 | 100 | func (p *Pair) ListLength() int { 101 | if p.isNull() { 102 | return 0 103 | } else { 104 | return p.Cdr.(*Pair).ListLength() + 1 105 | } 106 | } 107 | 108 | func (p *Pair) Append(object Object) *Pair { 109 | assertListMinimum(p, 0) 110 | 111 | listTail := p 112 | for { 113 | if listTail.isNull() { 114 | break 115 | } else { 116 | listTail = listTail.Cdr.(*Pair) 117 | } 118 | } 119 | 120 | listTail.Car = object 121 | listTail.Cdr = new(Pair) 122 | return p 123 | } 124 | 125 | func (p *Pair) AppendList(list Object) *Pair { 126 | assertListMinimum(p, 0) 127 | assertListMinimum(list, 0) 128 | 129 | listTail := p 130 | for { 131 | if listTail.isNull() { 132 | break 133 | } else { 134 | listTail = listTail.Cdr.(*Pair) 135 | } 136 | } 137 | 138 | listTail.Car = list.(*Pair).Car 139 | listTail.Cdr = list.(*Pair).Cdr 140 | return p 141 | } 142 | -------------------------------------------------------------------------------- /scheme/parser.go: -------------------------------------------------------------------------------- 1 | //line parser.go.y:2 2 | 3 | // Parser is a type to analyse scheme source's syntax. 4 | // It embeds Lexer to generate tokens from a source code. 5 | // Parser.Parse() does syntactic analysis and returns scheme object pointer. 6 | 7 | package scheme 8 | 9 | import __yyfmt__ "fmt" 10 | 11 | //line parser.go.y:6 12 | //line parser.go.y:9 13 | type yySymType struct { 14 | yys int 15 | objects []Object 16 | object Object 17 | token string 18 | } 19 | 20 | const IDENTIFIER = 57346 21 | const NUMBER = 57347 22 | const BOOLEAN = 57348 23 | const STRING = 57349 24 | 25 | var yyToknames = []string{ 26 | "IDENTIFIER", 27 | "NUMBER", 28 | "BOOLEAN", 29 | "STRING", 30 | } 31 | var yyStatenames = []string{} 32 | 33 | const yyEofCode = 1 34 | const yyErrCode = 2 35 | const yyMaxDepth = 200 36 | 37 | //line parser.go.y:87 38 | 39 | type Parser struct { 40 | *Lexer 41 | } 42 | 43 | func NewParser(source string) *Parser { 44 | return &Parser{NewLexer(source)} 45 | } 46 | 47 | func (p *Parser) Parse(parent Object) []Object { 48 | p.ensureAvailability() 49 | if yyParse(p.Lexer) != 0 { 50 | panic("parse error") 51 | } 52 | 53 | for _, r := range p.results { 54 | r.setParent(parent) 55 | } 56 | return p.results 57 | } 58 | 59 | func (p *Parser) parseObject(parent Object) Object { 60 | tokenType := p.TokenType() 61 | token := p.NextToken() 62 | 63 | switch tokenType { 64 | case '(': 65 | return p.parseApplication(parent) 66 | case '\'': 67 | return p.parseSingleQuote(parent) 68 | case IntToken: 69 | return NewNumber(token, parent) 70 | case IdentifierToken: 71 | return NewVariable(token, parent) 72 | case BooleanToken: 73 | return NewBoolean(token, parent) 74 | case StringToken: 75 | return NewString(token[1:len(token)-1], parent) 76 | default: 77 | return nil 78 | } 79 | return nil 80 | } 81 | 82 | // This is for parsing syntax sugar '*** => (quote ***) 83 | func (p *Parser) parseSingleQuote(parent Object) Object { 84 | if len(p.PeekToken()) == 0 { 85 | runtimeError("unterminated quote") 86 | } 87 | application := NewApplication(parent) 88 | application.procedure = NewVariable("quote", application) 89 | application.arguments = NewList(application, p.parseObject(application)) 90 | return application 91 | } 92 | 93 | // This function returns *Pair of first object and list from second. 94 | // Scanner position ends with the next of close parentheses. 95 | func (p *Parser) parseList(parent Object) Object { 96 | pair := NewPair(parent) 97 | pair.Car = p.parseObject(pair) 98 | if pair.Car == nil { 99 | return pair 100 | } 101 | pair.Cdr = p.parseList(pair).(*Pair) 102 | return pair 103 | } 104 | 105 | func (p *Parser) parseApplication(parent Object) Object { 106 | if p.PeekToken() == ")" { 107 | p.NextToken() 108 | return Null 109 | } 110 | application := NewApplication(parent) 111 | application.procedure = p.parseObject(application) 112 | application.arguments = p.parseList(application) 113 | 114 | return application 115 | } 116 | 117 | func (p *Parser) parseQuotedObject(parent Object) Object { 118 | tokenType := p.TokenType() 119 | token := p.NextToken() 120 | 121 | switch tokenType { 122 | case '(': 123 | return p.parseQuotedList(parent) 124 | case '\'': 125 | return p.parseSingleQuote(parent) 126 | case IntToken: 127 | return NewNumber(token, parent) 128 | case IdentifierToken: 129 | return NewSymbol(token) 130 | case BooleanToken: 131 | return NewBoolean(token, parent) 132 | case ')': 133 | return nil 134 | default: 135 | runtimeError("unterminated quote") 136 | return nil 137 | } 138 | } 139 | 140 | func (p *Parser) parseQuotedList(parent Object) Object { 141 | pair := NewPair(parent) 142 | pair.Car = p.parseQuotedObject(pair) 143 | if pair.Car == nil { 144 | return pair 145 | } 146 | pair.Cdr = p.parseQuotedList(pair).(*Pair) 147 | return pair 148 | } 149 | 150 | func (p *Parser) ensureAvailability() { 151 | // Error message will be printed by interpreter 152 | recover() 153 | } 154 | 155 | //line yacctab:1 156 | var yyExca = []int{ 157 | -1, 1, 158 | 1, -1, 159 | -2, 0, 160 | } 161 | 162 | const yyNprod = 16 163 | const yyPrivate = 57344 164 | 165 | var yyTokenNames []string 166 | var yyStates []string 167 | 168 | const yyLast = 33 169 | 170 | var yyAct = []int{ 171 | 172 | 16, 4, 7, 8, 9, 5, 6, 15, 4, 7, 173 | 8, 9, 5, 6, 10, 18, 21, 19, 20, 12, 174 | 7, 8, 9, 17, 13, 2, 3, 1, 0, 0, 175 | 14, 0, 11, 176 | } 177 | var yyPact = []int{ 178 | 179 | -1000, 4, -1000, -1000, -1000, 15, -3, -1000, -1000, -1000, 180 | -1000, -1000, -1000, -3, 4, -1000, 7, 4, 6, -1000, 181 | -1000, -1000, 182 | } 183 | var yyPgo = []int{ 184 | 185 | 0, 27, 0, 23, 14, 26, 186 | } 187 | var yyR1 = []int{ 188 | 189 | 0, 1, 1, 2, 2, 3, 3, 3, 3, 4, 190 | 4, 4, 5, 5, 5, 5, 191 | } 192 | var yyR2 = []int{ 193 | 194 | 0, 0, 2, 0, 2, 1, 1, 2, 4, 1, 195 | 1, 3, 1, 1, 1, 2, 196 | } 197 | var yyChk = []int{ 198 | 199 | -1000, -1, -3, -5, 4, 8, 9, 5, 6, 7, 200 | -4, -5, 4, 9, -3, 10, -2, -3, -2, 10, 201 | -2, 10, 202 | } 203 | var yyDef = []int{ 204 | 205 | 1, -2, 2, 5, 6, 0, 0, 12, 13, 14, 206 | 7, 9, 10, 0, 3, 15, 0, 3, 0, 11, 207 | 4, 8, 208 | } 209 | var yyTok1 = []int{ 210 | 211 | 1, 3, 3, 3, 3, 3, 3, 3, 3, 3, 212 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 213 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 3, 214 | 3, 3, 3, 3, 3, 3, 3, 3, 3, 8, 215 | 9, 10, 216 | } 217 | var yyTok2 = []int{ 218 | 219 | 2, 3, 4, 5, 6, 7, 220 | } 221 | var yyTok3 = []int{ 222 | 0, 223 | } 224 | 225 | //line yaccpar:1 226 | 227 | /* parser for yacc output */ 228 | 229 | var yyDebug = 0 230 | 231 | type yyLexer interface { 232 | Lex(lval *yySymType) int 233 | Error(s string) 234 | } 235 | 236 | const yyFlag = -1000 237 | 238 | func yyTokname(c int) string { 239 | // 4 is TOKSTART above 240 | if c >= 4 && c-4 < len(yyToknames) { 241 | if yyToknames[c-4] != "" { 242 | return yyToknames[c-4] 243 | } 244 | } 245 | return __yyfmt__.Sprintf("tok-%v", c) 246 | } 247 | 248 | func yyStatname(s int) string { 249 | if s >= 0 && s < len(yyStatenames) { 250 | if yyStatenames[s] != "" { 251 | return yyStatenames[s] 252 | } 253 | } 254 | return __yyfmt__.Sprintf("state-%v", s) 255 | } 256 | 257 | func yylex1(lex yyLexer, lval *yySymType) int { 258 | c := 0 259 | char := lex.Lex(lval) 260 | if char <= 0 { 261 | c = yyTok1[0] 262 | goto out 263 | } 264 | if char < len(yyTok1) { 265 | c = yyTok1[char] 266 | goto out 267 | } 268 | if char >= yyPrivate { 269 | if char < yyPrivate+len(yyTok2) { 270 | c = yyTok2[char-yyPrivate] 271 | goto out 272 | } 273 | } 274 | for i := 0; i < len(yyTok3); i += 2 { 275 | c = yyTok3[i+0] 276 | if c == char { 277 | c = yyTok3[i+1] 278 | goto out 279 | } 280 | } 281 | 282 | out: 283 | if c == 0 { 284 | c = yyTok2[1] /* unknown char */ 285 | } 286 | if yyDebug >= 3 { 287 | __yyfmt__.Printf("lex %s(%d)\n", yyTokname(c), uint(char)) 288 | } 289 | return c 290 | } 291 | 292 | func yyParse(yylex yyLexer) int { 293 | var yyn int 294 | var yylval yySymType 295 | var yyVAL yySymType 296 | yyS := make([]yySymType, yyMaxDepth) 297 | 298 | Nerrs := 0 /* number of errors */ 299 | Errflag := 0 /* error recovery flag */ 300 | yystate := 0 301 | yychar := -1 302 | yyp := -1 303 | goto yystack 304 | 305 | ret0: 306 | return 0 307 | 308 | ret1: 309 | return 1 310 | 311 | yystack: 312 | /* put a state and value onto the stack */ 313 | if yyDebug >= 4 { 314 | __yyfmt__.Printf("char %v in %v\n", yyTokname(yychar), yyStatname(yystate)) 315 | } 316 | 317 | yyp++ 318 | if yyp >= len(yyS) { 319 | nyys := make([]yySymType, len(yyS)*2) 320 | copy(nyys, yyS) 321 | yyS = nyys 322 | } 323 | yyS[yyp] = yyVAL 324 | yyS[yyp].yys = yystate 325 | 326 | yynewstate: 327 | yyn = yyPact[yystate] 328 | if yyn <= yyFlag { 329 | goto yydefault /* simple state */ 330 | } 331 | if yychar < 0 { 332 | yychar = yylex1(yylex, &yylval) 333 | } 334 | yyn += yychar 335 | if yyn < 0 || yyn >= yyLast { 336 | goto yydefault 337 | } 338 | yyn = yyAct[yyn] 339 | if yyChk[yyn] == yychar { /* valid shift */ 340 | yychar = -1 341 | yyVAL = yylval 342 | yystate = yyn 343 | if Errflag > 0 { 344 | Errflag-- 345 | } 346 | goto yystack 347 | } 348 | 349 | yydefault: 350 | /* default state action */ 351 | yyn = yyDef[yystate] 352 | if yyn == -2 { 353 | if yychar < 0 { 354 | yychar = yylex1(yylex, &yylval) 355 | } 356 | 357 | /* look through exception table */ 358 | xi := 0 359 | for { 360 | if yyExca[xi+0] == -1 && yyExca[xi+1] == yystate { 361 | break 362 | } 363 | xi += 2 364 | } 365 | for xi += 2; ; xi += 2 { 366 | yyn = yyExca[xi+0] 367 | if yyn < 0 || yyn == yychar { 368 | break 369 | } 370 | } 371 | yyn = yyExca[xi+1] 372 | if yyn < 0 { 373 | goto ret0 374 | } 375 | } 376 | if yyn == 0 { 377 | /* error ... attempt to resume parsing */ 378 | switch Errflag { 379 | case 0: /* brand new error */ 380 | yylex.Error("syntax error") 381 | Nerrs++ 382 | if yyDebug >= 1 { 383 | __yyfmt__.Printf("%s", yyStatname(yystate)) 384 | __yyfmt__.Printf(" saw %s\n", yyTokname(yychar)) 385 | } 386 | fallthrough 387 | 388 | case 1, 2: /* incompletely recovered error ... try again */ 389 | Errflag = 3 390 | 391 | /* find a state where "error" is a legal shift action */ 392 | for yyp >= 0 { 393 | yyn = yyPact[yyS[yyp].yys] + yyErrCode 394 | if yyn >= 0 && yyn < yyLast { 395 | yystate = yyAct[yyn] /* simulate a shift of "error" */ 396 | if yyChk[yystate] == yyErrCode { 397 | goto yystack 398 | } 399 | } 400 | 401 | /* the current p has no shift on "error", pop stack */ 402 | if yyDebug >= 2 { 403 | __yyfmt__.Printf("error recovery pops state %d\n", yyS[yyp].yys) 404 | } 405 | yyp-- 406 | } 407 | /* there is no state on the stack with an error shift ... abort */ 408 | goto ret1 409 | 410 | case 3: /* no shift yet; clobber input char */ 411 | if yyDebug >= 2 { 412 | __yyfmt__.Printf("error recovery discards %s\n", yyTokname(yychar)) 413 | } 414 | if yychar == yyEofCode { 415 | goto ret1 416 | } 417 | yychar = -1 418 | goto yynewstate /* try again in the same state */ 419 | } 420 | } 421 | 422 | /* reduction by production yyn */ 423 | if yyDebug >= 2 { 424 | __yyfmt__.Printf("reduce %v in:\n\t%v\n", yyn, yyStatname(yystate)) 425 | } 426 | 427 | yynt := yyn 428 | yypt := yyp 429 | _ = yypt // guard against "declared and not used" 430 | 431 | yyp -= yyR2[yyn] 432 | yyVAL = yyS[yyp+1] 433 | 434 | /* consult goto table to find next state */ 435 | yyn = yyR1[yyn] 436 | yyg := yyPgo[yyn] 437 | yyj := yyg + yyS[yyp].yys + 1 438 | 439 | if yyj >= yyLast { 440 | yystate = yyAct[yyg] 441 | } else { 442 | yystate = yyAct[yyj] 443 | if yyChk[yystate] != -yyn { 444 | yystate = yyAct[yyg] 445 | } 446 | } 447 | // dummy call; replaced with literal code 448 | switch yynt { 449 | 450 | case 1: 451 | //line parser.go.y:29 452 | { 453 | yyVAL.objects = []Object{} 454 | } 455 | case 2: 456 | //line parser.go.y:33 457 | { 458 | yyVAL.objects = append(yyS[yypt-1].objects, yyS[yypt-0].object) 459 | if l, ok := yylex.(*Lexer); ok { 460 | l.results = yyVAL.objects 461 | } 462 | } 463 | case 3: 464 | //line parser.go.y:41 465 | { 466 | yyVAL.object = Null 467 | } 468 | case 4: 469 | //line parser.go.y:43 470 | { 471 | pair := NewPair(nil) 472 | pair.Car = yyS[yypt-1].object 473 | pair.Car.setParent(pair) 474 | pair.Cdr = yyS[yypt-0].object 475 | pair.Cdr.setParent(pair) 476 | yyVAL.object = pair 477 | } 478 | case 5: 479 | //line parser.go.y:54 480 | { 481 | yyVAL.object = yyS[yypt-0].object 482 | } 483 | case 6: 484 | //line parser.go.y:56 485 | { 486 | yyVAL.object = NewVariable(yyS[yypt-0].token, nil) 487 | } 488 | case 7: 489 | //line parser.go.y:58 490 | { 491 | yyVAL.object = yyS[yypt-0].object 492 | } 493 | case 8: 494 | //line parser.go.y:60 495 | { 496 | app := NewApplication(nil) 497 | app.procedure = yyS[yypt-2].object 498 | app.procedure.setParent(app) 499 | app.arguments = yyS[yypt-1].object 500 | app.arguments.setParent(app) 501 | yyVAL.object = app 502 | } 503 | case 9: 504 | //line parser.go.y:71 505 | { 506 | yyVAL.object = yyS[yypt-0].object 507 | } 508 | case 10: 509 | //line parser.go.y:73 510 | { 511 | yyVAL.object = NewSymbol(yyS[yypt-0].token) 512 | } 513 | case 11: 514 | //line parser.go.y:75 515 | { 516 | yyVAL.object = yyS[yypt-1].object 517 | } 518 | case 12: 519 | //line parser.go.y:79 520 | { 521 | yyVAL.object = NewNumber(yyS[yypt-0].token) 522 | } 523 | case 13: 524 | //line parser.go.y:81 525 | { 526 | yyVAL.object = NewBoolean(yyS[yypt-0].token) 527 | } 528 | case 14: 529 | //line parser.go.y:83 530 | { 531 | yyVAL.object = NewString(yyS[yypt-0].token[1 : len(yyS[yypt-0].token)-1]) 532 | } 533 | case 15: 534 | //line parser.go.y:85 535 | { 536 | yyVAL.object = Null 537 | } 538 | } 539 | goto yystack /* stack new state and value */ 540 | } 541 | -------------------------------------------------------------------------------- /scheme/parser.go.y: -------------------------------------------------------------------------------- 1 | %{ 2 | // Parser is a type to analyse scheme source's syntax. 3 | // It embeds Lexer to generate tokens from a source code. 4 | // Parser.Parse() does syntactic analysis and returns scheme object pointer. 5 | 6 | package scheme 7 | %} 8 | 9 | %union{ 10 | objects []Object 11 | object Object 12 | token string 13 | } 14 | 15 | %type program 16 | %type list 17 | %type expr 18 | %type sexpr 19 | %type const 20 | 21 | %token IDENTIFIER 22 | %token NUMBER 23 | %token BOOLEAN 24 | %token STRING 25 | 26 | %% 27 | 28 | program: 29 | { 30 | $$ = []Object{} 31 | } 32 | | program expr 33 | { 34 | $$ = append($1, $2) 35 | if l, ok := yylex.(*Lexer); ok { 36 | l.results = $$ 37 | } 38 | } 39 | 40 | list: 41 | { $$ = Null } 42 | | expr list 43 | { 44 | pair := NewPair(nil) 45 | pair.Car = $1 46 | pair.Car.setParent(pair) 47 | pair.Cdr = $2 48 | pair.Cdr.setParent(pair) 49 | $$ = pair 50 | } 51 | 52 | expr: 53 | const 54 | { $$ = $1 } 55 | | IDENTIFIER 56 | { $$ = NewVariable($1, nil) } 57 | | '\'' sexpr 58 | { $$ = $2 } 59 | | '(' expr list ')' 60 | { 61 | app := NewApplication(nil) 62 | app.procedure = $2 63 | app.procedure.setParent(app) 64 | app.arguments = $3 65 | app.arguments.setParent(app) 66 | $$ = app 67 | } 68 | 69 | sexpr: 70 | const 71 | { $$ = $1 } 72 | | IDENTIFIER 73 | { $$ = NewSymbol($1) } 74 | | '(' list ')' 75 | { $$ = $2 } 76 | 77 | const: 78 | NUMBER 79 | { $$ = NewNumber($1) } 80 | | BOOLEAN 81 | { $$ = NewBoolean($1) } 82 | | STRING 83 | { $$ = NewString($1[1:len($1)-1]) } 84 | | '(' ')' 85 | { $$ = Null } 86 | 87 | %% 88 | 89 | type Parser struct { 90 | *Lexer 91 | } 92 | 93 | func NewParser(source string) *Parser { 94 | return &Parser{NewLexer(source)} 95 | } 96 | 97 | func (p *Parser) Parse(parent Object) []Object { 98 | p.ensureAvailability() 99 | if yyParse(p.Lexer) != 0 { 100 | panic("parse error") 101 | } 102 | 103 | for _, r := range p.results { 104 | r.setParent(parent) 105 | } 106 | return p.results 107 | } 108 | 109 | func (p *Parser) parseObject(parent Object) Object { 110 | tokenType := p.TokenType() 111 | token := p.NextToken() 112 | 113 | switch tokenType { 114 | case '(': 115 | return p.parseApplication(parent) 116 | case '\'': 117 | return p.parseSingleQuote(parent) 118 | case IntToken: 119 | return NewNumber(token, parent) 120 | case IdentifierToken: 121 | return NewVariable(token, parent) 122 | case BooleanToken: 123 | return NewBoolean(token, parent) 124 | case StringToken: 125 | return NewString(token[1:len(token)-1], parent) 126 | default: 127 | return nil 128 | } 129 | return nil 130 | } 131 | 132 | // This is for parsing syntax sugar '*** => (quote ***) 133 | func (p *Parser) parseSingleQuote(parent Object) Object { 134 | if len(p.PeekToken()) == 0 { 135 | runtimeError("unterminated quote") 136 | } 137 | application := NewApplication(parent) 138 | application.procedure = NewVariable("quote", application) 139 | application.arguments = NewList(application, p.parseObject(application)) 140 | return application 141 | } 142 | 143 | // This function returns *Pair of first object and list from second. 144 | // Scanner position ends with the next of close parentheses. 145 | func (p *Parser) parseList(parent Object) Object { 146 | pair := NewPair(parent) 147 | pair.Car = p.parseObject(pair) 148 | if pair.Car == nil { 149 | return pair 150 | } 151 | pair.Cdr = p.parseList(pair).(*Pair) 152 | return pair 153 | } 154 | 155 | func (p *Parser) parseApplication(parent Object) Object { 156 | if p.PeekToken() == ")" { 157 | p.NextToken() 158 | return Null 159 | } 160 | application := NewApplication(parent) 161 | application.procedure = p.parseObject(application) 162 | application.arguments = p.parseList(application) 163 | 164 | return application 165 | } 166 | 167 | func (p *Parser) parseQuotedObject(parent Object) Object { 168 | tokenType := p.TokenType() 169 | token := p.NextToken() 170 | 171 | switch tokenType { 172 | case '(': 173 | return p.parseQuotedList(parent) 174 | case '\'': 175 | return p.parseSingleQuote(parent) 176 | case IntToken: 177 | return NewNumber(token, parent) 178 | case IdentifierToken: 179 | return NewSymbol(token) 180 | case BooleanToken: 181 | return NewBoolean(token, parent) 182 | case ')': 183 | return nil 184 | default: 185 | runtimeError("unterminated quote") 186 | return nil 187 | } 188 | } 189 | 190 | func (p *Parser) parseQuotedList(parent Object) Object { 191 | pair := NewPair(parent) 192 | pair.Car = p.parseQuotedObject(pair) 193 | if pair.Car == nil { 194 | return pair 195 | } 196 | pair.Cdr = p.parseQuotedList(pair).(*Pair) 197 | return pair 198 | } 199 | 200 | func (p *Parser) ensureAvailability() { 201 | // Error message will be printed by interpreter 202 | recover() 203 | } 204 | -------------------------------------------------------------------------------- /scheme/parser.output: -------------------------------------------------------------------------------- 1 | 2 | state 0 3 | $accept: .program $end 4 | program: . (1) 5 | 6 | . reduce 1 (src line 28) 7 | 8 | program goto 1 9 | 10 | state 1 11 | $accept: program.$end 12 | program: program.expr 13 | 14 | $end accept 15 | IDENTIFIER shift 4 16 | NUMBER shift 7 17 | BOOLEAN shift 8 18 | STRING shift 9 19 | \' shift 5 20 | ( shift 6 21 | . error 22 | 23 | expr goto 2 24 | const goto 3 25 | 26 | state 2 27 | program: program expr. (2) 28 | 29 | . reduce 2 (src line 32) 30 | 31 | 32 | state 3 33 | expr: const. (5) 34 | 35 | . reduce 5 (src line 52) 36 | 37 | 38 | state 4 39 | expr: IDENTIFIER. (6) 40 | 41 | . reduce 6 (src line 55) 42 | 43 | 44 | state 5 45 | expr: \'.sexpr 46 | 47 | IDENTIFIER shift 12 48 | NUMBER shift 7 49 | BOOLEAN shift 8 50 | STRING shift 9 51 | ( shift 13 52 | . error 53 | 54 | sexpr goto 10 55 | const goto 11 56 | 57 | state 6 58 | expr: (.expr list ) 59 | const: (.) 60 | 61 | IDENTIFIER shift 4 62 | NUMBER shift 7 63 | BOOLEAN shift 8 64 | STRING shift 9 65 | \' shift 5 66 | ( shift 6 67 | ) shift 15 68 | . error 69 | 70 | expr goto 14 71 | const goto 3 72 | 73 | state 7 74 | const: NUMBER. (12) 75 | 76 | . reduce 12 (src line 77) 77 | 78 | 79 | state 8 80 | const: BOOLEAN. (13) 81 | 82 | . reduce 13 (src line 80) 83 | 84 | 85 | state 9 86 | const: STRING. (14) 87 | 88 | . reduce 14 (src line 82) 89 | 90 | 91 | state 10 92 | expr: \' sexpr. (7) 93 | 94 | . reduce 7 (src line 57) 95 | 96 | 97 | state 11 98 | sexpr: const. (9) 99 | 100 | . reduce 9 (src line 69) 101 | 102 | 103 | state 12 104 | sexpr: IDENTIFIER. (10) 105 | 106 | . reduce 10 (src line 72) 107 | 108 | 109 | 13: shift/reduce conflict (shift 15(0), red'n 3(0)) on ) 110 | state 13 111 | sexpr: (.list ) 112 | const: (.) 113 | list: . (3) 114 | 115 | IDENTIFIER shift 4 116 | NUMBER shift 7 117 | BOOLEAN shift 8 118 | STRING shift 9 119 | \' shift 5 120 | ( shift 6 121 | ) shift 15 122 | . error 123 | 124 | list goto 16 125 | expr goto 17 126 | const goto 3 127 | 128 | state 14 129 | expr: ( expr.list ) 130 | list: . (3) 131 | 132 | IDENTIFIER shift 4 133 | NUMBER shift 7 134 | BOOLEAN shift 8 135 | STRING shift 9 136 | \' shift 5 137 | ( shift 6 138 | . reduce 3 (src line 40) 139 | 140 | list goto 18 141 | expr goto 17 142 | const goto 3 143 | 144 | state 15 145 | const: ( ). (15) 146 | 147 | . reduce 15 (src line 84) 148 | 149 | 150 | state 16 151 | sexpr: ( list.) 152 | 153 | ) shift 19 154 | . error 155 | 156 | 157 | state 17 158 | list: expr.list 159 | list: . (3) 160 | 161 | IDENTIFIER shift 4 162 | NUMBER shift 7 163 | BOOLEAN shift 8 164 | STRING shift 9 165 | \' shift 5 166 | ( shift 6 167 | . reduce 3 (src line 40) 168 | 169 | list goto 20 170 | expr goto 17 171 | const goto 3 172 | 173 | state 18 174 | expr: ( expr list.) 175 | 176 | ) shift 21 177 | . error 178 | 179 | 180 | state 19 181 | sexpr: ( list ). (11) 182 | 183 | . reduce 11 (src line 74) 184 | 185 | 186 | state 20 187 | list: expr list. (4) 188 | 189 | . reduce 4 (src line 42) 190 | 191 | 192 | state 21 193 | expr: ( expr list ). (8) 194 | 195 | . reduce 8 (src line 59) 196 | 197 | 198 | 10 terminals, 6 nonterminals 199 | 16 grammar rules, 22/2000 states 200 | 1 shift/reduce, 0 reduce/reduce conflicts reported 201 | 55 working sets used 202 | memory: parser 20/30000 203 | 14 extra closures 204 | 39 shift entries, 1 exceptions 205 | 10 goto entries 206 | 6 entries saved by goto default 207 | Optimizer space used: output 33/30000 208 | 33 table entries, 3 zero 209 | maximum spread: 10, maximum offset: 17 210 | -------------------------------------------------------------------------------- /scheme/parser_test.go: -------------------------------------------------------------------------------- 1 | package scheme 2 | 3 | import ( 4 | "reflect" 5 | "testing" 6 | ) 7 | 8 | type easyParserTest struct { 9 | source string 10 | result string 11 | } 12 | 13 | type deepParserTest struct { 14 | source string 15 | result Object 16 | } 17 | 18 | var easyParserTests = []easyParserTest{ 19 | {"1", "1"}, 20 | {"-2", "-2"}, 21 | {"'12", "12"}, 22 | {"()", "()"}, 23 | {"'()", "()"}, 24 | {"#f", "#f"}, 25 | {"#t", "#t"}, 26 | {"'#f", "#f"}, 27 | {"'#t", "#t"}, 28 | {"hello", "hello"}, 29 | {"'hello", "hello"}, 30 | {"(+)", "(+)"}, 31 | {"(- 1)", "(- 1)"}, 32 | {"(+ 3 4 (- 3 2))", "(+ 3 4 (- 3 2))"}, 33 | {"(<= 1 2 1)", "(<= 1 2 1)"}, 34 | {"'(1 2 3)", "(1 2 3)"}, 35 | {"(string-append)", "(string-append)"}, 36 | {"((lambda (x y z) (* (+ x y) z)) 1 2 3)", "((lambda (x y z) (* (+ x y) z)) 1 2 3)"}, 37 | {"\"a b\"", "\"a b\""}, 38 | } 39 | 40 | var deepParserTests = []deepParserTest{ 41 | {"'hello", NewSymbol("hello")}, 42 | { 43 | "(+)", 44 | func() Object { 45 | app := &Application{ 46 | procedure: &Variable{identifier: "+"}, 47 | arguments: Null, 48 | } 49 | app.procedure.setParent(app) 50 | return app 51 | }(), 52 | }, 53 | { 54 | "(- 1)", 55 | func() Object { 56 | pair := &Pair{ 57 | Cdr: Null, 58 | } 59 | pair.Car = NewNumber(1, pair) 60 | app := &Application{ 61 | procedure: &Variable{identifier: "-"}, 62 | arguments: pair, 63 | } 64 | app.procedure.setParent(app) 65 | pair.setParent(app) 66 | return app 67 | }(), 68 | }, 69 | { 70 | "'(1)", 71 | func() Object { 72 | pair := &Pair{ 73 | Cdr: Null, 74 | } 75 | pair.Car = NewNumber(1, pair) 76 | pair.Car.setParent(pair) 77 | return pair 78 | }(), 79 | }, 80 | } 81 | 82 | func TestParser(t *testing.T) { 83 | for _, test := range easyParserTests { 84 | i := NewInterpreter(test.source) 85 | i.Peek() 86 | objects := i.Parse(nil) 87 | object := objects[0] 88 | 89 | if object.String() != test.result { 90 | t.Errorf( 91 | "%s:\n Expected:\n %s\n Got:\n %s\n", 92 | test.source, 93 | test.result, 94 | object, 95 | ) 96 | return 97 | } 98 | } 99 | 100 | for _, test := range deepParserTests { 101 | i := NewInterpreter(test.source) 102 | i.Peek() 103 | objects := i.Parse(nil) 104 | object := objects[0] 105 | 106 | if !reflect.DeepEqual(object, test.result) { 107 | t.Errorf( 108 | "%s:\n Expected:\n %#v\n Got:\n %#v\n", 109 | test.source, 110 | test.result, 111 | object, 112 | ) 113 | return 114 | } 115 | } 116 | } 117 | -------------------------------------------------------------------------------- /scheme/string.go: -------------------------------------------------------------------------------- 1 | // String is a type for scheme string object, which is 2 | // expressed like "string". 3 | 4 | package scheme 5 | 6 | import ( 7 | "fmt" 8 | ) 9 | 10 | type String struct { 11 | ObjectBase 12 | text string 13 | } 14 | 15 | func NewString(object interface{}, options ...Object) *String { 16 | text := "" 17 | switch object.(type) { 18 | case string: 19 | text = object.(string) 20 | case int: 21 | text = fmt.Sprintf("%d", object.(int)) 22 | default: 23 | runtimeError("Unexpected conversion") 24 | } 25 | if len(options) > 0 { 26 | return &String{ObjectBase: ObjectBase{parent: options[0]}, text: text} 27 | } else { 28 | return &String{text: text} 29 | } 30 | } 31 | 32 | func (s *String) Eval() Object { 33 | return s 34 | } 35 | 36 | func (s *String) String() string { 37 | return fmt.Sprintf("\"%s\"", s.text) 38 | } 39 | 40 | func (s *String) isString() bool { 41 | return true 42 | } 43 | -------------------------------------------------------------------------------- /scheme/subroutine.go: -------------------------------------------------------------------------------- 1 | // A type for builtin functions 2 | 3 | package scheme 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | type Subroutine struct { 10 | ObjectBase 11 | function func(*Subroutine, Object) Object 12 | } 13 | 14 | func NewSubroutine(function func(*Subroutine, Object) Object) *Subroutine { 15 | return &Subroutine{function: function} 16 | } 17 | 18 | func (s *Subroutine) String() string { 19 | return fmt.Sprintf("#", s.Bounder()) 20 | } 21 | 22 | func (s *Subroutine) Eval() Object { 23 | return s 24 | } 25 | 26 | func (s *Subroutine) Invoke(argument Object) Object { 27 | return s.function(s, argument) 28 | } 29 | 30 | func (s *Subroutine) isProcedure() bool { 31 | return true 32 | } 33 | 34 | func (s *Subroutine) booleanByFunc(arguments Object, typeCheckFunc func(Object) bool) Object { 35 | assertListEqual(arguments, 1) 36 | 37 | object := arguments.(*Pair).ElementAt(0).Eval() 38 | return NewBoolean(typeCheckFunc(object)) 39 | } 40 | 41 | func (s *Subroutine) compareNumbers(arguments Object, compareFunc func(int, int) bool) Object { 42 | assertListMinimum(arguments, 2) 43 | 44 | numbers := evaledObjects(arguments.(*Pair).Elements()) 45 | assertObjectsType(numbers, "number") 46 | 47 | oldValue := numbers[0].(*Number).value 48 | for _, number := range numbers[1:] { 49 | if !compareFunc(oldValue, number.(*Number).value) { 50 | return NewBoolean(false) 51 | } 52 | oldValue = number.(*Number).value 53 | } 54 | return NewBoolean(true) 55 | } 56 | -------------------------------------------------------------------------------- /scheme/symbol.go: -------------------------------------------------------------------------------- 1 | // Symbol is a type to express scheme symbol object, which 2 | // is expressed like 'symbol or (quote symbol). 3 | 4 | package scheme 5 | 6 | var ( 7 | symbols = make(map[string]*Symbol) 8 | undef = Object(&Symbol{identifier: "#"}) // FIXME: this should not be symbol 9 | ) 10 | 11 | type Symbol struct { 12 | ObjectBase 13 | identifier string 14 | } 15 | 16 | func NewSymbol(identifier string) *Symbol { 17 | if symbols[identifier] == nil { 18 | symbols[identifier] = &Symbol{ObjectBase: ObjectBase{parent: nil}, identifier: identifier} 19 | } 20 | return symbols[identifier] 21 | } 22 | 23 | func (s *Symbol) Eval() Object { 24 | return s 25 | } 26 | 27 | func (s *Symbol) String() string { 28 | return s.identifier 29 | } 30 | 31 | func (s *Symbol) isSymbol() bool { 32 | return true 33 | } 34 | -------------------------------------------------------------------------------- /scheme/syntax.go: -------------------------------------------------------------------------------- 1 | // This file is for statements by syntax form, such as set! 2 | 3 | package scheme 4 | 5 | import ( 6 | "fmt" 7 | ) 8 | 9 | var ( 10 | builtinSyntaxes = Binding{ 11 | "actor": NewSyntax(actorSyntax), 12 | "and": NewSyntax(andSyntax), 13 | "begin": NewSyntax(beginSyntax), 14 | "cond": NewSyntax(condSyntax), 15 | "define": NewSyntax(defineSyntax), 16 | "define-macro": NewSyntax(defineMacroSyntax), 17 | "do": NewSyntax(doSyntax), 18 | "if": NewSyntax(ifSyntax), 19 | "lambda": NewSyntax(lambdaSyntax), 20 | "let": NewSyntax(letSyntax), 21 | "let*": NewSyntax(letStarSyntax), 22 | "letrec": NewSyntax(letrecSyntax), 23 | "or": NewSyntax(orSyntax), 24 | "quote": NewSyntax(quoteSyntax), 25 | "set!": NewSyntax(setSyntax), 26 | } 27 | ) 28 | 29 | type Syntax struct { 30 | ObjectBase 31 | function func(*Syntax, Object) Object 32 | } 33 | 34 | func NewSyntax(function func(*Syntax, Object) Object) *Syntax { 35 | return &Syntax{ObjectBase: ObjectBase{parent: nil}, function: function} 36 | } 37 | 38 | func (s *Syntax) Invoke(arguments Object) Object { 39 | return s.function(s, arguments) 40 | } 41 | 42 | func (s *Syntax) String() string { 43 | return fmt.Sprintf("#", s.Bounder()) 44 | } 45 | 46 | func (s *Syntax) isSyntax() bool { 47 | return true 48 | } 49 | 50 | func (s *Syntax) malformedError() { 51 | syntaxError("malformed %s: %s", s.Bounder(), s.Bounder().Parent()) 52 | } 53 | 54 | func (s *Syntax) assertListEqual(arguments Object, length int) { 55 | if !arguments.isList() || arguments.(*Pair).ListLength() != length { 56 | s.malformedError() 57 | } 58 | } 59 | 60 | func (s *Syntax) assertListMinimum(arguments Object, minimum int) { 61 | if !arguments.isList() || arguments.(*Pair).ListLength() < minimum { 62 | s.malformedError() 63 | } 64 | } 65 | 66 | func (s *Syntax) assertListRange(arguments Object, lengthRange []int) { 67 | if !arguments.isList() { 68 | s.malformedError() 69 | } 70 | 71 | for _, length := range lengthRange { 72 | if length == arguments.(*Pair).ListLength() { 73 | return 74 | } 75 | } 76 | s.malformedError() 77 | } 78 | 79 | // Returns elements in list object with type assertion (syntax form specific error message) 80 | // Assertion is minimum 81 | func (s *Syntax) elementsMinimum(list Object, minimum int) []Object { 82 | if list.isApplication() { 83 | list = list.(*Application).toList() 84 | } 85 | s.assertListMinimum(list, minimum) 86 | return list.(*Pair).Elements() 87 | } 88 | 89 | // Returns elements in list object with type assertion (syntax form specific error message) 90 | // Assertion is equal 91 | func (s *Syntax) elementsExact(list Object, value int) []Object { 92 | if list.isApplication() { 93 | list = list.(*Application).toList() 94 | } 95 | s.assertListEqual(list, value) 96 | return list.(*Pair).Elements() 97 | } 98 | 99 | // Eval all given objects and returns last object's eval result. 100 | // When 'objects' is empty, returns #. 101 | func evalAll(objects []Object) Object { 102 | lastResult := undef 103 | for _, object := range objects { 104 | lastResult = object.Eval() 105 | } 106 | return lastResult 107 | } 108 | 109 | func actorSyntax(s *Syntax, arguments Object) Object { 110 | elements := s.elementsMinimum(arguments, 0) 111 | 112 | // Insert over the application to override scope 113 | application := arguments.Parent() 114 | actor := NewActor(application.Parent()) 115 | application.setParent(actor) 116 | 117 | for _, element := range elements { 118 | caseElements := s.elementsMinimum(element, 1) 119 | caseArguments := s.elementsMinimum(caseElements[0], 1) 120 | assertObjectType(caseArguments[0], "string") 121 | 122 | actor.functions[caseArguments[0].(*String).text] = func(objects []Object) { 123 | if len(caseArguments[1:]) != len(objects) { 124 | runtimeError("invalid message argument length: requires %d, but got %d", len(caseArguments[1:]), len(objects)) 125 | } 126 | 127 | for index, variable := range caseArguments[1:] { 128 | actor.tryDefine(variable, objects[index].Eval()) 129 | } 130 | evalAll(caseElements[1:]) 131 | } 132 | } 133 | 134 | return actor 135 | } 136 | 137 | func andSyntax(s *Syntax, arguments Object) Object { 138 | s.assertListMinimum(arguments, 0) 139 | 140 | lastResult := Object(NewBoolean(true)) 141 | for _, object := range arguments.(*Pair).Elements() { 142 | lastResult = object.Eval() 143 | if lastResult.isBoolean() && lastResult.(*Boolean).value == false { 144 | return NewBoolean(false) 145 | } 146 | } 147 | return lastResult 148 | } 149 | 150 | func beginSyntax(s *Syntax, arguments Object) Object { 151 | elements := s.elementsMinimum(arguments, 0) 152 | return evalAll(elements) 153 | } 154 | 155 | func condSyntax(s *Syntax, arguments Object) Object { 156 | elements := s.elementsMinimum(arguments, 0) 157 | if len(elements) == 0 { 158 | syntaxError("at least one clause is required for cond") 159 | } 160 | 161 | // First: syntax check 162 | elseExists := false 163 | for _, element := range elements { 164 | if elseExists { 165 | syntaxError("'else' clause followed by more clauses") 166 | } else if element.isApplication() && element.(*Application).procedure.isVariable() && 167 | element.(*Application).procedure.(*Variable).identifier == "else" { 168 | elseExists = true 169 | } 170 | 171 | if element.isNull() || !element.isApplication() { 172 | syntaxError("bad clause in cond") 173 | } 174 | } 175 | 176 | // Second: eval cases 177 | for _, element := range elements { 178 | lastResult := undef 179 | application := element.(*Application) 180 | 181 | isElse := application.procedure.isVariable() && application.procedure.(*Variable).identifier == "else" 182 | if !isElse { 183 | lastResult = application.procedure.Eval() 184 | } 185 | 186 | // first element is 'else' or not '#f' 187 | if isElse || !lastResult.isBoolean() || lastResult.(*Boolean).value == true { 188 | for _, object := range application.arguments.(*Pair).Elements() { 189 | lastResult = object.Eval() 190 | } 191 | return lastResult 192 | } 193 | } 194 | return undef 195 | } 196 | 197 | func defineSyntax(s *Syntax, arguments Object) Object { 198 | elements := s.elementsExact(arguments, 2) 199 | 200 | if elements[0].isVariable() { 201 | variable := elements[0].(*Variable) 202 | s.Bounder().define(variable.identifier, elements[1].Eval()) 203 | 204 | return NewSymbol(variable.identifier) 205 | } else if elements[0].isApplication() { 206 | closure := WrapClosure(arguments) 207 | 208 | defineElements := s.elementsMinimum(elements[0], 1) 209 | funcName := defineElements[0] 210 | closure.DefineFunction(s, defineElements[1:], elements[1:]) 211 | 212 | if funcName.isVariable() { 213 | s.Bounder().define(funcName.(*Variable).identifier, closure) 214 | return funcName 215 | } 216 | } 217 | return syntaxError("%s", s.Bounder().Parent()) 218 | } 219 | 220 | func defineMacroSyntax(s *Syntax, arguments Object) Object { 221 | elements := s.elementsMinimum(arguments, 2) 222 | 223 | macroElements := s.elementsMinimum(elements[0], 1) 224 | assertObjectType(macroElements[0], "variable") 225 | 226 | macro := NewMacro() 227 | s.Bounder().define(macroElements[0].(*Variable).identifier, macro) 228 | return undef 229 | } 230 | 231 | func doSyntax(s *Syntax, arguments Object) Object { 232 | closure := WrapClosure(arguments.Parent()) 233 | 234 | // Parse iterator list and define first variable 235 | elements := s.elementsMinimum(arguments, 2) 236 | iteratorBodies := s.elementsMinimum(elements[0], 0) 237 | for _, iteratorBody := range iteratorBodies { 238 | iteratorElements := s.elementsMinimum(iteratorBody, 2) 239 | if len(iteratorElements) > 3 { 240 | compileError("bad update expr in %s: %s", s.Bounder(), s.Bounder().Parent()) 241 | } 242 | closure.tryDefine(iteratorElements[0], iteratorElements[1].Eval()) 243 | } 244 | 245 | // eval test -> 246 | // true: eval testBody and returns its result 247 | // false: eval continueBody, eval iterator's update 248 | testElements := s.elementsMinimum(elements[1], 1) 249 | for { 250 | testResult := testElements[0].Eval() 251 | if !testResult.isBoolean() || testResult.(*Boolean).value == true { 252 | for _, element := range testElements[1:] { 253 | testResult = element.Eval() 254 | } 255 | return testResult 256 | } else { 257 | // eval continueBody 258 | evalAll(elements[2:]) 259 | 260 | // update iterators 261 | for _, iteratorBody := range iteratorBodies { 262 | iteratorElements := s.elementsMinimum(iteratorBody, 2) 263 | if len(iteratorElements) == 3 { 264 | closure.tryDefine(iteratorElements[0], iteratorElements[2].Eval()) 265 | } 266 | } 267 | } 268 | } 269 | return undef 270 | } 271 | 272 | func ifSyntax(s *Syntax, arguments Object) Object { 273 | s.assertListRange(arguments, []int{2, 3}) 274 | elements := arguments.(*Pair).Elements() 275 | 276 | result := elements[0].Eval() 277 | if result.isBoolean() && !result.(*Boolean).value { 278 | if len(elements) == 3 { 279 | return elements[2].Eval() 280 | } else { 281 | return undef 282 | } 283 | } else { 284 | return elements[1].Eval() 285 | } 286 | } 287 | 288 | func lambdaSyntax(s *Syntax, arguments Object) Object { 289 | closure := WrapClosure(arguments.Parent()) 290 | 291 | elements := s.elementsMinimum(arguments, 1) 292 | variables := s.elementsMinimum(elements[0], 0) 293 | closure.DefineFunction(s, variables, elements[1:]) 294 | return closure 295 | } 296 | 297 | func letSyntax(s *Syntax, arguments Object) Object { 298 | closure := WrapClosure(arguments.Parent()) 299 | elements := s.elementsMinimum(arguments, 1) 300 | 301 | // define arguments to local scope 302 | variables := []Object{} 303 | results := []Object{} 304 | for _, argumentElement := range s.elementsMinimum(elements[0], 0) { 305 | variableElements := s.elementsExact(argumentElement, 2) 306 | 307 | variableElements[1].setParent(closure.Parent()) 308 | variables = append(variables, variableElements[0]) 309 | results = append(results, variableElements[1].Eval()) 310 | } 311 | 312 | for index, variable := range variables { 313 | closure.tryDefine(variable, results[index]) 314 | } 315 | 316 | // eval body 317 | return evalAll(elements[1:]) 318 | } 319 | 320 | func letStarSyntax(s *Syntax, arguments Object) Object { 321 | closure := WrapClosure(arguments.Parent()) 322 | elements := s.elementsMinimum(arguments, 1) 323 | 324 | // define arguments to local scope 325 | for _, argumentElement := range s.elementsMinimum(elements[0], 0) { 326 | variableElements := s.elementsExact(argumentElement, 2) 327 | variable, result := variableElements[0], variableElements[1].Eval() 328 | 329 | closure.tryDefine(variable, result) 330 | result.setParent(closure.Parent()) 331 | } 332 | 333 | // eval body 334 | return evalAll(elements[1:]) 335 | } 336 | 337 | func letrecSyntax(s *Syntax, arguments Object) Object { 338 | closure := WrapClosure(arguments.Parent()) 339 | elements := s.elementsMinimum(arguments, 1) 340 | 341 | // define arguments to local scope 342 | variables := []Object{} 343 | results := []Object{} 344 | for _, argumentElement := range s.elementsMinimum(elements[0], 0) { 345 | variableElements := s.elementsExact(argumentElement, 2) 346 | variables = append(variables, variableElements[0]) 347 | 348 | results = append(results, variableElements[1].Eval()) 349 | } 350 | 351 | for index, variable := range variables { 352 | closure.tryDefine(variable, results[index]) 353 | } 354 | 355 | // eval body 356 | return evalAll(elements[1:]) 357 | } 358 | 359 | func orSyntax(s *Syntax, arguments Object) Object { 360 | s.assertListMinimum(arguments, 0) 361 | 362 | lastResult := Object(NewBoolean(false)) 363 | for _, object := range arguments.(*Pair).Elements() { 364 | lastResult = object.Eval() 365 | if !lastResult.isBoolean() || lastResult.(*Boolean).value != false { 366 | return lastResult 367 | } 368 | } 369 | return lastResult 370 | } 371 | 372 | func quoteSyntax(s *Syntax, arguments Object) Object { 373 | s.assertListEqual(arguments, 1) 374 | object := arguments.(*Pair).ElementAt(0) 375 | 376 | p := NewParser(object.String()) 377 | p.Peek() 378 | return p.parseQuotedObject(s.Bounder()) 379 | } 380 | 381 | func setSyntax(s *Syntax, arguments Object) Object { 382 | elements := s.elementsExact(arguments, 2) 383 | 384 | variable := elements[0] 385 | if !variable.isVariable() { 386 | s.malformedError() 387 | } 388 | value := elements[1].Eval() 389 | s.Bounder().set(variable.(*Variable).identifier, value) 390 | return value 391 | } 392 | -------------------------------------------------------------------------------- /scheme/variable.go: -------------------------------------------------------------------------------- 1 | // Scheme's identifier is classified to a symbol or a variable. 2 | // And this type owns a role to express a variable. 3 | // Variable itself does not have a value for identifier, 4 | // interpreter searches it from its code block scope by Variable's identifier. 5 | 6 | package scheme 7 | 8 | type Variable struct { 9 | ObjectBase 10 | identifier string 11 | } 12 | 13 | func NewVariable(identifier string, parent Object) *Variable { 14 | return &Variable{ 15 | ObjectBase: ObjectBase{parent: parent}, 16 | identifier: identifier, 17 | } 18 | } 19 | 20 | func (v *Variable) Eval() Object { 21 | object := v.content() 22 | if object == nil { 23 | runtimeError("unbound variable: %s", v.identifier) 24 | } 25 | object.setBounder(v) 26 | return object 27 | } 28 | 29 | func (v *Variable) String() string { 30 | return v.identifier 31 | } 32 | 33 | func (v *Variable) content() Object { 34 | return v.boundedObject(v.identifier) 35 | } 36 | 37 | func (v *Variable) isVariable() bool { 38 | return true 39 | } 40 | --------------------------------------------------------------------------------