nydp 0.0.2 → 0.0.3
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/Rakefile +1 -1
- data/lib/lisp/boot.nydp +11 -0
- data/lib/lisp/test-runner.nydp +12 -11
- data/lib/lisp/tests/foundation-test.nydp +50 -1
- data/lib/nydp.rb +23 -120
- data/lib/nydp/builtin/error.rb +1 -1
- data/lib/nydp/builtin/inspect.rb +1 -1
- data/lib/nydp/builtin/random_string.rb +3 -5
- data/lib/nydp/builtin/string_replace.rb +14 -0
- data/lib/nydp/builtin/string_split.rb +14 -0
- data/lib/nydp/builtin/to_string.rb +14 -0
- data/lib/nydp/builtin/type_of.rb +11 -0
- data/lib/nydp/core.rb +66 -0
- data/lib/nydp/function_invocation.rb +18 -19
- data/lib/nydp/interpreted_function.rb +1 -1
- data/lib/nydp/pair.rb +5 -4
- data/lib/nydp/parser.rb +11 -11
- data/lib/nydp/runner.rb +66 -0
- data/lib/nydp/string_atom.rb +4 -4
- data/lib/nydp/string_token.rb +1 -3
- data/lib/nydp/symbol.rb +2 -7
- data/lib/nydp/tokeniser.rb +9 -3
- data/lib/nydp/truth.rb +9 -7
- data/lib/nydp/version.rb +1 -1
- data/lib/nydp/vm.rb +15 -22
- data/spec/boot_spec.rb +1 -6
- data/spec/embedded_spec.rb +10 -12
- data/spec/nypd_spec.rb +5 -26
- data/spec/pair_spec.rb +16 -17
- data/spec/parser_spec.rb +14 -14
- data/spec/spec_helper.rb +19 -0
- data/spec/string_token_spec.rb +19 -0
- data/spec/symbol_spec.rb +0 -2
- metadata +9 -2
@@ -1,4 +1,7 @@
|
|
1
1
|
module Nydp
|
2
|
+
class InvocationFailed < StandardError
|
3
|
+
end
|
4
|
+
|
2
5
|
class InvokeFunctionInstruction
|
3
6
|
def initialize arg_count, source_expression
|
4
7
|
@source_expression = source_expression
|
@@ -8,24 +11,22 @@ module Nydp
|
|
8
11
|
def execute vm
|
9
12
|
args = vm.pop_args @arg_count
|
10
13
|
args.car.invoke vm, args.cdr
|
14
|
+
rescue Nydp::Error => ne
|
15
|
+
raise ne
|
16
|
+
rescue InvocationFailed => i_f
|
17
|
+
raise i_f
|
11
18
|
rescue Exception => e
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
vm.error
|
16
|
-
|
17
|
-
|
18
|
-
def inspect
|
19
|
-
"#{self.class.name}:#{source}"
|
19
|
+
msg = "failed to execute invocation #{args.inspect}"
|
20
|
+
msg += "\nsource was #{source.inspect}"
|
21
|
+
msg += "\nfunction name was #{source.car.inspect}"
|
22
|
+
i_f = InvocationFailed.new "#{msg}\n#{vm.error}#{e.message}"
|
23
|
+
i_f.set_backtrace e.backtrace
|
24
|
+
raise i_f
|
20
25
|
end
|
21
26
|
|
22
|
-
def source
|
23
|
-
|
24
|
-
end
|
25
|
-
|
26
|
-
def to_s
|
27
|
-
source
|
28
|
-
end
|
27
|
+
def inspect ; source.inspect ; end
|
28
|
+
def source ; @source_expression ; end
|
29
|
+
def to_s ; source.to_s ; end
|
29
30
|
end
|
30
31
|
|
31
32
|
class FunctionInvocation
|
@@ -44,9 +45,7 @@ module Nydp
|
|
44
45
|
vm.push_instructions @argument_instructions, vm.peek_context
|
45
46
|
end
|
46
47
|
|
47
|
-
def inspect;
|
48
|
-
def to_s
|
49
|
-
@source.to_s
|
50
|
-
end
|
48
|
+
def inspect ; @source.inspect ; end
|
49
|
+
def to_s ; @source.to_s ; end
|
51
50
|
end
|
52
51
|
end
|
data/lib/nydp/pair.rb
CHANGED
@@ -12,10 +12,11 @@ class Nydp::Pair
|
|
12
12
|
new a, b
|
13
13
|
end
|
14
14
|
|
15
|
-
def
|
16
|
-
def
|
17
|
-
def
|
18
|
-
def
|
15
|
+
def nydp_type ; :pair ; end
|
16
|
+
def caar ; car.car ; end
|
17
|
+
def cadr ; cdr.car ; end
|
18
|
+
def cdar ; car.cdr ; end
|
19
|
+
def cddr ; cdr.cdr ; end
|
19
20
|
|
20
21
|
def self.parse_list list
|
21
22
|
if sym? list.slice(-2), "."
|
data/lib/nydp/parser.rb
CHANGED
@@ -10,8 +10,7 @@ module Nydp
|
|
10
10
|
Nydp::Symbol.mk name.to_sym, ns
|
11
11
|
end
|
12
12
|
|
13
|
-
def read_list token_stream, termination_token
|
14
|
-
list = []
|
13
|
+
def read_list token_stream, termination_token, list=[]
|
15
14
|
token = token_stream.next_token
|
16
15
|
while token != nil && token.first != termination_token
|
17
16
|
list << next_form(token, token_stream)
|
@@ -51,19 +50,19 @@ module Nydp
|
|
51
50
|
Pair.from_list [sym(:unquote), parse_symbol($1)]
|
52
51
|
else
|
53
52
|
syms = txt.to_s.split /\./
|
54
|
-
return split_sym syms, sym(
|
53
|
+
return split_sym syms, sym("dot-syntax") if syms.length > 1
|
55
54
|
|
56
55
|
syms = txt.split /::/
|
57
|
-
return split_sym syms, sym(
|
56
|
+
return split_sym syms, sym("colon-colon-syntax") if syms.length > 1
|
58
57
|
|
59
58
|
syms = txt.split /:/
|
60
|
-
return split_sym syms, sym(
|
59
|
+
return split_sym syms, sym("colon-syntax") if syms.length > 1
|
61
60
|
|
62
61
|
syms = txt.split /->/
|
63
|
-
return split_sym syms, sym(
|
62
|
+
return split_sym syms, sym("arrow-syntax") if syms.length > 1
|
64
63
|
|
65
64
|
syms = txt.split(/=>/)
|
66
|
-
return split_sym syms, sym(
|
65
|
+
return split_sym syms, sym("rocket-syntax") if syms.length > 1
|
67
66
|
|
68
67
|
sym txt
|
69
68
|
end
|
@@ -89,6 +88,8 @@ module Nydp
|
|
89
88
|
string token_stream, token.last, close_delimiter_for(token.last)
|
90
89
|
when :left_paren
|
91
90
|
prefix_list token[1], read_list(token_stream, :right_paren)
|
91
|
+
when :left_brace
|
92
|
+
prefix_list token[1], read_list(token_stream, :right_brace, [sym("brace-list")])
|
92
93
|
when :symbol
|
93
94
|
parse_symbol token.last
|
94
95
|
when :comment
|
@@ -105,16 +106,15 @@ module Nydp
|
|
105
106
|
def string token_stream, open_delimiter, close_delimiter
|
106
107
|
fragments = [sym(:"string-pieces")]
|
107
108
|
string_token = token_stream.next_string_fragment(open_delimiter, close_delimiter)
|
108
|
-
fragments << string_token
|
109
|
+
fragments << Nydp::StringAtom.new(string_token.string, string_token)
|
109
110
|
while !(string_token.is_a? StringFragmentCloseToken)
|
110
111
|
fragments << expression(token_stream)
|
111
112
|
string_token = token_stream.next_string_fragment('', close_delimiter)
|
112
|
-
fragments << string_token
|
113
|
+
fragments << Nydp::StringAtom.new(string_token.string, string_token)
|
113
114
|
end
|
114
115
|
|
115
116
|
if fragments.size == 2
|
116
|
-
|
117
|
-
return Nydp::StringAtom.new tok.string, tok
|
117
|
+
return fragments[1]
|
118
118
|
else
|
119
119
|
return Pair.from_list fragments
|
120
120
|
end
|
data/lib/nydp/runner.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
module Nydp
|
2
|
+
class Runner
|
3
|
+
attr_accessor :vm, :ns
|
4
|
+
|
5
|
+
def initialize vm, ns
|
6
|
+
@vm = vm
|
7
|
+
@ns = ns
|
8
|
+
@precompile = Symbol.mk(:"pre-compile", ns)
|
9
|
+
@quote = Symbol.mk(:quote, ns)
|
10
|
+
end
|
11
|
+
|
12
|
+
def compile_and_eval expr
|
13
|
+
vm.thread Pair.new(Compiler.compile(expr, Nydp.NIL), Nydp.NIL)
|
14
|
+
end
|
15
|
+
|
16
|
+
def quote expr
|
17
|
+
Pair.from_list [@quote, expr]
|
18
|
+
end
|
19
|
+
|
20
|
+
def precompile expr
|
21
|
+
Pair.from_list [@precompile, quote(expr)]
|
22
|
+
end
|
23
|
+
|
24
|
+
def pre_compile expr
|
25
|
+
compile_and_eval(precompile(expr))
|
26
|
+
end
|
27
|
+
|
28
|
+
def evaluate expr
|
29
|
+
compile_and_eval(pre_compile(expr))
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
class StreamRunner < Runner
|
34
|
+
attr_accessor :stream, :parser
|
35
|
+
|
36
|
+
def initialize vm, ns, stream
|
37
|
+
super vm, ns
|
38
|
+
@stream = stream
|
39
|
+
@parser = Nydp::Parser.new(ns)
|
40
|
+
@tokens = Nydp::Tokeniser.new stream
|
41
|
+
end
|
42
|
+
|
43
|
+
def prompt *_
|
44
|
+
end
|
45
|
+
|
46
|
+
def run
|
47
|
+
res = Nydp.NIL
|
48
|
+
prompt
|
49
|
+
while !@tokens.finished
|
50
|
+
expr = parser.expression(@tokens)
|
51
|
+
unless expr.nil?
|
52
|
+
res = evaluate expr
|
53
|
+
prompt res
|
54
|
+
end
|
55
|
+
end
|
56
|
+
res
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
class Repl < StreamRunner
|
61
|
+
def prompt val=nil
|
62
|
+
puts val if val
|
63
|
+
print "nydp > "
|
64
|
+
end
|
65
|
+
end
|
66
|
+
end
|
data/lib/nydp/string_atom.rb
CHANGED
@@ -5,12 +5,11 @@ module Nydp
|
|
5
5
|
@string, @token = string, token
|
6
6
|
end
|
7
7
|
|
8
|
-
def
|
9
|
-
string
|
10
|
-
end
|
8
|
+
def nydp_type ; :string ; end
|
9
|
+
def to_s ; string ; end
|
11
10
|
|
12
11
|
def inspect
|
13
|
-
|
12
|
+
string.inspect
|
14
13
|
end
|
15
14
|
|
16
15
|
def == other
|
@@ -20,5 +19,6 @@ module Nydp
|
|
20
19
|
def + other
|
21
20
|
StringAtom.new "#{@string}#{other}"
|
22
21
|
end
|
22
|
+
|
23
23
|
end
|
24
24
|
end
|
data/lib/nydp/string_token.rb
CHANGED
data/lib/nydp/symbol.rb
CHANGED
@@ -29,13 +29,8 @@ class Nydp::Symbol
|
|
29
29
|
ns[name.to_sym]
|
30
30
|
end
|
31
31
|
|
32
|
-
def inspect
|
33
|
-
|
34
|
-
end
|
35
|
-
|
36
|
-
def to_s
|
37
|
-
name.to_s
|
38
|
-
end
|
32
|
+
def inspect ; to_s ; end
|
33
|
+
def to_s ; name.to_s ; end
|
39
34
|
|
40
35
|
def == other
|
41
36
|
other.is_a?(Nydp::Symbol) && (self.name == other.name)
|
data/lib/nydp/tokeniser.rb
CHANGED
@@ -28,8 +28,12 @@ module Nydp
|
|
28
28
|
if esc = s.scan(/\\/)
|
29
29
|
rep << esc
|
30
30
|
ch = s.getch
|
31
|
-
|
32
|
-
|
31
|
+
case ch
|
32
|
+
when "n" ; string << "\n"
|
33
|
+
when "t" ; string << "\t"
|
34
|
+
else string << ch
|
35
|
+
end
|
36
|
+
rep << ch
|
33
37
|
elsif closer = close_delimiter?(s, close_delimiter)
|
34
38
|
rep << closer
|
35
39
|
return StringFragmentCloseToken.new(string, rep)
|
@@ -62,13 +66,15 @@ module Nydp
|
|
62
66
|
tok = [:embed_suffix, embed_suffix]
|
63
67
|
elsif list_prefix = s.scan(/[^\s()]*\(/)
|
64
68
|
tok = [:left_paren, list_prefix[0...-1]]
|
69
|
+
elsif list_prefix = s.scan(/[^\s()]*\{/)
|
70
|
+
tok = [:left_brace, list_prefix[0...-1]]
|
65
71
|
elsif s.scan(/\)/)
|
66
72
|
tok = [:right_paren]
|
67
73
|
elsif number = s.scan(/[-+]?[0-9]*\.[0-9]+([eE][-+]?[0-9]+)?/)
|
68
74
|
tok = [:number, number.to_f]
|
69
75
|
elsif integer = s.scan(/[-+]?[0-9]+/)
|
70
76
|
tok = [:number, integer.to_i]
|
71
|
-
elsif atom = s.scan(/[^\s()]+/)
|
77
|
+
elsif atom = s.scan(/[^\s()"{}]+/)
|
72
78
|
tok = [:symbol, atom]
|
73
79
|
else
|
74
80
|
s.getch
|
data/lib/nydp/truth.rb
CHANGED
@@ -3,22 +3,21 @@ module Nydp
|
|
3
3
|
def to_s ; 't' ; end
|
4
4
|
def inspect ; 't[nydp::Truth]' ; end
|
5
5
|
def assign *_ ; self ; end
|
6
|
+
def nydp_type ; :truth ; end
|
6
7
|
end
|
7
8
|
|
8
9
|
class Nil
|
9
10
|
def car ; self ; end
|
10
11
|
def cdr ; self ; end
|
11
12
|
def size ; 0 ; end
|
12
|
-
def is? other ; other
|
13
|
-
def isnt? other ; other
|
14
|
-
def to_s ; "
|
13
|
+
def is? other ; (self.equal? other) ; end
|
14
|
+
def isnt? other ; !is?(other) ; end
|
15
|
+
def to_s ; "" ; end
|
15
16
|
def + other ; other ; end
|
16
17
|
def copy ; self ; end
|
17
18
|
def assign *_ ; self ; end
|
18
|
-
|
19
|
-
def
|
20
|
-
"nil[Nydp::Nil]"
|
21
|
-
end
|
19
|
+
def inspect ; "nil" ; end
|
20
|
+
def nydp_type ; :nil ; end
|
22
21
|
|
23
22
|
def execute vm
|
24
23
|
vm.push_arg self
|
@@ -32,6 +31,9 @@ module Nydp
|
|
32
31
|
@@nil = Nil.new
|
33
32
|
@@t = Truth.new
|
34
33
|
|
34
|
+
class Nil
|
35
|
+
def self.new ; raise "no" ; end
|
36
|
+
end
|
35
37
|
def self.NIL; @@nil; end
|
36
38
|
def self.T; @@t; end
|
37
39
|
end
|
data/lib/nydp/version.rb
CHANGED
data/lib/nydp/vm.rb
CHANGED
@@ -41,36 +41,29 @@ module Nydp
|
|
41
41
|
end
|
42
42
|
end
|
43
43
|
|
44
|
-
def error
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
puts "instruction stack"
|
50
|
-
puts "================="
|
44
|
+
def error
|
45
|
+
msg = ""
|
46
|
+
msg << "\n"
|
47
|
+
msg << "\ninstruction stack"
|
48
|
+
msg << "\n================="
|
51
49
|
instructions.each_with_index do |ii, ix|
|
52
|
-
|
50
|
+
msg << "\ninstructions##{ix} : #{ii} #{ii.source if ii.respond_to?(:source)}"
|
53
51
|
end
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
52
|
+
msg << "\n"
|
53
|
+
msg << "\n"
|
54
|
+
msg << "\ncontext stack"
|
55
|
+
msg << "\n================="
|
58
56
|
contexts.each_with_index do |ctx, ix|
|
59
|
-
|
57
|
+
msg << "\ncontext##{ix} : #{ctx}"
|
60
58
|
end
|
61
|
-
|
62
|
-
|
63
|
-
puts "ruby backtrace"
|
64
|
-
puts "================="
|
65
|
-
(e.backtrace || []).each_with_index do |ctx, ix|
|
66
|
-
puts "#{ctx}"
|
67
|
-
end
|
68
|
-
puts
|
69
|
-
puts
|
59
|
+
msg << "\n"
|
60
|
+
msg << "\n"
|
70
61
|
|
71
62
|
instructions = []
|
72
63
|
contexts = []
|
73
64
|
args = [Nydp.NIL]
|
65
|
+
|
66
|
+
msg
|
74
67
|
end
|
75
68
|
end
|
76
69
|
end
|
data/spec/boot_spec.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nydp do
|
4
|
-
let(:ns) { { } }
|
5
4
|
let(:vm) { Nydp::VM.new }
|
6
5
|
|
7
6
|
before {
|
@@ -10,10 +9,6 @@ describe Nydp do
|
|
10
9
|
Nydp::StreamRunner.new(vm, ns, File.new(boot_path)).run
|
11
10
|
}
|
12
11
|
|
13
|
-
def sym name
|
14
|
-
Nydp::Symbol.mk name.to_sym, ns
|
15
|
-
end
|
16
|
-
|
17
12
|
def list *things
|
18
13
|
Nydp::Pair.from_list things.map { |thing|
|
19
14
|
case thing
|
@@ -102,7 +97,7 @@ describe Nydp do
|
|
102
97
|
describe :or do
|
103
98
|
it "should produce some nested conds" do
|
104
99
|
result = run "(reset-uniq-counter) (pre-compile '(or a b c))"
|
105
|
-
expect(result
|
100
|
+
expect(result).to eq parse "((fn (ora-1) (cond ora-1 ora-1 ((fn (ora-2) (cond ora-2 ora-2 ((fn (ora-3) (cond ora-3 ora-3 nil)) c))) b))) a)"
|
106
101
|
end
|
107
102
|
end
|
108
103
|
|
data/spec/embedded_spec.rb
CHANGED
@@ -1,8 +1,6 @@
|
|
1
1
|
require 'spec_helper'
|
2
2
|
|
3
3
|
describe Nydp::Parser do
|
4
|
-
|
5
|
-
let(:ns) { { } }
|
6
4
|
let(:aa) { Nydp::Symbol.mk :aa, ns }
|
7
5
|
let(:a) { Nydp::Symbol.mk :a, ns }
|
8
6
|
let(:b) { Nydp::Symbol.mk :b, ns }
|
@@ -21,18 +19,10 @@ describe Nydp::Parser do
|
|
21
19
|
let(:cocosyn) { Nydp::Symbol.mk :"colon-colon-syntax", ns }
|
22
20
|
let(:colosyn) { Nydp::Symbol.mk :"colon-syntax", ns }
|
23
21
|
|
24
|
-
def sym name
|
25
|
-
Nydp::Symbol.mk name.to_sym, ns
|
26
|
-
end
|
27
|
-
|
28
22
|
def parse_string txt, open_delim, close_delim
|
29
23
|
Nydp::Parser.new(ns).string(Nydp::Tokeniser.new(txt), open_delim, close_delim)
|
30
24
|
end
|
31
25
|
|
32
|
-
def pair_list xs, last=Nydp.NIL
|
33
|
-
Nydp::Pair.from_list xs, last
|
34
|
-
end
|
35
|
-
|
36
26
|
it "should parse empty string" do
|
37
27
|
expected = pair_list([sym('string-pieces'), Nydp::StringFragmentCloseToken.new('','$%')])
|
38
28
|
actual = parse_string "%", '$', /%/
|
@@ -42,21 +32,23 @@ describe Nydp::Parser do
|
|
42
32
|
it "should parse external text" do
|
43
33
|
actual = parse_string "a fluffy bunny!", 'EAT ', /!/
|
44
34
|
expect(actual) .to eq "a fluffy bunny"
|
45
|
-
expect(actual.inspect).to eq "
|
35
|
+
expect(actual.inspect).to eq '"a fluffy bunny"'
|
46
36
|
end
|
47
37
|
|
48
38
|
it "should parse a string delimited by eof" do
|
49
39
|
expected = pair_list([sym('string-pieces'), Nydp::StringFragmentCloseToken.new('a fluffy bunny!','a fluffy bunny!')])
|
50
40
|
actual = parse_string "a fluffy bunny!", '', :eof
|
51
41
|
expect(actual) .to eq "a fluffy bunny!"
|
52
|
-
expect(actual.inspect).to eq "a fluffy bunny!"
|
42
|
+
expect(actual.inspect).to eq '"a fluffy bunny!"'
|
53
43
|
end
|
54
44
|
|
55
45
|
it "should parse a string with embedded code, delimited by eof" do
|
56
46
|
x1 = sym('string-pieces')
|
57
47
|
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ',':a fluffy bunny! %%')
|
48
|
+
x2 = Nydp::StringAtom.new(x2.string, x2)
|
58
49
|
x3 = sym('expr')
|
59
50
|
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
51
|
+
x4 = Nydp::StringAtom.new(x4.string, x4)
|
60
52
|
|
61
53
|
expected = pair_list([x1,x2,x3,x4])
|
62
54
|
actual = parse_string "a fluffy bunny! %%expr a purple cow!", ':', :eof
|
@@ -71,8 +63,10 @@ describe Nydp::Parser do
|
|
71
63
|
|
72
64
|
x1 = sym('string-pieces')
|
73
65
|
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ','------->a fluffy bunny! %%')
|
66
|
+
x2 = Nydp::StringAtom.new(x2.string, x2)
|
74
67
|
x3 = pair_list [n1, n2, n3, n4]
|
75
68
|
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
69
|
+
x4 = Nydp::StringAtom.new(x4.string, x4)
|
76
70
|
|
77
71
|
expected = pair_list([x1,x2,x3,x4])
|
78
72
|
actual = parse_string "a fluffy bunny! %%(foo bar \"an embedded bunny :)\" zop) a purple cow!", '------->', :eof
|
@@ -85,8 +79,10 @@ describe Nydp::Parser do
|
|
85
79
|
|
86
80
|
s1 = sym('string-pieces')
|
87
81
|
s2 = Nydp::StringFragmentToken.new('a rather ','"a rather %%')
|
82
|
+
s2 = Nydp::StringAtom.new(s2.string, s2)
|
88
83
|
s3 = pair_list [e1, e2]
|
89
84
|
s4 = Nydp::StringFragmentCloseToken.new(' bunny :)',' bunny :)"')
|
85
|
+
s4 = Nydp::StringAtom.new(s4.string, s4)
|
90
86
|
|
91
87
|
n1 = sym(:foo)
|
92
88
|
n2 = sym(:bar)
|
@@ -95,8 +91,10 @@ describe Nydp::Parser do
|
|
95
91
|
|
96
92
|
x1 = sym('string-pieces')
|
97
93
|
x2 = Nydp::StringFragmentToken.new('a fluffy bunny! ','------->a fluffy bunny! %%')
|
94
|
+
x2 = Nydp::StringAtom.new(x2.string, x2)
|
98
95
|
x3 = pair_list [n1, n2, n3, n4]
|
99
96
|
x4 = Nydp::StringFragmentCloseToken.new(' a purple cow!',' a purple cow!')
|
97
|
+
x4 = Nydp::StringAtom.new(x4.string, x4)
|
100
98
|
|
101
99
|
expected = pair_list([x1,x2,x3,x4])
|
102
100
|
actual = parse_string "a fluffy bunny! %%(foo bar \"a rather %%(describe bunny) bunny :)\" zop) a purple cow!", '------->', :eof
|