Skip to content

Commit 4c78a92

Browse files
committed
Merge branch 'release-0.1.0'
2 parents 150ef59 + abc204d commit 4c78a92

22 files changed

Lines changed: 1065 additions & 103 deletions

README.md

Lines changed: 11 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,8 @@
11
# CodeBreaker
22

3+
[![Build Status](https://travis-ci.org/daigaku-ruby/code_breaker.svg?branch=master)](https://travis-ci.org/daigaku-ruby/code_breaker)
4+
[![Gem Version](https://badge.fury.io/rb/code_breaker.svg)](http://badge.fury.io/rb/code_breaker)
5+
36
CodeBreaker breaks a line of Ruby code into its receiver classes and the methods
47
that are called on them.
58

@@ -21,18 +24,16 @@ programming tasks in learning tools like [Daigaku](https://github.com/daigaku-ru
2124
Add this line to your application's Gemfile:
2225

2326
```ruby
24-
gem 'code_breaker', github: 'daigaku-ruby/code_breaker'
27+
gem 'code_breaker'
2528
```
2629

2730
And then execute:
2831

2932
$ bundle
3033

31-
Or download and install it yourself as:
34+
Or install it yourself as:
3235

33-
$ git clone git@github.com:daigaku-ruby/code_breaker.git
34-
$ cd code_breaker
35-
$ rake install
36+
$ gem install 'code_breaker'
3637

3738
## Usage
3839

@@ -43,7 +44,7 @@ require 'code_breaker'
4344

4445
code_snippet = 'crazy_number = Rational(3, 5) + 42 - Complex(2.3, 6.4) * 1.2'
4546
CodeBreaker.parse(code_snippet)
46-
# => [:crazy_number, :'=', Rational, :+, Fixnum, :-, Complex, :*, Float]
47+
# => {:lvasgn=>[:crazy_number, [Rational, :+, Fixnum, :-, Complex, :*, Float]]}
4748

4849
code_snippet = '"hello" + "World"'
4950
CodeBreaker.parse(code_snippet)
@@ -66,6 +67,10 @@ parser.input
6667
# => "\"hello\"+\"world\""
6768
```
6869

70+
## Implemented syntax
71+
72+
For an overview of the implemented syntax and examples for certain parsed Ruby statement please see the project's [Wiki](https://github.com/daigaku-ruby/code_breaker/wiki).
73+
6974
## Development
7075

7176
After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

lib/code_breaker.rb

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
1-
require "code_breaker/version"
2-
require "code_breaker/parser"
1+
require 'code_breaker/version'
2+
require 'code_breaker/parser'
33

44
module CodeBreaker
55

lib/code_breaker/parsable.rb

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
require 'code_breaker/parsable/node'
2+
require 'code_breaker/parsable/wrappers'
3+
require 'code_breaker/parsable/variable_types'
4+
require 'code_breaker/parsable/data_types'
5+
require 'code_breaker/parsable/assignments'
6+
require 'code_breaker/parsable/ranges'
7+
require 'code_breaker/parsable/keywords'
8+
require 'code_breaker/parsable/language_elements'
Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
require 'active_support/concern'
2+
3+
module CodeBreaker
4+
module Parsable
5+
module Assignments
6+
7+
extend ActiveSupport::Concern
8+
include Parsable::Node
9+
10+
included do
11+
alias :parse_lvasgn_node :parse_as_hash # local variable assignment
12+
alias :parse_ivasgn_node :parse_as_hash # instance variable assignment
13+
alias :parse_cvasgn_node :parse_as_hash # class variable assignment
14+
alias :parse_gvasgn_node :parse_as_hash # global variable assignment
15+
alias :parse_op_asgn_node :parse_as_hash # operation assignment
16+
alias :parse_or_asgn_node :parse_as_hash # or assignment
17+
alias :parse_and_asgn_node :parse_as_hash # and assignment
18+
19+
# multiple assignment
20+
def parse_masgn_node(node)
21+
lhs, rhs = parse_children(node)
22+
23+
values = if rhs.kind_of?(Hash) && rhs.has_key?(:array)
24+
rhs[:array]
25+
else
26+
[rhs] + (1...lhs.count).map { NilClass }
27+
end
28+
29+
{ node.type => { lhs => values } }
30+
end
31+
32+
# multiple left hand side
33+
def parse_mlhs_node(node)
34+
parse_children(node).map(&:values).flatten
35+
end
36+
37+
# constant assignment
38+
def parse_casgn_node(node)
39+
name = node.children[1]
40+
children = node.children[2]
41+
value = children.nil? ? name : [name, parse(node.children[2])]
42+
43+
{ node.type => value }
44+
end
45+
46+
end
47+
48+
end
49+
end
50+
end
Lines changed: 60 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,60 @@
1+
require 'active_support/concern'
2+
3+
module CodeBreaker
4+
module Parsable
5+
module DataTypes
6+
7+
extend ActiveSupport::Concern
8+
include Parsable::Node
9+
10+
included do
11+
def parse_nil_node(node)
12+
NilClass
13+
end
14+
15+
def parse_true_node(node)
16+
TrueClass
17+
end
18+
19+
def parse_false_node(node)
20+
FalseClass
21+
end
22+
23+
def parse_str_node(node)
24+
String
25+
end
26+
27+
# interpolated executed string
28+
def parse_xstr_node(node)
29+
{ node.type => parse_children(node).first }
30+
end
31+
32+
def parse_sym_node(node)
33+
Symbol
34+
end
35+
36+
def parse_float_node(node)
37+
Float
38+
end
39+
40+
def parse_regexp_node(node)
41+
Regexp
42+
end
43+
44+
def parse_int_node(node)
45+
node.children[0].class
46+
end
47+
48+
def parse_hash_node(node)
49+
{ node.type => parse_children(node).inject(:merge).to_h }
50+
end
51+
52+
def parse_pair_node(node)
53+
{ parse(node.children[0]) => parse(node.children[1]) }
54+
end
55+
56+
alias :parse_array_node :parse_as_hash
57+
end
58+
end
59+
end
60+
end
Lines changed: 104 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,104 @@
1+
require 'active_support/concern'
2+
3+
module CodeBreaker
4+
module Parsable
5+
module Keywords
6+
7+
extend ActiveSupport::Concern
8+
include Parsable::Node
9+
10+
included do
11+
alias :parse_or_node :parse_as_hash
12+
alias :parse_and_node :parse_as_hash
13+
alias :parse_def_node :parse_as_hash
14+
alias :parse_module_node :parse_as_hash
15+
alias :parse_yield_node :parse_as_hash
16+
17+
alias :parse_break_node :parse_as_node_type
18+
alias :parse_next_node :parse_as_node_type
19+
alias :parse_retry_node :parse_as_node_type
20+
alias :parse_self_node :parse_as_node_type
21+
alias :parse_rescue_node :parse_as_hash
22+
alias :parse_resbody_node :parse_as_hash
23+
alias :parse_case_node :parse_as_hash
24+
25+
def parse_loop_node(node)
26+
condition = node.children[0]
27+
body = node.children[1]
28+
29+
{ node.type => parse(condition), do: parse(body) }
30+
end
31+
32+
alias :parse_while_node :parse_loop_node
33+
alias :parse_until_node :parse_loop_node
34+
35+
def parse_for_node(node)
36+
variable = node.children[0]
37+
range = node.children[1]
38+
body = node.children[2]
39+
40+
{ node.type => parse(variable), in: parse(range), do: parse(body) }
41+
end
42+
43+
def parse_if_node(node)
44+
condition = node.children[0]
45+
if_body = node.children[1]
46+
else_body = node.children[2]
47+
48+
clause = { node.type => parse(condition), then: parse(if_body) }
49+
clause[:else] = parse(else_body) if else_body
50+
51+
clause
52+
end
53+
54+
def parse_module_node(node)
55+
name = parse(node.children[0])
56+
body = node.children[1].nil? ? nil : parse(node.children[1])
57+
value = body ? [name, body] : [name]
58+
59+
{ node.type => value }
60+
end
61+
62+
def parse_return_node(node)
63+
children = parse_children(node)
64+
values = children.length == 1 ? children[0] : children
65+
66+
{ node.type => values }
67+
end
68+
69+
def parse_kwbegin_node(node)
70+
rescue_given = node.children.first.type == :rescue
71+
72+
if rescue_given
73+
rescue_part = parse(node.children.first)[:rescue]
74+
75+
{
76+
begin: rescue_part.first,
77+
rescue: rescue_part.last[:resbody].first
78+
}
79+
else
80+
{ begin: parse(node.children.last) }
81+
end
82+
end
83+
84+
def parse_case_node(node)
85+
case_part = parse(node.children.first)
86+
when_parts = node.children[1...-1].map { |child| parse(child) }
87+
else_part = parse(node.children.last)
88+
89+
statement = { case: when_parts.unshift(case_part) }
90+
statement[:case] << { else: else_part } if else_part
91+
statement
92+
end
93+
94+
def parse_when_node(node)
95+
when_part = parse(node.children[0])
96+
then_part = parse(node.children[1])
97+
98+
{ when: when_part, then: then_part }
99+
end
100+
end
101+
102+
end
103+
end
104+
end
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
require 'active_support/concern'
2+
3+
module CodeBreaker
4+
module Parsable
5+
module LanguageElements
6+
7+
extend ActiveSupport::Concern
8+
include Parsable::Node
9+
10+
included do
11+
alias :parse_block_node :parse_as_hash
12+
alias :parse_args_node :parse_as_hash
13+
alias :parse_arg_node :parse_as_last_child_hash
14+
alias :parse_blockarg_node :parse_as_last_child_hash
15+
alias :parse_restarg_node :parse_as_last_child_hash
16+
alias :parse_optarg_node :parse_as_hash # optional argument
17+
alias :parse_kwarg_node :parse_as_last_child_hash # keyword argument
18+
alias :parse_kwoptarg_node :parse_as_hash # optional keyword argument
19+
alias :parse_kwrestarg_node :parse_as_last_child_hash # keyword rest argument
20+
21+
def parse_block_pass_node(node)
22+
{ node.type => node.children.first.children.last }
23+
end
24+
25+
def parse_splat_node(node)
26+
children = parse_children(node).flatten(1)
27+
values = children.length == 1 ? children[0] : children
28+
29+
{ node.type => values }
30+
end
31+
end
32+
end
33+
end
34+
end

lib/code_breaker/parsable/node.rb

Lines changed: 52 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
1+
module CodeBreaker
2+
module Parsable
3+
module Node
4+
5+
def parse(node)
6+
return if node.nil?
7+
8+
if node.kind_of?(Symbol)
9+
node
10+
else
11+
send("parse_#{node.type}_node", node)
12+
end
13+
end
14+
15+
def parse_children(node)
16+
node.children.reduce([]) do |nodes, child|
17+
nodes << parse(child) unless child.nil?
18+
nodes
19+
end
20+
end
21+
22+
def parse_as_hash(node)
23+
{ node.type => parse_children(node) }
24+
end
25+
26+
def parse_as_last_child_hash(node)
27+
{ node.type => node.children.last }
28+
end
29+
30+
def parse_as_node_type(node)
31+
node.type
32+
end
33+
34+
def method_missing(method, *args, &block)
35+
node_type = method.to_s.match(/^parse_(.+)_node$/).captures.first
36+
37+
if node_type.empty?
38+
super
39+
else
40+
message = [
41+
"Breaking the node type \"#{node_type}\" is not yet implemented.",
42+
"You can open an issue on this in the project's Github repo under:",
43+
"https://github.com/daigaku-ruby/code_breaker/issues/new\n"
44+
].join("\n")
45+
46+
raise NotImplementedError, message
47+
end
48+
end
49+
50+
end
51+
end
52+
end
Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
require 'active_support/concern'
2+
3+
module CodeBreaker
4+
module Parsable
5+
module Ranges
6+
7+
extend ActiveSupport::Concern
8+
include Parsable::Node
9+
10+
included do
11+
alias :parse_irange_node :parse_as_hash # inclusive range a..b
12+
alias :parse_erange_node :parse_as_hash # exclusive range a...b
13+
end
14+
end
15+
end
16+
end

0 commit comments

Comments
 (0)