Git Product home page Git Product logo

go-scheme's Introduction

Implementation of Scheme (subset version) by Go Lang

Overview

  • Implemented a Lisp for Go Lang lessons. (It's Scheme base)
  • As an implementation goal, we will provide an environment for easily operating a graphic program.

Quality

TEST & Run (CLI interpreter)

Requirement

  • go lang installed.
cd ${HOME}
git clone https://github.com/hidekuno/go-scheme
cd go-scheme/scheme
go test -v
go run cmd/lisp/main.go

TEST coverage(C0)

cd ${HOME}/go-scheme/scheme
go test -cover -coverprofile /tmp/cover.out
go tool cover -func=/tmp/cover.out

TEST Benchmark

cd ${HOME}/go-scheme/scheme
go test -bench .

TEST & Run (Scheme on GUI for Draw)

Requirement

  • X server is running.
cd ${HOME}/go-scheme/draw
go test -v
go run cmd/lisp/main.go
  • Then type "(draw-init)"

TEST & Run (Scheme on Web API Server)

cd {HOME}/go-scheme/web
go test -v
go run cmd/api/main.go

Build & Run (Scheme on Web Assembly)

cd {HOME}/go-scheme/web/wasm
cp /usr/local/go/misc/wasm/wasm_exec.js .
GOARCH=wasm GOOS=js go build -o lisp.wasm lisp_wasm.go
cd {HOME}/go-scheme/web
go run cmd/wasm/main.go

Run on docker

Requirement

  • docker is running.
  • X Server is running.(XQuartz 2.7.11 for mac)

Build(my operation log)

cd ${WHERE}/go-scheme/docker
docker build --target=go-scheme -t hidekuno/go-scheme --file=./Dockerfile .
docker login
docker push hidekuno/go-scheme
docker logout

macOS

docker pull hidekuno/go-scheme
xhost +
docker run -it --name go-scheme -e DISPLAY=docker.for.mac.localhost:0 hidekuno/go-scheme /root/glisp

Linux

docker pull hidekuno/go-scheme
xhost +
docker run -it --name go-scheme -e DISPLAY=${HOSTIP}:0.0 hidekuno/go-scheme /root/glisp

Windows11+WSL2+WSLg

docker pull hidekuno/go-scheme
docker run -it -v /tmp/.X11-unix:/tmp/.X11-unix -e DISPLAY=:0 --name go-scheme hidekuno/go-scheme /root/glisp

For environments where the X server is not running

docker pull hidekuno/go-scheme
docker run -it --name go-scheme hidekuno/go-scheme /root/lisp

go-scheme's People

Contributors

hidekuno avatar dependabot[bot] avatar

Watchers

 avatar

go-scheme's Issues

仮想マシンを作る

コンパイラを作成する前に、まずVMを作る。

  • プログラムカウンタ(eip)
  • スタック(esp)
  • 命令セット(インストラクション)
  • GCはGo任せなので、実装しない

パフォーマンスが劣化する

実験結果はこの通り

[kunohi@centos7-dev-docker src]$ go run lisp_main_dev.go lisp.go
scheme.go>  (define test-list (map (lambda (n) (rand-integer 10000))(iota 600)))
test-list
scheme.go>  (define qsort
  (lambda (l)
    (if (null? l) l
        (append (qsort (filter (lambda (n) (< n (car l))) (cdr l)))
                (cons (car l) (qsort (filter (lambda (n) (not (< n (car l))))(cdr l)) ))))))
    qsort
scheme.go>  (time (qsort test-list))
740.788075ms
nil
scheme.go>  (time (qsort test-list))
2.624298923s
nil
scheme.go>  (time (qsort test-list))
4.897149342s
nil
scheme.go>  (time (qsort test-list))
7.732988955s
nil
scheme.go>

プラットホーム、アーキテクチャによる振る舞いの違い

Linux 32bit

[hideki@gentoo src]$ go run lisp.go 
scheme.go>  (atan 1)
0.7853981633974483
scheme.go>  (define pi (* 4 (atan 1)))
pi
scheme.go>  pi
3.141592653589793
scheme.go>  (tan (/ (* 45 pi) 180))
0.9999999999999999
scheme.go>  (quit)
[hideki@gentoo src]$ uname -a
Linux gentoo.mukogawa.or.jp 2.6.18-411.el5 #1 SMP Mon Jul 11 18:16:41 CDT 2016 i686 i686 i386 GNU/Linux
[hideki@gentoo src]$ 

MacOSX 64bit

macbookair:src hideki$ go run lisp.go 
scheme.go>  (define pi (* 4 (atan 1)))
pi
scheme.go>  pi
3.141592653589793
scheme.go>  (tan (/ (* 45 pi) 180))
1
scheme.go>  (quit)
macbookair:src hideki$ uname -a
Darwin macbookair.local 17.6.0 Darwin Kernel Version 17.6.0: Tue May  8 15:22:16 PDT 2018; root:xnu-4570.61.1~1/RELEASE_X86_64 x86_64
macbookair:src hideki$ 

Webに対応する

  • いくつかのAPIを実装する
  • マルチユーザーに対応する(環境)

0(ゼロ)除算、0(ゼロ)剰余演算のチェック

このままだとpanicになる

scheme.go>  (/ 10 0)
panic: runtime error: integer divide by zero

goroutine 1 [running]:
main.(*Integer).Div(0xc42000a800, 0x54cd40, 0xc42000a7a0, 0x7fbdef02e101, 0x54cd40)
        /home/kunohi/go-scheme/src/lisp.go:212 +0x70

Let式の中で複数の式を評価するようにした

diff --git a/src/lisp.go b/src/lisp.go
index 1c99d22..f90f552 100644
--- a/src/lisp.go
+++ b/src/lisp.go
@@ -1381,7 +1381,17 @@ func build_func() {
                if letsym != nil {
                        (*env).Regist(letsym.Value, NewLetLoop(NewList(pname), v[body]))
                }
-               return eval(v[body], NewSimpleEnv(env, &local_env))
+
+               nse := NewSimpleEnv(env, &local_env)
+               var last_exp Expression
+               for idx := body; idx < len(v); idx += 1 {
+                       if exp, err := eval(v[idx], nse); err == nil {
+                               last_exp = exp
+                       } else {
+                               return nil, err
+                       }
+               }
+               return last_exp, nil
        }

Dockerに対応する

CentOS7の公式を利用する

[kunohi@centos7-dev-docker ~]$ docker search centos --limit 5
NAME                          DESCRIPTION                                     STARS               OFFICIAL            AUTOMATED
centos                        The official build of CentOS.                   4553                [OK]
jdeathe/centos-ssh            CentOS-6 6.9 x86_64 / CentOS-7 7.4.1708 x86_…   98                                      [OK]
openshift/base-centos7        A Centos7 derived base image for Source-To-I…   31
pivotaldata/centos-gpdb-dev   CentOS image for GPDB development. Tag names…   6
pivotaldata/centos            Base centos, freshened up a little with a Do…   2
[kunohi@centos7-dev-docker ~]$

変数評価でpanic()が発生

今回の末尾再帰には関連性はないと思われる。(既存バグの可能性大)

[kunohi@centos7-dev-docker src]$ go run lisp.go lisp_main.go
scheme.go>  (let loop ((i 0)(j 10)(k 10)) (if (<= 1000000 i) i (if (= j k) (loop (+ 100 i)(+ 1 i)))))
panic: runtime error: index out of range

goroutine 1 [running]:
main.(*LetLoop).Execute(0xc42006c190, 0xc42000e4e0, 0xc420050290, 0x2, 0x3, 0x0, 0x0, 0x0, 0x0)

()を入力するとpanic

[kunohi@centos7-dev-docker src]$ go run lisp.go
scheme.go>  ()
panic: runtime error: index out of range

goroutine 1 [running]:
main.eval(0x548300, 0xc42007a240, 0xc42000e3d0, 0x548300, 0xc42007a240, 0x2, 0x0)
	/home/kunohi/go-scheme/src/lisp.go:616 +0xb7d
main.do_core_logic(0xc4200140bc, 0x2, 0xc42000e3d0, 0x400, 0xc4200140bc, 0x2, 0x0)
	/home/kunohi/go-scheme/src/lisp.go:667 +0xa8
main.do_interactive()
	/home/kunohi/go-scheme/src/lisp.go:693 +0x330
main.main()
	/home/kunohi/go-scheme/src/lisp.go:1217 +0x25
exit status 2
[kunohi@centos7-dev-docker src]$ 

有理数の対応

Gaucheでは

gosh> (/ 10 3)
10/3
gosh>

現状

scheme.go>  (/ 10 3)
3
scheme.go>  

となる

パラメータ評価の不具合

評価式が副作用に影響されている

scheme.go>  (let loop ((a (list 1 2 3))(b 0)) (if (null? a) b (loop (cdr a)(+ b (car a)\|                                return sexp, err                                         
))))                                                                                    |                        }                                                                
Not Enough Parameter Number                                                             |                        fn, ok := e.(*Function)                                          
scheme.go>  (let loop ((b 0)(a (list 1 2 3))) (if (null? a) b (loop (+ b (car a))(cdr a\|                        if !ok {                                                         
))))                                                                                    |                                return sexp, NewRuntimeError("Not Function")             
6                                                                                       |                        }                                                                
scheme.go> 

パフォーマンス改善その2

単純に、eval()がcallされるので、こーなる。

scheme.go>  (let loop ((i 0)) (if (<= 1000000 i) i (loop (+ 1 i))))
runtime: goroutine stack exceeds 1000000000-byte limit
fatal error: stack overflow

runtime stack:
runtime.throw(0x4eb3e1, 0xe)
        /usr/lib/golang/src/runtime/panic.go:605 +0x95
runtime.newstack(0x0)
        /usr/lib/golang/src/runtime/stack.go:1050 +0x6e1
runtime.morestack()
        /usr/lib/golang/src/runtime/asm_amd64.s:415 +0x86

loopが関数として評価されるため、eval()のcall回数が増大する

パフォーマンスチューニング

学習用とはいえ、いくらなんでも、10万件のソートで14秒もかかるのは、如何なモノか。

scheme.go>  (define test-list (map (lambda (n) (rand-integer 10000))(iota 100000)))
test-list
scheme.go>  (define qsort (lambda (l)(if (null? l) l (append (qsort (filter (lambda (n) (< n (car l)))(cdr l)))(cons (car l)(qsort (filter (lambda (n) (not (< n (car l))))(cdr l\
))))))))
qsort
scheme.go>  (time (qsort test-list))
14.78499272s
nil
scheme.go>  (time (qsort test-list))
14.714609871s
nil
scheme.go>

cons,appendの修正

(define hanoi                                                                                                                                                                     
  (lambda (from to work n)                                                                                                                                                        
    (if (>= 0 n) '() (append (hanoi from work to (- n 1)) (list (list (cons from to) n)) (hanoi work to from (- n 1))))))

Mapの実装

Filter reduceを組み込む
Goshでサンプル作成

Parser処理の不具合

[kunohi@centos7-dev-docker go-scheme]$ git diff a65e8a9deee6e813a6ce7e250f4d11251ed4b432 src/lisp.go
diff --git a/src/lisp.go b/src/lisp.go
index 11ad780..a393e2c 100644
--- a/src/lisp.go
+++ b/src/lisp.go
@@ -448,6 +448,11 @@ func create_ast(tokens []string) (Expression, int, error) {
 
                count := 1
                for {
+                       if tokens[0] == ")" {
+                               count = count + 1
+                               break
+                       }
+
                        exp, c, err := create_ast(tokens)
                        if err != nil {
                                return nil, c, err
@@ -460,10 +465,6 @@ func create_ast(tokens []string) (Expression, int, error) {
                                err := NewSyntaxError("unexpected ')' while reading")
                                return nil, 0, err
                        }
-                       if tokens[0] == ")" {
-                               count = count + 1
-                               break
-                       }
                }
                item := NewList(L)
                return item, count, nil
[kunohi@centos7-dev-docker go-scheme]$ 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.