Giter VIP home page Giter VIP logo

ruby-ll's Introduction

ruby-ll

ruby-ll is a high performance LL(1) table based parser generator for Ruby. The parser driver is written in C/Java to ensure good runtime performance, the compiler is written entirely in Ruby.

ruby-ll was written to serve as a fast and easy to use alternative to Racc for the various parsers used in Oga. However, ruby-ll isn't limited to just Oga, you can use it to write a parser for any language that can be represented using an LL(1) grammar.

ruby-ll is self-hosting, this allows one to use ruby-ll to modify its own parser. Self-hosting was achieved by bootstrapping the parser using a Racc parser that outputs the same AST as the ruby-ll parser. The Racc parser remains in the repository for historical purposes and in case it's ever needed again, it can be found in bootstrap/parser.y.

For more information on LL parsing, see https://en.wikipedia.org/wiki/LL_parser.

Features

  • Support for detecting first/first and first/follow conflicts.
  • clang-like error/warning messages to ease debugging parsers.
  • High performance and a low memory footprint.
  • Support for the *, + and ? operators.

Requirements

Ruby Required
MRI >= 2.6.0
JRuby >= 9.0
Rubinius Not supported
Maglev Not supported
Topaz Not supported
mruby Not supported

For MRI you'll need a C90 compatible compiler such as clang or gcc. For JRuby you don't need any compilers to be installed as the .jar is packaged with the Gem itself.

When hacking on ruby-ll you'll also need to have the following installed:

  • Ragel 6 for building the grammar lexer
  • javac for building the JRuby extension

Installation

ruby-ll can be installed from RubyGems:

gem install ruby-ll

Usage

The CLI takes a grammar input file (see below for the exact syntax) with the extension .rll and turns it into a corresponding Ruby file. For example:

ruby-ll lib/my-gem/parser.rll

This would result in the parser being written to lib/my-gem/parser.rb. If you want to customize the output path you can do so using the -o / --output options:

ruby-ll lib/my-gem/parser.rll -o lib/my-gem/my-parser.rb

By default ruby-ll adds various require calls to ensure you can load the parser without having to load all of ruby-ll (e.g. the compiler code). If you want to disable this behaviour you can use the --no-requires option when processing a grammar:

ruby-ll lib/my-gem/parser.rll --no-requires

Once generated you can use the parser class like any other parser. To start parsing simply call the parse method:

parser = MyGem::Parser.new

parser.parse

The return value of this method is whatever the root rule (= the first rule defined) returned.

Parser Input

For a parser to work it must receive its input from a separate lexer. To pass input to the parser you must define the method each_token in an %inner block. This method should yield an Array containing two values:

  1. The token type as a Symbol (e.g. :T_STRING)
  2. The token value, this can be any type of value

The last Array yielded by this method should be [-1, -1] to signal the end of the input. For example:

def each_token
  yield [:T_STRING, 'foo']
  yield [:T_STRING, 'bar']
  yield [-1, -1]
end

Error Handling

Parser errors are handled by LL::Driver#parser_error. By default this method raises an instance of LL::ParserError with a message depending on the current parser context and input. If you want to customize this behaviour simply overwrite the method (e.g. in an %inner block).

Grammar Syntax

The syntax of a ruby-ll grammar file is fairly simple and consists out of directives, rules, comments and code blocks.

Directives can be seen as configuration options, for example to set the name of the parser class. Rules are, well, the parsing rules. Code blocks can be used to associate Ruby code with either a branch of a rule or a certain section of the parser (the header or its inner body).

Directives and rules must be terminated using a semicolon, this is not needed for %inner / %header blocks.

For a full example, see ruby-ll's own parser located at lib/ll/parser.rll.

Comments

Comments start with a hash (#) sign and continue until the end of the line, just like Ruby. Example:

# Some say comments are a code smell.

%name

The %name directive is used to set the full name/namespace of the parser class. The name consists out of a single identifier or multiple identifiers separated by :: (just like Ruby). Some examples:

%name A;
%name A::B;
%name A::B::C;

The last identifier is used as the actual class name. This class will be nested inside a module for every other segment leading up to the last one. For example, this:

%name A;

Gets turned into this:

class A < LL::Driver

end

While this:

%name A::B::C;

Gets turned into this:

module A
module B
class C < LL::Driver

end
end
end

By nesting the parser class in modules any constants in the scope can be referred to without requiring the use of a full namespace. For example, the constant A::B::X can just be referred to as X in the above example.

Multiple calls to this directive will result in previous values being overwritten.

%terminals

The %terminals directive is used to list one or more terminals of the grammar. Each terminal is an identifier separated by a space. For example:

%terminals A B C;

This would define 3 terminals: A, B and C. While there's no specific requirement as to how you name your terminals it's common practise to capitalize them and prefix them with T_, like so:

%terminals T_A T_B T_C;

Multiple calls to this directive will result in the terminals being appended to the existing list.

%inner

The %inner directive can be used to specify a code block that should be placed inside the parser's body, just after the section containing all parsing tables. This directive should be used for adding custom methods and such to the parser. For example:

%inner
{
  def initialize(input)
    @input = input
  end
}

This would result in the following:

class A < LL::Driver
  def initialize(input)
    @input = input
  end
end

Curly braces can either be placed on the same line as the %inner directive or on a new line, it's up to you.

Unlike regular directives this directive should not be terminated using a semicolon.

%header

The %header directive is similar to the %inner directive in that it can be used to add a code block to the parser. The code of this directive is placed just before the class definition of the parser. This directive can be used to add documentation to the parser class. For example:

%header
{
  # Hello world
}

This would result in the following:

# Hello world
class A < LL::Driver
end

Rules

Rules consist out of a name followed by an equals sign (=) followed by 1 or more branches. Each branch is separated using a pipe (|). A branch can consist out of 1 or many steps, or an epsilon. Branches can be followed by a code block starting with { and ending with }. A rule must be terminated using a semicolon.

An epsilon is represented as a single underscore (_) and is used to denote a wildcard/nothingness.

A simple example:

%terminals A;

numbers = A | B;

Here the rule numbers is defined and has two branches. If we wanted a rule that would match terminal A or nothing we'd use the following:

%terminals A;

numbers = A | _;

Code blocks can also be added:

numbers
  = A { 'A' }
  | B { 'B' }
  ;

When the terminal A would be processed the returned value would be 'A', for terminal B the returned value would be 'B'.

Code blocks have access to an array called val which contains the values of every step of a branch. For example:

numbers = A B { val };

Here val would return [A, B]. Since val is just an Array you can also return specific elements from it:

numbers = A B { val[0] };

Values returned by code blocks are passed to whatever other rule called it. This allows code blocks to be used for building ASTs and the likes.

If no explicit code block is defined then ruby-ll will generate one for you. If a branch consists out of only a single step (e.g. A = B;) then only the first value is returned, otherwise all values are returned.

This means that in the following example the output will be whatever value C contains:

A = B { p val[0] };
B = C;

However, here the output would be [C, D] as the B rule's branch contains multiple steps:

A = B { p val[0] };
B = C D;

To summarize (# => denotes the return value):

A = B;   # => B
A = B C; # => [B, C]

You can override this behaviour simply by defining your own code block.

ruby-ll parsers recurse into rules before unwinding, this means that the inner-most rule is processed first.

Branches of a rule can also refer to other rules:

numbers    = A other_rule;
other_rule = B;

The value for other_rule in the numbers rule would be whatever the other_rule below it returns.

The grammar compiler adds errors whenever it encounters a rule with the same name as a terminal, as such the following is invalid:

%terminals A B;

A = B;

It's also an error to re-define an existing rule.

Operators

Grammars can use two operators to define a sequence of terminals/non-terminals: the star (*) and plus (+) operators. There's also the ? (question) operator which can be used to indicate something as being optional.

The star operator indicates that something should occur 0 or more times. Here the "B" identifier could occur 0 times, once, twice or many more times:

A = B*;

The plus operator indicates that something should occur at least once followed by any number of more occurrences. For example, this grammar states that "B" should occur at least once but can also occur, say, 10 times:

A = B+;

The question operator can be used as an alternative to the following pattern:

# "A" or "A C"
A = B A_follow;

A_follow = C | _;

Using this operator you can simply write the following:

A = B C?;

Operators can be applied either to a single terminal/rule or a series of terminals/rules grouped together using parenthesis. For example, both are perfectly valid:

A = B+;
A = (B C)+;

When calling an operator on a single terminal/rule the corresponding entry in the val array is simply set to the terminal/rule value. For example:

A = B+ { p val[0] };

For input B B B this would output [B, B, B].

However, when grouping multiple terminals/rules using parenthesis every occurrence is wrapped in an Array. For example:

A = (B C)+ { p val[0] };

For input B C B C this would output [[B, C], [B, C]]. To work around this you can simply move the group of identifiers to its own rule and only return whatever you need:

A  = A1+ { p val[0] };
A1 = B C { val[0] }; # only return "B"

For input B C B C this would output [B, B].

Conflicts

LL(1) grammars can have two kinds of conflicts in a rule:

  • first/first
  • first/follow

first/first

A first/first conflict means that multiple branches of a rule start with the same terminal, resulting in the parser being unable to choose what branch to use. For example:

%terminals A B;

rule = A | A B;

This would result in the following output:

example.rll:5:1:error: first/first conflict, multiple branches start with the same terminals
rule = A | A B;
^
example.rll:5:8:error: branch starts with: A
rule = A | A B;
       ^
example.rll:5:12:error: branch starts with: A
rule = A | A B;
           ^

To solve a first/first conflict you'll have to factor out the common left factor. For example:

%name Example;

%terminals A B;

rule        = A rule_follow;
rule_follow = B | _;

Here the rule rule starts with terminal A and can optionally be followed by B, without introducing any first/first conflicts.

first/follow

A first/follow conflict occurs when a branch in a rule starts with an epsilon and is followed by one or more terminals and/or rules. An example of a first/follow conflict:

%name Example;

%terminals A B;

rule       = other_rule B;
other_rule = A | _;

This produces the following errors:

example.rll:5:14:error: first/follow conflict, branch can start with epsilon and is followed by (non) terminals
rule       = other_rule B;
             ^
example.rll:6:18:error: epsilon originates from here
other_rule = A | _;
                 ^

There's no specific procedure to solving such a conflict other than simply removing the starting epsilon.

Performance

One of the goals of ruby-ll is to be faster than existing parser generators, Racc in particular. How much faster ruby-ll will be depends on the use case. For example, for the benchmark benchmark/ll/simple_json_bench.rb the performance gains of ruby-ll over Racc are as following:

Ruby Speed
MRI 2.2 1.75x
Rubinius 2.5.2 3.85x
JRuby 1.7.18 6.44x
JRuby 9000 pre1 7.50x

This benchmark was run on a Thinkpad T520 laptop so it's probably best to run the benchmark yourself to see how it behaves on your platform.

Depending on the complexity of your parser you might end up with different different numbers. The above metrics are simply an indication of the maximum performance gain of ruby-ll compared to Racc.

Thread Safety

Parsers generated by ruby-ll share an internal, mutable state on a per instance basis. As a result of this a single instance of your parser can not be used by multiple threads in parallel. If it wasn't for MRI's C API (specifically due to how rb_block_call works) this wouldn't have been an issue.

To mitigate the above simply create a new instance of your parser every time you need it and have the GC clean it up once you're done. This will introduce a slight allocation overhead but it beats having to deal with race conditions.

License

All source code in this repository is subject to the terms of the Mozilla Public License, version 2.0 unless stated otherwise. A copy of this license can be found the file "LICENSE" or at https://www.mozilla.org/MPL/2.0/.

The following files are licensed under a different license:

  • ext/c/khash.h: MIT license (see source code)
  • ext/c/kvec.h: MIT license (see source code)

ruby-ll's People

Contributors

pabloh avatar ttasanen avatar yorickpeterse avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

ruby-ll's Issues

Windows support

While ruby-ll should for the most part support Windows just fine there are a few things left to take care of:

  • Compiler flags, as discussed originally in #13
  • Automated testing, there was some CI for this but I keep forgetting the name

Look into removing need for Array arguments for driver callbacks

Currently the native code of the driver builds a Ruby Array containing all the steps of a branch. This Array is then passed to a callback method of a branch upon finishing all the steps. If possible (and if it gives any performance benefits) it would be neat to instead pass steps as separate arguments, removing the need for Array allocations.

Chat dump between me and @headius about this:

13:49:11         headius | my first pass moved the array creation into Ruby as var*                                                          
13:49:20         headius | oddly enough that was 15% faster than creating it blind                                                           
13:49:39   yorickpeterse | So yeah, I think https://github.com/YorickPeterse/ruby-ll/blob/master/ext/java/org/libll/Driver.java#L195 can     
                         | just be another IRubyObject[]                                                                                     
13:49:44         headius | i.e. the native code just dispatches however many args and lets the Ruby side arrayify it                         
13:50:16         headius | so then the next step was getting the generated bodies to receive the right number of args                        
13:50:41         headius | to eliminate all (or most) allocation dispatching from native to ruby                                             
13:50:52   yorickpeterse | well, it's basically a "feature" for them to have a single "val" argument                                         
13:51:09         headius | it's an expensive feature :-)                                                                                     
13:51:26   yorickpeterse | well yeah, but the alternative would either be to use "arg1", "arg2", etc _or_ use named captures                 
13:51:29   yorickpeterse | The latter is not available yet                                                                                   
13:51:36   yorickpeterse | and syntax wise I'm not sure yet on how to add it                                                                 
13:51:59   yorickpeterse | I'm not much of a fan of what Bison does ($$1, $$2, etc)                                                          
13:52:15         headius | I understand...but this array was the lions share of allocation for the benchmarks                                
13:52:40         headius | also some optional args you initialize to empty hash                                                              
13:52:44   yorickpeterse | I know, I just can't think of any sane alternative from the top of my head                                        
13:52:48         headius | hmm                                                                                                               
13:53:12   yorickpeterse | keyword args would be an option, but then it would be 2.0+, plus you'd have to give everything in your grammar a  
                         | named capture                                                                                                     
13:53:16         headius | I did try to reuse the array object but there were weird side effects                                             
13:55:00   yorickpeterse | euh, the one used for arguments?                                                                                  
13:55:15         headius | yes                                                                                                               
13:55:18   yorickpeterse | You can only re-use it if you call Array#clear after every callback                                               
13:55:25         headius | of course, that's what I did                                                                                      
13:55:25   yorickpeterse | since the callback itself doesn't clear it                                                                        
13:55:26   yorickpeterse | ah                                                                                                                
13:55:47   yorickpeterse | An option would be to use placeholders and then behind the scenes "link" that to the physical arguments           
13:55:52   yorickpeterse | but then you get stuff like $$1, $$2, etc                                                                         
13:55:58   yorickpeterse | which is just as ugly as arg1, arg2, arg3, ...                                                                    
13:56:12         headius | var[0] isn't much different than var1                                                                             
13:56:27   yorickpeterse | No, but it does allow you to do something like....                                                                
13:56:49   yorickpeterse | https://github.com/YorickPeterse/ruby-ll/blob/operators/lib/ll/parser.rll#L37                                     
13:56:54   yorickpeterse | (the splat)                                                                                                       
13:57:11   yorickpeterse | hm wait, separate args would still allow that                                                                     
13:57:44         headius | yeah, that's just *var3                                                                                           
13:57:57         headius | val3 whatever                                                                                                     
13:59:57   yorickpeterse | thinking of it, splitting it into separate arguments at least in Ruby (and in the code generator) is the easy     
                         | part                                                                                                              
14:00:08   yorickpeterse | I just wonder if MRI allows rb_funcall to take a C array                                                          
14:00:19         headius | if nothing else you could build a va_list yourself                                                                
14:00:33         headius | but there must be a way to call with an array somewhere                                                           
14:01:30         headius | rb_funcallv?                                                                                                      
14:01:39         headius | VALUE rb_funcallv(VALUE, ID, int, const VALUE*);                                                                  
14:01:41         headius | seems good                                                                                                        
14:02:24   yorickpeterse | hmmm                                                                                                              
14:02:36         headius | that's funcall2, unsure which is official                                                                         
14:02:39   yorickpeterse | heh                                                                                                               
14:03:02   yorickpeterse | I'll pop open a ticket, otherwise I'll forget                                                                     
14:03:05         headius | what this will enable in JRuby, at least, is overloaded calling that doesn't allocate for < 3 args                
14:03:09         headius | <=                                                                                                                
14:03:29         headius | so you'll get dispatch back to Ruby with no alloc overhead...should help a lot                                    

Reduce allocation overhead

Overhanging issue for allocation overhead and work to reduce it.

I've included a short profile of the parser bench from Oga here: https://gist.github.com/headius/fd328074ac0e20a0a62f

The top two items are the Array being allocated as detailed in #10. This could be eliminated by having exact argument lists in generated parser methods and not indirecting through an array.

The two large byte[] are for the two times this was able to load the XML into memory. Ignore these; I can do a profile run against IO later.

The fifth, seventh, and eighth items (byte[], RubyString, ByteList) are the strings being allocated by the lexer for distribution into the parser. Hard to eliminate this, but if there are tokens that can be shared or reused (e.g. reserved words, single-character tokens, etc) we could reduce it quite a bit.

The sixth item (RubyArray) is happening in Ruby code, in a block/proc that physically constructs an array. Will have to do a run with forced JIT to get more details here.

The ninth item (part of a RubyHash) is being allocated for a default argument at lib/oga/xml/element.rb:51. Replacing this with nil or a pre-made frozen hash would eliminate a number of objects further down in the profile.

The last item is the lock being allocated in every Text node. Eliminating the lock would eliminate allocation of this and several other related objects in the top 20.

ELL(1) Support

Have you consider adding support for Extended LL(1) grammars?.

Cannot load such file .../ruby-ll-2.1.1/lib/libll

I am trying to use the Oga XML/HTML parser but I have run into this issue. I get the same when I attempt to use ruby-ll on its own. Below is a truncated form of the error:

~/.gem/ruby/gems/ruby-ll-2.1.1/lib/ll/setup.rb:6:in `require_relative': cannot load such file -- ~/.gem/ruby/gems/ruby-ll-2.1.1/lib/libll (LoadError)
from ~/.gem/ruby/gems/ruby-ll-2.1.1/lib/ll/setup.rb:6:in `<top (required)>'
.......

Ruby-LL was originally installed as a dependency when I installed Oga, but I have tried installing manually myself as well using Rubygems.

Revise error handling

Currently error handling is quite basic and not very pleasant to use, hence I don't really consider it a public API just yet. To make this more pleasant to use the 3 existing callbacks should be merged into a single one (parser_error) that takes the following arguments:

  • stack_type
  • stack_value
  • token type
  • token value

Officially this will break semantic versioning (since the old methods will be removed) but since the current system is so broken I'm willing to do so.

C code leaks memory

Most likely this is due to the parsing stack (which uses kvec) not shrinking arrays when popping values. In an early version I used https://github.com/rxi/vec which did do that, I might have to revert to that library again.

First release

TODO:

  • General
    • Write a TODO
    • CLI
    • Proper project setup (README, LICENSE, contributing guide, etc)
    • Travis CI
    • Benchmarks
  • Compiler
    • Actual implementation
    • Adding errors for first/first conflicts
    • Adding errors for first/follow conflicts
    • Tests
    • Documentation
  • ConfigurationCompiler
    • Generating transition tables (TABLE)
    • Generating rules tables (RULES)
    • Generating mapping of terminals (TOKENS, rename to TERMINALS)
    • Generating of action table (ACTIONS)
  • CodeGenerator
    • Actual implementation
    • Tests
    • Documentation
  • Driver
    • Refactor C code so it doesn't look like a camel's butt
    • Support for JRuby
    • Bounds checking for rule/table arrays (especially in C)
    • Use each_token with a while loop inside it for token input
    • Ensure it works with Unicode input
    • Tests
    • Documentation
    • Check C code using Valgrind
  • Bootstrapping
    • Generate an LL(1) parser using the Racc parser
    • Use said LL(1) parser to generate a copy of itself (primarily to see if it actually works)
    • Track said parser in Git (required as otherwise the bootstrap process has to be done over and over again)

Verifying first/first fails if branches start with undefined rules/terminals

Using the following grammar:

%name Example;

%terminals A B C;

rule1 = rule2 | rule3 | rule3;

%inner
{
  def each_token
    yield [:A, 'A']
    yield [:C, 'C']
    yield [-1, -1]
  end
}

Produces the following error:

/home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:102:in `block (3 levels) in verify_first_first': undefined method `name' for nil:NilClass (NoMethodError)
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:101:in `map'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:101:in `block (2 levels) in verify_first_first'
        from /home/yorickpeterse/.rubies/ruby-2.1.4/lib/ruby/2.1.0/set.rb:263:in `each_key'
        from /home/yorickpeterse/.rubies/ruby-2.1.4/lib/ruby/2.1.0/set.rb:263:in `each'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:100:in `block in verify_first_first'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:76:in `each'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:76:in `verify_first_first'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/grammar_compiler.rb:19:in `compile'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/cli.rb:51:in `generate'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/lib/ll/cli.rb:26:in `run'
        from /home/yorickpeterse/.gem/ruby/2.1.4/gems/ruby-ll-1.0.0/bin/ruby-ll:5:in `<top (required)>'
        from /home/yorickpeterse/.gem/ruby/2.1.4/bin/ruby-ll:23:in `load'
        from /home/yorickpeterse/.gem/ruby/2.1.4/bin/ruby-ll:23:in `<main>'

Support for Graphiz output

It would be neat if ruby-ll could spit out a Graphviz transition graph of all the rules/terminals (e.g. like Ragel does).

C extension sometimes segfaults in ll_driver_config_mark()

I'm not sure why but some benchmarks of Oga sometimes segfault upon booting up. Seeing how ll_driver_config_mark only marks objects already retained in Ruby itself I'm going to see if I can simply remove this function alltogether, thus hopefully preventing any segmentation faults.

GDB backtrace:


Thread 2 (Thread 0x7ffff7ff7700 (LWP 32321)):
#0  0x00007ffff6f4344d in poll () from /usr/lib/libc.so.6
#1  0x000055555574fc20 in timer_thread_sleep (gvl=0x555555a44f28) at thread_pthread.c:1381
#2  thread_timer (p=0x555555a44f28) at thread_pthread.c:1454
#3  0x00007ffff7bc6314 in start_thread () from /usr/lib/libpthread.so.0
#4  0x00007ffff6f4c24d in clone () from /usr/lib/libc.so.6

Thread 1 (Thread 0x7ffff7fc5700 (LWP 32317)):
#0  0x00007ffff6350cbc in ll_driver_config_mark (config=0x5555560ef810) at driver_config.c:41
#1  0x00005555555f37ba in gc_mark_children (objspace=objspace@entry=0x555555a459a0, ptr=93824997459520) at gc.c:3893
#2  0x00005555555f498a in gc_mark_stacked_objects (objspace=0x555555a459a0) at gc.c:3978
#3  gc_marks_body (objspace=0x555555a459a0, full_mark=<optimized out>) at gc.c:4162
#4  0x00005555555f69b5 in gc_marks (full_mark=0, objspace=0x555555a459a0) at gc.c:4523
#5  garbage_collect_body (objspace=objspace@entry=0x555555a459a0, full_mark=full_mark@entry=0, immediate_sweep=immediate_sweep@entry=0, reason=<optimized out>, reason@entry=256)
    at gc.c:5021
#6  0x00005555555f7501 in heap_prepare_freepage (heap=0x555555a459b0, objspace=0x555555a459a0) at gc.c:1219
#7  heap_get_freeobj_from_next_freepage (heap=0x555555a459b0, objspace=0x555555a459a0) at gc.c:1237
#8  heap_get_freeobj (heap=0x555555a459b0, objspace=0x555555a459a0) at gc.c:1259
#9  newobj_of (klass=93824997822760, flags=8231, v1=0, v2=0, v3=0) at gc.c:1303
#10 0x00005555555863cf in rb_ary_new_capa (capa=2) at array.c:491
#11 rb_ary_new_from_values (elts=0x555555cef730, n=2) at array.c:524
#12 rb_ary_resurrect (ary=<optimized out>) at array.c:1900
#13 0x0000555555735f0b in vm_exec_core (th=th@entry=0x555555a455b0, initial=initial@entry=0) at insns.def:441
#14 0x000055555573b3d8 in vm_exec (th=0x555555a455b0) at vm.c:1398
#15 0x000055555573d26c in rb_iseq_eval (iseqval=<optimized out>) at vm.c:1649
#16 0x00005555555da89e in rb_load_internal0 (th=0x555555a455b0, fname=fname@entry=93825002925200, wrap=wrap@entry=0) at load.c:615
#17 0x00005555555dc56e in rb_load_internal (wrap=0, fname=93825002925200) at load.c:644
#18 rb_require_safe (fname=93825002925640, safe=0) at load.c:996
#19 0x000055555572f942 in vm_call_cfunc_with_frame (th=th@entry=0x555555a455b0, reg_cfp=reg_cfp@entry=0x7ffff6e62e30, ci=ci@entry=0x555555fcef50) at vm_insnhelper.c:1489
#20 0x0000555555734d8d in vm_call_cfunc (ci=0x555555fcef50, reg_cfp=0x7ffff6e62e30, th=0x555555a455b0) at vm_insnhelper.c:1579
#21 vm_call_method (th=0x555555a455b0, cfp=0x7ffff6e62e30, ci=0x555555fcef50) at vm_insnhelper.c:1767
#22 0x000055555573689c in vm_exec_core (th=th@entry=0x555555a455b0, initial=initial@entry=0) at insns.def:1028
#23 0x000055555573b3d8 in vm_exec (th=0x555555a455b0) at vm.c:1398
#24 0x000055555573d26c in rb_iseq_eval (iseqval=<optimized out>) at vm.c:1649
#25 0x00005555555da89e in rb_load_internal0 (th=0x555555a455b0, fname=fname@entry=93825001737480, wrap=wrap@entry=0) at load.c:615
#26 0x00005555555dc56e in rb_load_internal (wrap=0, fname=93825001737480) at load.c:644
#27 rb_require_safe (fname=93825001737600, safe=0) at load.c:996
#28 0x000055555572f942 in vm_call_cfunc_with_frame (th=th@entry=0x555555a455b0, reg_cfp=reg_cfp@entry=0x7ffff6e62ed0, ci=ci@entry=0x555555e6a2e0) at vm_insnhelper.c:1489
#29 0x0000555555734d8d in vm_call_cfunc (ci=0x555555e6a2e0, reg_cfp=0x7ffff6e62ed0, th=0x555555a455b0) at vm_insnhelper.c:1579
#30 vm_call_method (th=0x555555a455b0, cfp=0x7ffff6e62ed0, ci=0x555555e6a2e0) at vm_insnhelper.c:1767
#31 0x000055555573689c in vm_exec_core (th=th@entry=0x555555a455b0, initial=initial@entry=0) at insns.def:1028
#32 0x000055555573b3d8 in vm_exec (th=0x555555a455b0) at vm.c:1398
#33 0x000055555573d26c in rb_iseq_eval (iseqval=<optimized out>) at vm.c:1649
#34 0x00005555555da89e in rb_load_internal0 (th=0x555555a455b0, fname=fname@entry=93825001676800, wrap=wrap@entry=0) at load.c:615
#35 0x00005555555dc56e in rb_load_internal (wrap=0, fname=93825001676800) at load.c:644
#36 rb_require_safe (fname=93825001676880, safe=0) at load.c:996
#37 0x000055555572f942 in vm_call_cfunc_with_frame (th=th@entry=0x555555a455b0, reg_cfp=reg_cfp@entry=0x7ffff6e62f70, ci=ci@entry=0x555555e5e340) at vm_insnhelper.c:1489
#38 0x0000555555734d8d in vm_call_cfunc (ci=0x555555e5e340, reg_cfp=0x7ffff6e62f70, th=0x555555a455b0) at vm_insnhelper.c:1579
#39 vm_call_method (th=0x555555a455b0, cfp=0x7ffff6e62f70, ci=0x555555e5e340) at vm_insnhelper.c:1767
#40 0x000055555573689c in vm_exec_core (th=th@entry=0x555555a455b0, initial=initial@entry=0) at insns.def:1028
#41 0x000055555573b3d8 in vm_exec (th=th@entry=0x555555a455b0) at vm.c:1398
#42 0x000055555573d4d6 in rb_iseq_eval_main (iseqval=iseqval@entry=93825001677840) at vm.c:1662
#43 0x00005555555d503f in ruby_exec_internal (n=0x555555e56410) at eval.c:253
#44 0x00005555555d89df in ruby_exec_node (n=0x555555e56410) at eval.c:318
#45 ruby_run_node (n=<optimized out>) at eval.c:310
#46 0x00005555555771db in main (argc=2, argv=0x7fffffffe2a8) at main.c:36
A debugging session is active.

    Inferior 1 [process 32317] will be killed.

Quit anyway? (y or n) 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.