nydp 0.0.2 → 0.0.3
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 +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
|