lisp 1.3.0 → 1.5.2
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 +5 -5
- data/.github/workflows/ci.yaml +19 -0
- data/.gitignore +3 -0
- data/Gemfile +3 -3
- data/README.md +43 -19
- data/Rakefile +25 -3
- data/bin/{lisp-rb → lisp-repl} +1 -1
- data/lib/lisp.rb +2 -70
- data/lib/lisp/interpreter.rb +74 -0
- data/lib/lisp/repl.rb +58 -14
- data/lib/lisp/version.rb +1 -1
- data/lisp.gemspec +7 -5
- data/test/lisp_test.rb +144 -0
- data/test/support/simplecov.rb +5 -0
- metadata +37 -19
- data/test/test_lisp.rb +0 -65
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 600c4afdb71f1e0635249197b7d0eb7fa9c55a5dabf11bffa5e636f61858712e
|
4
|
+
data.tar.gz: c73dade15637b2226120e61b31d41ae23363fb25cc4ba462e302dc1c32822855
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 88191d156aba236ac005556a8002a24ab12f54129cb3aac6e82502dfa0741f320cf9e535299a0716e8fd2f41ce1f6f005b11d0a88c746a516d0951ae3d0188b4
|
7
|
+
data.tar.gz: 919dc08c1f74c65b878e734443d868a2cd006c6bda3a24d4d6c5850aeff1428da591da8d45b392509d4c238f780e86dc4450e7490535d0f3287d6fcee4144d44
|
@@ -0,0 +1,19 @@
|
|
1
|
+
name: Continuous Integration
|
2
|
+
|
3
|
+
on: [push, pull_request]
|
4
|
+
|
5
|
+
jobs:
|
6
|
+
build:
|
7
|
+
runs-on: ubuntu-latest
|
8
|
+
|
9
|
+
strategy:
|
10
|
+
matrix:
|
11
|
+
ruby: [ '2.3', '2.7' ]
|
12
|
+
|
13
|
+
steps:
|
14
|
+
- uses: actions/checkout@v2
|
15
|
+
- uses: ruby/setup-ruby@v1
|
16
|
+
with:
|
17
|
+
ruby-version: ${{ matrix.ruby }}
|
18
|
+
bundler-cache: true
|
19
|
+
- run: bundle exec rake
|
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,35 +1,55 @@
|
|
1
|
-
lisp
|
2
|
-
|
1
|
+
lisp
|
2
|
+
====
|
3
3
|
|
4
|
-
|
4
|
+
![Gem Version][3] ![Gem][1] ![Build Status][2]
|
5
5
|
|
6
|
-
Lisp
|
6
|
+
Minimal Lisp interpreter using 75LOC and only standard libraries excluding the REPL. 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
|
-
[](http://badge.fury.io/rb/lisp)
|
28
23
|
|
29
24
|
```
|
30
25
|
gem install lisp
|
31
26
|
```
|
32
27
|
|
28
|
+
Usage
|
29
|
+
-----
|
30
|
+
|
31
|
+
```clojure
|
32
|
+
require "lisp"
|
33
|
+
|
34
|
+
Lisp.eval(<<-eos)
|
35
|
+
(begin
|
36
|
+
(define fact
|
37
|
+
(lambda (n)
|
38
|
+
(if (<= n 1)
|
39
|
+
1
|
40
|
+
(* n (fact (- n 1))))))
|
41
|
+
(fact 10))
|
42
|
+
eos # => 3628800
|
43
|
+
```
|
44
|
+
|
45
|
+
Commandline
|
46
|
+
-----------
|
47
|
+
|
48
|
+
```
|
49
|
+
lisp-repl
|
50
|
+
```
|
51
|
+
|
52
|
+
|
33
53
|
Features
|
34
54
|
--------
|
35
55
|
|
@@ -50,3 +70,7 @@ Features
|
|
50
70
|
- [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
71
|
|
52
72
|
- [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_
|
73
|
+
|
74
|
+
[1]: https://img.shields.io/gem/dt/lisp
|
75
|
+
[2]: https://travis-ci.org/jamesmoriarty/lisp.svg?branch=master
|
76
|
+
[3]: https://img.shields.io/gem/v/lisp
|
data/Rakefile
CHANGED
@@ -1,7 +1,29 @@
|
|
1
1
|
require "bundler/gem_tasks"
|
2
|
+
require "rake/testtask"
|
3
|
+
require 'yard'
|
2
4
|
|
3
|
-
|
5
|
+
YARD::Rake::YardocTask.new do |t|
|
6
|
+
t.files = ['lib/**/*.rb', 'README', 'CHANGELOG', 'CODE_OF_CONDUCT']
|
7
|
+
t.options = []
|
8
|
+
t.stats_options = ['--list-undoc']
|
9
|
+
end
|
10
|
+
|
11
|
+
Rake::TestTask.new(:test) do |t|
|
12
|
+
t.libs << "test"
|
13
|
+
t.libs << "lib"
|
14
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
15
|
+
end
|
4
16
|
|
5
|
-
|
6
|
-
|
17
|
+
namespace :gh do
|
18
|
+
desc "Deploy yard docs to github pages"
|
19
|
+
task :pages => :yard do
|
20
|
+
`git add -f doc`
|
21
|
+
`git commit -am "update: $(date)"`
|
22
|
+
`git subtree split --prefix doc -b gh-pages`
|
23
|
+
`git push -f origin gh-pages:gh-pages`
|
24
|
+
`git branch -D gh-pages`
|
25
|
+
`git reset head~1`
|
26
|
+
end
|
7
27
|
end
|
28
|
+
|
29
|
+
task :default => :test
|
data/bin/{lisp-rb → lisp-repl}
RENAMED
data/lib/lisp.rb
CHANGED
@@ -1,76 +1,8 @@
|
|
1
|
-
|
1
|
+
$LOAD_PATH.unshift File.dirname(__FILE__)
|
2
2
|
|
3
3
|
require "lisp/version"
|
4
|
+
require "lisp/interpreter"
|
4
5
|
require "lisp/repl"
|
5
6
|
|
6
7
|
module Lisp
|
7
|
-
def self.eval(string)
|
8
|
-
execute(parse(tokenize(string)))
|
9
|
-
end
|
10
|
-
|
11
|
-
def self.tokenize(string)
|
12
|
-
string.gsub("("," ( ").gsub(")"," ) ").split
|
13
|
-
end
|
14
|
-
|
15
|
-
def self.parse(tokens, tree = [])
|
16
|
-
raise "unexpected: eof" if tokens.size.zero?
|
17
|
-
|
18
|
-
case token = tokens.shift
|
19
|
-
when "("
|
20
|
-
while tokens[0] != ")" do
|
21
|
-
tree.push parse(tokens)
|
22
|
-
end
|
23
|
-
tokens.shift
|
24
|
-
tree
|
25
|
-
when ")"
|
26
|
-
raise "unexpected: )"
|
27
|
-
else
|
28
|
-
evaluator(token)
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
def self.evaluator(token)
|
33
|
-
case token
|
34
|
-
when /\d/
|
35
|
-
token.to_f
|
36
|
-
else
|
37
|
-
token.to_sym
|
38
|
-
end
|
39
|
-
end
|
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
|
44
|
-
|
45
|
-
case exp[0]
|
46
|
-
when :define
|
47
|
-
_, var, exp = exp
|
48
|
-
scope[var] = execute(exp, scope)
|
49
|
-
when :lambda
|
50
|
-
_, params, exp = exp
|
51
|
-
lambda { |*args| execute(exp, scope.merge(Hash[params.zip(args)])) }
|
52
|
-
when :if
|
53
|
-
_, test, conseq, alt = exp
|
54
|
-
exp = execute(test, scope) ? conseq : alt
|
55
|
-
execute(exp, scope)
|
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
|
59
|
-
when :begin
|
60
|
-
_, *exp = exp
|
61
|
-
exp.map { |exp| execute(exp) }.last
|
62
|
-
else
|
63
|
-
func, *args = exp.map { |exp| execute(exp, scope) }
|
64
|
-
func.call(*args)
|
65
|
-
end
|
66
|
-
end
|
67
|
-
|
68
|
-
def self.global
|
69
|
-
@scope ||= begin
|
70
|
-
operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]
|
71
|
-
operators.inject({}) do |scope, operator|
|
72
|
-
scope.merge(operator => lambda { |*args| args.inject(&operator) })
|
73
|
-
end
|
74
|
-
end
|
75
|
-
end
|
76
8
|
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
module Lisp
|
2
|
+
class << self
|
3
|
+
def eval string
|
4
|
+
execute parse tokenize string
|
5
|
+
end
|
6
|
+
|
7
|
+
def tokenize string
|
8
|
+
string.gsub("(", " ( ").gsub(")", " ) ").split
|
9
|
+
end
|
10
|
+
|
11
|
+
def parse tokens, tree = []
|
12
|
+
raise "unexpected: eof" if tokens.size.zero?
|
13
|
+
|
14
|
+
case token = tokens.shift
|
15
|
+
when "("
|
16
|
+
while tokens[0] != ")" do
|
17
|
+
tree.push parse tokens
|
18
|
+
end
|
19
|
+
tokens.shift
|
20
|
+
tree
|
21
|
+
when ")"
|
22
|
+
raise "unexpected: )"
|
23
|
+
else
|
24
|
+
atom token
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def execute expression, scope = global
|
29
|
+
return scope.fetch(expression) { |var| raise "#{var} is undefined" } if expression.is_a? Symbol
|
30
|
+
return expression unless expression.is_a? Array
|
31
|
+
|
32
|
+
case expression[0]
|
33
|
+
when :define
|
34
|
+
_, var, expression = expression
|
35
|
+
scope[var] = execute expression, scope
|
36
|
+
when :lambda
|
37
|
+
_, params, expression = expression
|
38
|
+
lambda { |*args| execute expression, scope.merge(Hash[params.zip(args)]) }
|
39
|
+
when :if
|
40
|
+
_, test, consequent, alternative = expression
|
41
|
+
expression = if execute test, scope then consequent else alternative end
|
42
|
+
execute expression, scope
|
43
|
+
when :set!
|
44
|
+
_, var, expression = expression
|
45
|
+
if scope.has_key?(var) then scope[var] = execute expression, scope else raise "#{var} is undefined" end
|
46
|
+
when :begin
|
47
|
+
_, *expression = expression
|
48
|
+
expression.map { |expression| execute expression, scope }.last
|
49
|
+
else
|
50
|
+
function, *args = expression.map { |expression| execute expression, scope }
|
51
|
+
function.call *args
|
52
|
+
end
|
53
|
+
end
|
54
|
+
|
55
|
+
private
|
56
|
+
|
57
|
+
def atom token
|
58
|
+
case token
|
59
|
+
when /^[\p{N}\.]+$/
|
60
|
+
token.to_f % 1 > 0 ? token.to_f : token.to_i
|
61
|
+
else
|
62
|
+
token.to_sym
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
def global
|
67
|
+
operators = [:==, :"!=", :"<", :"<=", :">", :">=", :+, :-, :*, :/]
|
68
|
+
|
69
|
+
operators.inject({}) do |scope, operator|
|
70
|
+
scope.merge operator => lambda { |*args| args.inject &operator }
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/lib/lisp/repl.rb
CHANGED
@@ -1,22 +1,66 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
1
|
require "coolline"
|
2
|
+
require "coderay"
|
3
3
|
|
4
4
|
module Lisp
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
rescue Exception => e
|
13
|
-
e.message
|
5
|
+
class REPL
|
6
|
+
attr_reader :io, :input
|
7
|
+
|
8
|
+
def initialize
|
9
|
+
@io = Coolline.new do |config|
|
10
|
+
config.transform_proc = proc do
|
11
|
+
CodeRay.scan(config.line, :clojure).term
|
14
12
|
end
|
15
13
|
end
|
14
|
+
|
15
|
+
reset_input!
|
16
|
+
end
|
17
|
+
|
18
|
+
def run
|
19
|
+
trap("SIGINT") { throw :exit }
|
20
|
+
|
21
|
+
puts "ctrl-c to exit"
|
22
|
+
|
23
|
+
catch(:exit) do
|
24
|
+
loop do
|
25
|
+
begin
|
26
|
+
@input += io.readline prompt
|
27
|
+
|
28
|
+
if open_count == close_count
|
29
|
+
puts Lisp.eval input
|
30
|
+
|
31
|
+
reset_input!
|
32
|
+
end
|
33
|
+
rescue Exception => e
|
34
|
+
puts e.message
|
35
|
+
|
36
|
+
reset_input!
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
private
|
43
|
+
|
44
|
+
def prompt
|
45
|
+
count = open_count - close_count
|
46
|
+
char = count > 0 ? ?( : ?)
|
47
|
+
"#{char * count}> "
|
48
|
+
end
|
49
|
+
|
50
|
+
def reset_input!
|
51
|
+
@input = String.new
|
16
52
|
end
|
17
|
-
end
|
18
|
-
end
|
19
53
|
|
20
|
-
|
21
|
-
|
54
|
+
def open_count
|
55
|
+
tokens.count(?()
|
56
|
+
end
|
57
|
+
|
58
|
+
def close_count
|
59
|
+
tokens.count(?))
|
60
|
+
end
|
61
|
+
|
62
|
+
def tokens
|
63
|
+
Lisp.tokenize input
|
64
|
+
end
|
65
|
+
end
|
22
66
|
end
|
data/lib/lisp/version.rb
CHANGED
data/lisp.gemspec
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
# coding: utf-8
|
2
|
+
|
2
3
|
lib = File.expand_path('../lib', __FILE__)
|
3
4
|
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
5
|
require 'lisp/version'
|
@@ -8,9 +9,9 @@ Gem::Specification.new do |spec|
|
|
8
9
|
spec.version = Lisp::VERSION
|
9
10
|
spec.authors = ["James Moriarty"]
|
10
11
|
spec.email = ["jamespaulmoriarty@gmail.com"]
|
11
|
-
spec.summary = %q{Lisp
|
12
|
-
spec.description = ""
|
13
|
-
spec.homepage = ""
|
12
|
+
spec.summary = %q{Lisp interpreter}
|
13
|
+
spec.description = "Minimal Lisp interpreter using 75LOC and only standard libraries excluding the REPL. Inspired by Lis.py."
|
14
|
+
spec.homepage = "https://github.com/jamesmoriarty/lisp"
|
14
15
|
spec.license = "MIT"
|
15
16
|
|
16
17
|
spec.files = `git ls-files -z`.split("\x0")
|
@@ -19,7 +20,8 @@ Gem::Specification.new do |spec|
|
|
19
20
|
spec.require_paths = ["lib"]
|
20
21
|
|
21
22
|
spec.add_dependency "coolline"
|
22
|
-
spec.
|
23
|
+
spec.add_dependency "coderay"
|
24
|
+
spec.add_development_dependency "bundler"
|
23
25
|
spec.add_development_dependency "rake"
|
24
|
-
spec.add_development_dependency "
|
26
|
+
spec.add_development_dependency "minitest", "> 5"
|
25
27
|
end
|
data/test/lisp_test.rb
ADDED
@@ -0,0 +1,144 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
require "bundler/setup"
|
3
|
+
|
4
|
+
Dir[File.expand_path(File.join(File.dirname(__FILE__), 'support', '*.rb'))].each { |file| require file }
|
5
|
+
|
6
|
+
require "lisp"
|
7
|
+
require "minitest/autorun"
|
8
|
+
|
9
|
+
class TestLisp < MiniTest::Unit::TestCase
|
10
|
+
|
11
|
+
# parser
|
12
|
+
|
13
|
+
def test_tokenize
|
14
|
+
assert_equal ["(", "+", "1", "1", ")"], Lisp.tokenize("(+ 1 1)")
|
15
|
+
end
|
16
|
+
|
17
|
+
def test_parse_open
|
18
|
+
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize("(")) }
|
19
|
+
end
|
20
|
+
|
21
|
+
def test_parse_close
|
22
|
+
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize(")")) }
|
23
|
+
end
|
24
|
+
|
25
|
+
# ast
|
26
|
+
|
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
|
32
|
+
assert_equal [:lambda, [:r], [:*, 3.141592653, [:*, :r, :r]]],
|
33
|
+
Lisp.parse(Lisp.tokenize("(lambda (r) (* 3.141592653 (* r r)))"))
|
34
|
+
end
|
35
|
+
|
36
|
+
# execution
|
37
|
+
|
38
|
+
def test_execution_return
|
39
|
+
assert_equal 1, Lisp.execute(1)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_execution_reduce
|
43
|
+
assert_equal 2, Lisp.execute([:*, 2, [:+, 1, 0]])
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_eval
|
47
|
+
assert_equal 2, Lisp.eval("(* 2 (+ 1 0) )")
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_define
|
51
|
+
assert_equal 6.283185306, Lisp.eval(<<-eos)
|
52
|
+
(begin
|
53
|
+
(define pi 3.141592653)
|
54
|
+
(* pi 2))
|
55
|
+
eos
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_if_cont
|
59
|
+
assert_equal 2, Lisp.eval("(if(== 1 2) 1 2)")
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_if_alt
|
63
|
+
assert_equal 1, Lisp.eval("(if(!= 1 2) 1 2)")
|
64
|
+
end
|
65
|
+
|
66
|
+
def test_set!
|
67
|
+
assert_raises(RuntimeError) { Lisp.eval("(set! foo 0)") }
|
68
|
+
end
|
69
|
+
|
70
|
+
def test_begin
|
71
|
+
assert_equal 4, Lisp.eval(<<-eos)
|
72
|
+
(begin
|
73
|
+
(define x 1)
|
74
|
+
(set! x (+ x 1)) (* x 2))
|
75
|
+
eos
|
76
|
+
end
|
77
|
+
|
78
|
+
def test_lambda
|
79
|
+
assert_equal 28.274333877, Lisp.eval(<<-eos)
|
80
|
+
(begin
|
81
|
+
(define area
|
82
|
+
(lambda (r)
|
83
|
+
(* 3.141592653 (* r r))))
|
84
|
+
(area 3))
|
85
|
+
eos
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_lambda_no_args
|
89
|
+
assert_equal 42, Lisp.eval(<<-eos)
|
90
|
+
(begin
|
91
|
+
(define test
|
92
|
+
(lambda () 42))
|
93
|
+
(test))
|
94
|
+
eos
|
95
|
+
end
|
96
|
+
|
97
|
+
def test_lambda_call_self
|
98
|
+
assert_equal 3628800, Lisp.eval(<<-eos)
|
99
|
+
(begin
|
100
|
+
(define fact
|
101
|
+
(lambda (n)
|
102
|
+
(if (<= n 1)
|
103
|
+
1
|
104
|
+
(* n (fact (- n 1))))))
|
105
|
+
(fact 10))
|
106
|
+
eos
|
107
|
+
end
|
108
|
+
|
109
|
+
def test_lambda_call_arg
|
110
|
+
assert_equal 40, Lisp.eval(<<-eos)
|
111
|
+
(begin
|
112
|
+
(define twice (lambda (x) (* 2 x)))
|
113
|
+
(define repeat (lambda (f) (lambda (x) (f (f x)))))
|
114
|
+
((repeat twice) 10)))
|
115
|
+
eos
|
116
|
+
end
|
117
|
+
|
118
|
+
def test_program
|
119
|
+
assert_equal 2, Lisp.eval(<<-eos)
|
120
|
+
(begin
|
121
|
+
(define incf
|
122
|
+
(lambda (x)
|
123
|
+
(set! x (+ x 1))))
|
124
|
+
(define one 1)
|
125
|
+
(incf one))
|
126
|
+
eos
|
127
|
+
end
|
128
|
+
|
129
|
+
def test_repl
|
130
|
+
skip if ENV['CI']
|
131
|
+
|
132
|
+
pid = Process.pid
|
133
|
+
subject = Lisp::REPL.new
|
134
|
+
thread = Thread.new do
|
135
|
+
sleep 1
|
136
|
+
Process.kill("INT", pid)
|
137
|
+
thread.join
|
138
|
+
end
|
139
|
+
|
140
|
+
assert_output("ctrl-c to exit\n") do
|
141
|
+
subject.run
|
142
|
+
end
|
143
|
+
end
|
144
|
+
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.2
|
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: 2021-05-26 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,26 +66,44 @@ dependencies:
|
|
66
66
|
- - ">="
|
67
67
|
- !ruby/object:Gem::Version
|
68
68
|
version: '0'
|
69
|
-
|
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'
|
83
|
+
description: Minimal Lisp interpreter using 75LOC and only standard libraries excluding
|
84
|
+
the REPL. Inspired by Lis.py.
|
70
85
|
email:
|
71
86
|
- jamespaulmoriarty@gmail.com
|
72
87
|
executables:
|
73
|
-
- lisp-
|
88
|
+
- lisp-repl
|
74
89
|
extensions: []
|
75
90
|
extra_rdoc_files: []
|
76
91
|
files:
|
92
|
+
- ".github/workflows/ci.yaml"
|
77
93
|
- ".gitignore"
|
78
94
|
- Gemfile
|
79
95
|
- LICENSE.txt
|
80
96
|
- README.md
|
81
97
|
- Rakefile
|
82
|
-
- bin/lisp-
|
98
|
+
- bin/lisp-repl
|
83
99
|
- lib/lisp.rb
|
100
|
+
- lib/lisp/interpreter.rb
|
84
101
|
- lib/lisp/repl.rb
|
85
102
|
- lib/lisp/version.rb
|
86
103
|
- lisp.gemspec
|
87
|
-
- test/
|
88
|
-
|
104
|
+
- test/lisp_test.rb
|
105
|
+
- test/support/simplecov.rb
|
106
|
+
homepage: https://github.com/jamesmoriarty/lisp
|
89
107
|
licenses:
|
90
108
|
- MIT
|
91
109
|
metadata: {}
|
@@ -104,10 +122,10 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
104
122
|
- !ruby/object:Gem::Version
|
105
123
|
version: '0'
|
106
124
|
requirements: []
|
107
|
-
|
108
|
-
rubygems_version: 2.2.2
|
125
|
+
rubygems_version: 3.0.3
|
109
126
|
signing_key:
|
110
127
|
specification_version: 4
|
111
|
-
summary: Lisp
|
128
|
+
summary: Lisp interpreter
|
112
129
|
test_files:
|
113
|
-
- test/
|
130
|
+
- test/lisp_test.rb
|
131
|
+
- test/support/simplecov.rb
|
data/test/test_lisp.rb
DELETED
@@ -1,65 +0,0 @@
|
|
1
|
-
#!/usr/bin/env ruby
|
2
|
-
require "bundler/setup"
|
3
|
-
require "codeclimate-test-reporter"
|
4
|
-
CodeClimate::TestReporter.start
|
5
|
-
require "minitest/autorun"
|
6
|
-
require "pry"
|
7
|
-
|
8
|
-
require_relative "../lib/lisp"
|
9
|
-
|
10
|
-
class TestLisp < MiniTest::Unit::TestCase
|
11
|
-
|
12
|
-
# parser
|
13
|
-
|
14
|
-
def test_tokenize
|
15
|
-
assert_equal ["(", "+", "1", "1", ")"], Lisp.tokenize("(+ 1 1)")
|
16
|
-
end
|
17
|
-
|
18
|
-
def test_parse
|
19
|
-
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize("(")) }
|
20
|
-
assert_raises(RuntimeError) { Lisp.parse(Lisp.tokenize(")")) }
|
21
|
-
end
|
22
|
-
|
23
|
-
# representation
|
24
|
-
|
25
|
-
def test_representation
|
26
|
-
assert_equal [:*, 2, [:+, 1, 0]], Lisp.parse(Lisp.tokenize("(* 2 (+ 1 0) )"))
|
27
|
-
assert_equal [:lambda, [:r], [:*, 3.141592653, [:*, :r, :r]]], Lisp.parse(Lisp.tokenize("(lambda (r) (* 3.141592653 (* r r)))"))
|
28
|
-
end
|
29
|
-
|
30
|
-
# execution
|
31
|
-
|
32
|
-
def test_execution
|
33
|
-
assert_equal 1, Lisp.execute(1)
|
34
|
-
assert_equal 2, Lisp.execute([:*, 2, [:+, 1, 0]])
|
35
|
-
end
|
36
|
-
|
37
|
-
def test_eval
|
38
|
-
assert_equal 2, Lisp.eval("(* 2 (+ 1 0) )")
|
39
|
-
end
|
40
|
-
|
41
|
-
def test_define
|
42
|
-
Lisp.eval("(define pi 3.141592653)")
|
43
|
-
assert_equal 6.283185306, Lisp.eval("(* pi 2)")
|
44
|
-
end
|
45
|
-
|
46
|
-
def test_if
|
47
|
-
assert_equal 2, Lisp.eval("(if(== 1 2) 1 2)")
|
48
|
-
assert_equal 1, Lisp.eval("(if(!= 1 2) 1 2)")
|
49
|
-
end
|
50
|
-
|
51
|
-
def test_set!
|
52
|
-
assert_raises(RuntimeError) { Lisp.eval("(set! foo 0)") }
|
53
|
-
end
|
54
|
-
|
55
|
-
def test_begin
|
56
|
-
assert_equal 4, Lisp.eval("(begin (define x 1) (set! x (+ x 1)) (* x 2))")
|
57
|
-
end
|
58
|
-
|
59
|
-
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)")
|
64
|
-
end
|
65
|
-
end
|