ripper2ruby 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.
- data/MIT-LICENSE +20 -0
- data/README.markdown +10 -0
- data/lib/core_ext/array/flush.rb +5 -0
- data/lib/core_ext/hash/delete_at.rb +5 -0
- data/lib/core_ext/object/meta_class.rb +5 -0
- data/lib/core_ext/object/try.rb +12 -0
- data/lib/erb/stripper.rb +48 -0
- data/lib/highlighters/ansi.rb +29 -0
- data/lib/ripper/event_log.rb +45 -0
- data/lib/ripper/ruby_builder.rb +168 -0
- data/lib/ripper/ruby_builder/buffer.rb +34 -0
- data/lib/ripper/ruby_builder/events/args.rb +40 -0
- data/lib/ripper/ruby_builder/events/array.rb +71 -0
- data/lib/ripper/ruby_builder/events/assignment.rb +55 -0
- data/lib/ripper/ruby_builder/events/block.rb +80 -0
- data/lib/ripper/ruby_builder/events/call.rb +123 -0
- data/lib/ripper/ruby_builder/events/case.rb +17 -0
- data/lib/ripper/ruby_builder/events/const.rb +47 -0
- data/lib/ripper/ruby_builder/events/for.rb +13 -0
- data/lib/ripper/ruby_builder/events/hash.rb +24 -0
- data/lib/ripper/ruby_builder/events/identifier.rb +41 -0
- data/lib/ripper/ruby_builder/events/if.rb +37 -0
- data/lib/ripper/ruby_builder/events/lexer.rb +159 -0
- data/lib/ripper/ruby_builder/events/literal.rb +47 -0
- data/lib/ripper/ruby_builder/events/method.rb +21 -0
- data/lib/ripper/ruby_builder/events/operator.rb +23 -0
- data/lib/ripper/ruby_builder/events/statements.rb +50 -0
- data/lib/ripper/ruby_builder/events/string.rb +117 -0
- data/lib/ripper/ruby_builder/events/symbol.rb +22 -0
- data/lib/ripper/ruby_builder/events/while.rb +27 -0
- data/lib/ripper/ruby_builder/queue.rb +33 -0
- data/lib/ripper/ruby_builder/stack.rb +125 -0
- data/lib/ripper/ruby_builder/token.rb +91 -0
- data/lib/ripper2ruby.rb +1 -0
- data/lib/ruby.rb +28 -0
- data/lib/ruby/aggregate.rb +71 -0
- data/lib/ruby/alternation/args.rb +25 -0
- data/lib/ruby/alternation/hash.rb +25 -0
- data/lib/ruby/alternation/list.rb +19 -0
- data/lib/ruby/args.rb +36 -0
- data/lib/ruby/array.rb +27 -0
- data/lib/ruby/assignment.rb +32 -0
- data/lib/ruby/assoc.rb +17 -0
- data/lib/ruby/block.rb +42 -0
- data/lib/ruby/call.rb +34 -0
- data/lib/ruby/case.rb +30 -0
- data/lib/ruby/const.rb +49 -0
- data/lib/ruby/for.rb +18 -0
- data/lib/ruby/hash.rb +14 -0
- data/lib/ruby/if.rb +35 -0
- data/lib/ruby/list.rb +40 -0
- data/lib/ruby/literal.rb +45 -0
- data/lib/ruby/method.rb +19 -0
- data/lib/ruby/node.rb +47 -0
- data/lib/ruby/node/composite.rb +68 -0
- data/lib/ruby/node/conversions.rb +66 -0
- data/lib/ruby/node/position.rb +35 -0
- data/lib/ruby/node/source.rb +29 -0
- data/lib/ruby/node/text.rb +121 -0
- data/lib/ruby/node/traversal.rb +82 -0
- data/lib/ruby/operator.rb +49 -0
- data/lib/ruby/params.rb +41 -0
- data/lib/ruby/statements.rb +45 -0
- data/lib/ruby/string.rb +40 -0
- data/lib/ruby/symbol.rb +27 -0
- data/lib/ruby/token.rb +51 -0
- data/lib/ruby/while.rb +32 -0
- data/test/all.rb +3 -0
- data/test/builder/stack_test.rb +67 -0
- data/test/builder/text_test.rb +118 -0
- data/test/context_test.rb +54 -0
- data/test/erb_stripper_test.rb +29 -0
- data/test/fixtures/all.rb.src +150 -0
- data/test/fixtures/source_1.rb +16 -0
- data/test/fixtures/source_2.rb +1 -0
- data/test/fixtures/stuff.rb +371 -0
- data/test/fixtures/template.html.erb +22 -0
- data/test/fixtures/tmp.rb +6 -0
- data/test/lib_test.rb +92 -0
- data/test/lib_test_helper.rb +103 -0
- data/test/libs.txt +227 -0
- data/test/nodes/args_test.rb +100 -0
- data/test/nodes/array_test.rb +141 -0
- data/test/nodes/assignment_test.rb +49 -0
- data/test/nodes/block_test.rb +125 -0
- data/test/nodes/call_test.rb +229 -0
- data/test/nodes/case_test.rb +68 -0
- data/test/nodes/comments_test.rb +25 -0
- data/test/nodes/const_test.rb +46 -0
- data/test/nodes/conversions_test.rb +9 -0
- data/test/nodes/for_test.rb +34 -0
- data/test/nodes/hash_test.rb +71 -0
- data/test/nodes/heredoc_test.rb +202 -0
- data/test/nodes/identifier_test.rb +51 -0
- data/test/nodes/if_test.rb +100 -0
- data/test/nodes/literals_test.rb +63 -0
- data/test/nodes/method_test.rb +92 -0
- data/test/nodes/namespaces_test.rb +65 -0
- data/test/nodes/node_test.rb +74 -0
- data/test/nodes/nodes_test.rb +23 -0
- data/test/nodes/operator_test.rb +241 -0
- data/test/nodes/separators_test.rb +97 -0
- data/test/nodes/statements_test.rb +70 -0
- data/test/nodes/string_test.rb +92 -0
- data/test/nodes/symbol_test.rb +57 -0
- data/test/nodes/unless_test.rb +42 -0
- data/test/nodes/until_test.rb +61 -0
- data/test/nodes/while_test.rb +71 -0
- data/test/test_helper.rb +51 -0
- data/test/traversal_test.rb +53 -0
- metadata +163 -0
@@ -0,0 +1,27 @@
|
|
1
|
+
class Ripper
|
2
|
+
class RubyBuilder < Ripper::SexpBuilder
|
3
|
+
module While
|
4
|
+
def on_while(expression, statements)
|
5
|
+
rdelim = pop_token(:@end)
|
6
|
+
ldelim = pop_token(:@do)
|
7
|
+
identifier = pop_token(:@while)
|
8
|
+
Ruby::While.new(identifier, expression, statements, ldelim, rdelim)
|
9
|
+
end
|
10
|
+
|
11
|
+
def on_while_mod(expression, statement)
|
12
|
+
Ruby::WhileMod.new(pop_token(:@while), expression, statement)
|
13
|
+
end
|
14
|
+
|
15
|
+
def on_until(expression, statements)
|
16
|
+
rdelim = pop_token(:@end)
|
17
|
+
ldelim = pop_token(:@do)
|
18
|
+
identifier = pop_token(:@until)
|
19
|
+
Ruby::Until.new(identifier, expression, statements, ldelim, rdelim)
|
20
|
+
end
|
21
|
+
|
22
|
+
def on_until_mod(expression, statement)
|
23
|
+
Ruby::UntilMod.new(pop_token(:@until), expression, statement)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
# When tokens are pushed to the stack they will be pushed to a queue. Tokens
|
2
|
+
# that open new constructs in Ruby (parentheses, semicolons, keywords like
|
3
|
+
# class, do, if, etc.) will be held in the queue until the next token is
|
4
|
+
# pushed. The queue will then empty itself and return the previously queued
|
5
|
+
# token together with the currently pushed token.
|
6
|
+
#
|
7
|
+
# The reason for this is the way Ripper parses Ruby code. The lexer will fire
|
8
|
+
# events every time a known token is found. The parser will fire events when
|
9
|
+
# known Ruby constructs are completed. Thus often times when a parser event
|
10
|
+
# fires the lexer has already pushed the next (opening) token to the stack.
|
11
|
+
#
|
12
|
+
# Otoh, event handlers responding to a parser event will want to check for
|
13
|
+
# expected tokens (e.g. an opening parentheses for a method call). Thus, when
|
14
|
+
# the opening tokens (added by lexer events) are held in a queue while the
|
15
|
+
# parser event is fired it will be easier to pop the right tokens belonging
|
16
|
+
# to the parser event from the stack.
|
17
|
+
|
18
|
+
class Ripper
|
19
|
+
class RubyBuilder < Ripper::SexpBuilder
|
20
|
+
class Queue < ::Array
|
21
|
+
def <<(token)
|
22
|
+
result = [shift]
|
23
|
+
if token.nil?
|
24
|
+
elsif token.opener?
|
25
|
+
push(token)
|
26
|
+
else
|
27
|
+
result << token
|
28
|
+
end
|
29
|
+
result.compact
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
require 'ripper/ruby_builder/queue'
|
2
|
+
require 'ripper/ruby_builder/buffer'
|
3
|
+
|
4
|
+
# The stack holds the current "state" of the parser and facilitates communication
|
5
|
+
# between lexer events (which fire on known Ruby tokens) and parser events (which
|
6
|
+
# fire on known Ruby constructs).
|
7
|
+
#
|
8
|
+
# E.g. when the Ruby code foo(:bar) is parsed the lexer will fire events for
|
9
|
+
# the identifiers 'foo' and 'bar', for the opening and closing parentheses and
|
10
|
+
# for the colon . Tt fires the events in the order of the occurence of the
|
11
|
+
# tokens from left to right. RubyBuilder pushes all these tokens to the stack.
|
12
|
+
#
|
13
|
+
# See RubyBuilder::Queue and RubyBuilder::Buffer for what else happens when a
|
14
|
+
# token is pushed to the stack.
|
15
|
+
#
|
16
|
+
# The parser on the other hand will fire events for known Ruby constructs such
|
17
|
+
# as the argument list, arguments being added to the argument list, the method
|
18
|
+
# call etc. The parser fires these events in the order of Ruby constructs being
|
19
|
+
# recognized - i.e. when they are completed. RubyBuilder responds to these
|
20
|
+
# events and will pop tokens off from the stack as required (e.g. for
|
21
|
+
# constructing an argument list it will try to pop off the corresponding
|
22
|
+
# left and right parentheses.)
|
23
|
+
#
|
24
|
+
# When RubyBuilder pops tokens off from the stack it wants to be careful not to
|
25
|
+
# pop off tokens that belong to higher level constructs that haven't yet fired.
|
26
|
+
# E.g. for a nested method call foo(bar(1)) the inner call fires first because
|
27
|
+
# it completes first. Thus, when RubyBuilder constructs this call it must not
|
28
|
+
# pop off the opening parentheses belonging to the outer call (which of course)
|
29
|
+
# is already on the stack.
|
30
|
+
#
|
31
|
+
# For that reason when popping off tokens the stack by default stops searching
|
32
|
+
# for the token when an opening token is found. RubyBuilder can force it to
|
33
|
+
# search past opening tokens by setting the :pass option to true. Similarly
|
34
|
+
# RubyBuilder can set constraints to what tokens it wants to be popped off:
|
35
|
+
#
|
36
|
+
# :pass => true # search past opening tokens
|
37
|
+
# :max => count # number of tokens
|
38
|
+
# :value => 'foo' # value of the token
|
39
|
+
# :pos => pos # position of the token
|
40
|
+
# :left => token # token must be located right of the given token
|
41
|
+
# :right => token # token must be located left of the given token
|
42
|
+
# :reverse => true # searches the stack in reverse order (i.e. tokens are shifted)
|
43
|
+
|
44
|
+
class Ripper
|
45
|
+
class RubyBuilder < Ripper::SexpBuilder
|
46
|
+
class Stack < ::Array
|
47
|
+
attr_reader :queue, :buffer
|
48
|
+
|
49
|
+
def initialize
|
50
|
+
@queue = Queue.new
|
51
|
+
@buffer = Buffer.new
|
52
|
+
end
|
53
|
+
|
54
|
+
def push(token)
|
55
|
+
return token if buffer.aggregate(token)
|
56
|
+
tokens = queue << token
|
57
|
+
tokens.each do |token|
|
58
|
+
self << token
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
alias :_pop :pop
|
63
|
+
def pop(*types)
|
64
|
+
options = types.last.is_a?(::Hash) ? types.pop : {}
|
65
|
+
max, pass, revert = options.delete_at(:max, :pass, :reverse)
|
66
|
+
ignored, tokens = [], []
|
67
|
+
reverse! if revert
|
68
|
+
|
69
|
+
while !empty? && !(max && tokens.length >= max)
|
70
|
+
if types.include?(last.type) && matches?(options)
|
71
|
+
tokens << _pop()
|
72
|
+
elsif last.opener? && !pass
|
73
|
+
break
|
74
|
+
else
|
75
|
+
ignored.unshift(_pop())
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
replace(self + ignored)
|
80
|
+
reverse! if revert
|
81
|
+
tokens
|
82
|
+
end
|
83
|
+
|
84
|
+
protected
|
85
|
+
|
86
|
+
def matches?(conditions)
|
87
|
+
conditions.inject(true) do |result, (type, value)|
|
88
|
+
result && case type
|
89
|
+
when :value
|
90
|
+
has_value?(value)
|
91
|
+
when :pos
|
92
|
+
at?(value)
|
93
|
+
when :right
|
94
|
+
left_of?(value)
|
95
|
+
when :left
|
96
|
+
right_of?(value)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def at?(pos)
|
102
|
+
pos.nil? || last.position == pos
|
103
|
+
end
|
104
|
+
|
105
|
+
def left_of?(right)
|
106
|
+
right.nil? || last.nil? || last < right
|
107
|
+
end
|
108
|
+
|
109
|
+
def right_of?(left)
|
110
|
+
left.nil? || last.nil? || left < last
|
111
|
+
end
|
112
|
+
|
113
|
+
def has_value?( value)
|
114
|
+
case value
|
115
|
+
when nil
|
116
|
+
true
|
117
|
+
when ::Array
|
118
|
+
value.include?(last.value)
|
119
|
+
else
|
120
|
+
last.value == value
|
121
|
+
end
|
122
|
+
end
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
@@ -0,0 +1,91 @@
|
|
1
|
+
require 'ruby/node/position'
|
2
|
+
|
3
|
+
# Tokens are simple value objects that hold the token type, value and position.
|
4
|
+
# There are a bunch of helper methods to check the token type and convert the
|
5
|
+
# token to Ruby nodes.
|
6
|
+
#
|
7
|
+
# We mostly operate with Ripper's token types (such as :@ident etc.). For Ripper's
|
8
|
+
# sexp types :@kw (keyword) and :@op we use more specific token types based on
|
9
|
+
# the sexp's value. E.g. Ripper's sexp [:@op, '+', [0, 0]] would become a token
|
10
|
+
# with the type :@+.
|
11
|
+
|
12
|
+
class Ripper
|
13
|
+
class RubyBuilder < Ripper::SexpBuilder
|
14
|
+
class Token
|
15
|
+
include Comparable
|
16
|
+
|
17
|
+
attr_accessor :type, :token, :position, :prolog
|
18
|
+
|
19
|
+
def initialize(type = nil, token = nil, position = nil)
|
20
|
+
@type = token_type(type, token)
|
21
|
+
@token = token
|
22
|
+
@position = position if position
|
23
|
+
end
|
24
|
+
|
25
|
+
def newline?
|
26
|
+
NEWLINE.include?(type)
|
27
|
+
end
|
28
|
+
|
29
|
+
def whitespace?
|
30
|
+
WHITESPACE.include?(type)
|
31
|
+
end
|
32
|
+
|
33
|
+
def opener?
|
34
|
+
OPENERS.include?(type)
|
35
|
+
end
|
36
|
+
|
37
|
+
def keyword?
|
38
|
+
KEYWORDS.include?(type)
|
39
|
+
end
|
40
|
+
|
41
|
+
def operator?
|
42
|
+
OPERATORS.include?(type)
|
43
|
+
end
|
44
|
+
|
45
|
+
def separator?
|
46
|
+
SEPARATORS.include?(type)
|
47
|
+
end
|
48
|
+
|
49
|
+
def prolog?
|
50
|
+
whitespace? or separator? or heredoc?
|
51
|
+
end
|
52
|
+
|
53
|
+
def known?
|
54
|
+
keyword? || operator? || opener? || whitespace? || [:@backtick].include?(type)
|
55
|
+
end
|
56
|
+
|
57
|
+
def comment?
|
58
|
+
type == :@comment
|
59
|
+
end
|
60
|
+
|
61
|
+
def heredoc?
|
62
|
+
type == :@heredoc
|
63
|
+
end
|
64
|
+
|
65
|
+
def to_sexp
|
66
|
+
[type, token, [row + 1, column]]
|
67
|
+
end
|
68
|
+
|
69
|
+
def to_identifier
|
70
|
+
Ruby::Identifier.new(token, position, prolog)
|
71
|
+
end
|
72
|
+
|
73
|
+
def <=>(other)
|
74
|
+
position <=> (other.respond_to?(:position) ? other.position : other)
|
75
|
+
end
|
76
|
+
|
77
|
+
protected
|
78
|
+
|
79
|
+
def token_type(type, token)
|
80
|
+
case type
|
81
|
+
when :@kw
|
82
|
+
:"@#{token.gsub(/\W/, '')}"
|
83
|
+
when :@op
|
84
|
+
:"@#{token}"
|
85
|
+
else
|
86
|
+
type
|
87
|
+
end
|
88
|
+
end
|
89
|
+
end
|
90
|
+
end
|
91
|
+
end
|
data/lib/ripper2ruby.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'ripper/ruby_builder'
|
data/lib/ruby.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
Dir[File.dirname(__FILE__) + '/ruby/*.rb'].each do |file|
|
2
|
+
require "ruby/#{File.basename(file)}"
|
3
|
+
end
|
4
|
+
|
5
|
+
# Object oriented representation of Ruby code.
|
6
|
+
#
|
7
|
+
# The base class is Ruby::Node. It facilitates
|
8
|
+
#
|
9
|
+
# * a composite pattern (see Ruby::Node::Composite)
|
10
|
+
# * means for extracting from the original source (see Ruby::Node::Source)
|
11
|
+
#
|
12
|
+
# There are two main concrete classes derived from Node: Token and Aggregate.
|
13
|
+
#
|
14
|
+
# Tokens are "atomic" node types that represent non-composite Ruby constructs
|
15
|
+
# such as Keyword, Identifier, StringContent and literal types such as integers,
|
16
|
+
# floats, true, false, nil etc. Aggregates are composed node types that hold
|
17
|
+
# one or many tokens, such as Class, Module, Block, If, For, Case, While etc.
|
18
|
+
#
|
19
|
+
# Each node type supports the to_ruby method which will return an exact copy
|
20
|
+
# of the orginal code it was parsed from.
|
21
|
+
#
|
22
|
+
# There are also a few helper methods for converting a node to another type
|
23
|
+
# (see Ruby::Node::Conversions) and very few helper methods for altering
|
24
|
+
# existing code structures (see Ruby::Alternation).
|
25
|
+
|
26
|
+
module Ruby
|
27
|
+
include Conversions
|
28
|
+
end
|
@@ -0,0 +1,71 @@
|
|
1
|
+
require 'ruby/node'
|
2
|
+
|
3
|
+
module Ruby
|
4
|
+
class Aggregate < Node
|
5
|
+
def position(prolog = false)
|
6
|
+
nodes = self.nodes
|
7
|
+
nodes.unshift(self.prolog) if prolog
|
8
|
+
nodes.compact.each { |n| return n.position.dup if n } && nil
|
9
|
+
end
|
10
|
+
|
11
|
+
def position=(position)
|
12
|
+
nodes.each { |n| return n.position = position if n }
|
13
|
+
end
|
14
|
+
|
15
|
+
def prolog
|
16
|
+
nodes.each { |n| return n.prolog if n } && nil
|
17
|
+
end
|
18
|
+
|
19
|
+
def prolog=(prolog)
|
20
|
+
nodes.each { |n| return n.prolog = prolog if n }
|
21
|
+
end
|
22
|
+
|
23
|
+
def to_ruby(prolog = false)
|
24
|
+
nodes = self.nodes.compact
|
25
|
+
(nodes.shift.try(:to_ruby, prolog) || '') + nodes.map { |node| node.to_ruby(true) }.join
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
class DelimitedAggregate < Aggregate
|
30
|
+
child_accessor :ldelim, :rdelim
|
31
|
+
|
32
|
+
def initialize(ldelim = nil, rdelim = nil)
|
33
|
+
self.ldelim = ldelim
|
34
|
+
self.rdelim = rdelim
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
class NamedAggregate < DelimitedAggregate
|
39
|
+
child_accessor :identifier
|
40
|
+
|
41
|
+
def initialize(identifier, ldelim = nil, rdelim = nil)
|
42
|
+
self.identifier = identifier
|
43
|
+
super(ldelim, rdelim)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
require 'ruby/token'
|
48
|
+
class Variable < Token # TODO join with DelimitedVariable
|
49
|
+
end
|
50
|
+
|
51
|
+
class DelimitedVariable < DelimitedAggregate
|
52
|
+
child_accessor :identifier
|
53
|
+
|
54
|
+
def initialize(identifier, ldelim = nil)
|
55
|
+
self.identifier = identifier
|
56
|
+
super(ldelim)
|
57
|
+
end
|
58
|
+
|
59
|
+
def value
|
60
|
+
identifier.token.to_sym
|
61
|
+
end
|
62
|
+
|
63
|
+
def nodes
|
64
|
+
[ldelim, identifier].compact
|
65
|
+
end
|
66
|
+
|
67
|
+
def method_missing(method, *args, &block)
|
68
|
+
identifier.respond_to?(method) ? identifier.send(method, *args, &block) : super
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ruby
|
2
|
+
module Alternation
|
3
|
+
module ArgsList
|
4
|
+
def options
|
5
|
+
last.arg.is_a?(Ruby::Hash) ? last : nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def set_option(key, value)
|
9
|
+
if options.nil?
|
10
|
+
# TODO gotta add a separator as well, so maybe better replace the whole options hash
|
11
|
+
self << to_node({key => value}, position.tap { |p| p[1] += length })
|
12
|
+
else
|
13
|
+
options[key] = to_node(value, options[key].position, options[key].prolog)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
protected
|
18
|
+
|
19
|
+
def to_node(arg, position = nil, prolog = nil)
|
20
|
+
arg = super
|
21
|
+
arg.is_a?(Arg) ? arg : Arg.new(arg)
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
module Ruby
|
2
|
+
module Alternation
|
3
|
+
module Hash
|
4
|
+
def [](key)
|
5
|
+
each { |assoc| return assoc.value if assoc.key.value == key } or nil
|
6
|
+
end
|
7
|
+
|
8
|
+
def []=(key, value)
|
9
|
+
value = Ruby.from_native(value, nil, ' ') unless value.is_a?(Node)
|
10
|
+
if assoc = detect { |assoc| assoc.key.value == key }
|
11
|
+
assoc.value = value
|
12
|
+
else
|
13
|
+
# # TODO never happens, fix positions
|
14
|
+
# separators << Token.new(',')
|
15
|
+
# elements << Assoc.new(key, value)
|
16
|
+
# self[key]
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def delete(key)
|
21
|
+
delete_if { |assoc| assoc.key.value == key }
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Ruby
|
2
|
+
module Alternation
|
3
|
+
module List
|
4
|
+
def <<(element)
|
5
|
+
elements << element
|
6
|
+
self
|
7
|
+
end
|
8
|
+
|
9
|
+
def pop
|
10
|
+
[elements.pop]
|
11
|
+
end
|
12
|
+
|
13
|
+
def []=(ix, element)
|
14
|
+
element = to_node(element, self[ix].position, self[ix].prolog)
|
15
|
+
super
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|