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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: cd79b300ba14a7d6c45496292f33a72767c9f839
4
- data.tar.gz: a7500b1c04cdc5262d899dee558b6a68648cb141
3
+ metadata.gz: 42a5f2b1b6e96db9ab76de11598d650aa99a4f4c
4
+ data.tar.gz: 258c6c47e9dca2afd7ab5e7e9c265caadfe7fa8a
5
5
  SHA512:
6
- metadata.gz: 43b51dfb0eb3ace2920704f9bf54cf4690889a79bf38edca8e06cc703e453fddc3dd6055e45f8965e6a4d1410c8883ae9c2472624ed1e79d37e5157c9441f067
7
- data.tar.gz: e67b94f6412a29046f759f1666d8fcefef4c5a4f07439f4ff2cfba076cfe04b63d4d14e5ee5ecb581fcd184e79a0684c2dc1a7c0cb963daff398a8d05ca9e3ea
6
+ metadata.gz: a80b1fdda67cffb7e3a8ca1369a27b418b1d591efc460422a60dcc2196f24bd7919be6f40e068453d6c949c059f3d37a485cc04e74018f4d11e9b8b99d7e1a9f
7
+ data.tar.gz: cd35640693b386066596e6dfc26433f088650170e817715a174ddaf018cdec9f421a2373f8a72a332da7b74412e57af8c300d8a94c271711cf278a0d39d226b8
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  Gemfile.lock
2
2
  env
3
3
  pkg
4
+ coverage
@@ -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
@@ -1,7 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- group :development do
4
- gem "codeclimate-test-reporter", require: nil
3
+ group :test do
4
+ gem "simplecov"
5
5
  end
6
6
 
7
7
  gemspec
data/README.md CHANGED
@@ -1,30 +1,26 @@
1
- lisp-rb
2
- =======
1
+ lisp
2
+ ====
3
3
 
4
- [![Code Climate](https://codeclimate.com/github/jamesmoriarty/lisp-rb.png)](https://codeclimate.com/github/jamesmoriarty/lisp-rb) [![Code Climate](https://codeclimate.com/github/jamesmoriarty/lisp-rb/coverage.png)](https://codeclimate.com/github/jamesmoriarty/lisp-rb)
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-rb
8
+ ```clojure
9
+ $ lisp-repl
10
10
  ctrl-c to exit
11
- > (define pi 3.14)
12
- 3.14
13
- > (* pi 2)
14
- 6.28
15
- > (define area (lambda (r) (* 3.141592653 (* r r))))
16
- #<Proc:0x007f8af9c59ff0@./lisp.rb:44>
17
- > (area 3)
18
- 28.274333877
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
- [![Gem Version](https://badge.fury.io/rb/lisp.svg)](http://badge.fury.io/rb/lisp)
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
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative "../lib/lisp"
4
4
 
5
- Lisp.repl
5
+ Lisp::REPL.new.run
@@ -1,66 +1,67 @@
1
- require "bundler/setup"
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(string)
7
- execute(parse(tokenize(string)))
7
+ def self.eval string
8
+ execute parse tokenize string
8
9
  end
9
10
 
10
- def self.tokenize(string)
11
+ def self.tokenize string
11
12
  string.gsub("("," ( ").gsub(")"," ) ").split
12
13
  end
13
14
 
14
- def self.parse(tokens, tree = [])
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(tokens)
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
- evaluator(token)
28
+ atom token
28
29
  end
29
30
  end
30
31
 
31
- def self.evaluator(token)
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(exp, scope = global)
41
- return scope[exp] if exp.is_a? Symbol
42
- return exp unless exp.is_a? Array
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 exp[0]
45
+ case expression[0]
45
46
  when :define
46
- _, var, exp = exp
47
- scope[var] = execute(exp, scope)
47
+ _, var, expression = expression
48
+ scope[var] = execute expression, scope
48
49
  when :lambda
49
- _, params, exp = exp
50
- lambda { |*args| execute(exp, scope.merge(Hash[params.zip(args)])) }
50
+ _, params, expression = expression
51
+ lambda { |*args| execute expression, scope.merge(Hash[params.zip(args)]) }
51
52
  when :if
52
- _, test, conseq, alt = exp
53
- exp = execute(test, scope) ? conseq : alt
54
- execute(exp, scope)
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, exp = exp
57
- if scope.has_key?(var) then scope[var] = execute(exp, scope) else raise "#{var} is undefined" end
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
- _, *exp = exp
60
- exp.map { |exp| execute(exp) }.last
60
+ _, *expression = expression
61
+ expression.map { |expression| execute expression, scope }.last
61
62
  else
62
- func, *args = exp.map { |exp| execute(exp, scope) }
63
- func.call(*args)
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(operator => lambda { |*args| args.inject(&operator) })
72
+ scope.merge operator => lambda { |*args| args.inject &operator }
72
73
  end
73
74
  end
74
75
  end
@@ -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
- def self.repl
6
- trap("SIGINT") { throw :exit }
7
- puts "ctrl-c to exit"
8
- catch(:exit) do
9
- loop do
10
- puts begin
11
- eval Coolline.new.readline
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
- if __FILE__ == $0
21
- Lisp.repl
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
@@ -1,3 +1,3 @@
1
1
  module Lisp
2
- VERSION = "1.1.0"
2
+ VERSION = '1.5.0'.freeze
3
3
  end
@@ -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.add_development_dependency "bundler", "~> 1.6"
22
+ spec.add_dependency "coderay"
23
+ spec.add_development_dependency "bundler"
23
24
  spec.add_development_dependency "rake"
24
- spec.add_development_dependency "pry"
25
+ spec.add_development_dependency "minitest", "> 5"
25
26
  end
@@ -0,0 +1,5 @@
1
+ require 'simplecov'
2
+
3
+ SimpleCov.add_filter 'vendor'
4
+ SimpleCov.add_filter 'test'
5
+ SimpleCov.start
@@ -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
- require_relative "../lib/lisp"
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 test_parse
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
- # representation
25
+ # ast
24
26
 
25
- def test_representation
26
- assert_equal [:*, 2, [:+, 1, 0]], Lisp.parse(Lisp.tokenize("(* 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 test_execution
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("(define pi 3.141592653)")
43
- assert_equal 6.283185306, Lisp.eval("(* pi 2)")
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 test_if
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("(begin (define x 1) (set! x (+ x 1)) (* x 2))")
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("(define area (lambda (r) (* 3.141592653 (* r r))))")
61
- assert_equal 28.274333877, Lisp.eval("(area 3)")
62
- Lisp.eval("(define fact (lambda (n) (if (<= n 1) 1 (* n (fact (- n 1))))))")
63
- assert_equal 3628800, Lisp.eval("(fact 10)")
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.1.0
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: 2015-01-22 00:00:00.000000000 Z
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: bundler
28
+ name: coderay
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - "~>"
31
+ - - ">="
32
32
  - !ruby/object:Gem::Version
33
- version: '1.6'
34
- type: :development
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: '1.6'
40
+ version: '0'
41
41
  - !ruby/object:Gem::Dependency
42
- name: rake
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: pry
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-rb
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-rb
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.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