macros 0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 0c9898a7427d06f00baffd3b44089cb932afb9b9
4
+ data.tar.gz: de39271f19a73ee5fd2877ebaf231b3904abd477
5
+ SHA512:
6
+ metadata.gz: d74a8017921471d947eb243c8e3319f827ed70fbbbc8f8a0dd29dcd9a185b2042d089c0920644268bd9d5a88b5f38743c94e09e0f304af8e0e14176b2e189f49
7
+ data.tar.gz: d309a711b05cbc4c9859983a4d61aac73ed94dc5c4b5f3a48a951f123f74df4253254469a8c5d88894da88d7340058542ae033c925db29386a6fca7522bacba1
data/Gemfile ADDED
@@ -0,0 +1,3 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
@@ -0,0 +1,55 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ macros (0.1)
5
+ parser (> 0)
6
+ unparser (> 0)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ abstract_type (0.0.7)
12
+ adamantium (0.2.0)
13
+ ice_nine (~> 0.11.0)
14
+ memoizable (~> 0.4.0)
15
+ ast (2.1.0)
16
+ concord (0.1.5)
17
+ adamantium (~> 0.2.0)
18
+ equalizer (~> 0.0.9)
19
+ diff-lcs (1.2.5)
20
+ equalizer (0.0.11)
21
+ ice_nine (0.11.1)
22
+ memoizable (0.4.2)
23
+ thread_safe (~> 0.3, >= 0.3.1)
24
+ parser (2.2.3.0)
25
+ ast (>= 1.1, < 3.0)
26
+ procto (0.0.2)
27
+ rspec (3.3.0)
28
+ rspec-core (~> 3.3.0)
29
+ rspec-expectations (~> 3.3.0)
30
+ rspec-mocks (~> 3.3.0)
31
+ rspec-core (3.3.2)
32
+ rspec-support (~> 3.3.0)
33
+ rspec-expectations (3.3.1)
34
+ diff-lcs (>= 1.2.0, < 2.0)
35
+ rspec-support (~> 3.3.0)
36
+ rspec-mocks (3.3.2)
37
+ diff-lcs (>= 1.2.0, < 2.0)
38
+ rspec-support (~> 3.3.0)
39
+ rspec-support (3.3.0)
40
+ thread_safe (0.3.5)
41
+ unparser (0.2.4)
42
+ abstract_type (~> 0.0.7)
43
+ adamantium (~> 0.2.0)
44
+ concord (~> 0.1.5)
45
+ diff-lcs (~> 1.2.5)
46
+ equalizer (~> 0.0.9)
47
+ parser (~> 2.2.2)
48
+ procto (~> 0.0.2)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ macros!
55
+ rspec (~> 3.0)
@@ -0,0 +1,114 @@
1
+ # Macros
2
+
3
+ Macros for Ruby
4
+
5
+ ## Install
6
+
7
+ ```
8
+ gem install macros
9
+ ```
10
+
11
+ ## Usage
12
+
13
+ Any source file that contains macro definitions, or that requires macro
14
+ expansion, should be loaded with `Macros.require`.
15
+
16
+ ``` ruby
17
+ require 'macros'
18
+
19
+ Macros.require 'my_macros'
20
+ Macros.require 'my_code_using_macros'
21
+ ```
22
+
23
+ Macros look like normal method definitions, but they are defined inside a
24
+ `Macros do ; end` block.
25
+
26
+ ``` ruby
27
+ # my_macros.rb
28
+
29
+ Macros do
30
+ # Replace the +output+ method with the +puts+ method
31
+ def with_output(ast)
32
+ treemap(ast) do |node|
33
+ if smatch? node, s(:send, nil, :output)
34
+ s(:send, nil, :puts, *node.children.drop(2))
35
+ else
36
+ node
37
+ end
38
+ end
39
+ end
40
+ end
41
+ ```
42
+
43
+ Now this code
44
+
45
+ ``` ruby
46
+ # my_code_using_macros.rb
47
+
48
+ with_output do
49
+ output "foo"
50
+ output "bar"
51
+ end
52
+ ```
53
+
54
+ Will be transformed into
55
+
56
+ ``` ruby
57
+ puts "foo"
58
+ puts "bar"
59
+ ```
60
+
61
+ ## Helpers
62
+
63
+ The macro will receive an instance of `Parser::AST::Node`, and must return an
64
+ instance of `Parser::AST::Node`. Inside the macro definition the following
65
+ convenience functions are available:
66
+
67
+ ### `s(type, *children)`
68
+
69
+ Construct an AST node of given type, with specific children.
70
+
71
+ ### `node?(n)`
72
+
73
+ Is the given object an AST node?
74
+
75
+ ### `treemap(node, &tranform)`
76
+
77
+ Similar to `Enumerable#map`, but performs a full tree walk, passing any
78
+ `AST::Node` to the block.
79
+
80
+ ### `treefilter(node, &pred)`
81
+
82
+ Returns an array of any node in the tree that satisfies the predicate
83
+
84
+ ### `sfind(node, spec)`
85
+
86
+ `spec` is an Array of symbols, integers, and arrays. It is used a bit like XPath
87
+ or CSS locators.
88
+
89
+ ``` ruby
90
+ node = s(:def, :a_name, s(:args, s(:arg, :x), s(:arg, :y)))
91
+ sfind(node, [:def, 1, :args, [:arg, 0]])
92
+ # => [:x, y]
93
+ ```
94
+
95
+ ### `smatch?(node, pattern)`
96
+
97
+ Checks if the node matches the "pattern"
98
+
99
+ ``` ruby
100
+ node = s(:def, :a_name, s(:args, s(:arg, :x), s(:arg, :y)))
101
+ smatch?(node, s(:def, :a_name))
102
+ # => true
103
+ ```
104
+
105
+ ## Is this a joke?
106
+
107
+ Well, it works, but it's a toy. Working with Ruby syntax trees is pretty
108
+ awkward, and macros can easily lead to a mess. You have been warned!
109
+
110
+ ## License
111
+
112
+ © Arne Brasseur 2015
113
+
114
+ Eclipse Public License
@@ -0,0 +1,6 @@
1
+ require_relative '../lib/macros'
2
+
3
+ $:.unshift File.dirname(__FILE__)
4
+
5
+ Macros.require 'my_macros'
6
+ Macros.require 'the_code'
@@ -0,0 +1,11 @@
1
+ Macros do
2
+ def macro1(ast)
3
+ treemap(ast) do |node|
4
+ if smatch? node, s(:send, nil, :output)
5
+ s(:send, nil, :puts, *node.children.drop(2))
6
+ else
7
+ node
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,4 @@
1
+ macro1 do
2
+ output 'foo'
3
+ output 'bar'
4
+ end
@@ -0,0 +1,70 @@
1
+ require 'pathname'
2
+
3
+ require 'parser/current'
4
+ require 'unparser'
5
+
6
+ MAIN = self
7
+
8
+ module Macros
9
+
10
+ NotFound = Module.new.freeze
11
+
12
+ def self.parse(string)
13
+ Parser::CurrentRuby.parse(string)
14
+ end
15
+
16
+ def self.require(path)
17
+ @loader ||= Loader.new
18
+ @loader.require(path)
19
+ end
20
+ end
21
+
22
+ class Parser::AST::Node
23
+ def inspect(indent=0)
24
+ indented = " " * indent
25
+ sexp = "#{indented}s(:#{@type}"
26
+
27
+ first_node_child = children.index do |child|
28
+ child.is_a?(AST::Node) || child.is_a?(Array)
29
+ end || children.count
30
+
31
+ children.each_with_index do |child, idx|
32
+ if child.is_a?(AST::Node) && idx >= first_node_child
33
+ sexp << ",\n#{child.inspect(indent + 1)}"
34
+ else
35
+ sexp << ", #{child.inspect}"
36
+ end
37
+ end
38
+
39
+ sexp << ")"
40
+
41
+ sexp
42
+ end
43
+ end
44
+
45
+ # module Kernel
46
+ # alias system_require require
47
+ # def require(name)
48
+ # $LOAD_PATH.each do |p|
49
+ # so_file = (Pathname(p) + "#{name}.so")
50
+ # rb_file = (Pathname(p) + "#{name}.rb")
51
+
52
+ # if so_file.file?
53
+ # system_require(name)
54
+ # break
55
+ # end
56
+
57
+ # if rb_file.file?
58
+ # unless $".include? rb_file.to_s
59
+ # Macros::Loader.new.load(rb_file)
60
+ # $" << rb_file.to_s
61
+ # end
62
+ # end
63
+ # end
64
+ # end
65
+ # end
66
+
67
+ require_relative 'macros/sexp'
68
+ require_relative 'macros/compiler'
69
+ require_relative 'macros/expander'
70
+ require_relative 'macros/loader'
@@ -0,0 +1,47 @@
1
+ module Macros
2
+
3
+ class Compiler
4
+ include Sexp
5
+
6
+ def collect_defmacros(sexp)
7
+ Hash[
8
+ treefilter(sexp, &method(:macros_node?)).flat_map(&method(:compile_macros))
9
+ ]
10
+ end
11
+
12
+ def reject_defmacros(node)
13
+ if macros_node?(node)
14
+ s(:begin)
15
+ else
16
+ s(node.type, *node.children.reject(&method(:macros_node?)))
17
+ end
18
+ end
19
+
20
+ def macros_node?(node)
21
+ smatch?(node, s(:block, s(:send, nil, :Macros)))
22
+ end
23
+
24
+ def compile_macros(node)
25
+ treefilter(node) { |n| smatch?(n, s(:def)) }.map(&method(:compile_macro))
26
+ end
27
+
28
+ def compile_macro(node)
29
+ name, args, body = extract_macro(node)
30
+ [name,
31
+ s(:block,
32
+ s(:send, nil, :lambda),
33
+ s(:args, *args.map { |a| s(:arg, a) }),
34
+ body)]
35
+ end
36
+
37
+ def extract_macro(node)
38
+ name = sfind(node, [0])
39
+
40
+ args = sfind(node, [:def, 1, :args, [0]])
41
+ body = sfind(node, [:def, 2])
42
+
43
+ [name, args, body]
44
+ end
45
+
46
+ end
47
+ end
@@ -0,0 +1,39 @@
1
+ module Macros
2
+
3
+ class Expander
4
+ include Sexp
5
+
6
+ def initialize(macros)
7
+ @macros = macros
8
+ end
9
+
10
+ def macroexpand(sexp)
11
+ treemap(sexp, &method(:macroexpand_1))
12
+ end
13
+
14
+ def macroexpand_1(node)
15
+ return node unless macro_block?(node)
16
+ macro = seval @macros[sfind(node, [:block, 0, :send, 1])]
17
+ body = sfind(node, [:block, 2])
18
+ macro.call(body)
19
+ end
20
+
21
+ # foo(1,2) { some_more_code }
22
+ #
23
+ # >> (block
24
+ # >> (send nil :foo
25
+ # >> (int 1)
26
+ # >> (int 2))
27
+ # >> (args)
28
+ # >> (send nil :some_more_code)))
29
+ def macro_block?(node)
30
+ # send with block
31
+ node.type == :block &&
32
+ # target is implicit self
33
+ sfind(node, [:block, 0, :send, 0]).nil? &&
34
+ # known macro
35
+ @macros.key?(sfind(node, [:block, 0, :send, 1]))
36
+ end
37
+ end
38
+
39
+ end
@@ -0,0 +1,31 @@
1
+ module Macros
2
+ class Loader
3
+ include Macros::Sexp
4
+
5
+ def initialize
6
+ @compiler = Macros::Compiler.new
7
+ @macros = {}
8
+ @expander = Macros::Expander.new(@macros)
9
+ end
10
+
11
+ def require(name)
12
+ $LOAD_PATH.each do |p|
13
+ rb_file = (Pathname(p) + "#{name}.rb")
14
+
15
+ if rb_file.file?
16
+ unless $".include? rb_file.to_s
17
+ load(rb_file)
18
+ $" << rb_file.to_s
19
+ end
20
+ end
21
+ end
22
+ end
23
+
24
+ def load(pathname)
25
+ ast = Macros.parse pathname.read
26
+ @macros.merge! @compiler.collect_defmacros(ast)
27
+ rest_ast = @compiler.reject_defmacros(ast)
28
+ MAIN.instance_eval Unparser.unparse @expander.macroexpand(rest_ast)
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,59 @@
1
+ module Macros
2
+
3
+ module Sexp
4
+ extend self
5
+
6
+ def s(type, *children)
7
+ Parser::AST::Node.new(type, children)
8
+ end
9
+
10
+ # Traverse into sexp by type and child position
11
+ def sfind(sexp, specs)
12
+ specs.inject(sexp) do |node, spec|
13
+ return NotFound if node.nil?
14
+
15
+ if spec.is_a?(Symbol) && node.type == spec
16
+ node
17
+ elsif spec.is_a?(Integer) && node.children.length > spec
18
+ node.children[spec]
19
+ elsif spec.is_a?(Array)
20
+ node.children.grep(AST::Node)
21
+ .flat_map { |child| sfind(child, spec) }
22
+ .reject { |child| child == NotFound }
23
+ else
24
+ return NotFound
25
+ end
26
+ end
27
+ end
28
+
29
+ def treemap(node, &block)
30
+ block.call(s(node.type, *node.children.map do |child|
31
+ next child unless child.is_a? AST::Node
32
+ block.call(treemap(child, &block))
33
+ end))
34
+ end
35
+
36
+ def treefilter(node, &block)
37
+ acc = []
38
+ acc << node if block.call(node)
39
+ treemap(node) { |n| acc << n if block.call(n) ; n}
40
+ acc.freeze
41
+ end
42
+
43
+ def smatch?(node, pattern)
44
+ return false unless node.type == pattern.type
45
+ pattern.children.zip(node.children).all? do |a, b|
46
+ a == b || node?(a) && smatch?(b, a)
47
+ end
48
+ end
49
+
50
+ def node?(node)
51
+ node.is_a?(AST::Node)
52
+ end
53
+
54
+ def seval(ast)
55
+ eval(Unparser.unparse(ast))
56
+ end
57
+ end
58
+
59
+ end
@@ -0,0 +1,20 @@
1
+ Gem::Specification.new do |gem|
2
+ gem.name = 'macros'
3
+ gem.version = '0.1'
4
+ gem.authors = [ 'Arne Brasseur' ]
5
+ gem.email = [ 'arne@arnebrasseur.net' ]
6
+ gem.description = 'Macros for Ruby'
7
+ gem.summary = gem.description
8
+ gem.homepage = 'https://github.com/plexus/macros'
9
+ gem.license = 'MPL'
10
+
11
+ gem.require_paths = %w[lib]
12
+ gem.files = `git ls-files`.split $/
13
+ gem.test_files = gem.files.grep(/^spec/)
14
+ gem.extra_rdoc_files = %w[README.md]
15
+
16
+ gem.add_runtime_dependency 'parser', '> 0'
17
+ gem.add_runtime_dependency 'unparser', '> 0'
18
+
19
+ gem.add_development_dependency 'rspec', '~> 3.0'
20
+ end
@@ -0,0 +1,38 @@
1
+ require '/home/arne/projects/macros/lib/macros'
2
+
3
+ code = %q{
4
+ Macros do
5
+ def foo(param1_ast, param2_ast, &block_ast)
6
+ inside_macro_1(param1_ast, param2_ast, block_ast)
7
+ inside_macro_2(param1_ast, param2_ast, block_ast)
8
+ end
9
+
10
+ def bar(a,b)
11
+ inside_macro_1(param1_ast, param2_ast, block_ast)
12
+ inside_macro_2(param1_ast, param2_ast, block_ast)
13
+ end
14
+ end
15
+
16
+ foo(1, 2) do |x|
17
+ some_more_code
18
+ end
19
+
20
+ }
21
+
22
+ include Macros::Sexp
23
+
24
+ tree = Parser::CurrentRuby.parse(code)
25
+
26
+ dm = tree.children.first # !> assigned but unused variable - dm
27
+ #p sfind(dm, [:block, 0, :send, 2, 1])
28
+
29
+ #puts Macros::Compiler.new.collect_defmacros(Parser::CurrentRuby.parse(code))
30
+
31
+ p Parser::CurrentRuby.parse(code).children.last
32
+ # >> (block
33
+ # >> (send nil :foo
34
+ # >> (int 1)
35
+ # >> (int 2))
36
+ # >> (args
37
+ # >> (arg :x))
38
+ # >> (send nil :some_more_code))
@@ -0,0 +1,66 @@
1
+ require_relative '../../lib/macros'
2
+
3
+ RSpec.describe Macros::Compiler do
4
+ include Macros::Sexp
5
+
6
+ let(:code) {
7
+ %q{
8
+ Macros do
9
+ def foo(param1_ast, param2_ast, &block_ast)
10
+ inside_macro_1(param1_ast, param2_ast, block_ast)
11
+ inside_macro_2(param1_ast, param2_ast, block_ast)
12
+ end
13
+
14
+ def bar(a,b)
15
+ inside_macro_1(param1_ast, param2_ast, block_ast)
16
+ inside_macro_2(param1_ast, param2_ast, block_ast)
17
+ end
18
+ end
19
+ }
20
+ }
21
+
22
+ let(:ast) { Macros.parse(code) }
23
+
24
+ describe '#collect_defmacros' do
25
+ specify do
26
+ expect(subject.collect_defmacros(ast)).to eql(
27
+ foo: s(:block,
28
+ s(:send, nil, :lambda),
29
+ s(:args,
30
+ s(:arg, :param1_ast),
31
+ s(:arg, :param2_ast),
32
+ s(:arg, :block_ast)),
33
+ s(:begin,
34
+ s(:send, nil, :inside_macro_1,
35
+ s(:lvar, :param1_ast),
36
+ s(:lvar, :param2_ast),
37
+ s(:lvar, :block_ast)),
38
+ s(:send, nil, :inside_macro_2,
39
+ s(:lvar, :param1_ast),
40
+ s(:lvar, :param2_ast),
41
+ s(:lvar, :block_ast)))),
42
+ bar: s(:block,
43
+ s(:send, nil, :lambda),
44
+ s(:args,
45
+ s(:arg, :a),
46
+ s(:arg, :b)),
47
+ s(:begin,
48
+ s(:send, nil, :inside_macro_1,
49
+ s(:send, nil, :param1_ast),
50
+ s(:send, nil, :param2_ast),
51
+ s(:send, nil, :block_ast)),
52
+ s(:send, nil, :inside_macro_2,
53
+ s(:send, nil, :param1_ast),
54
+ s(:send, nil, :param2_ast),
55
+ s(:send, nil, :block_ast)))))
56
+ end
57
+ end
58
+
59
+ describe '#macros_node?' do
60
+ it 'will match "Macros" blocks' do
61
+ expect(subject.macros_node? Macros.parse('Macros { foo }')).to be true
62
+ end
63
+ end
64
+
65
+ describe ''
66
+ end
@@ -0,0 +1,37 @@
1
+ require_relative '../../lib/macros'
2
+
3
+ RSpec.describe Macros::Expander do
4
+ include Macros::Sexp
5
+
6
+ let(:macro) { Macros.parse("
7
+ -> ast {
8
+ S = Macros::Sexp
9
+ S.treemap(ast) do |node|
10
+ if S.smatch? node, s(:send, nil, :output)
11
+ s(:send, nil, :puts, *node.children.drop(2))
12
+ else
13
+ node
14
+ end
15
+ end
16
+ }")}
17
+
18
+
19
+ subject { described_class.new(foo: macro) }
20
+
21
+ describe '#macro_block?' do
22
+ specify do
23
+ expect(subject.macro_block?(Macros.parse("foo { bar }"))).to be true
24
+ end
25
+ end
26
+
27
+ describe '#macroexpand_1' do
28
+ specify do
29
+ expect(subject.macroexpand_1(Macros.parse("foo { output 'x' ; output 'y' }")))
30
+ .to eql s(:begin,
31
+ s(:send, nil, :puts,
32
+ s(:str, "x")),
33
+ s(:send, nil, :puts,
34
+ s(:str, "y")))
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,46 @@
1
+ require_relative '../../lib/macros'
2
+
3
+ RSpec.describe Macros::Sexp do
4
+ include described_class
5
+
6
+ let(:block_node) {
7
+ s(:block,
8
+ s(:send, nil, :foo,
9
+ s(:int, 1),
10
+ s(:int, 2)),
11
+ s(:args,
12
+ s(:arg, :x)),
13
+ s(:send, nil, :some_more_code))
14
+ }
15
+
16
+ describe '#smatch?' do
17
+ specify do
18
+ expect(smatch?(s(:send, nil, :foo), s(:send))).to be true
19
+ end
20
+
21
+ specify do
22
+ expect(
23
+ smatch?(
24
+ s(:send, nil, :foo),
25
+ s(:send, nil, :bar))).to be false
26
+ end
27
+
28
+ specify do
29
+ expect(
30
+ smatch?(
31
+ block_node,
32
+ s(:block, s(:send, nil, :foo))
33
+ )
34
+ ).to be true
35
+ end
36
+ end
37
+
38
+ describe '#treefilter' do
39
+ specify do
40
+ expect(treefilter(block_node) { |n| smatch?(n, s(:send))})
41
+ .to eql [
42
+ s(:send, nil, :foo, s(:int, 1), s(:int, 2)),
43
+ s(:send, nil, :some_more_code)]
44
+ end
45
+ end
46
+ end
metadata ADDED
@@ -0,0 +1,107 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: macros
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.1'
5
+ platform: ruby
6
+ authors:
7
+ - Arne Brasseur
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-10-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: parser
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">"
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">"
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: unparser
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">"
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">"
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rspec
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '3.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '3.0'
55
+ description: Macros for Ruby
56
+ email:
57
+ - arne@arnebrasseur.net
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files:
61
+ - README.md
62
+ files:
63
+ - Gemfile
64
+ - Gemfile.lock
65
+ - README.md
66
+ - example/main.rb
67
+ - example/my_macros.rb
68
+ - example/the_code.rb
69
+ - lib/macros.rb
70
+ - lib/macros/compiler.rb
71
+ - lib/macros/expander.rb
72
+ - lib/macros/loader.rb
73
+ - lib/macros/sexp.rb
74
+ - macros.gemspec
75
+ - scratch.rb
76
+ - spec/macros/compiler_spec.rb
77
+ - spec/macros/expander_spec.rb
78
+ - spec/macros/sexp_spec.rb
79
+ homepage: https://github.com/plexus/macros
80
+ licenses:
81
+ - MPL
82
+ metadata: {}
83
+ post_install_message:
84
+ rdoc_options: []
85
+ require_paths:
86
+ - lib
87
+ required_ruby_version: !ruby/object:Gem::Requirement
88
+ requirements:
89
+ - - ">="
90
+ - !ruby/object:Gem::Version
91
+ version: '0'
92
+ required_rubygems_version: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ requirements: []
98
+ rubyforge_project:
99
+ rubygems_version: 2.2.3
100
+ signing_key:
101
+ specification_version: 4
102
+ summary: Macros for Ruby
103
+ test_files:
104
+ - spec/macros/compiler_spec.rb
105
+ - spec/macros/expander_spec.rb
106
+ - spec/macros/sexp_spec.rb
107
+ has_rdoc: