2014/05/01


golang が提供するインタフェースの中で代表的な物の使い方をまとめてみる。

io.Reader

type Reader interface {
    Read(p []byte) (n int, err error)
}
ご存じ io.Reader。このシグネチャの Read を実装しておけば golang のありとあらゆる入力機能に対して自分のコードを提供する事が出来る。 例えば永遠に「おっぱい」と言い続ける Reader だと以下の様な実装になる。
package main

import (
    "io"
    "os"
)

var text = []rune("おっぱい")

type OppaiReader struct {
    n int
}

func (r *OppaiReader) Read(p []byte) (interror) {
    in := len(p)
    nw := 0
    for i := 0; i < in; i++ {
        cb := []byte(string(text[r.n%len(text)]))
        if nw+len(cb) > in {
            break
        }
        cbl := len(cb)
        copy(p[nw:nw+cbl], cb)
        nw += cbl
        r.n++
    }
    return nw, nil
}

func main() {
    io.Copy(os.Stdout, &OppaiReader{})
}
ただしこういうことをする場合は、RuneReader を使った方が綺麗かもしれない。

io.Writer

type Writer interface {
    Write(p []byte) (n int, err error)
}
io.Reader のペアとなるインタフェース。同じくこの関数を実装しておけば golang のあらゆる出力機能に対して自分のコードを提供出来る。
例えば出力される文字列を Pascal Case で出力する Writer だと以下のコードになる。
package main

import (
    "fmt"
    "io"
    "os"
)

type pascalCaseWriter struct {
    w    io.Writer
    last byte
}

func (w *pascalCaseWriter) Write(p []byte) (interror) {
    r := 0
    for n, _ := range p {
        switch w.last {
        case ' ''\t''\r''\n'0:
            if 'a' <= p[n] && p[n] <= 'z' {
                p[n] -= 32
            }
        }

        nw, err := w.w.Write(p[n : n+1])
        if err != nil {
            return r + nw, err
        }
        w.last = p[n]
    }
    return r, nil
}

func NewPascalCaseWriter(w io.Writer) *pascalCaseWriter {
    return &pascalCaseWriter{w, 0}
}

func main() {
    w := NewPascalCaseWriter(os.Stdout)
    fmt.Fprintln(w, "hello world")
}

io.Closer

type Closer interface {
    Close() error
}
Close メソッドを提供するインタフェースとなるが、実際には単品で使われる事はなく、Reader かつ Closer、Writer かつ Closer という使われ方になる。

io.Seeker

type Seeker interface {
    Seek(offset int64, whence int) (int64error)
}
名前の通り、指定オフセット位置でシーク出来るインタフェースとなる。これを実装した代表的な物としては os.File がある。

io.ReadWriter

type ReadWriter interface {
    Reader
    Writer
}
Reader でありかつ Writer であるインタフェースで双方向通信を行う為のインタフェース。代表的な実装としては net.Conn がある。

io.WriteCloser

type WriteCloser interface {
    Writer
    Closer
}
こちらは os.Create で作成した os.File が実装するインタフェース。

io.ByteReader

type ByteReader interface {
    ReadByte() (c byte, err error)
}
ある入力から1バイトずつ読み込む事が出来るインタフェース。tail 的な処理を書きたい場合に用いる。

その他、組み合わせで用いる事が出来る io.ReadWriteCloser io.ReadSeeker io.WriteSeeker io.ReadWriteSeeker io.ByteScanner io.ByteWriter io.RuneReader io.RuneScanner がある。

compress/flate/inflate.Reader

type Reader interface {
    io.Reader
    io.ByteReader
}
compress 圧縮の Reader を提供する。圧縮されたファイル等をこれを用いて Read すると等価的に内容が読めるという物。
実際には NewReader を用いて使用する。

database/sql.Driver

type Driver interface {
    Open(name string) (Conn, error)
}
データベースドライバ実装を表すインタフェース。新しいデータベースのバインディングをサポートする場合、まずこのインタフェース実装を書くことになる。

database/sql.Execer

type Execer interface {
    Exec(query string, args []Value) (Result, error)
}
通常は database/sql では Prepare して Exec するのが普通の使い方だが、データベース接続が Execer を実装している場合は、いちいち Prepare 等せずいきなり接続から実行できる事を明示する事が出来る。
ドライバの実装は必須ではない。

database/sql.Queryer

type Queryer interface {
    Query(query string, args []Value) (Rows, error)
}
Execer と同様に、結果を得るクエリの場合 Prepare して Query する必要があるが、データベース接続から直接 Query 出来るドライバである事を明示する事が出来る。
ドライバの実装は必須ではない。

database/sql.ColumnConverter

type ColumnConverter interface {
    ColumnConverter(idx int) ValueConverter
}
golang の database/sql は自動で型変換を行ってはくれるが、ある特殊な型に対して変換を行いたい場合にはこのインタフェースを実装しする。このインタフェースを実装する場合、ValueConverter も実装する必要がある。

database/sql.ValueConverter

type ValueConverter interface {
    ConvertValue(v interface{}) (Value, error)
}
各型に対する変換処理を提供するインタフェース。例えば bool 型から各型への変換を提供する boolType では以下の実装になっている。
func (boolType) ConvertValue(src interface{}) (Value, error) {
    switch s := src.(type) {
    case bool:
        return s, nil
    case string:
        b, err := strconv.ParseBool(s)
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    case []byte:
        b, err := strconv.ParseBool(string(s))
        if err != nil {
            return nil, fmt.Errorf("sql/driver: couldn't convert %q into type bool", s)
        }
        return b, nil
    }

    sv := reflect.ValueOf(src)
    switch sv.Kind() {
    case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
        iv := sv.Int()
        if iv == 1 || iv == 0 {
            return iv == 1nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", iv)
    case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
        uv := sv.Uint()
        if uv == 1 || uv == 0 {
            return uv == 1nil
        }
        return nil, fmt.Errorf("sql/driver: couldn't convert %d into type bool", uv)
    }

    return nil, fmt.Errorf("sql/driver: couldn't convert %v (%T) into type bool", src, src)
}
ドライバの実装は必須ではない。

database/sql.Valuer

type Valuer interface {
    Value() (Value, error)
}
golang のデータベースドライバを実装するパッケージに対して golang 側から値を要求する際に用いられるインタフェース。
ドライバの実装は必須ではない。

database/sql.Scanner

type Scanner interface {
    Scan(src interface{}) error
}
rows.Scan に対して自前の実装を入れたい場合にインプリメントする。
ドライバの実装は必須ではない。

binary.ByteOrder

type ByteOrder interface {
    Uint16([]byteuint16
    Uint32([]byteuint32
    Uint64([]byteuint64
    PutUint16([]byteuint16)
    PutUint32([]byteuint32)
    PutUint64([]byteuint64)
    String() string
}
binary.BigEndianbinary.LittleEndian を実装に持つインタフェース。ユーザがさらに実装を追加する事はおそらく無い、BigEndian と LittleEndian どちらでやってくるか分からない処理に対して等価的に処理を行いたい場合には、このインタフェースを引数に取って扱う。

encoding.BinaryMarshaler

type BinaryMarshaler interface {
    MarshalBinary() (data []byte, err error)
}
あるインスタンスがバイナリ列へシリアライズ可能かどうかを示すインタフェース。

binary.BinaryUnmarshaler

type BinaryUnmarshaler interface {
    UnmarshalBinary(data []byteerror
}
あるインスタンスがバイナリ列からデシリアライズ可能かどうかを示すインタフェース。

encoding.TextMarshaler encoding.TextUnmarshaler も同様に、文字列に対する交換インタフェースを提供する。

encoding/gob.GobEncoder

type GobEncoder interface {
    GobEncode() ([]byteerror)
}
golang には標準で Gob というシリアライザブルフォーマットに対するインタフェースが提供されているが、インスタンスに対して Gob フォーマットへの交換が可能かどうかを明示出来る。

encoding/gob.GobDecoder

type GobDecoder interface {
    GobDecode([]byteerror
}
同様に Gob からある型への交換が可能かどうかを明示出来る。

encoding/json.Unmarshaler

type Unmarshaler interface {
    UnmarshalJSON([]byteerror
}
JSON を示すバイト列からある型への交換が可能である事を示す。

encoding/json.Marshaler

type Marshaler interface {
    MarshalJSON() ([]byteerror)
}
Unmarshaler とは逆にバイト列からある型への交換が可能である事を示す。
Marshaler と Unmarshaler の例を以下に示す。
package main

import (
    "encoding/base64"
    "encoding/json"
    "fmt"
    "log"
)

type User struct {
    Name     string
    Password string
}

func (user *User) UnmarshalJSON(p []byteerror {
    var v map[string]interface{}
    err := json.Unmarshal(p, &v)
    if err != nil {
        return err
    }
    for _, f := range []string{"Name""name"} {
        if vv, ok := v[f]; ok {
            user.Name = fmt.Sprint(vv)
            break
        }
    }
    for _, f := range []string{"Password""password""PassWord""passWord"} {
        if vv, ok := v[f]; ok {
            b, err := base64.StdEncoding.DecodeString(fmt.Sprint(vv))
            if err != nil {
                return nil
            }
            user.Password = string(b)
            break
        }
    }
    return nil
}

func (user *User) MarshalJSON() ([]byteerror) {
    return []byte(fmt.Sprintf(`{"Name": %q: "Password": %q}`,
        user.Name, base64.StdEncoding.EncodeToString([]byte(user.Password)))), nil
}

func main() {
    p := &User{"ウルトラマン""henshin"}
    b, err := p.MarshalJSON()
    if err != nil {
        log.Fatal(err)
    }
    // {"Name": "ウルトラマン", "Password": "aGVuc2hpbg=="}
    fmt.Println(string(b))

    err = p.UnmarshalJSON([]byte(`
    {
        "Name": "仮面ライダー",
        "Password": "aGVuc2hpbg=="
    }
    `))
    if err != nil {
        log.Fatal(err)
    }
    // 仮面ライダー henshin
    fmt.Printf("%s : %s\n", p.Name, p.Password)
}
encoding.xml.Unmarshaler encoding.xml.Marshaler も json と同様。

flag.Getter

type Getter interface {
    Value
    Get() interface{}
}
golang の flag パッケージを用いて、プログラム引数から独自の型へ変換を行いたい場合に使う。

fmt.Formatter

type State interface {
    // Write is the function to call to emit formatted output to be printed.
    Write(b []byte) (ret int, err error)
    // Width returns the value of the width option and whether it has been set.
    Width() (wid int, ok bool)
    // Precision returns the value of the precision option and whether it has been set.
    Precision() (prec int, ok bool)

    // Flag reports whether the flag c, a character, has been set.
    Flag(c intbool
}
fmt.Printf と同様の処理を自前実装したい場合に使う。

fmt.Stringer

type Stringer interface {
    String() string
}
実はこのインタフェースが一番使われる事が多い。デバッグではよく fmt.Println を使う事が多いと思うが、これを実装しておくと独自の型が fmt.Println 等に渡った時にどの様に表示されるかを実装する事が出来る。
package main

import (
    "fmt"
)

type Oppai bool

func (o Oppai) String() string {
    if o {
        return "(・)(・)"
    }
    return "(◎)(◎)"
}

func main() {
    var o Oppai

    o = true
    fmt.Println(o) // (・)(・)

    o = false
    fmt.Println(o) // "(◎)(◎)"
}
fmt.GoStringer は同様のインタフェースではあるが、実際には内部処理のみで使われている。

fmt.Scanner

type Scanner interface {
    Scan(state ScanState, verb runeerror
}
fmt.Scan と同様の処理を自前実装したい場合に使う。

image/draw.Quantizer

type Quantizer interface {
    Quantize(p color.Palette, m image.Image) color.Palette
}
独自のカラーパレットを実装したい場合に使用する。gif は 256 色しか出力出来ないのでこれを使って減色処理を行っている。
Drawer はこのパレットを用いて描画を行う際に実装する。

image/jpeg.Reader

type Reader interface {
    io.Reader
    ReadByte() (c byte, err error)
}
jpeg を自前でデコードしたい場合に実装する。

net/http.RoundTripper

type RoundTripper interface {
    RoundTrip(*Request) (*Response, error)
}
http のトランスポート層を抽象化する為に用いられるインタフェース。
デフォルトのタイムアウトやダイアラ、未知のプロキシに対応する場合はこれを実装する事になる。golang の http.Clientfile:// な URL でも Get する事が出来るが、これは内部でスキーマを判定して RoundTripper を切り替える事で実現している。

net/http.Handler

type Handler interface {
    ServeHTTP(ResponseWriter, *Request)
}
ご存じ http.Handler。これを実装しておくと、とりあえず golang 標準のウェブサーバに対するリクエストを処理出来る。
例えば go-uwsgi はこのインタフェースを実装する事で uWSGI インタフェースを実現している。

net/http.ResponseWriter

type ResponseWriter interface {
    Header() Header
    Write([]byte) (interror)
    WriteHeader(int)
}
golang 標準の Web サーバ機能を使う場合にレスポンスを書き込むインタフェース。実際にはこれをユーザ側が実装する事はまずない。主にテスト結果を得る為に用いられる。

net/http.Flusher

内部処理やテストで用いられる。

net/http.Hijacker

type Hijacker interface {
    Hijack() (net.Conn, *bufio.ReadWriter, error)
}
golang の http 通信は大部分が隠ぺいされている。しかし自前で接続を切ってしまったり、https の connect メソッドを実現する場合には都合が悪いインタフェースだ。そこで req.Hijack() を呼び出し、http 以外の通信を行う事が出来る。
websocket パッケージはこれを使って http ネゴシエーションを実現している。

net/http.CloseNotifier

type CloseNotifier interface {
    CloseNotify() <-chan bool
}
ResponseWriter を自前実装した場合、クライアントから接続を切られた事を検知する為に実装するインタフェース。

net.Listener

type Listener interface {
    Accept() (c Conn, err error)
    Close() error
    Addr() Addr
}
自前実装をソケットとして扱わせる為に実装するインタフェース。上記で紹介した go-uwsgi でも自前で Accept を実装する事で http サーバとの間の通信層で uWSGI を喋っている。

sync.Locker

type Locker interface {
        Lock()
        Unlock()
}
sync.Mutex が実装しているが、自前でもロックインタフェースを実装したい場合にインプリメントする。
実装するのは自由だが、インタフェースが取り決められていないと発散するのでそれを定義しているといった所だろうか。

以上、golang の標準パッケージに含まれる代表的なインタフェースを紹介してみた。
golang のコードがエレガントになるかどうかの半分は、このインタフェースにかかっている。ぜひ極めましょう。

2014/04/25


よし、卓球をしよう。
pingpong
ちょっとしたクラサバで何かを動かす時、クライアントが死んだらサーバも死んで欲しい時がある。自分で作ったプログラムならいいけれど、そうじゃないならいちいちサーバで CTRL-C タイプしなきゃならなかったりする。

「そこ自動でしょ!」

と思ったのでツールを作ってみました。
mattn/pingpong - GitHub
https://github.com/mattn/pingpong
2つのツールで構成されます。pping は引数で与えられたタスク名で ppong に一定周期で生存通知を行います。ppong は指定されたタスクが起動していなければ起動し、一定期間 ping が来なければそのプロセスを終了します。
pping は引数を取る事ができ、例えばサーバのあるタスクを起動させてからクライアントを起動したい場合には以下の様に実行します。

$ pping -n game-server game-client
こうすると ppong は game-server.json を読み
    "name""game-server",
    "args": ["-f""game.conf"],
    "timeout"30
予め指定されたコマンドと引数でサーバプロセスを起動します。ppong は30秒間 ping が来なければ強制的に pping を終了します。複数クライアントいる場合、1台でも起動していればサーバは起動し続けます。

本当は ffmpeg/ffserver でストリーミングをやっていて、ffmpeg が終了したら ffserver を落としたいという理由で作りましたが、色んな用途に使えるかもしれないので汎用ツールにしてみました。
何に使えるかは分かりませんが、よろしければどうぞ。

2014/04/18


Big Sky :: プログラミング言語の作り方
Big Sky :: プログラミング言語の作り方(2)
Big Sky :: プログラミング言語の作り方(3)
Big Sky :: プログラミング言語の作り方(4)

本来ならばここらあたりでエラー処理とか例外を入れるべきでしたが、先に以下の実装を行いました。
  • 配列
  • ハッシュ
  • 配列要素へのアクセス
  • ハッシュ要素へのアクセス
  • if
  • while
  • for
  • return
  • break
  • continue
現在のシンタックスは以下の通り。
#define STRUCTURE \
"                                                                       \n" \
"number     : /-?[0-9]+(\\.[0-9]*)?(e[0-9]+)?/ ;                         \n" \
"true       : \"true\" ;                                                 \n" \
"false      : \"false\" ;                                                \n" \
"nil        : \"nil\" ;                                                  \n" \
"factor     : '(' <lexp> ')'                                             \n" \
"           | <number>                                                   \n" \
"           | <string>                                                   \n" \
"           | <array>                                                    \n" \
"           | <hash>                                                     \n" \
"           | <true>                                                     \n" \
"           | <false>                                                    \n" \
"           | <nil>                                                      \n" \
"           | <call>                                                     \n" \
"           | <new>                                                      \n" \
"           | <ident> ;                                                  \n" \
"string     : /\"(\\\\.|[^\"])*\"/ ;                                     \n" \
"item       : <factor> ('[' <lexp> ']')+ ;                               \n" \
"prop       : <factor> ('.' <ident>)+ ;                                  \n" \
"cmp        : <factor>                                                     " \
"         (\"!=\" | \"==\" | \"<=\" | \"<\" | \">=\" | \">\" )             " \
"         <factor> ;                                                     \n" \
"call       : <ident> '(' <lexp>? (',' <lexp>)* ')' ;                    \n" \
"anoncall   : <factor> '(' <lexp>? (',' <lexp>)* ')' ;                   \n" \
"methodcall : <prop> '(' <lexp>? (',' <lexp>)* ')' ;                     \n" \
"array      : '[' <lexp>? (',' <lexp>)* ']' ;                            \n" \
"pair       : <string> ':' <lexp> ;                                      \n" \
"hash       : '{' <pair>? (',' <pair>)* '}' ;                            \n" \
"ident      : /[a-zA-Z_][a-zA-Z0-9_]*/ ;                                  \n" \
"                                                                        \n" \
"term       : (<lambda> | <item> | <methodcall> | <cmp> | <prop>           " \
"         | <anoncall> | <call>                                          \n" \
"         | <factor> (('*' | '/' | '%') <factor>)*) ;                    \n" \
"lexp       : <term> (('+' | '-') <term>)* ;                             \n" \
"let_v      : <ident> '=' <lexp> ';' ;                                   \n" \
"let_a      : <item> '=' <lexp> ';' ;                                    \n" \
"let_p      : <prop> '=' <lexp> ';' ;                                    \n" \
"else_if    : \"else\" \"if\" '(' <lexp> ')' '{' <stmts> '}' ;           \n" \
"else       : \"else\" '{' <stmts> '}' ;                                 \n" \
"if_stmt    : \"if\" '(' <lexp> ')' '{' <stmts> '}' ;                    \n" \
"if         : <if_stmt> <else_if>* <else>? ;                             \n" \
"while      : \"while\" '(' <lexp> ')' '{' <stmts> '}' ;                 \n" \
"for_in     : \"for\" '(' <ident> \"in\" <lexp> ')' '{' <stmts> '}' ;    \n" \
"var        : \"var\" <ident> '=' <lexp> ';' ;                           \n" \
"vararg     : \"...\" ;                                                  \n" \
"stmts      : <stmt>* ;                                                  \n" \
"                                                                        \n" \
"lambda     : \"func\"                                                     " \
"         '(' <ident>? (<vararg> | (',' <ident>)*) ')' '{' <stmts> '}' ; \n" \
"func       : \"func\" <ident>                                             " \
"         '(' <ident>? (<vararg> | (',' <ident>)*) ')' '{' <stmts> '}' ; \n" \
"template   : (<var> | <func>)* ;                                        \n" \
"class      : \"class\" <ident> '{' <template> '}' ;                     \n" \
"new        : \"new\" <ident> '(' <lexp>? (',' <lexp>)* ')' ;            \n" \
"                                                                        \n" \
"break      : \"break\" ';' ;                                            \n" \
"continue   : \"continue\" ';' ;                                         \n" \
"return     : \"return\" <lexp> ';' ;                                    \n" \
"comment    : /#[^\n]*/ ;                                                \n" \
"eof        : /$/ ;                                                      \n" \
"stmt       : (<let_v> | <let_a> | <let_p> | <var> | <if>                  " \
"         | <while> | <for_in>                                             " \
"         | <func> | <class> | <return> | <break>                        \n" \
"         | <continue> | <comment> | (<lexp> ';')) ;                     \n" \
"program    : <stmts> <eof> ;                                            \n"
配列 array は簡単ですね。lexp がカンマ区切りになっているだけです。
"array      : '[' <lexp>? (',' <lexp>)* ']' ;                            \n" \
ハッシュは少し難しくなります。まずキーと値を組にした pair という物を宣言し、それの繰り返しという定義となります。
"pair       : <string> ':' <lexp> ;                                      \n" \
"hash       : '{' <pair>? (',' <pair>)* '}' ;                            \n" \
配列、ハッシュの要素へのアクセスですが、実装方法にもよりますが代入左項を lexp として処理してしまい
a[1] = 2;
値の参照のみを行ってしまうと、実際に要素を変更する a への変更が出来ません。値の出所をリファレンスで持っておくのも良いですが、面倒なので要素代入というステートメントで処理しています。

if、else if、else はそれぞれ AST を処理しやすい様に以下の構造を作りました。
#!ore
if (0) {
  println("foo");
else if (false) {
  println("boo");
else {
  println("zoo");
}
ref array 1 008CB850

  stmts|> 
    stmt|comment|regex:1:1 '#!ore'
    if|> 
      if_stmt|> 
        string:2:1 'if'
        char:2:4 '('
        lexp|term|factor|number|regex:2:5 '0'
        char:2:6 ')'
        char:2:8 '{'
        stmt|> 
          call|> 
            ident|regex:3:3 'println'
            char:3:10 '('
            lexp|term|factor|string|regex:3:11 '"foo"'
            char:3:16 ')'
          char:3:17 ';'
        char:4:1 '}'
      else_if|> 
        string:4:3 'else'
        string:4:8 'if'
        char:4:11 '('
        lexp|term|factor|false|string:4:12 'false'
        char:4:17 ')'
        char:4:19 '{'
        stmt|> 
          call|> 
            ident|regex:5:3 'println'
            char:5:10 '('
            lexp|term|factor|string|regex:5:11 '"boo"'
            char:5:16 ')'
          char:5:17 ';'
        char:6:1 '}'
      else|> 
        string:6:3 'else'
        char:6:8 '{'
        stmt|> 
          call|> 
            ident|regex:7:3 'println'
            char:7:10 '('
            lexp|term|factor|string|regex:7:11 '"zoo"'
            char:7:16 ')'
          char:7:17 ';'
        char:8:1 '}'
  eof|regex 
AST をそのまま保持しておき、条件を実行した結果でどのステートメント群を実行するかを処理します。
if (is_a(t, "if")) {
  ore_value v;
  int i;
  for (i = 0; i < t->children_num; i++) {
    int r = 0;
    mpc_ast_t* f = t->children[i];
    if (is_a(f, "if_stmt")) {
      r = ore_is_true(ore_eval(ore, f->children[2]));
    } else if (is_a(f, "else_if")) {
      r = ore_is_true(ore_eval(ore, f->children[3]));
    } else {
      r = 1;
    }
    if (r)
      return ore_eval(ore, ore_find_statements(f));
  }
  return ore_value_nil();
}
for や while も同様です。要約フィボナッチ数の計算が出来る様になりました。
func fib(n) {
  if (n < 2) {
    return n;
  }
  return fib(n-2) + fib(n-1);
}

println(fib(20));
実はリポジトリ内では既にクラスオブジェクトの生成が出来る様になっています。興味のある方はこちらからどうぞ。
mattn/orelang - GitHub

俺言語

https://github.com/mattn/orelang
さて、今日は本当ならばエラー処理を書きたかったのですが実は使っているパーサの mpc が AST から行番号を取れないという問題を見つけ、問題報告していた為に実装出来ませんでした。

Hope to get code location from mpc_ast_t ・ Issue #4 ・ orangeduck/mpc - GitHub

Hey. This should be added in the newest version. You can use the state member of mpc_ast_t . You can...

https://github.com/orangeduck/mpc/issues/4
報告したら実装してくれましたので、今度はエラー処理を書こうと思います。

実は、言語処理系を実装する上で少し面倒なのが return なのです。return は大域脱出になりえます。
例えば関数の中に if 文があり、その中に for 文があり、その中で return 文があると、その if 文や for 文をキャンセルして戻り値と共に大域脱出する必要があります。この実装に longjmp/setjmp を使う言語処理系もありますが、今回は return をエラーと見立ててあらゆる箇所で中断処理を実行させ、関数処理内に戻ってきたらエラー扱いではなく正しい return として処理させるという方法を使っています。
なので例えば関数内でなければ不正なエラーとなる訳です。逆に都合がいいですね。break や continue も同じ手法を使っています。