diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml index d35471fa..9f95cc9d 100644 --- a/.github/workflows/main.yml +++ b/.github/workflows/main.yml @@ -12,10 +12,12 @@ jobs: - '3.0' - '3.1' - head + - truffleruby-head name: CI runs-on: ubuntu-latest env: CI: true + TESTOPTS: --verbose steps: - uses: actions/checkout@master - uses: ruby/setup-ruby@v1 diff --git a/Rakefile b/Rakefile index 4973d45e..6de81bd8 100644 --- a/Rakefile +++ b/Rakefile @@ -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 diff --git a/lib/syntax_tree/cli.rb b/lib/syntax_tree/cli.rb index b847e059..518b58a6 100644 --- a/lib/syntax_tree/cli.rb +++ b/lib/syntax_tree/cli.rb @@ -192,9 +192,10 @@ 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) diff --git a/lib/syntax_tree/pattern.rb b/lib/syntax_tree/pattern.rb index aa558361..c612c4ea 100644 --- a/lib/syntax_tree/pattern.rb +++ b/lib/syntax_tree/pattern.rb @@ -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 @@ -104,34 +104,37 @@ 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 } + 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| + unless keyword.is_a?(Label) + raise CompilationError, PP.pp(root, +"").chomp + end [keyword.value.chomp(":").to_sym, compile_node(value)] end @@ -148,25 +151,28 @@ 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 diff --git a/test/cli_test.rb b/test/cli_test.rb index b4ef0afc..b5316d7f 100644 --- a/test/cli_test.rb +++ b/test/cli_test.rb @@ -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 @@ -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 = diff --git a/test/idempotency_test.rb b/test/idempotency_test.rb index 1f560db2..76116572 100644 --- a/test/idempotency_test.rb +++ b/test/idempotency_test.rb @@ -1,6 +1,6 @@ # frozen_string_literal: true -return unless ENV["CI"] +return unless ENV["CI"] and RUBY_ENGINE != "truffleruby" require_relative "test_helper" module SyntaxTree diff --git a/test/location_test.rb b/test/location_test.rb index 2a697281..26831fb1 100644 --- a/test/location_test.rb +++ b/test/location_test.rb @@ -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 diff --git a/test/node_test.rb b/test/node_test.rb index 1a5af125..ce26f9ea 100644 --- a/test/node_test.rb +++ b/test/node_test.rb @@ -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 diff --git a/test/test_helper.rb b/test/test_helper.rb index 80e514f0..c421d8ee 100644 --- a/test/test_helper.rb +++ b/test/test_helper.rb @@ -17,6 +17,8 @@ require "pp" require "minitest/autorun" +SUPPORTS_PATTERN_MATCHING = RUBY_ENGINE != "truffleruby" + module SyntaxTree module Assertions class Recorder @@ -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)