Skip to content

Commit

Permalink
Update to latest prism
Browse files Browse the repository at this point in the history
  • Loading branch information
kddnewton committed Sep 27, 2023
1 parent 3102cfb commit 30a56c0
Show file tree
Hide file tree
Showing 12 changed files with 114 additions and 88 deletions.
6 changes: 6 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,10 @@ gem "rake"
gem "rubocop"
gem "test-unit"

if File.directory?("../../ruby/prism")
gem "prism", path: "../../ruby/prism"
else
gem "prism"
end

gemspec
13 changes: 9 additions & 4 deletions Gemfile.lock
Original file line number Diff line number Diff line change
@@ -1,9 +1,14 @@
PATH
remote: ../../ruby/prism
specs:
prism (0.12.0)

PATH
remote: .
specs:
parser-yarp (0.1.0)
parser-prism (0.1.0)
parser
yarp
prism

GEM
remote: https://rubygems.org/
Expand Down Expand Up @@ -41,14 +46,14 @@ GEM
test-unit (3.6.1)
power_assert
unicode-display_width (2.4.2)
yarp (0.12.0)

PLATFORMS
arm64-darwin-22

DEPENDENCIES
benchmark-ips
parser-yarp!
parser-prism!
prism!
rake
rubocop
test-unit
Expand Down
42 changes: 21 additions & 21 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,13 +1,13 @@
# parser-yarp
# parser-prism

This is an early experiment in building the [whitequark/parser](https://github.com/whitequark/parser) gem's syntax tree using the [YARP](https://github.com/ruby/yarp) parser.
This is an early experiment in building the [whitequark/parser](https://github.com/whitequark/parser) gem's syntax tree using the [prism](https://github.com/ruby/prism) parser.

## Installation

Add this line to your application's Gemfile:

```ruby
gem "parser-yarp"
gem "parser-prism"
```

And then execute:
Expand All @@ -16,53 +16,53 @@ And then execute:

Or install it yourself as:

$ gem install parser-yarp
$ gem install parser-prism

## Usage

The `parser` gem provides multiple parsers to support different versions of the Ruby grammar. This includes all of the Ruby versions going back to 1.8, as well as third-party parsers like MacRuby and RubyMotion. The `parser-yarp` gem provides another parser that uses the `YARP` parser to build the syntax tree.
The `parser` gem provides multiple parsers to support different versions of the Ruby grammar. This includes all of the Ruby versions going back to 1.8, as well as third-party parsers like MacRuby and RubyMotion. The `parser-prism` gem provides another parser that uses the `prism` parser to build the syntax tree.

You can use the `YARP` parser like you would any other. After requiring the parser, you should be able to call any of the regular `Parser::Base` APIs that you would normally use.
You can use the `prism` parser like you would any other. After requiring the parser, you should be able to call any of the regular `Parser::Base` APIs that you would normally use.

```ruby
require "parser/yarp"
require "parser/prism"

Parser::YARP.parse_file("path/to/file.rb")
Parser::Prism.parse_file("path/to/file.rb")
```

### RuboCop

To run RuboCop using the `parser-yarp` gem as the parser, you will need to require the `parser/yarp/rubocop` file. This file injects `YARP` into the known options for both `rubocop` and `rubocop-ast`, such that you can specify it in your `.rubocop.yml` file. Unfortunately `rubocop` doesn't support any direct way to do this, so we have to get a bit hacky.
To run RuboCop using the `parser-prism` gem as the parser, you will need to require the `parser/prism/rubocop` file. This file injects `prism` into the known options for both `rubocop` and `rubocop-ast`, such that you can specify it in your `.rubocop.yml` file. Unfortunately `rubocop` doesn't support any direct way to do this, so we have to get a bit hacky.

First, set the `TargetRubyVersion` in your RuboCop configuration file to `89_65_82_80.33`. This is the version of Ruby that `YARP` reports itself as. (The leading numbers are the ASCII values for `YARP`.)
First, set the `TargetRubyVersion` in your RuboCop configuration file to `89_65_82_80.33`. This is the version of Ruby that `prism` reports itself as. (The leading numbers are the ASCII values for `PRISM`.)

```yaml
AllCops:
TargetRubyVersion: 89_65_82_80.33
```
Now when you run `rubocop` you will need to require the `parser/yarp/rubocop` file before executing so that it can inject the `YARP` parser into the known options.
Now when you run `rubocop` you will need to require the `parser/prism/rubocop` file before executing so that it can inject the `prism` parser into the known options.

```
bundle exec ruby -rparser/yarp/rubocop $(bundle exec which rubocop)
bundle exec ruby -rparser/prism/rubocop $(bundle exec which rubocop)
```
This should run RuboCop using the `YARP` parser.
This should run RuboCop using the `prism` parser.
## Benchmarks
As a whole, this parser should be significantly faster than the `parser` gem. The `bin/bench` script in this repository compares the performance of `Parser::CurrentRuby` and `Parser::YARP`. Running against a large file like `lib/parser/yarp/compiler.rb` yields:
As a whole, this parser should be significantly faster than the `parser` gem. The `bin/bench` script in this repository compares the performance of `Parser::CurrentRuby` and `Parser::Prism`. Running against a large file like `lib/parser/prism/compiler.rb` yields:
```
Warming up --------------------------------------
Parser::CurrentRuby 1.000 i/100ms
Parser::YARP 6.000 i/100ms
Parser::Prism 6.000 i/100ms
Calculating -------------------------------------
Parser::CurrentRuby 17.184 (± 0.0%) i/s - 86.000 in 5.006166s
Parser::YARP 68.121 (± 2.9%) i/s - 342.000 in 5.023905s
Parser::Prism 68.121 (± 2.9%) i/s - 342.000 in 5.023905s

Comparison:
Parser::YARP: 68.1 i/s
Parser::Prism: 68.1 i/s
Parser::CurrentRuby: 17.2 i/s - 3.96x slower
```
Expand All @@ -71,13 +71,13 @@ When running with `--yjit`, the comparison is even more stark:
```
Warming up --------------------------------------
Parser::CurrentRuby 1.000 i/100ms
Parser::YARP 9.000 i/100ms
Parser::Prism 9.000 i/100ms
Calculating -------------------------------------
Parser::CurrentRuby 19.570 (± 5.1%) i/s - 98.000 in 5.016435s
Parser::YARP 109.392 (± 4.6%) i/s - 549.000 in 5.032282s
Parser::Prism 109.392 (± 4.6%) i/s - 549.000 in 5.032282s

Comparison:
Parser::YARP: 109.4 i/s
Parser::Prism: 109.4 i/s
Parser::CurrentRuby: 19.6 i/s - 5.59x slower
```
Expand All @@ -89,7 +89,7 @@ Run `rake test` to run the tests. This runs tests exported from the `parser` gem
## Contributing
Bug reports and pull requests are welcome on GitHub at https://github.com/kddnewton/parser-yarp.
Bug reports and pull requests are welcome on GitHub at https://github.com/kddnewton/parser-prism.
## License
Expand Down
6 changes: 3 additions & 3 deletions bin/bench
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,13 @@

require "bundler/setup"
require "benchmark/ips"
require "parser/yarp"
require "parser/prism"
require "parser/current"

filepath = ARGV.fetch(0) { File.expand_path("../lib/parser/yarp/compiler.rb", __dir__) }
filepath = ARGV.fetch(0) { File.expand_path("../lib/parser/prism/compiler.rb", __dir__) }

Benchmark.ips do |x|
x.report("Parser::CurrentRuby") { Parser::CurrentRuby.parse_file(filepath) }
x.report("Parser::YARP") { Parser::YARP.parse_file(filepath) }
x.report("Parser::Prism") { Parser::Prism.parse_file(filepath) }
x.compare!
end
10 changes: 5 additions & 5 deletions bin/parse
Original file line number Diff line number Diff line change
Expand Up @@ -4,20 +4,20 @@
require "bundler/setup"
$:.unshift(File.expand_path("../lib", __dir__))

require "parser/yarp"
require "parser/yarp/compare"
require "parser/prism"
require "parser/prism/compare"

if ARGV[0].nil?
filepaths = Dir[File.expand_path("../test/fixtures/*.rb", __dir__)]
failures = 0

filepaths.each do |filepath|
failures += 1 unless Parser::YARP.compare(filepath)
failures += 1 unless Parser::Prism.compare(filepath)
end

puts "#{filepaths.size - failures}/#{filepaths.size} tests passed"
elsif ARGV[0] == "-e"
puts "match!" if Parser::YARP.compare("-e", ARGV[1])
puts "match!" if Parser::Prism.compare("-e", ARGV[1])
else
puts "match!" if Parser::YARP.compare(ARGV[0])
puts "match!" if Parser::Prism.compare(ARGV[0])
end
16 changes: 8 additions & 8 deletions lib/parser/yarp.rb → lib/parser/prism.rb
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
# frozen_string_literal: true

require "parser"
require "yarp"
require "prism"

module Parser
class YARP < Base
class Prism < Base
Racc_debug_parser = false

def version
Expand All @@ -24,7 +24,7 @@ def default_encoding
def parse(source_buffer)
@source_buffer = source_buffer

build_ast(::YARP.parse(source_buffer.source, source_buffer.name).value)
build_ast(::Prism.parse(source_buffer.source, source_buffer.name).value)
ensure
@source_buffer = nil
end
Expand All @@ -39,7 +39,7 @@ def parse(source_buffer)
def parse_with_comments(source_buffer)
@source_buffer = source_buffer

result = ::YARP.parse(source_buffer.source, source_buffer.name)
result = ::Prism.parse(source_buffer.source, source_buffer.name)
[build_ast(result.value), build_comments(result.comments)]
ensure
@source_buffer = nil
Expand All @@ -55,15 +55,15 @@ def parse_with_comments(source_buffer)
def tokenize(source_buffer, _recover = false)
@source_buffer = source_buffer

result = ::YARP.parse_lex(source_buffer.source, source_buffer.name)
result = ::Prism.parse_lex(source_buffer.source, source_buffer.name)
program, tokens = result.value

[build_ast(program), build_comments(result.comments), build_tokens(tokens)]
ensure
@source_buffer = nil
end

# Since YARP resolves num params for us, we don't need to support this kind
# Since prism resolves num params for us, we don't need to support this kind
# of logic here.
def try_declare_numparam(node)
node.children[0].match?(/\A_[1-9]\z/)
Expand Down Expand Up @@ -91,5 +91,5 @@ def build_tokens(tokens)
end
end

require_relative "yarp/compiler"
require_relative "yarp/lexer"
require_relative "prism/compiler"
require_relative "prism/lexer"
4 changes: 2 additions & 2 deletions lib/parser/yarp/compare.rb → lib/parser/prism/compare.rb
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ def ==(other)
)

module Parser
class YARP
class Prism
# Compare the ASTs between the translator and the whitequark/parser gem.
def self.compare(filepath, source = nil, compare_tokens: true)
buffer = Source::Buffer.new(filepath, 1)
Expand All @@ -46,7 +46,7 @@ def self.compare(filepath, source = nil, compare_tokens: true)
return true
end

actual_ast, actual_comments, actual_tokens = YARP.new.tokenize(buffer)
actual_ast, actual_comments, actual_tokens = Prism.new.tokenize(buffer)

if expected_ast != actual_ast
puts filepath
Expand Down
Loading

0 comments on commit 30a56c0

Please sign in to comment.