nydp 0.0.1
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 +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
|