lisp 1.3.0 → 1.5.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|
-
[![Gem Version](https://badge.fury.io/rb/lisp.svg)](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
|