macros 0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -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: