lisp 1.3.0 → 1.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/Gemfile +2 -1
- data/README.md +4 -4
- data/bin/lisp-rb +1 -1
- data/lib/lisp.rb +26 -26
- data/lib/lisp/repl.rb +58 -13
- data/lib/lisp/version.rb +1 -1
- data/lisp.gemspec +1 -1
- data/test/support/codeclimate.rb +2 -0
- data/test/support/simplecov.rb +5 -0
- data/test/test_lisp.rb +67 -12
- metadata +26 -22
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8415c51f19fcf2d2407b4e648726362aed1ff518
|
4
|
+
data.tar.gz: d7fa8e0de34061c8dcdb030a50b382fd77a9d627
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 3c38eaf6967bc43c5ab122095664cd41ae9a2f36dde51207f4c926592c4ff7431a033942ad8cf0c695385f59e011f6f68571141ee77ed2e0774e967c4154e905
|
7
|
+
data.tar.gz: 14f4a2af0e2b7413301f6b86ee1d3ce0228f1806244ac98cccf5c6179bbbac28dd33e6a21a6e17cec7a2015dc6a8f255c8f8ec028599b328522f51c9ca360f2a
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,9 +1,9 @@
|
|
1
|
-
lisp
|
2
|
-
|
1
|
+
lisp
|
2
|
+
====
|
3
3
|
|
4
|
-
[![Code Climate](https://codeclimate.com/github/jamesmoriarty/lisp-rb.
|
4
|
+
[![Code Climate](https://codeclimate.com/github/jamesmoriarty/lisp-rb/badges/gpa.svg)](https://codeclimate.com/github/jamesmoriarty/lisp-rb) [![Test Coverage](https://codeclimate.com/github/jamesmoriarty/lisp-rb/badges/coverage.svg)](https://codeclimate.com/github/jamesmoriarty/lisp-rb/coverage)
|
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
8
|
```
|
9
9
|
$ lisp-rb
|
data/bin/lisp-rb
CHANGED
data/lib/lisp.rb
CHANGED
@@ -4,64 +4,64 @@ require "lisp/version"
|
|
4
4
|
require "lisp/repl"
|
5
5
|
|
6
6
|
module Lisp
|
7
|
-
def self.eval
|
8
|
-
execute
|
7
|
+
def self.eval string
|
8
|
+
execute parse tokenize string
|
9
9
|
end
|
10
10
|
|
11
|
-
def self.tokenize
|
11
|
+
def self.tokenize string
|
12
12
|
string.gsub("("," ( ").gsub(")"," ) ").split
|
13
13
|
end
|
14
14
|
|
15
|
-
def self.parse
|
15
|
+
def self.parse tokens, tree = []
|
16
16
|
raise "unexpected: eof" if tokens.size.zero?
|
17
17
|
|
18
18
|
case token = tokens.shift
|
19
19
|
when "("
|
20
20
|
while tokens[0] != ")" do
|
21
|
-
tree.push parse
|
21
|
+
tree.push parse tokens
|
22
22
|
end
|
23
23
|
tokens.shift
|
24
24
|
tree
|
25
25
|
when ")"
|
26
26
|
raise "unexpected: )"
|
27
27
|
else
|
28
|
-
|
28
|
+
atom token
|
29
29
|
end
|
30
30
|
end
|
31
31
|
|
32
|
-
def self.
|
32
|
+
def self.atom token
|
33
33
|
case token
|
34
34
|
when /\d/
|
35
|
-
token.to_f
|
35
|
+
token.to_f % 1 > 0 ? token.to_f : token.to_i
|
36
36
|
else
|
37
37
|
token.to_sym
|
38
38
|
end
|
39
39
|
end
|
40
40
|
|
41
|
-
def self.execute
|
42
|
-
return scope[
|
43
|
-
return
|
41
|
+
def self.execute expression, scope = global
|
42
|
+
return scope[expression] if expression.is_a? Symbol
|
43
|
+
return expression unless expression.is_a? Array
|
44
44
|
|
45
|
-
case
|
45
|
+
case expression[0]
|
46
46
|
when :define
|
47
|
-
_, var,
|
48
|
-
scope[var]
|
47
|
+
_, var, expression = expression
|
48
|
+
scope[var] = execute expression, scope
|
49
49
|
when :lambda
|
50
|
-
_, params,
|
51
|
-
lambda { |*args| execute
|
50
|
+
_, params, expression = expression
|
51
|
+
lambda { |*args| execute expression, scope.merge(Hash[params.zip(args)]) }
|
52
52
|
when :if
|
53
|
-
_, test,
|
54
|
-
|
55
|
-
execute
|
53
|
+
_, test, consequent, alternative = expression
|
54
|
+
expression = if execute test, scope then consequent else alternative end
|
55
|
+
execute expression, scope
|
56
56
|
when :set!
|
57
|
-
_, var,
|
58
|
-
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
|
59
59
|
when :begin
|
60
|
-
_, *
|
61
|
-
|
60
|
+
_, *expression = expression
|
61
|
+
expression.map { |expression| execute expression }.last
|
62
62
|
else
|
63
|
-
|
64
|
-
|
63
|
+
function, *args = expression.map { |expression| execute expression, scope }
|
64
|
+
function.call *args
|
65
65
|
end
|
66
66
|
end
|
67
67
|
|
@@ -69,7 +69,7 @@ module Lisp
|
|
69
69
|
@scope ||= begin
|
70
70
|
operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]
|
71
71
|
operators.inject({}) do |scope, operator|
|
72
|
-
scope.merge
|
72
|
+
scope.merge operator => lambda { |*args| args.inject &operator }
|
73
73
|
end
|
74
74
|
end
|
75
75
|
end
|
data/lib/lisp/repl.rb
CHANGED
@@ -1,22 +1,67 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
2
|
require "coolline"
|
3
|
+
require "coderay"
|
3
4
|
|
4
5
|
module Lisp
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rescue Exception => e
|
13
|
-
e.message
|
6
|
+
class REPL
|
7
|
+
attr_reader :io, :input
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
@io = Coolline.new do |config|
|
11
|
+
config.transform_proc = proc do
|
12
|
+
CodeRay.scan(config.line, :clojure).term
|
14
13
|
end
|
15
14
|
end
|
15
|
+
|
16
|
+
reset_input!
|
17
|
+
end
|
18
|
+
|
19
|
+
def run
|
20
|
+
trap("SIGINT") { throw :exit }
|
21
|
+
|
22
|
+
puts "ctrl-c to exit"
|
23
|
+
|
24
|
+
catch(:exit) do
|
25
|
+
loop do
|
26
|
+
begin
|
27
|
+
@input += io.readline prompt
|
28
|
+
|
29
|
+
if open_count == close_count
|
30
|
+
puts Lisp.eval input
|
31
|
+
|
32
|
+
reset_input!
|
33
|
+
end
|
34
|
+
rescue Exception => e
|
35
|
+
puts e.message
|
36
|
+
|
37
|
+
reset_input!
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
private
|
44
|
+
|
45
|
+
def prompt
|
46
|
+
count = open_count - close_count
|
47
|
+
char = count > 0 ? ?( : ?)
|
48
|
+
"#{char * count}> "
|
49
|
+
end
|
50
|
+
|
51
|
+
def reset_input!
|
52
|
+
@input = String.new
|
16
53
|
end
|
17
|
-
end
|
18
|
-
end
|
19
54
|
|
20
|
-
|
21
|
-
|
55
|
+
def open_count
|
56
|
+
tokens.count(?()
|
57
|
+
end
|
58
|
+
|
59
|
+
def close_count
|
60
|
+
tokens.count(?))
|
61
|
+
end
|
62
|
+
|
63
|
+
def tokens
|
64
|
+
Lisp.tokenize input
|
65
|
+
end
|
66
|
+
end
|
22
67
|
end
|
data/lib/lisp/version.rb
CHANGED
data/lisp.gemspec
CHANGED
@@ -19,7 +19,7 @@ Gem::Specification.new do |spec|
|
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
21
|
spec.add_dependency "coolline"
|
22
|
+
spec.add_dependency "coderay"
|
22
23
|
spec.add_development_dependency "bundler", "~> 1.6"
|
23
24
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "pry"
|
25
25
|
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
|
|
@@ -39,8 +38,11 @@ class TestLisp < MiniTest::Unit::TestCase
|
|
39
38
|
end
|
40
39
|
|
41
40
|
def test_define
|
42
|
-
Lisp.eval(
|
43
|
-
|
41
|
+
assert_equal 6.283185306, Lisp.eval(<<-eos)
|
42
|
+
(begin
|
43
|
+
(define pi 3.141592653)
|
44
|
+
(* pi 2))
|
45
|
+
eos
|
44
46
|
end
|
45
47
|
|
46
48
|
def test_if
|
@@ -53,13 +55,66 @@ class TestLisp < MiniTest::Unit::TestCase
|
|
53
55
|
end
|
54
56
|
|
55
57
|
def test_begin
|
56
|
-
assert_equal 4, Lisp.eval(
|
58
|
+
assert_equal 4, Lisp.eval(<<-eos)
|
59
|
+
(begin
|
60
|
+
(define x 1)
|
61
|
+
(set! x (+ x 1)) (* x 2))
|
62
|
+
eos
|
57
63
|
end
|
58
64
|
|
59
65
|
def test_lambda
|
60
|
-
Lisp.eval(
|
61
|
-
|
62
|
-
|
63
|
-
|
66
|
+
assert_equal 28.274333877, Lisp.eval(<<-eos)
|
67
|
+
(begin
|
68
|
+
(define area
|
69
|
+
(lambda (r)
|
70
|
+
(* 3.141592653 (* r r))))
|
71
|
+
(area 3))
|
72
|
+
eos
|
73
|
+
end
|
74
|
+
|
75
|
+
def test_lambda_call_self
|
76
|
+
assert_equal 3628800, Lisp.eval(<<-eos)
|
77
|
+
(begin
|
78
|
+
(define fact
|
79
|
+
(lambda (n)
|
80
|
+
(if (<= n 1)
|
81
|
+
1
|
82
|
+
(* n (fact (- n 1))))))
|
83
|
+
(fact 10))
|
84
|
+
eos
|
85
|
+
end
|
86
|
+
|
87
|
+
def test_lambda_call_arg
|
88
|
+
assert_equal 40, Lisp.eval(<<-eos)
|
89
|
+
(begin
|
90
|
+
(define twice (lambda (x) (* 2 x)))
|
91
|
+
(define repeat (lambda (f) (lambda (x) (f (f x)))))
|
92
|
+
((repeat twice) 10)))
|
93
|
+
eos
|
94
|
+
end
|
95
|
+
|
96
|
+
def test_program
|
97
|
+
assert_equal 2, Lisp.eval(<<-eos)
|
98
|
+
(begin
|
99
|
+
(define incf
|
100
|
+
(lambda (x)
|
101
|
+
(set! x (+ x 1))))
|
102
|
+
(define one 1)
|
103
|
+
(incf one))
|
104
|
+
eos
|
105
|
+
end
|
106
|
+
|
107
|
+
def test_repl
|
108
|
+
pid = Process.pid
|
109
|
+
subject = Lisp::REPL.new
|
110
|
+
thread = Thread.new do
|
111
|
+
sleep 1
|
112
|
+
Process.kill("INT", pid)
|
113
|
+
thread.join
|
114
|
+
end
|
115
|
+
|
116
|
+
assert_output("ctrl-c to exit\n") do
|
117
|
+
subject.run
|
118
|
+
end
|
64
119
|
end
|
65
120
|
end
|
metadata
CHANGED
@@ -1,69 +1,69 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: lisp
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 1.
|
4
|
+
version: 1.4.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: 2016-09-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: coolline
|
15
15
|
requirement: !ruby/object:Gem::Requirement
|
16
16
|
requirements:
|
17
|
-
- -
|
17
|
+
- - '>='
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '0'
|
20
20
|
type: :runtime
|
21
21
|
prerelease: false
|
22
22
|
version_requirements: !ruby/object:Gem::Requirement
|
23
23
|
requirements:
|
24
|
-
- -
|
24
|
+
- - '>='
|
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
|
+
- - ~>
|
46
46
|
- !ruby/object:Gem::Version
|
47
|
-
version: '
|
47
|
+
version: '1.6'
|
48
48
|
type: :development
|
49
49
|
prerelease: false
|
50
50
|
version_requirements: !ruby/object:Gem::Requirement
|
51
51
|
requirements:
|
52
|
-
- -
|
52
|
+
- - ~>
|
53
53
|
- !ruby/object:Gem::Version
|
54
|
-
version: '
|
54
|
+
version: '1.6'
|
55
55
|
- !ruby/object:Gem::Dependency
|
56
|
-
name:
|
56
|
+
name: rake
|
57
57
|
requirement: !ruby/object:Gem::Requirement
|
58
58
|
requirements:
|
59
|
-
- -
|
59
|
+
- - '>='
|
60
60
|
- !ruby/object:Gem::Version
|
61
61
|
version: '0'
|
62
62
|
type: :development
|
63
63
|
prerelease: false
|
64
64
|
version_requirements: !ruby/object:Gem::Requirement
|
65
65
|
requirements:
|
66
|
-
- -
|
66
|
+
- - '>='
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
69
|
description: ''
|
@@ -74,7 +74,7 @@ executables:
|
|
74
74
|
extensions: []
|
75
75
|
extra_rdoc_files: []
|
76
76
|
files:
|
77
|
-
-
|
77
|
+
- .gitignore
|
78
78
|
- Gemfile
|
79
79
|
- LICENSE.txt
|
80
80
|
- README.md
|
@@ -84,6 +84,8 @@ files:
|
|
84
84
|
- lib/lisp/repl.rb
|
85
85
|
- lib/lisp/version.rb
|
86
86
|
- lisp.gemspec
|
87
|
+
- test/support/codeclimate.rb
|
88
|
+
- test/support/simplecov.rb
|
87
89
|
- test/test_lisp.rb
|
88
90
|
homepage: ''
|
89
91
|
licenses:
|
@@ -95,19 +97,21 @@ require_paths:
|
|
95
97
|
- lib
|
96
98
|
required_ruby_version: !ruby/object:Gem::Requirement
|
97
99
|
requirements:
|
98
|
-
- -
|
100
|
+
- - '>='
|
99
101
|
- !ruby/object:Gem::Version
|
100
102
|
version: '0'
|
101
103
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
102
104
|
requirements:
|
103
|
-
- -
|
105
|
+
- - '>='
|
104
106
|
- !ruby/object:Gem::Version
|
105
107
|
version: '0'
|
106
108
|
requirements: []
|
107
109
|
rubyforge_project:
|
108
|
-
rubygems_version: 2.
|
110
|
+
rubygems_version: 2.0.14.1
|
109
111
|
signing_key:
|
110
112
|
specification_version: 4
|
111
113
|
summary: Lisp Interpreter in Ruby.
|
112
114
|
test_files:
|
115
|
+
- test/support/codeclimate.rb
|
116
|
+
- test/support/simplecov.rb
|
113
117
|
- test/test_lisp.rb
|