lisp 1.1.0 → 1.5.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.travis.yml +19 -0
- data/Gemfile +2 -2
- data/README.md +19 -19
- data/bin/{lisp-rb → lisp-repl} +1 -1
- data/lib/lisp.rb +28 -27
- data/lib/lisp/repl.rb +59 -13
- data/lib/lisp/version.rb +1 -1
- data/lisp.gemspec +3 -2
- data/test/support/simplecov.rb +5 -0
- data/test/test_lisp.rb +94 -18
- metadata +30 -13
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 42a5f2b1b6e96db9ab76de11598d650aa99a4f4c
|
4
|
+
data.tar.gz: 258c6c47e9dca2afd7ab5e7e9c265caadfe7fa8a
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a80b1fdda67cffb7e3a8ca1369a27b418b1d591efc460422a60dcc2196f24bd7919be6f40e068453d6c949c059f3d37a485cc04e74018f4d11e9b8b99d7e1a9f
|
7
|
+
data.tar.gz: cd35640693b386066596e6dfc26433f088650170e817715a174ddaf018cdec9f421a2373f8a72a332da7b74412e57af8c300d8a94c271711cf278a0d39d226b8
|
data/.gitignore
CHANGED
data/.travis.yml
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
sudo: false
|
2
|
+
|
3
|
+
language: ruby
|
4
|
+
|
5
|
+
rvm:
|
6
|
+
- 2.3.8
|
7
|
+
|
8
|
+
env:
|
9
|
+
global:
|
10
|
+
- CC_TEST_REPORTER_ID=0a4c13b71635c00cee43aba776853ade9404cc745c42977e25a5877a3e148527
|
11
|
+
|
12
|
+
before_script:
|
13
|
+
- curl -L https://codeclimate.com/downloads/test-reporter/test-reporter-latest-linux-amd64 > ./cc-test-reporter
|
14
|
+
- chmod +x ./cc-test-reporter
|
15
|
+
- ./cc-test-reporter before-build
|
16
|
+
- gem install bundler -v 2.0.2
|
17
|
+
|
18
|
+
after_script:
|
19
|
+
- ./cc-test-reporter after-build --exit-code $TRAVIS_TEST_RESULT
|
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,30 +1,26 @@
|
|
1
|
-
lisp
|
2
|
-
|
1
|
+
lisp
|
2
|
+
====
|
3
3
|
|
4
|
-
|
4
|
+
![Gem][1] ![Build Status][2]
|
5
5
|
|
6
|
-
Lisp Interpreter in Ruby. Inspired by [Lis.py](http://norvig.com/lispy.html).
|
6
|
+
Lisp Interpreter in the Ruby Programming Language - 70 LOC. Inspired by [Lis.py](http://norvig.com/lispy.html).
|
7
7
|
|
8
|
-
```
|
9
|
-
$ lisp-
|
8
|
+
```clojure
|
9
|
+
$ lisp-repl
|
10
10
|
ctrl-c to exit
|
11
|
-
> (
|
12
|
-
|
13
|
-
> (
|
14
|
-
|
15
|
-
>
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
> (define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))
|
20
|
-
#<Proc:0x007f9a4cc4acf0@./lisp.rb:63 (lambda)>
|
21
|
-
> (fact 10)
|
22
|
-
3628800.0
|
11
|
+
> (begin
|
12
|
+
(> (define incf
|
13
|
+
((> (lambda (x)
|
14
|
+
(((> (set! x (+ x 1))))
|
15
|
+
(> (define one 1)
|
16
|
+
(> (incf one))
|
17
|
+
2
|
18
|
+
>
|
23
19
|
```
|
24
20
|
|
25
21
|
Install
|
26
22
|
-------
|
27
|
-
|
23
|
+
![Gem Version][3]
|
28
24
|
|
29
25
|
```
|
30
26
|
gem install lisp
|
@@ -50,3 +46,7 @@ Features
|
|
50
46
|
- [x] __assignment__ - (set! var exp) Evaluate exp and assign that value to var, which must have been previously defined (with a define or as a parameter to an enclosing procedure). _Example: (set! x2 (* x x))_
|
51
47
|
|
52
48
|
- [x] __sequencing__ - (begin exp...) Evaluate each of the expressions in left-to-right order, and return the final value. _Example: (begin (set! x 1) (set! x (+ x 1)) (* x 2)) ⇒ 4_
|
49
|
+
|
50
|
+
[1]: https://img.shields.io/gem/dt/lisp
|
51
|
+
[2]: https://travis-ci.org/jamesmoriarty/lisp.svg?branch=master
|
52
|
+
[3]: https://img.shields.io/gem/v/lisp
|
data/bin/{lisp-rb → lisp-repl}
RENAMED
data/lib/lisp.rb
CHANGED
@@ -1,66 +1,67 @@
|
|
1
|
-
|
1
|
+
$:.unshift File.dirname(__FILE__)
|
2
|
+
|
2
3
|
require "lisp/version"
|
3
4
|
require "lisp/repl"
|
4
5
|
|
5
6
|
module Lisp
|
6
|
-
def self.eval
|
7
|
-
execute
|
7
|
+
def self.eval string
|
8
|
+
execute parse tokenize string
|
8
9
|
end
|
9
10
|
|
10
|
-
def self.tokenize
|
11
|
+
def self.tokenize string
|
11
12
|
string.gsub("("," ( ").gsub(")"," ) ").split
|
12
13
|
end
|
13
14
|
|
14
|
-
def self.parse
|
15
|
+
def self.parse tokens, tree = []
|
15
16
|
raise "unexpected: eof" if tokens.size.zero?
|
16
17
|
|
17
18
|
case token = tokens.shift
|
18
19
|
when "("
|
19
20
|
while tokens[0] != ")" do
|
20
|
-
tree.push parse
|
21
|
+
tree.push parse tokens
|
21
22
|
end
|
22
23
|
tokens.shift
|
23
24
|
tree
|
24
25
|
when ")"
|
25
26
|
raise "unexpected: )"
|
26
27
|
else
|
27
|
-
|
28
|
+
atom token
|
28
29
|
end
|
29
30
|
end
|
30
31
|
|
31
|
-
def self.
|
32
|
+
def self.atom token
|
32
33
|
case token
|
33
34
|
when /\d/
|
34
|
-
token.to_f
|
35
|
+
token.to_f % 1 > 0 ? token.to_f : token.to_i
|
35
36
|
else
|
36
37
|
token.to_sym
|
37
38
|
end
|
38
39
|
end
|
39
40
|
|
40
|
-
def self.execute
|
41
|
-
return scope
|
42
|
-
return
|
41
|
+
def self.execute expression, scope = global
|
42
|
+
return scope.fetch(expression) { |var| raise "#{var} is undefined" } if expression.is_a? Symbol
|
43
|
+
return expression unless expression.is_a? Array
|
43
44
|
|
44
|
-
case
|
45
|
+
case expression[0]
|
45
46
|
when :define
|
46
|
-
_, var,
|
47
|
-
scope[var]
|
47
|
+
_, var, expression = expression
|
48
|
+
scope[var] = execute expression, scope
|
48
49
|
when :lambda
|
49
|
-
_, params,
|
50
|
-
lambda { |*args| execute
|
50
|
+
_, params, expression = expression
|
51
|
+
lambda { |*args| execute expression, scope.merge(Hash[params.zip(args)]) }
|
51
52
|
when :if
|
52
|
-
_, test,
|
53
|
-
|
54
|
-
execute
|
53
|
+
_, test, consequent, alternative = expression
|
54
|
+
expression = if execute test, scope then consequent else alternative end
|
55
|
+
execute expression, scope
|
55
56
|
when :set!
|
56
|
-
_, var,
|
57
|
-
if scope.has_key?(var) then scope[var] = execute
|
57
|
+
_, var, expression = expression
|
58
|
+
if scope.has_key?(var) then scope[var] = execute expression, scope else raise "#{var} is undefined" end
|
58
59
|
when :begin
|
59
|
-
_, *
|
60
|
-
|
60
|
+
_, *expression = expression
|
61
|
+
expression.map { |expression| execute expression, scope }.last
|
61
62
|
else
|
62
|
-
|
63
|
-
|
63
|
+
function, *args = expression.map { |expression| execute expression, scope }
|
64
|
+
function.call *args
|
64
65
|
end
|
65
66
|
end
|
66
67
|
|
@@ -68,7 +69,7 @@ module Lisp
|
|
68
69
|
@scope ||= begin
|
69
70
|
operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]
|
70
71
|
operators.inject({}) do |scope, operator|
|
71
|
-
scope.merge
|
72
|
+
scope.merge operator => lambda { |*args| args.inject &operator }
|
72
73
|
end
|
73
74
|
end
|
74
75
|
end
|
data/lib/lisp/repl.rb
CHANGED
@@ -1,22 +1,68 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
|
2
3
|
require "coolline"
|
4
|
+
require "coderay"
|
3
5
|
|
4
6
|
module Lisp
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rescue Exception => e
|
13
|
-
e.message
|
7
|
+
class REPL
|
8
|
+
attr_reader :io, :input
|
9
|
+
|
10
|
+
def initialize
|
11
|
+
@io = Coolline.new do |config|
|
12
|
+
config.transform_proc = proc do
|
13
|
+
CodeRay.scan(config.line, :clojure).term
|
14
14
|
end
|
15
15
|
end
|
16
|
+
|
17
|
+
reset_input!
|
16
18
|
end
|
17
|
-
end
|
18
|
-
end
|
19
19
|
|
20
|
-
|
21
|
-
|
20
|
+
def run
|
21
|
+
trap("SIGINT") { throw :exit }
|
22
|
+
|
23
|
+
puts "ctrl-c to exit"
|
24
|
+
|
25
|
+
catch(:exit) do
|
26
|
+
loop do
|
27
|
+
begin
|
28
|
+
@input += io.readline prompt
|
29
|
+
|
30
|
+
if open_count == close_count
|
31
|
+
puts Lisp.eval input
|
32
|
+
|
33
|
+
reset_input!
|
34
|
+
end
|
35
|
+
rescue Exception => e
|
36
|
+
puts e.message
|
37
|
+
|
38
|
+
reset_input!
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
def prompt
|
47
|
+
count = open_count - close_count
|
48
|
+
char = count > 0 ? ?( : ?)
|
49
|
+
"#{char * count}> "
|
50
|
+
end
|
51
|
+
|
52
|
+
def reset_input!
|
53
|
+
@input = String.new
|
54
|
+
end
|
55
|
+
|
56
|
+
def open_count
|
57
|
+
tokens.count(?()
|
58
|
+
end
|
59
|
+
|
60
|
+
def close_count
|
61
|
+
tokens.count(?))
|
62
|
+
end
|
63
|
+
|
64
|
+
def tokens
|
65
|
+
Lisp.tokenize input
|
66
|
+
end
|
67
|
+
end
|
22
68
|
end
|
data/lib/lisp/version.rb
CHANGED
data/lisp.gemspec
CHANGED
@@ -19,7 +19,8 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "coolline"
|
22
|
-
spec.
|
22
|
+
spec.add_dependency "coderay"
|
23
|
+
spec.add_development_dependency "bundler"
|
23
24
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "
|
25
|
+
spec.add_development_dependency "minitest", "> 5"
|
25
26
|
end
|
data/test/test_lisp.rb
CHANGED
@@ -1,11 +1,10 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require "bundler/setup"
|
3
|
-
require "codeclimate-test-reporter"
|
4
|
-
CodeClimate::TestReporter.start
|
5
|
-
require "minitest/autorun"
|
6
|
-
require "pry"
|
7
3
|
|
8
|
-
|
4
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__),'support', '*.rb'))].each { |file| require file }
|
5
|
+
|
6
|
+
require "lisp"
|
7
|
+
require "minitest/autorun"
|
9
8
|
|
10
9
|
class TestLisp < MiniTest::Unit::TestCase
|
11
10
|
|
@@ -15,22 +14,31 @@ class TestLisp < MiniTest::Unit::TestCase
|
|
15
14
|
assert_equal ["(", "+", "1", "1", ")"], Lisp.tokenize("(+ 1 1)")
|
16
15
|
end
|
17
16
|
|
18
|
-
def
|
17
|
+
def test_parse_open
|
19
18
|
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize("(")) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_parse_close
|
20
22
|
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize(")")) }
|
21
23
|
end
|
22
24
|
|
23
|
-
#
|
25
|
+
# ast
|
24
26
|
|
25
|
-
def
|
26
|
-
assert_equal [:*, 2, [:+, 1, 0]],
|
27
|
+
def test_ast_nested
|
28
|
+
assert_equal [:*, 2, [:+, 1, 0]], Lisp.parse(Lisp.tokenize("(* 2 (+ 1 0) )"))
|
29
|
+
end
|
30
|
+
|
31
|
+
def test_ast_call
|
27
32
|
assert_equal [:lambda, [:r], [:*, 3.141592653, [:*, :r, :r]]], Lisp.parse(Lisp.tokenize("(lambda (r) (* 3.141592653 (* r r)))"))
|
28
33
|
end
|
29
34
|
|
30
35
|
# execution
|
31
36
|
|
32
|
-
def
|
37
|
+
def test_execution_return
|
33
38
|
assert_equal 1, Lisp.execute(1)
|
39
|
+
end
|
40
|
+
|
41
|
+
def test_execution_reduce
|
34
42
|
assert_equal 2, Lisp.execute([:*, 2, [:+, 1, 0]])
|
35
43
|
end
|
36
44
|
|
@@ -39,12 +47,18 @@ class TestLisp < MiniTest::Unit::TestCase
|
|
39
47
|
end
|
40
48
|
|
41
49
|
def test_define
|
42
|
-
Lisp.eval(
|
43
|
-
|
50
|
+
assert_equal 6.283185306, Lisp.eval(<<-eos)
|
51
|
+
(begin
|
52
|
+
(define pi 3.141592653)
|
53
|
+
(* pi 2))
|
54
|
+
eos
|
44
55
|
end
|
45
56
|
|
46
|
-
def
|
57
|
+
def test_if_cont
|
47
58
|
assert_equal 2, Lisp.eval("(if(== 1 2) 1 2)")
|
59
|
+
end
|
60
|
+
|
61
|
+
def test_if_alt
|
48
62
|
assert_equal 1, Lisp.eval("(if(!= 1 2) 1 2)")
|
49
63
|
end
|
50
64
|
|
@@ -53,13 +67,75 @@ class TestLisp < MiniTest::Unit::TestCase
|
|
53
67
|
end
|
54
68
|
|
55
69
|
def test_begin
|
56
|
-
assert_equal 4, Lisp.eval(
|
70
|
+
assert_equal 4, Lisp.eval(<<-eos)
|
71
|
+
(begin
|
72
|
+
(define x 1)
|
73
|
+
(set! x (+ x 1)) (* x 2))
|
74
|
+
eos
|
57
75
|
end
|
58
76
|
|
59
77
|
def test_lambda
|
60
|
-
Lisp.eval(
|
61
|
-
|
62
|
-
|
63
|
-
|
78
|
+
assert_equal 28.274333877, Lisp.eval(<<-eos)
|
79
|
+
(begin
|
80
|
+
(define area
|
81
|
+
(lambda (r)
|
82
|
+
(* 3.141592653 (* r r))))
|
83
|
+
(area 3))
|
84
|
+
eos
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_lambda_no_args
|
88
|
+
assert_equal 42, Lisp.eval(<<-eos)
|
89
|
+
(begin
|
90
|
+
(define test
|
91
|
+
(lambda () 42))
|
92
|
+
(test))
|
93
|
+
eos
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_lambda_call_self
|
97
|
+
assert_equal 3628800, Lisp.eval(<<-eos)
|
98
|
+
(begin
|
99
|
+
(define fact
|
100
|
+
(lambda (n)
|
101
|
+
(if (<= n 1)
|
102
|
+
1
|
103
|
+
(* n (fact (- n 1))))))
|
104
|
+
(fact 10))
|
105
|
+
eos
|
106
|
+
end
|
107
|
+
|
108
|
+
def test_lambda_call_arg
|
109
|
+
assert_equal 40, Lisp.eval(<<-eos)
|
110
|
+
(begin
|
111
|
+
(define twice (lambda (x) (* 2 x)))
|
112
|
+
(define repeat (lambda (f) (lambda (x) (f (f x)))))
|
113
|
+
((repeat twice) 10)))
|
114
|
+
eos
|
115
|
+
end
|
116
|
+
|
117
|
+
def test_program
|
118
|
+
assert_equal 2, Lisp.eval(<<-eos)
|
119
|
+
(begin
|
120
|
+
(define incf
|
121
|
+
(lambda (x)
|
122
|
+
(set! x (+ x 1))))
|
123
|
+
(define one 1)
|
124
|
+
(incf one))
|
125
|
+
eos
|
126
|
+
end
|
127
|
+
|
128
|
+
def test_repl
|
129
|
+
pid = Process.pid
|
130
|
+
subject = Lisp::REPL.new
|
131
|
+
thread = Thread.new do
|
132
|
+
sleep 1
|
133
|
+
Process.kill("INT", pid)
|
134
|
+
thread.join
|
135
|
+
end
|
136
|
+
|
137
|
+
assert_output("ctrl-c to exit\n") do
|
138
|
+
subject.run
|
139
|
+
end
|
64
140
|
end
|
65
141
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lisp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.5.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- James Moriarty
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2020-06-18 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coolline
|
@@ -25,21 +25,21 @@ dependencies:
|
|
25
25
|
- !ruby/object:Gem::Version
|
26
26
|
version: '0'
|
27
27
|
- !ruby/object:Gem::Dependency
|
28
|
-
name:
|
28
|
+
name: coderay
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - "
|
31
|
+
- - ">="
|
32
32
|
- !ruby/object:Gem::Version
|
33
|
-
version: '
|
34
|
-
type: :
|
33
|
+
version: '0'
|
34
|
+
type: :runtime
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - "
|
38
|
+
- - ">="
|
39
39
|
- !ruby/object:Gem::Version
|
40
|
-
version: '
|
40
|
+
version: '0'
|
41
41
|
- !ruby/object:Gem::Dependency
|
42
|
-
name:
|
42
|
+
name: bundler
|
43
43
|
requirement: !ruby/object:Gem::Requirement
|
44
44
|
requirements:
|
45
45
|
- - ">="
|
@@ -53,7 +53,7 @@ dependencies:
|
|
53
53
|
- !ruby/object:Gem::Version
|
54
54
|
version: '0'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
59
|
- - ">="
|
@@ -66,24 +66,40 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: minitest
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - ">"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '5'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - ">"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '5'
|
69
83
|
description: ''
|
70
84
|
email:
|
71
85
|
- jamespaulmoriarty@gmail.com
|
72
86
|
executables:
|
73
|
-
- lisp-
|
87
|
+
- lisp-repl
|
74
88
|
extensions: []
|
75
89
|
extra_rdoc_files: []
|
76
90
|
files:
|
77
91
|
- ".gitignore"
|
92
|
+
- ".travis.yml"
|
78
93
|
- Gemfile
|
79
94
|
- LICENSE.txt
|
80
95
|
- README.md
|
81
96
|
- Rakefile
|
82
|
-
- bin/lisp-
|
97
|
+
- bin/lisp-repl
|
83
98
|
- lib/lisp.rb
|
84
99
|
- lib/lisp/repl.rb
|
85
100
|
- lib/lisp/version.rb
|
86
101
|
- lisp.gemspec
|
102
|
+
- test/support/simplecov.rb
|
87
103
|
- test/test_lisp.rb
|
88
104
|
homepage: ''
|
89
105
|
licenses:
|
@@ -105,9 +121,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
105
121
|
version: '0'
|
106
122
|
requirements: []
|
107
123
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.2.
|
124
|
+
rubygems_version: 2.5.2.3
|
109
125
|
signing_key:
|
110
126
|
specification_version: 4
|
111
127
|
summary: Lisp Interpreter in Ruby.
|
112
128
|
test_files:
|
129
|
+
- test/support/simplecov.rb
|
113
130
|
- test/test_lisp.rb
|