Skip to content

Make the test suite pass on TruffleRuby and add it in CI #183

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 7 commits into from
Oct 28, 2022
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 7 additions & 1 deletion Rakefile
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,13 @@ require "syntax_tree/rake_tasks"
Rake::TestTask.new(:test) do |t|
t.libs << "test"
t.libs << "lib"
t.test_files = FileList["test/**/*_test.rb"]
test_files = FileList["test/**/*_test.rb"]
if RUBY_ENGINE == "truffleruby"
# language_server.rb uses pattern matching
test_files -= FileList["test/language_server/*_test.rb"]
test_files -= FileList["test/language_server_test.rb"]
end
t.test_files = test_files
end

task default: :test
Expand Down
6 changes: 3 additions & 3 deletions lib/syntax_tree/cli.rb
Original file line number Diff line number Diff line change
Expand Up @@ -192,9 +192,9 @@ def run(item)
# would match the first expression of the input given.
class Expr < Action
def run(item)
case item.handler.parse(item.source)
in Program[statements: Statements[body: [expression]]]
puts expression.construct_keys
program = item.handler.parse(item.source)
if Program === program and expressions = program.statements.body and expressions.size == 1
puts expressions.first.construct_keys
else
warn("The input to `stree expr` must be a single expression.")
exit(1)
Expand Down
63 changes: 31 additions & 32 deletions lib/syntax_tree/pattern.rb
Original file line number Diff line number Diff line change
Expand Up @@ -84,11 +84,11 @@ def combine_or(left, right)
end

def compile_node(root)
case root
in AryPtn[constant:, requireds:, rest: nil, posts: []]
if AryPtn === root and root.rest.nil? and root.posts.empty?
constant = root.constant
compiled_constant = compile_node(constant) if constant

preprocessed = requireds.map { |required| compile_node(required) }
preprocessed = root.requireds.map { |required| compile_node(required) }

compiled_requireds = ->(node) do
deconstructed = node.deconstruct
Expand All @@ -104,34 +104,32 @@ def compile_node(root)
else
compiled_requireds
end
in Binary[left:, operator: :|, right:]
combine_or(compile_node(left), compile_node(right))
in Const[value:] if SyntaxTree.const_defined?(value)
clazz = SyntaxTree.const_get(value)
elsif Binary === root and root.operator == :|
combine_or(compile_node(root.left), compile_node(root.right))
elsif Const === root and SyntaxTree.const_defined?(root.value)
clazz = SyntaxTree.const_get(root.value)

->(node) { node.is_a?(clazz) }
in Const[value:] if Object.const_defined?(value)
clazz = Object.const_get(value)
elsif Const === root and Object.const_defined?(root.value)
clazz = Object.const_get(root.value)

->(node) { node.is_a?(clazz) }
in ConstPathRef[
parent: VarRef[value: Const[value: "SyntaxTree"]], constant:
]
compile_node(constant)
in DynaSymbol[parts: []]
elsif ConstPathRef === root and VarRef === root.parent and Const === root.parent.value and root.parent.value.value == "SyntaxTree"
compile_node(root.constant)
elsif DynaSymbol === root and root.parts.empty?
symbol = :""

->(node) { node == symbol }
in DynaSymbol[parts: [TStringContent[value:]]]
symbol = value.to_sym
elsif DynaSymbol === root and parts = root.parts and parts.size == 1 and TStringContent === parts[0]
symbol = parts[0].value.to_sym

->(attribute) { attribute == value }
in HshPtn[constant:, keywords:, keyword_rest: nil]
compiled_constant = compile_node(constant)
->(node) { node == symbol }
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tried to add a test to validate this change but couldn't find a way to trigger this case.
I tried things like:

    def test_search_string_simple
      results = search(%{"#{'abc'}"}, "StringLiteral[parts: [TStringContent[value: 'abc']]]")

      assert_equal 1, results.length
    end

    def test_search_symbol_simple
      puts %s{:"#{'abc'}"}
      puts ':"#{\'abc\'}"'
      results = search(':"#{\'abc\'}"', ':abc')
      results = search(':"#{\'abc\'}"', ':"#{\'abc\'}"')
      results = search(%{"#{'abc'}"}, "DynaSymbol[parts: [TStringContent[value: 'abc']]]")

      assert_equal 1, results.length
    end

But none seem to match this specific condition.

elsif HshPtn === root and root.keyword_rest.nil?
compiled_constant = compile_node(root.constant)

preprocessed =
keywords.to_h do |keyword, value|
raise NoMatchingPatternError unless keyword.is_a?(Label)
root.keywords.to_h do |keyword, value|
raise CompilationError, PP.pp(root, +"").chomp unless keyword.is_a?(Label)
[keyword.value.chomp(":").to_sym, compile_node(value)]
end

Expand All @@ -148,25 +146,26 @@ def compile_node(root)
else
compiled_keywords
end
in RegexpLiteral[parts: [TStringContent[value:]]]
regexp = /#{value}/
elsif RegexpLiteral === root and parts = root.parts and parts.size == 1 and TStringContent === parts[0]
regexp = /#{parts[0].value}/

->(attribute) { regexp.match?(attribute) }
in StringLiteral[parts: []]
elsif StringLiteral === root and root.parts.empty?
->(attribute) { attribute == "" }
in StringLiteral[parts: [TStringContent[value:]]]
elsif StringLiteral === root and parts = root.parts and parts.size == 1 and TStringContent === parts[0]
value = parts[0].value
->(attribute) { attribute == value }
in SymbolLiteral[value:]
symbol = value.value.to_sym
elsif SymbolLiteral === root
symbol = root.value.value.to_sym

->(attribute) { attribute == symbol }
in VarRef[value: Const => value]
compile_node(value)
in VarRef[value: Kw[value: "nil"]]
elsif VarRef === root and Const === root.value
compile_node(root.value)
elsif VarRef === root and Kw === root.value and root.value.value.nil?
->(attribute) { attribute.nil? }
else
raise CompilationError, PP.pp(root, +"").chomp
end
rescue NoMatchingPatternError
raise CompilationError, PP.pp(root, +"").chomp
end
end
end
2 changes: 2 additions & 0 deletions test/cli_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,7 @@ def test_inline_script
end

def test_multiple_inline_scripts
skip if RUBY_ENGINE == "truffleruby" # Relies on a thread-safe StringIO
stdio, = capture_io { SyntaxTree::CLI.run(%w[format -e 1+1 -e 2+2]) }
assert_equal(["1 + 1", "2 + 2"], stdio.split("\n").sort)
end
Expand All @@ -172,6 +173,7 @@ def test_plugins
def test_language_server
prev_stdin = $stdin
prev_stdout = $stdout
skip unless SUPPORTS_PATTERN_MATCHING

request = { method: "shutdown" }.merge(jsonrpc: "2.0").to_json
$stdin =
Expand Down
12 changes: 4 additions & 8 deletions test/location_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,15 @@ def test_lines
def test_deconstruct
location = Location.fixed(line: 1, char: 0, column: 0)

case location
in [start_line, 0, 0, *]
assert_equal(1, start_line)
end
assert_equal(1, location.start_line)
assert_equal(0, location.start_char)
assert_equal(0, location.start_column)
end

def test_deconstruct_keys
location = Location.fixed(line: 1, char: 0, column: 0)

case location
in start_line:
assert_equal(1, start_line)
end
assert_equal(1, location.start_line)
end
end
end
7 changes: 3 additions & 4 deletions test/node_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -759,10 +759,9 @@ def test_program
program = parser.parse
refute(parser.error?)

case program
in statements: { body: [statement] }
assert_kind_of(VCall, statement)
end
statements = program.statements.body
assert_equal 1, statements.size
assert_kind_of(VCall, statements.first)

json = JSON.parse(program.to_json)
io = StringIO.new
Expand Down
22 changes: 13 additions & 9 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,8 @@
require "pp"
require "minitest/autorun"

SUPPORTS_PATTERN_MATCHING = RUBY_ENGINE != "truffleruby"

module SyntaxTree
module Assertions
class Recorder
Expand Down Expand Up @@ -67,15 +69,17 @@ def assert_syntax_tree(node)
refute_includes(json, "#<")
assert_equal(type, JSON.parse(json)["type"])

# Get a match expression from the node, then assert that it can in fact
# match the node.
# rubocop:disable all
assert(eval(<<~RUBY))
case node
in #{node.construct_keys}
true
end
RUBY
if SUPPORTS_PATTERN_MATCHING
# Get a match expression from the node, then assert that it can in fact
# match the node.
# rubocop:disable all
assert(eval(<<~RUBY))
case node
in #{node.construct_keys}
true
end
RUBY
end
end

Minitest::Test.include(self)
Expand Down