lisp 1.3.0 → 1.4.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: 728f826f5c7ad7f2b90d6861cdd6d0792c9af21f
4
- data.tar.gz: 21b608d9ee103ad308ab4a2b346f459609684120
3
+ metadata.gz: 8415c51f19fcf2d2407b4e648726362aed1ff518
4
+ data.tar.gz: d7fa8e0de34061c8dcdb030a50b382fd77a9d627
5
5
  SHA512:
6
- metadata.gz: 451e92aa8b46ea6b0ad3b6d7bab7b0118052f2ec43fdc3c9779ce93817e6a178e6cf99c11e92732e193b952d8f65375eac2338f0360bac181ad892270d14d999
7
- data.tar.gz: d297679c014dfa9f0c52be2e8e73baf9c9175e4b3a1038440da0cb20d2597139a526a68d90845d0ab6efdec0f2ef55ae567af36c0219a45ec333d02c31554b3e
6
+ metadata.gz: 3c38eaf6967bc43c5ab122095664cd41ae9a2f36dde51207f4c926592c4ff7431a033942ad8cf0c695385f59e011f6f68571141ee77ed2e0774e967c4154e905
7
+ data.tar.gz: 14f4a2af0e2b7413301f6b86ee1d3ce0228f1806244ac98cccf5c6179bbbac28dd33e6a21a6e17cec7a2015dc6a8f255c8f8ec028599b328522f51c9ca360f2a
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
1
  Gemfile.lock
2
2
  env
3
3
  pkg
4
+ coverage
data/Gemfile CHANGED
@@ -1,6 +1,7 @@
1
1
  source "https://rubygems.org"
2
2
 
3
- group :development do
3
+ group :test do
4
+ gem "simplecov"
4
5
  gem "codeclimate-test-reporter", require: nil
5
6
  end
6
7
 
data/README.md CHANGED
@@ -1,9 +1,9 @@
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
+ [![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
@@ -2,4 +2,4 @@
2
2
 
3
3
  require_relative "../lib/lisp"
4
4
 
5
- Lisp.repl
5
+ Lisp::REPL.new.run
@@ -4,64 +4,64 @@ require "lisp/version"
4
4
  require "lisp/repl"
5
5
 
6
6
  module Lisp
7
- def self.eval(string)
8
- execute(parse(tokenize(string)))
7
+ def self.eval string
8
+ execute parse tokenize string
9
9
  end
10
10
 
11
- def self.tokenize(string)
11
+ def self.tokenize string
12
12
  string.gsub("("," ( ").gsub(")"," ) ").split
13
13
  end
14
14
 
15
- def self.parse(tokens, tree = [])
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(tokens)
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
- evaluator(token)
28
+ atom token
29
29
  end
30
30
  end
31
31
 
32
- def self.evaluator(token)
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(exp, scope = global)
42
- return scope[exp] if exp.is_a? Symbol
43
- return exp unless exp.is_a? Array
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 exp[0]
45
+ case expression[0]
46
46
  when :define
47
- _, var, exp = exp
48
- scope[var] = execute(exp, scope)
47
+ _, var, expression = expression
48
+ scope[var] = execute expression, scope
49
49
  when :lambda
50
- _, params, exp = exp
51
- 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)]) }
52
52
  when :if
53
- _, test, conseq, alt = exp
54
- exp = execute(test, scope) ? conseq : alt
55
- execute(exp, scope)
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, exp = exp
58
- 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
59
59
  when :begin
60
- _, *exp = exp
61
- exp.map { |exp| execute(exp) }.last
60
+ _, *expression = expression
61
+ expression.map { |expression| execute expression }.last
62
62
  else
63
- func, *args = exp.map { |exp| execute(exp, scope) }
64
- func.call(*args)
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(operator => lambda { |*args| args.inject(&operator) })
72
+ scope.merge operator => lambda { |*args| args.inject &operator }
73
73
  end
74
74
  end
75
75
  end
@@ -1,22 +1,67 @@
1
1
  #!/usr/bin/env ruby
2
2
  require "coolline"
3
+ require "coderay"
3
4
 
4
5
  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
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
- if __FILE__ == $0
21
- Lisp.repl
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
@@ -1,3 +1,3 @@
1
1
  module Lisp
2
- VERSION = "1.3.0"
2
+ VERSION = "1.4.0"
3
3
  end
@@ -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
@@ -0,0 +1,2 @@
1
+ require "codeclimate-test-reporter"
2
+ CodeClimate::TestReporter.start
@@ -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
 
@@ -39,8 +38,11 @@ class TestLisp < MiniTest::Unit::TestCase
39
38
  end
40
39
 
41
40
  def test_define
42
- Lisp.eval("(define pi 3.141592653)")
43
- assert_equal 6.283185306, Lisp.eval("(* pi 2)")
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("(begin (define x 1) (set! x (+ x 1)) (* x 2))")
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("(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)")
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.3.0
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: 2015-01-23 00:00:00.000000000 Z
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: 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
+ - - ~>
46
46
  - !ruby/object:Gem::Version
47
- version: '0'
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: '0'
54
+ version: '1.6'
55
55
  - !ruby/object:Gem::Dependency
56
- name: pry
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
- - ".gitignore"
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.2.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