Permalink
Browse files

Parsing of expander assignments.

The ruby-lint parser is now capable of parsing code such as `*numbers = 10` and
`*numbers, number = 10`

This commit fixes #6.

Signed-off-by: Yorick Peterse <yorickpeterse@gmail.com>
  • Loading branch information...
1 parent 3db5a75 commit 348f07e234b45e448f01b9ab75f164afdbbb9781 @YorickPeterse committed Nov 14, 2012
Showing with 251 additions and 106 deletions.
  1. +68 −37 lib/ruby-lint/parser.rb
  2. +183 −0 spec/ruby-lint/parser/expander_assignments.rb
  3. +0 −69 spec/ruby-lint/parser/variables.rb
View
@@ -62,7 +62,7 @@ class Parser < Ripper::SexpBuilderPP
:block_var,
:const_ref,
:top_const_ref,
- :mlhs_add_star
+ :mlhs_paren
]
##
@@ -393,6 +393,17 @@ def on_dot2(start, stop)
end
##
+ # Called when a range using 3 dots is found.
+ #
+ # @see RubyLint::Parser#on_dot2
+ # @todo There's a difference between ranges created using two and three
+ # dots, this method should return a different token in the future.
+ #
+ def on_dot3(start, stop)
+ return on_dot2(start, stop)
+ end
+
+ ##
# Called when a regular expression is found.
#
# @param [Array] regexp The regular expression's value.
@@ -403,17 +414,21 @@ def on_regexp_literal(regexp, modes)
regexp = regexp[0]
modes_array = []
+ value = regexp.respond_to?(:value) ? regexp.value : nil
+ line = regexp.respond_to?(:line) ? regexp.line : lineno
+ col = regexp.respond_to?(:column) ? regexp.column : column
+
if modes
modes_array = modes.value.split('').select { |c| c =~ /\w/ }
end
return Token::RegexpToken.new(
:type => :regexp,
- :value => regexp.value,
- :line => regexp.line,
- :column => regexp.column,
+ :value => value,
+ :line => line,
+ :column => col,
:modes => modes_array,
- :code => code(lineno)
+ :code => code(line)
)
end
@@ -462,38 +477,23 @@ def on_assign(variable, value)
#
def on_massign(variables, values)
assignments = []
- assigned = false
-
- variables.each_with_index do |variable, index|
- value = nil
-
- # Determine what value to use for the current variable.
- if values.is_a?(Array) and values[index]
- value = values[index]
-
- # A single value (e.g. an array or number) is assigned to multiple
- # variables.
- elsif values.is_a?(Token::Token)
- # A token with a list of values is being assigned (e.g. an array).
- if values.value.is_a?(Array) and values.value[index]
- value = values.value[index]
-
- # A single value is being assigned such as `foo, bar = 10`.
- elsif !assigned
- value = values
- assigned = true
- end
- end
-
- # Values set using expand assignments are always arrays.
- if variable.expand and value
- value = Token::Token.new(
+ variables = variables.flatten
+ assigned = []
+ expander = nil
+ value_index = 0
+
+ variables.each_with_index do |variable, var_index|
+ if variable.expand
+ expander = var_index
+ value = Token::Token.new(
:type => :array,
- :line => value.line,
- :column => value.column,
- :code => value.code,
- :value => [value]
+ :line => variable.line,
+ :column => variable.column,
+ :code => variable.code,
+ :value => []
)
+ else
+ value = nil
end
assignments << Token::AssignmentToken.new(
@@ -502,11 +502,42 @@ def on_massign(variables, values)
:column => variable.column,
:code => variable.code,
:type => variable.type,
- :expand => variable.expand,
- :value => value,
+ :expand => variable.expand,
+ :value => value
)
end
+ # Unpack array based values and ensure that `values` is always an array.
+ if values.is_a?(Token::Token) and values.type == :array
+ values = values.value
+ elsif values.is_a?(Token::Token)
+ values = [values]
+ end
+
+ equal_length = assignments.length == values.length
+
+ # Assign the values to each variable. The remaining values will be
+ # assigned to the expanding variable.
+ assignments.each do |assignment|
+ if !assignment.expand and values[value_index]
+ assignment.value = values[value_index]
+
+ assigned << values[value_index]
+ value_index += 1
+ end
+
+ # THINK: I really wonder if this is the way to make sure the expander
+ # value is assigned correctly.
+ if assignment.expand and equal_length
+ value_index += 1
+ end
+ end
+
+ # Assign the remaining values to the expander variable.
+ if expander
+ assignments[expander].value.value = values - assigned
+ end
+
return assignments
end
@@ -0,0 +1,183 @@
+require File.expand_path('../../../helper', __FILE__)
+
+describe 'Rlint::Parser' do
+ it 'Parse the assignment of a value to a local and * variable' do
+ ast = RubyLint::Parser.new('a, * = 10').parse[0]
+
+ ast.class.should == Array
+ ast.length.should == 1
+
+ token = ast[0]
+
+ token.class.should == RubyLint::Token::AssignmentToken
+ token.type.should == :local_variable
+ token.name.should == 'a'
+
+ token.value.class.should == RubyLint::Token::Token
+ token.value.type.should == :integer
+ token.value.value.should == '10'
+ end
+
+ it 'Parse a left hand expand assignment' do
+ tokens = RubyLint::Parser.new('*numbers = 10').parse[0]
+
+ tokens.class.should == Array
+ tokens.length.should == 1
+
+ token = tokens[0]
+
+ token.class.should == RubyLint::Token::AssignmentToken
+ token.type.should == :local_variable
+ token.expand.should == true
+ token.name.should == 'numbers'
+
+ token.value.class.should == RubyLint::Token::Token
+ token.value.type.should == :array
+
+ token.value.value.class.should == Array
+ token.value.value.length.should == 1
+
+ val = token.value.value[0]
+
+ val.class.should == RubyLint::Token::Token
+ val.type.should == :integer
+ val.value.should == '10'
+ end
+
+ it 'Parse a left hand expand and local variable assignment' do
+ tokens = RubyLint::Parser.new('number, *numbers = 10').parse[0]
+
+ tokens.class.should == Array
+ tokens.length.should == 2
+
+ number = tokens[0]
+ numbers = tokens[1]
+
+ number.class.should == RubyLint::Token::AssignmentToken
+ number.name.should == 'number'
+ number.type.should == :local_variable
+ number.expand.should == false
+
+ number.value.class.should == RubyLint::Token::Token
+ number.value.type.should == :integer
+ number.value.value.should == '10'
+
+ numbers.class.should == RubyLint::Token::AssignmentToken
+ numbers.name.should == 'numbers'
+ numbers.type.should == :local_variable
+ numbers.expand.should == true
+
+ numbers.value.class.should == RubyLint::Token::Token
+ numbers.value.type.should == :array
+ numbers.value.value.length.should == 0
+ end
+
+ it 'Parse a mass assignment using an expand variable on the left' do
+ tokens = RubyLint::Parser.new('*numbers, number, numberx = 10').parse[0]
+
+ tokens.class.should == Array
+ tokens.length.should == 3
+
+ numbers = tokens[0]
+ number = tokens[1]
+ numberx = tokens[2]
+
+ numbers.class.should == RubyLint::Token::AssignmentToken
+ numbers.name.should == 'numbers'
+ numbers.type.should == :local_variable
+ numbers.expand.should == true
+
+ numbers.value.class.should == RubyLint::Token::Token
+ numbers.value.type.should == :array
+ numbers.value.value.class.should == Array
+ numbers.value.value.length.should == 0
+
+ number.class.should == RubyLint::Token::AssignmentToken
+ number.name.should == 'number'
+ number.type.should == :local_variable
+ number.expand.should == false
+
+ number.value.class.should == RubyLint::Token::Token
+ number.value.type.should == :integer
+ number.value.value.should == '10'
+
+ numberx.class.should == RubyLint::Token::AssignmentToken
+ numberx.name.should == 'numberx'
+ numberx.type.should == :local_variable
+ numberx.expand.should == false
+
+ numberx.value.nil?.should == true
+ end
+
+ it 'Parse the assignment of 2 values to 2 variables using an expander' do
+ tokens = RubyLint::Parser.new('*numbers, number = 10, 20').parse[0]
+
+ tokens.class.should == Array
+ tokens.length.should == 2
+
+ numbers = tokens[0]
+ number = tokens[1]
+
+ numbers.class.should == RubyLint::Token::AssignmentToken
+ numbers.name.should == 'numbers'
+ numbers.type.should == :local_variable
+
+ numbers.value.class.should == RubyLint::Token::Token
+ numbers.value.type.should == :array
+
+ numbers.value.value.class.should == Array
+ numbers.value.value.length.should == 1
+
+ numbers_val = numbers.value.value[0]
+
+ numbers_val.class.should == RubyLint::Token::Token
+ numbers_val.type.should == :integer
+ numbers_val.value.should == '10'
+
+ number.class.should == RubyLint::Token::AssignmentToken
+ number.name.should == 'number'
+ number.type.should == :local_variable
+
+ number.value.class.should == RubyLint::Token::Token
+ number.value.type.should == :integer
+ number.value.value.should == '20'
+ end
+
+ it 'Parse the assignment of 2 values to 3 variables using an expander' do
+ tokens = RubyLint::Parser.new('*numbers, number, numberx = 10, 20') \
+ .parse[0]
+
+ tokens.class.should == Array
+ tokens.length.should == 3
+
+ numbers = tokens[0]
+ number = tokens[1]
+ numberx = tokens[2]
+
+ numbers.class.should == RubyLint::Token::AssignmentToken
+ numbers.name.should == 'numbers'
+ numbers.type.should == :local_variable
+
+ numbers.value.class.should == RubyLint::Token::Token
+ numbers.value.type.should == :array
+
+ numbers.value.value.class.should == Array
+ numbers.value.value.length.should == 0
+
+ number.class.should == RubyLint::Token::AssignmentToken
+ number.name.should == 'number'
+ number.type.should == :local_variable
+
+ number.value.class.should == RubyLint::Token::Token
+ number.value.type.should == :integer
+ number.value.value.should == '10'
+
+ numberx.class.should == RubyLint::Token::AssignmentToken
+ numberx.name.should == 'numberx'
+ numberx.type.should == :local_variable
+
+ numberx.value.class.should == RubyLint::Token::Token
+ numberx.value.type.should == :integer
+ numberx.value.value.should == '20'
+ end
+end
Oops, something went wrong.

0 comments on commit 348f07e

Please sign in to comment.