Skip to content

Commit

Permalink
Merge pull request #2137 from herwinw/pattern_match_local_variable
Browse files Browse the repository at this point in the history
Allow local variables of LHS in MatchRequiredNode
  • Loading branch information
seven1m committed Jun 24, 2024
2 parents ea9e31d + 51c7a97 commit 9a96003
Show file tree
Hide file tree
Showing 3 changed files with 27 additions and 21 deletions.
10 changes: 4 additions & 6 deletions lib/natalie/compiler/pass1.rb
Original file line number Diff line number Diff line change
Expand Up @@ -109,10 +109,6 @@ def expand_macros(node)
@macro_expander.expand(node, locals: locals, depth: @depth, file: @file)
end

def current_locals
@locals_stack.last
end

def transform_body(body, location:, used:)
return transform_begin_node(body, used:) if body.is_a?(Prism::BeginNode)
body = body.body if body.is_a?(Prism::StatementsNode)
Expand Down Expand Up @@ -2005,8 +2001,10 @@ def transform_match_last_line_node(node, used:)
end

def transform_match_required_node(node, used:)
match_required_node_compiler = Transformers::MatchRequiredNode.new(self)
instructions = match_required_node_compiler.call(node)
match_required_node_compiler = Transformers::MatchRequiredNode.new
code_str = match_required_node_compiler.call(node)
parser = Natalie::Parser.new(code_str, @file.path, locals: @locals_stack.last)
instructions = transform_expression(parser.ast.statements, used: false)
instructions << PushNilInstruction.new if used
instructions
end
Expand Down
18 changes: 3 additions & 15 deletions lib/natalie/compiler/transformers/match_required_node.rb
Original file line number Diff line number Diff line change
Expand Up @@ -4,12 +4,6 @@ module Natalie
class Compiler
module Transformers
class MatchRequiredNode
attr_reader :compiler

def initialize(compiler)
@compiler = compiler
end

def call(node)
case node.pattern.type
when :array_pattern_node
Expand All @@ -30,7 +24,7 @@ def transform_array_pattern_node(node, value)

# Transform `expr => [a, b] into `a, b = ->(expr) { expr.deconstruct }.call(expr)`
target = node.requireds.map(&:name).join(', ')
code_str = <<~RUBY
<<~RUBY
#{target} = lambda do |result|
values = result.deconstruct
if values.size != #{node.requireds.size}
Expand All @@ -41,32 +35,26 @@ def transform_array_pattern_node(node, value)
raise ::NoMatchingPatternError, "\#{result}: \#{result} does not respond to #deconstruct"
end.call(#{value.location.slice})
RUBY
parser = Natalie::Parser.new(code_str, compiler.file.path, locals: compiler.current_locals)
compiler.transform_expression(parser.ast.statements, used: false)
end

def transform_eqeqeq_check(node, value)
# Transform `expr => var` into `->(res, var) { res === var }.call(expr, var)`
code_str = <<~RUBY
<<~RUBY
lambda do |result, expect|
unless expect === result
raise ::NoMatchingPatternError, "\#{result}: \#{expect} === \#{result} does not return true"
end
end.call(#{value.location.slice}, #{node.location.slice})
RUBY
parser = Natalie::Parser.new(code_str, compiler.file.path, locals: compiler.current_locals)
compiler.transform_expression(parser.ast.statements, used: false)
end

def transform_local_variable_target_node(node, value)
# Transform `expr => var` into `var = ->(res) { res }.call(expr)`
code_str = <<~RUBY
<<~RUBY
#{node.name} = lambda do |result|
result
end.call(#{value.location.slice})
RUBY
parser = Natalie::Parser.new(code_str, compiler.file.path, locals: [node.name])
compiler.transform_expression(parser.ast.statements, used: false)
end
end
end
Expand Down
20 changes: 20 additions & 0 deletions test/natalie/pattern_matching_test.rb
Original file line number Diff line number Diff line change
@@ -1,12 +1,32 @@
require_relative '../spec_helper'

module PatternMatchingHelper
def self.one = 1
end

# NATFIXME: Temprorary file until we can run some things in `language/pattern_matching_spec.rb`
describe 'pattern matching' do
it 'can assign a single value' do
1 => a
a.should == 1
end

it 'can assign a single value from an expression' do
1 + 1 => a
a.should == 2
end

it 'can assign a single value from a variable' do
a = 1
a => b
b.should == 1
end

it 'can assign a single value from a method call' do
PatternMatchingHelper.one => a
a.should == 1
end

it 'does not define a local variable if the expression fails' do
-> {
(raise RuntimeError, 'expected error'; 2) => a
Expand Down

0 comments on commit 9a96003

Please sign in to comment.