nydp 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/.gitignore +18 -0
- data/.rspec +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +58 -0
- data/Rakefile +1 -0
- data/bin/nydp +5 -0
- data/bin/nydp-tests +5 -0
- data/lib/lisp/boot.nydp +219 -0
- data/lib/lisp/test-runner.nydp +39 -0
- data/lib/lisp/tests/foundation-test.nydp +28 -0
- data/lib/nydp.rb +143 -0
- data/lib/nydp/assignment.rb +40 -0
- data/lib/nydp/builtin.rb +8 -0
- data/lib/nydp/builtin/apply.rb +16 -0
- data/lib/nydp/builtin/car.rb +5 -0
- data/lib/nydp/builtin/cdr.rb +5 -0
- data/lib/nydp/builtin/cdr_set.rb +8 -0
- data/lib/nydp/builtin/comment.rb +5 -0
- data/lib/nydp/builtin/cons.rb +16 -0
- data/lib/nydp/builtin/divide.rb +13 -0
- data/lib/nydp/builtin/error.rb +6 -0
- data/lib/nydp/builtin/eval.rb +14 -0
- data/lib/nydp/builtin/greater_than.rb +10 -0
- data/lib/nydp/builtin/hash.rb +30 -0
- data/lib/nydp/builtin/inspect.rb +5 -0
- data/lib/nydp/builtin/is_equal.rb +5 -0
- data/lib/nydp/builtin/less_than.rb +10 -0
- data/lib/nydp/builtin/millisecs.rb +7 -0
- data/lib/nydp/builtin/minus.rb +13 -0
- data/lib/nydp/builtin/plus.rb +28 -0
- data/lib/nydp/builtin/pre_compile.rb +5 -0
- data/lib/nydp/builtin/puts.rb +7 -0
- data/lib/nydp/builtin/quit.rb +5 -0
- data/lib/nydp/builtin/random_string.rb +11 -0
- data/lib/nydp/builtin/times.rb +13 -0
- data/lib/nydp/builtin/to_string.rb +12 -0
- data/lib/nydp/builtin/to_sym.rb +21 -0
- data/lib/nydp/builtin/vm_info.rb +13 -0
- data/lib/nydp/closure.rb +17 -0
- data/lib/nydp/compiler.rb +49 -0
- data/lib/nydp/cond.rb +56 -0
- data/lib/nydp/context_symbol.rb +22 -0
- data/lib/nydp/error.rb +4 -0
- data/lib/nydp/function_invocation.rb +52 -0
- data/lib/nydp/helper.rb +32 -0
- data/lib/nydp/interpreted_function.rb +79 -0
- data/lib/nydp/lexical_context.rb +38 -0
- data/lib/nydp/literal.rb +40 -0
- data/lib/nydp/pair.rb +112 -0
- data/lib/nydp/parser.rb +123 -0
- data/lib/nydp/string_atom.rb +24 -0
- data/lib/nydp/string_token.rb +21 -0
- data/lib/nydp/symbol.rb +47 -0
- data/lib/nydp/symbol_lookup.rb +43 -0
- data/lib/nydp/tokeniser.rb +80 -0
- data/lib/nydp/truth.rb +37 -0
- data/lib/nydp/version.rb +3 -0
- data/lib/nydp/vm.rb +76 -0
- data/nydp.gemspec +27 -0
- data/spec/boot_spec.rb +119 -0
- data/spec/embedded_spec.rb +106 -0
- data/spec/nypd_spec.rb +127 -0
- data/spec/pair_spec.rb +102 -0
- data/spec/parser_spec.rb +191 -0
- data/spec/spec_helper.rb +10 -0
- data/spec/symbol_spec.rb +32 -0
- metadata +176 -0
@@ -0,0 +1,106 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nydp::Parser do
|
4
|
+
|
5
|
+
let(:ns) { { } }
|
6
|
+
let(:aa) { Nydp::Symbol.mk :aa, ns }
|
7
|
+
let(:a) { Nydp::Symbol.mk :a, ns }
|
8
|
+
let(:b) { Nydp::Symbol.mk :b, ns }
|
9
|
+
let(:c) { Nydp::Symbol.mk :c, ns }
|
10
|
+
let(:d) { Nydp::Symbol.mk :d, ns }
|
11
|
+
let(:zz) { Nydp::Symbol.mk :zz, ns }
|
12
|
+
let(:foo) { Nydp::Symbol.mk :foo, ns }
|
13
|
+
let(:bar) { Nydp::Symbol.mk :bar, ns }
|
14
|
+
let(:zab) { Nydp::Symbol.mk :zab, ns }
|
15
|
+
let(:quote) { Nydp::Symbol.mk :quote, ns }
|
16
|
+
let(:quasiquote) { Nydp::Symbol.mk :quasiquote, ns }
|
17
|
+
let(:unquote) { Nydp::Symbol.mk :unquote, ns }
|
18
|
+
let(:unquote_splicing) { Nydp::Symbol.mk :"unquote-splicing", ns }
|
19
|
+
let(:comment) { Nydp::Symbol.mk :comment, ns }
|
20
|
+
let(:dotsyn) { Nydp::Symbol.mk :"dot-syntax", ns }
|
21
|
+
let(:cocosyn) { Nydp::Symbol.mk :"colon-colon-syntax", ns }
|
22
|
+
let(:colosyn) { Nydp::Symbol.mk :"colon-syntax", ns }
|
23
|
+
|
24
|
+
def sym name
|
25
|
+
Nydp::Symbol.mk name.to_sym, ns
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse_string txt, open_delim, close_delim
|
29
|
+
Nydp::Parser.new(ns).string(Nydp::Tokeniser.new(txt), open_delim, close_delim)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pair_list xs, last=Nydp.NIL
|
33
|
+
Nydp::Pair.from_list xs, last
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should parse empty string" do
|
37
|
+
expected = pair_list([sym('string-pieces'), Nydp::StringFragmentCloseToken.new('','$%')])
|
38
|
+
actual = parse_string "%", '$', /%/
|
39
|
+
expect(actual).to eq ''
|
40
|
+
end
|
41
|
+
|
42
|
+
it "should parse external text" do
|
43
|
+
actual = parse_string "a fluffy bunny!", 'EAT ', /!/
|
44
|
+
expect(actual) .to eq "a fluffy bunny"
|
45
|
+
expect(actual.inspect).to eq "EAT a fluffy bunny!"
|
46
|
+
end
|
47
|
+
|
48
|
+
it "should parse a string delimited by eof" do
|
49
|
+
expected = pair_list([sym('string-pieces'), Nydp::StringFragmentCloseToken.new('a fluffy bunny!','a fluffy bunny!')])
|
50
|
+
actual = parse_string "a fluffy bunny!", '', :eof
|
51
|
+
expect(actual) .to eq "a fluffy bunny!"
|
52
|
+
expect(actual.inspect).to eq "a fluffy bunny!"
|
53
|
+
end
|
54
|
+
|
55
|
+
it "should parse a string with embedded code, delimited by eof" do
|
56
|
+
x1 = sym('string-pieces')
|
57
|
+
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ',':a fluffy bunny! %%')
|
58
|
+
x3 = sym('expr')
|
59
|
+
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
60
|
+
|
61
|
+
expected = pair_list([x1,x2,x3,x4])
|
62
|
+
actual = parse_string "a fluffy bunny! %%expr a purple cow!", ':', :eof
|
63
|
+
expect(actual).to eq expected
|
64
|
+
end
|
65
|
+
|
66
|
+
it "should parse a string with embedded code containing a nested string, delimited by eof" do
|
67
|
+
n1 = sym(:foo)
|
68
|
+
n2 = sym(:bar)
|
69
|
+
n3 = 'an embedded bunny :)'
|
70
|
+
n4 = sym(:zop)
|
71
|
+
|
72
|
+
x1 = sym('string-pieces')
|
73
|
+
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ','------->a fluffy bunny! %%')
|
74
|
+
x3 = pair_list [n1, n2, n3, n4]
|
75
|
+
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
76
|
+
|
77
|
+
expected = pair_list([x1,x2,x3,x4])
|
78
|
+
actual = parse_string "a fluffy bunny! %%(foo bar \"an embedded bunny :)\" zop) a purple cow!", '------->', :eof
|
79
|
+
expect(actual).to eq expected
|
80
|
+
end
|
81
|
+
|
82
|
+
it "should parse a string with embedded code containing a nested string containing more embedded code, delimited by eof" do
|
83
|
+
e1 = sym(:describe)
|
84
|
+
e2 = sym(:bunny)
|
85
|
+
|
86
|
+
s1 = sym('string-pieces')
|
87
|
+
s2 = Nydp::StringFragmentToken.new('a rather ','"a rather %%')
|
88
|
+
s3 = pair_list [e1, e2]
|
89
|
+
s4 = Nydp::StringFragmentCloseToken.new(' bunny :)',' bunny :)"')
|
90
|
+
|
91
|
+
n1 = sym(:foo)
|
92
|
+
n2 = sym(:bar)
|
93
|
+
n3 = pair_list [s1, s2, s3, s4]
|
94
|
+
n4 = sym(:zop)
|
95
|
+
|
96
|
+
x1 = sym('string-pieces')
|
97
|
+
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ','------->a fluffy bunny! %%')
|
98
|
+
x3 = pair_list [n1, n2, n3, n4]
|
99
|
+
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
100
|
+
|
101
|
+
expected = pair_list([x1,x2,x3,x4])
|
102
|
+
actual = parse_string "a fluffy bunny! %%(foo bar \"a rather %%(describe bunny) bunny :)\" zop) a purple cow!", '------->', :eof
|
103
|
+
expect(actual).to eq expected
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
data/spec/nypd_spec.rb
ADDED
@@ -0,0 +1,127 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nydp do
|
4
|
+
let(:root_ns) { { } }
|
5
|
+
let(:parser) { Nydp::Parser.new(root_ns) }
|
6
|
+
let(:vm) { Nydp::VM.new }
|
7
|
+
|
8
|
+
def sym name
|
9
|
+
Nydp::Symbol.mk name.to_sym, root_ns
|
10
|
+
end
|
11
|
+
|
12
|
+
def parse txt
|
13
|
+
tokens = Nydp::Tokeniser.new txt
|
14
|
+
expressions = []
|
15
|
+
expr = parser.expression(tokens)
|
16
|
+
while (expr != nil)
|
17
|
+
expressions << expr
|
18
|
+
expr = parser.expression(tokens)
|
19
|
+
end
|
20
|
+
expressions
|
21
|
+
end
|
22
|
+
|
23
|
+
def run txt
|
24
|
+
Nydp.setup root_ns
|
25
|
+
expressions = parse(txt)
|
26
|
+
result = nil
|
27
|
+
expressions.each do |expr|
|
28
|
+
result = Nydp.compile_and_eval vm, expr
|
29
|
+
end
|
30
|
+
result
|
31
|
+
end
|
32
|
+
|
33
|
+
it "should make a symbol from a string" do
|
34
|
+
expect(run '(sym "the-family")').to eq sym(:"the-family")
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should sum integers" do
|
38
|
+
expect(run "(+ 1 2)").to eq 3
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should add strings" do
|
42
|
+
expect(run '(+ "hello" " " "world")').to eq "hello world"
|
43
|
+
end
|
44
|
+
|
45
|
+
it "should add Pairs" do
|
46
|
+
expect(run "(+ '(a b) '(c d))").to eq Nydp::Pair.from_list([sym(:a), sym(:b), sym(:c), sym(:d)])
|
47
|
+
end
|
48
|
+
|
49
|
+
it "should add Pairs without recursing" do
|
50
|
+
alist = Nydp::Pair.from_list([sym(:a), sym(:a)])
|
51
|
+
blist = Nydp::Pair.from_list([sym(:b), sym(:b)])
|
52
|
+
clist = Nydp::Pair.from_list([sym(:c), sym(:c)])
|
53
|
+
dlist = Nydp::Pair.from_list([sym(:d), sym(:d)])
|
54
|
+
expect(run "(+ '((a a) (b b)) '((c c) (d d)))").to eq Nydp::Pair.from_list([alist, blist, clist, dlist])
|
55
|
+
end
|
56
|
+
|
57
|
+
it "should diff integers" do
|
58
|
+
expect(run "(- 144 121)").to eq 23
|
59
|
+
end
|
60
|
+
|
61
|
+
it "should multiply integers" do
|
62
|
+
expect(run "(* 7 11)").to eq 77
|
63
|
+
end
|
64
|
+
|
65
|
+
it "should convert items to strings" do
|
66
|
+
expect(run "(to-string 3.1415)").to eq "3.1415"
|
67
|
+
end
|
68
|
+
|
69
|
+
it "should compare integers" do
|
70
|
+
expect(run "(> 13 17)").to eq Nydp.NIL
|
71
|
+
expect(run "(> 29 23)").to eq Nydp.T
|
72
|
+
expect(run "(< 13 17)").to eq Nydp.T
|
73
|
+
expect(run "(< 29 23)").to eq Nydp.NIL
|
74
|
+
end
|
75
|
+
|
76
|
+
it "should execute an inline list function" do
|
77
|
+
expected = Nydp::Pair.from_list (1..3).to_a
|
78
|
+
expect(run "((fn a a) 1 2 3)").to eq expected
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should execute an inline sum function" do
|
82
|
+
expect(run "((fn (a b) (+ a b)) 9 16)").to eq 25
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should assign a function to a global variable and execute it" do
|
86
|
+
f1 = "(fn (a b) (+ a b))"
|
87
|
+
f2 = "(assign f1 #{f1})"
|
88
|
+
f3 = "(f1 36 64)"
|
89
|
+
result = run "#{f2} #{f3}"
|
90
|
+
expect(result).to eq 100
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should call functions in a chain" do
|
94
|
+
f1 = "(fn (a b) (+ a b))"
|
95
|
+
f2 = "(assign f1 #{f1})"
|
96
|
+
f3 = "(fn (x y) (* x y))"
|
97
|
+
f4 = "(assign f3 #{f3})"
|
98
|
+
f5 = "(f1 (f3 6 6) (f3 8 8))"
|
99
|
+
result = run "#{f2} #{f4} #{f5}"
|
100
|
+
expect(result).to eq 100
|
101
|
+
end
|
102
|
+
|
103
|
+
it "should recurse without consuming extra memory" do
|
104
|
+
program = "(assign f1 (fn (x acc) (cond (< x 1) (vm-info) (f1 (- x 1) (+ x acc))))) (f1 1000)"
|
105
|
+
expected = parse "((contexts . 0) (instructions . 0) (args . 0))"
|
106
|
+
expect(run program).to eq expected.first
|
107
|
+
end
|
108
|
+
|
109
|
+
describe :cond do
|
110
|
+
it "should execute false conditionals" do
|
111
|
+
cond = "(cond (> 31 37) 'foo 'bar)"
|
112
|
+
expect(run cond).to eq sym(:bar)
|
113
|
+
end
|
114
|
+
|
115
|
+
it "should execute conditionals" do
|
116
|
+
cond = "(cond (> 37 31) 'foo 'bar)"
|
117
|
+
expect(run cond).to eq sym(:foo)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
describe "eval" do
|
122
|
+
it "should eval the given expression and return the result" do
|
123
|
+
code = "(eval '(+ 2 (* 3 5)))"
|
124
|
+
expect(run code).to eq 17
|
125
|
+
end
|
126
|
+
end
|
127
|
+
end
|
data/spec/pair_spec.rb
ADDED
@@ -0,0 +1,102 @@
|
|
1
|
+
require "spec_helper"
|
2
|
+
|
3
|
+
describe Nydp::Pair do
|
4
|
+
let(:ns) { { } }
|
5
|
+
let(:a) { Nydp::Symbol.mk :a, ns }
|
6
|
+
let(:b) { Nydp::Symbol.mk :b, ns }
|
7
|
+
let(:c) { Nydp::Symbol.mk :c, ns }
|
8
|
+
let(:d) { Nydp::Symbol.mk :d, ns }
|
9
|
+
let(:foo) { Nydp::Symbol.mk :foo, ns }
|
10
|
+
let(:dot) { Nydp::Symbol.mk ".".to_sym, ns }
|
11
|
+
|
12
|
+
describe :== do
|
13
|
+
it "should be true for two empty lists" do
|
14
|
+
expect(Nydp::Pair.new(NIL, NIL)).to eq Nydp::Pair.new(NIL, NIL)
|
15
|
+
end
|
16
|
+
|
17
|
+
it "should be true for nested empty lists" do
|
18
|
+
e1 = Nydp::Pair.new(Nydp.NIL, Nydp.NIL)
|
19
|
+
e2 = Nydp::Pair.new(Nydp.NIL, Nydp.NIL)
|
20
|
+
e3 = Nydp::Pair.new(Nydp.NIL, Nydp.NIL)
|
21
|
+
e4 = Nydp::Pair.new(Nydp.NIL, Nydp.NIL)
|
22
|
+
expect(Nydp::Pair.new(e1, e2)).to eq Nydp::Pair.new(e3, e4)
|
23
|
+
end
|
24
|
+
|
25
|
+
it "should define #== to return true for an identical list" do
|
26
|
+
p1 = Nydp::Pair.from_list [:a, :b, :c, :d]
|
27
|
+
p2 = Nydp::Pair.from_list [:a, :b, :c, :d]
|
28
|
+
expect(p1).to eq p2
|
29
|
+
end
|
30
|
+
|
31
|
+
it "should define #== to return true for identical improper lists" do
|
32
|
+
p1 = Nydp::Pair.from_list [:a, :b, :c, :d], 4
|
33
|
+
p2 = Nydp::Pair.from_list [:a, :b, :c, :d], 4
|
34
|
+
expect(p1).to eq p2
|
35
|
+
end
|
36
|
+
|
37
|
+
it "should define #== to return false for a non-identical list" do
|
38
|
+
p1 = Nydp::Pair.from_list [:a, :b, :c, :d]
|
39
|
+
p2 = Nydp::Pair.from_list [:a, :b, :c, 22]
|
40
|
+
expect(p1).not_to eq p2
|
41
|
+
end
|
42
|
+
|
43
|
+
it "should define #== to return false for lists which differ only in their terminating element" do
|
44
|
+
p1 = Nydp::Pair.from_list [:a, :b, :c], :d
|
45
|
+
p2 = Nydp::Pair.from_list [:a, :b, :c], 22
|
46
|
+
expect(p1).not_to eq p2
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
it "should create a new pair" do
|
51
|
+
p = Nydp::Pair.mk :a, :b
|
52
|
+
expect(p.car).to eq :a
|
53
|
+
expect(p.cdr).to eq :b
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should convert a ruby list" do
|
57
|
+
p = Nydp::Pair.from_list [:a, :b, :c, :d]
|
58
|
+
expect(p.car).to eq :a
|
59
|
+
p = p.cdr
|
60
|
+
expect(p.car).to eq :b
|
61
|
+
p = p.cdr
|
62
|
+
expect(p.car).to eq :c
|
63
|
+
p = p.cdr
|
64
|
+
expect(p.car).to eq :d
|
65
|
+
p = p.cdr
|
66
|
+
expect(p.car).to eq Nydp.NIL
|
67
|
+
expect(p.cdr).to eq Nydp.NIL
|
68
|
+
p = p.cdr
|
69
|
+
expect(p.car).to eq Nydp.NIL
|
70
|
+
expect(p.cdr).to eq Nydp.NIL
|
71
|
+
end
|
72
|
+
|
73
|
+
it "should have size zero when empty" do
|
74
|
+
expect(Nydp::Pair.from_list([]).size).to eq 0
|
75
|
+
end
|
76
|
+
|
77
|
+
it "should report the number of elements is contains" do
|
78
|
+
expect(Nydp::Pair.from_list([:a, :b, :c]).size).to eq 3
|
79
|
+
end
|
80
|
+
|
81
|
+
it "should report the number of elements in an improper list, excluding last item" do
|
82
|
+
expect(Nydp::Pair.from_list([:a, :b, :c, :d], :foo).size).to eq 4
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should represent itself as a string" do
|
86
|
+
expect(Nydp::Pair.from_list([a, b, c, d]).to_s).to eq "(a b c d)"
|
87
|
+
end
|
88
|
+
|
89
|
+
it "should represent an improper list as a string" do
|
90
|
+
expect(Nydp::Pair.from_list([a, b, c, d], foo).to_s).to eq "(a b c d . foo)"
|
91
|
+
end
|
92
|
+
|
93
|
+
it "should parse a list" do
|
94
|
+
p = Nydp::Pair.from_list [a, b, c, d]
|
95
|
+
expect(Nydp::Pair.parse_list([a, b, c, d])).to eq p
|
96
|
+
end
|
97
|
+
|
98
|
+
it "should parse a list" do
|
99
|
+
p = Nydp::Pair.from_list [a, b, c, d], foo
|
100
|
+
expect(Nydp::Pair.parse_list([a, b, c, d, dot, foo])).to eq p
|
101
|
+
end
|
102
|
+
end
|
data/spec/parser_spec.rb
ADDED
@@ -0,0 +1,191 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Nydp::Parser do
|
4
|
+
|
5
|
+
let(:ns) { { } }
|
6
|
+
let(:aa) { Nydp::Symbol.mk :aa, ns }
|
7
|
+
let(:a) { Nydp::Symbol.mk :a, ns }
|
8
|
+
let(:b) { Nydp::Symbol.mk :b, ns }
|
9
|
+
let(:c) { Nydp::Symbol.mk :c, ns }
|
10
|
+
let(:d) { Nydp::Symbol.mk :d, ns }
|
11
|
+
let(:zz) { Nydp::Symbol.mk :zz, ns }
|
12
|
+
let(:foo) { Nydp::Symbol.mk :foo, ns }
|
13
|
+
let(:bar) { Nydp::Symbol.mk :bar, ns }
|
14
|
+
let(:zab) { Nydp::Symbol.mk :zab, ns }
|
15
|
+
let(:quote) { Nydp::Symbol.mk :quote, ns }
|
16
|
+
let(:quasiquote) { Nydp::Symbol.mk :quasiquote, ns }
|
17
|
+
let(:unquote) { Nydp::Symbol.mk :unquote, ns }
|
18
|
+
let(:unquote_splicing) { Nydp::Symbol.mk :"unquote-splicing", ns }
|
19
|
+
let(:comment) { Nydp::Symbol.mk :comment, ns }
|
20
|
+
let(:dotsyn) { Nydp::Symbol.mk :"dot-syntax", ns }
|
21
|
+
let(:cocosyn) { Nydp::Symbol.mk :"colon-colon-syntax", ns }
|
22
|
+
let(:colosyn) { Nydp::Symbol.mk :"colon-syntax", ns }
|
23
|
+
|
24
|
+
def sym name
|
25
|
+
Nydp::Symbol.mk name.to_sym, ns
|
26
|
+
end
|
27
|
+
|
28
|
+
def parse txt
|
29
|
+
Nydp::Parser.new(ns).expression(Nydp::Tokeniser.new txt)
|
30
|
+
end
|
31
|
+
|
32
|
+
def pair_list xs, last=Nydp.NIL
|
33
|
+
Nydp::Pair.from_list xs, last
|
34
|
+
end
|
35
|
+
|
36
|
+
it "should return a stream of tokens" do
|
37
|
+
t = Nydp::Tokeniser.new ""
|
38
|
+
expect(t.next_token).to eq nil
|
39
|
+
end
|
40
|
+
|
41
|
+
it "should return another stream of tokens" do
|
42
|
+
t = Nydp::Tokeniser.new "(a b c 1 2 3)"
|
43
|
+
tt = []
|
44
|
+
tok = t.next_token
|
45
|
+
while tok
|
46
|
+
tt << tok
|
47
|
+
tok = t.next_token
|
48
|
+
end
|
49
|
+
expect(tt).to eq [[:left_paren, ""], [:symbol, "a"], [:symbol, "b"], [:symbol, "c"], [:number, 1.0], [:number, 2.0], [:number, 3.0], [:right_paren]]
|
50
|
+
end
|
51
|
+
|
52
|
+
it "should parse an empty expression" do
|
53
|
+
expect(parse "").to be_nil
|
54
|
+
end
|
55
|
+
|
56
|
+
it "should parse an empty expression" do
|
57
|
+
expect(parse "()").to eq Nydp.NIL
|
58
|
+
end
|
59
|
+
|
60
|
+
it "should parse a lisp expression" do
|
61
|
+
expect(parse "(foo bar)").to eq pair_list([foo, bar])
|
62
|
+
end
|
63
|
+
|
64
|
+
it "should parse numbers expression" do
|
65
|
+
expect(parse "(1 2 3)").to eq pair_list([1, 2, 3])
|
66
|
+
end
|
67
|
+
|
68
|
+
it "should parse an improper list" do
|
69
|
+
expect(parse "(1 2 3 . 4)").to eq pair_list([1, 2, 3], 4)
|
70
|
+
end
|
71
|
+
|
72
|
+
it "should parse a string" do
|
73
|
+
s1 = sym 'string-pieces'
|
74
|
+
s2 = Nydp::StringFragmentCloseToken.new "hello there", '"hello there"'
|
75
|
+
|
76
|
+
x1 = 1
|
77
|
+
x2 = "hello there"
|
78
|
+
x3 = 3
|
79
|
+
|
80
|
+
expected = pair_list [x1, x2, x3]
|
81
|
+
|
82
|
+
expect(parse '(1 "hello there" 3)').to eq expected
|
83
|
+
end
|
84
|
+
|
85
|
+
it "should parse a string" do
|
86
|
+
x1 = sym 'join'
|
87
|
+
x2 = " - "
|
88
|
+
x3 = 1
|
89
|
+
x4 = 2
|
90
|
+
x5 = 3
|
91
|
+
|
92
|
+
expected = pair_list [x1, x2, x3, x4, x5]
|
93
|
+
|
94
|
+
expect(parse '(join " - " 1 2 3)').to eq expected
|
95
|
+
end
|
96
|
+
|
97
|
+
it "should not get confused by embedded lisp in a string" do
|
98
|
+
s1 = sym 'string-pieces'
|
99
|
+
s2 = Nydp::StringFragmentCloseToken.new "hello (1 2 3) there", '"hello (1 2 3) there"'
|
100
|
+
|
101
|
+
x1 = 1
|
102
|
+
x2 = "hello (1 2 3) there"
|
103
|
+
x3 = 3
|
104
|
+
|
105
|
+
expected = pair_list [x1, x2, x3]
|
106
|
+
|
107
|
+
expect(parse '(1 "hello (1 2 3) there" 3)').to eq expected
|
108
|
+
end
|
109
|
+
|
110
|
+
it "should handle escaped quotes inside a string" do
|
111
|
+
s1 = sym 'string-pieces'
|
112
|
+
s2 = Nydp::StringFragmentCloseToken.new "hello there \"jimmy\"", '"hello there \"jimmy\""'
|
113
|
+
|
114
|
+
x1 = 1
|
115
|
+
x2 = "hello there \"jimmy\""
|
116
|
+
x3 = 3
|
117
|
+
|
118
|
+
expected = pair_list [x1, x2, x3]
|
119
|
+
|
120
|
+
expect(parse '(1 "hello there \"jimmy\"" 3)').to eq expected
|
121
|
+
end
|
122
|
+
|
123
|
+
it "should parse a plain symbol" do
|
124
|
+
expect(parse "foo").to eq foo
|
125
|
+
end
|
126
|
+
|
127
|
+
it "should parse a dotted symbol" do
|
128
|
+
expect(parse "foo.bar").to eq pair_list([dotsyn, foo, bar])
|
129
|
+
end
|
130
|
+
|
131
|
+
it "should parse a colon-colon symbol" do
|
132
|
+
expect(parse "foo::bar").to eq pair_list([cocosyn, foo, bar])
|
133
|
+
end
|
134
|
+
|
135
|
+
it "should parse a colon-symbol within a colon-colon within a dotted symbol" do
|
136
|
+
expect(parse "aa.foo:foo::bar:bar.zz").to eq pair_list([dotsyn, aa, pair_list([cocosyn, pair_list([colosyn, foo, foo]), pair_list([colosyn, bar, bar])]), zz])
|
137
|
+
end
|
138
|
+
|
139
|
+
it "should quote symbols" do
|
140
|
+
expect(parse "'foo").to eq pair_list([quote, foo])
|
141
|
+
end
|
142
|
+
|
143
|
+
it "should quote-unquote symbols" do
|
144
|
+
expect(parse "',foo").to eq pair_list([quote, pair_list([unquote, foo])])
|
145
|
+
end
|
146
|
+
|
147
|
+
it "should quote lists" do
|
148
|
+
expect(parse "'(foo)").to eq pair_list([quote, pair_list([foo])])
|
149
|
+
end
|
150
|
+
|
151
|
+
it "should unquote atoms" do
|
152
|
+
expect(parse ",foo").to eq pair_list([unquote, foo])
|
153
|
+
end
|
154
|
+
|
155
|
+
it "should unquote lists" do
|
156
|
+
expect(parse ",(bar)").to eq pair_list([unquote, pair_list([bar])])
|
157
|
+
end
|
158
|
+
|
159
|
+
it "should unquote-splicing atoms" do
|
160
|
+
expect(parse ",@foo").to eq pair_list([unquote_splicing, foo])
|
161
|
+
end
|
162
|
+
|
163
|
+
it "should unquote-splicing lists" do
|
164
|
+
expect(parse ",@(bar)").to eq pair_list([unquote_splicing, pair_list([bar])])
|
165
|
+
end
|
166
|
+
|
167
|
+
it "should quasiquote atoms" do
|
168
|
+
expect(parse "`foo").to eq pair_list([quasiquote, foo])
|
169
|
+
end
|
170
|
+
|
171
|
+
it "should quasiquote lists" do
|
172
|
+
expect(parse "`(bar)").to eq pair_list([quasiquote, pair_list([bar])])
|
173
|
+
end
|
174
|
+
|
175
|
+
it "should parse nested lists" do
|
176
|
+
expect(parse "(a b (c) d)").to eq pair_list([a, b, pair_list([c]), d])
|
177
|
+
end
|
178
|
+
|
179
|
+
it "should even parse comments" do
|
180
|
+
txt = "(def foo (bar)
|
181
|
+
; here's a comment
|
182
|
+
(zab))
|
183
|
+
"
|
184
|
+
c1 = pair_list([comment, "here's a comment"])
|
185
|
+
fbar = pair_list([bar])
|
186
|
+
fzab = pair_list([Nydp::Symbol.mk(:zab, ns)])
|
187
|
+
fdef = Nydp::Symbol.mk(:def, ns)
|
188
|
+
expr = pair_list([fdef, foo, fbar, c1, fzab])
|
189
|
+
expect(parse txt).to eq expr
|
190
|
+
end
|
191
|
+
end
|