maccro 0.1.0

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,85 @@
1
+ require_relative "node"
2
+ require_relative "literal"
3
+
4
+ module Maccro
5
+ module DSL
6
+ class LocalVariable < Node
7
+ # LVAR: local variable in method local scope (method or local arguments)
8
+ # DVAR: dynamically defined local variable (defined&initialized in local scope)
9
+ SUB_NODETYPE_LIST = [:LVAR, :DVAR].freeze
10
+
11
+ def self.match?(node)
12
+ SUB_NODETYPE_LIST.include?(node.type)
13
+ end
14
+ end
15
+
16
+ class GlobalVariable < Node
17
+ def type; :GVAR; end
18
+ def self.match?(node); node.type == :GVAR; end
19
+ end
20
+
21
+ class InstanceVariable < Node
22
+ def type; :IVAR; end
23
+ def self.match?(node); node.type == :IVAR; end
24
+ end
25
+
26
+ class ClassVariable < Node
27
+ def type; :CVAR; end
28
+ def self.match?(node); node.type == :CVAR; end
29
+ end
30
+
31
+ class Variable < Node
32
+ SUB_TYPES = [LocalVariable, GlobalVariable, InstanceVariable, ClassVariable].freeze
33
+
34
+ def type; :MACCRO_VARIABLE; end
35
+
36
+ def self.match?(node)
37
+ SUB_TYPES.any?{|s| s.match?(node) }
38
+ end
39
+ end
40
+
41
+ class NthRefVariable < Node
42
+ # $1, $2 (regexp group capture reference)
43
+ def type; :NTH_REF; end
44
+ def self.match?(node); node.type == :NTH_REF; end
45
+ end
46
+
47
+ class BackRefVariable < Node
48
+ # $&, $`, ...
49
+ def type; :BACK_REF; end
50
+ def self.match?(node); node.type == :BACK_REF; end
51
+ end
52
+
53
+ class SpecialVariable < Node
54
+ SUB_TYPES = [NthRefVariable, BackRefVariable].freeze
55
+
56
+ def type; :MACCRO_SPECIAL_VARIABLE; end
57
+
58
+ def self.match?(node)
59
+ SUB_TYPES.any?{|s| s.match?(node) }
60
+ end
61
+ end
62
+
63
+ class Constant < Node
64
+ def type; :CONST; end
65
+ def self.match?(node); node.type == :CONST; end
66
+ end
67
+
68
+ class Self < Node
69
+ def type; :SELF; end
70
+ def self.match?(node); node.type == :SELF; end
71
+ end
72
+
73
+ class Value < Node
74
+ SUB_TYPES = [
75
+ Variable, SpecialVariable, Constant,
76
+ Literal, String, RegexpCompiledOnce, DRegexp, DSymbol,
77
+ Self, NilValue, TrueValue, FalseValue, Lambda, Array, Hash,
78
+ ].freeze
79
+
80
+ def self.match?(node)
81
+ SUB_TYPES.any?{|s| s.match?(node) }
82
+ end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,15 @@
1
+ require_relative "./code_util"
2
+
3
+ module Kernel
4
+ def dirty_require(feature)
5
+ Maccro::CodeUtil.suppress_warning do
6
+ require feature
7
+ end
8
+ end
9
+
10
+ def dirty_load(file, priv = false)
11
+ Maccro::CodeUtil.suppress_warning do
12
+ load(file, priv)
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,34 @@
1
+ require_relative 'code_util'
2
+
3
+ module Maccro
4
+ Match = Struct.new(:rule, :placeholders, :range, keyword_init: true)
5
+ # placeholders: name => code_range (in method)
6
+
7
+ class Matched
8
+ attr_reader :matches
9
+
10
+ def initialize(matches)
11
+ @matches = matches.sort{|a, b| a.range <=> b.range }
12
+ end
13
+
14
+ def self.get_replace_pairs(entire_source, placeholders) # name => code_ranges
15
+ replace_pairs = {}
16
+ placeholders.each_pair do |name, code_range|
17
+ replace_pairs[name] = CodeUtil.code_range_to_code(entire_source, code_range)
18
+ end
19
+ replace_pairs # name => snippets
20
+ end
21
+
22
+ def rewrite(source)
23
+ # TODO: implement @safe_reference
24
+ source = source.dup
25
+ # move tail to head, not to break code positions of unprocessed matches
26
+ @matches.reverse.each do |match|
27
+ replace_pairs = self.class.get_replace_pairs(source, match.placeholders)
28
+ range = CodeUtil.code_range_to_range(source, match.range)
29
+ source[range] = match.rule.after_code(replace_pairs)
30
+ end
31
+ source
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,4 @@
1
+ require "maccro/builtin"
2
+
3
+ Maccro::Builtin.register_all
4
+ Maccro.enable
@@ -0,0 +1,106 @@
1
+ require_relative 'dsl'
2
+ require_relative 'matched'
3
+ require_relative 'code_util'
4
+
5
+ module Maccro
6
+ class Rule
7
+ attr_reader :name, :before, :after, :matcher, :under, :safe_reference
8
+
9
+ def initialize(name, before, after, under: nil, safe_reference: false)
10
+ @name = name
11
+ @before = before
12
+ @after = after
13
+ @under = under
14
+ @safe_reference = safe_reference
15
+
16
+ # TODO: check all placeholder in @after exist in @before
17
+ # (placeholders in @before are not required to exist in @after, because it may be removed)
18
+ # TODO: check $TARGET exists in under just once
19
+
20
+ # TODO: implement a matcher in @before matches in multi times
21
+
22
+ @matcher = DSL.matcher(before)
23
+ @pruner = under && DSL.matcher(under) || nil
24
+ end
25
+
26
+ def match(ast)
27
+ matches = []
28
+
29
+ if @pruner
30
+ dig_prune(matches, ast)
31
+ else
32
+ dig_match(matches, ast)
33
+ end
34
+
35
+ return nil if matches.empty?
36
+
37
+ Matched.new(matches)
38
+ end
39
+
40
+ def dig_prune(matches, ast)
41
+ if @pruner.match?(ast)
42
+ placeholders = {}
43
+ @pruner.capture(ast, placeholders)
44
+ dig_match(matches, placeholders[:__target__])
45
+ elsif ast.respond_to?(:children)
46
+ ast.children.each do |c|
47
+ dig_prune(matches, c)
48
+ end
49
+ elsif ast.respond_to?(:each)
50
+ ast.each do |i|
51
+ dig_prune(matches, i)
52
+ end
53
+ end
54
+ end
55
+
56
+ def dig_match(matches, ast)
57
+ if @matcher.match?(ast)
58
+ placeholders = {}
59
+ @matcher.capture(ast, placeholders)
60
+ matches << Match.new(rule: self, placeholders: placeholders, range: ast.to_code_range)
61
+ elsif ast.respond_to?(:children)
62
+ ast.children.each do |c|
63
+ dig_match(matches, c)
64
+ end
65
+ elsif ast.respond_to?(:each)
66
+ ast.each do |i|
67
+ dig_match(matches, i)
68
+ end
69
+ end
70
+ end
71
+
72
+ def self.find_placeholder_code_ranges(ast, name)
73
+ return [] unless ast.is_a? RubyVM::AbstractSyntaxTree::Node
74
+
75
+ if ast.type == :VCALL && ast.children.first.to_s == name
76
+ return [CodeRange.from_node(ast)]
77
+ end
78
+
79
+ ranges = []
80
+ ast.children.each do |c|
81
+ rs = find_placeholder_code_ranges(c, name)
82
+ ranges.concat(rs) unless rs.empty?
83
+ end
84
+ ranges.sort
85
+ end
86
+
87
+ def after_code(replace_pairs) # name => snippet
88
+ code = @after.dup
89
+ ast = CodeUtil.parse_to_ast(@after)
90
+ code_range_to_name = {}
91
+ replace_pairs.each_key do |name|
92
+ self.class.find_placeholder_code_ranges(ast, name).each do |r|
93
+ code_range_to_name[r] = name
94
+ end
95
+ end
96
+ # reverse is not to break code position for unprocessed code ranges
97
+ code_range_to_name.keys.sort.reverse.each do |code_range|
98
+ name = code_range_to_name[code_range]
99
+ snippet = replace_pairs[name]
100
+ range = CodeUtil.code_range_to_range(@after.dup, code_range)
101
+ code[range] = snippet
102
+ end
103
+ code
104
+ end
105
+ end
106
+ end
@@ -0,0 +1,3 @@
1
+ module Maccro
2
+ VERSION = "0.1.0"
3
+ end
data/maccro.gemspec ADDED
@@ -0,0 +1,27 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "maccro/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "maccro"
8
+ spec.version = Maccro::VERSION
9
+ spec.authors = ["TAGOMORI Satoshi"]
10
+ spec.email = ["tagomoris@gmail.com"]
11
+
12
+ spec.summary = %q{Pure black magic scroll}
13
+ spec.description = %q{Macro processor for Ruby 2.6 or later}
14
+ spec.homepage = "https://github.com/tagomoris/maccro"
15
+ spec.license = "MIT"
16
+
17
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
18
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
19
+ end
20
+ spec.bindir = "exe"
21
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
22
+ spec.require_paths = ["lib"]
23
+
24
+ spec.add_development_dependency "bundler"
25
+ spec.add_development_dependency "rake"
26
+ spec.add_development_dependency "test-unit"
27
+ end
metadata ADDED
@@ -0,0 +1,108 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: maccro
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - TAGOMORI Satoshi
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-19 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
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: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
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: test-unit
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description: Macro processor for Ruby 2.6 or later
56
+ email:
57
+ - tagomoris@gmail.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - LICENSE.txt
65
+ - README.md
66
+ - Rakefile
67
+ - bin/console
68
+ - bin/setup
69
+ - lib/maccro.rb
70
+ - lib/maccro/builtin.rb
71
+ - lib/maccro/code_range.rb
72
+ - lib/maccro/code_util.rb
73
+ - lib/maccro/dsl.rb
74
+ - lib/maccro/dsl/assign.rb
75
+ - lib/maccro/dsl/expression.rb
76
+ - lib/maccro/dsl/literal.rb
77
+ - lib/maccro/dsl/node.rb
78
+ - lib/maccro/dsl/value.rb
79
+ - lib/maccro/kernel_ext.rb
80
+ - lib/maccro/matched.rb
81
+ - lib/maccro/rewrite_the_world.rb
82
+ - lib/maccro/rule.rb
83
+ - lib/maccro/version.rb
84
+ - maccro.gemspec
85
+ homepage: https://github.com/tagomoris/maccro
86
+ licenses:
87
+ - MIT
88
+ metadata: {}
89
+ post_install_message:
90
+ rdoc_options: []
91
+ require_paths:
92
+ - lib
93
+ required_ruby_version: !ruby/object:Gem::Requirement
94
+ requirements:
95
+ - - ">="
96
+ - !ruby/object:Gem::Version
97
+ version: '0'
98
+ required_rubygems_version: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - ">="
101
+ - !ruby/object:Gem::Version
102
+ version: '0'
103
+ requirements: []
104
+ rubygems_version: 3.0.3
105
+ signing_key:
106
+ specification_version: 4
107
+ summary: Pure black magic scroll
108
+ test_files: []