ruby_grammar_builder 0.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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +21 -0
  3. data/lib/textmate_grammar/generated/grammar.rb +32 -0
  4. data/lib/textmate_grammar/generated/rule.rb +144 -0
  5. data/lib/textmate_grammar/grammar.rb +670 -0
  6. data/lib/textmate_grammar/grammar_plugin.rb +189 -0
  7. data/lib/textmate_grammar/import_patterns.rb +14 -0
  8. data/lib/textmate_grammar/linters/flat_includes.rb +32 -0
  9. data/lib/textmate_grammar/linters/includes_then_tag_as.rb +48 -0
  10. data/lib/textmate_grammar/linters/standard_naming.rb +226 -0
  11. data/lib/textmate_grammar/linters/start_match_empty.rb +49 -0
  12. data/lib/textmate_grammar/linters/tests.rb +19 -0
  13. data/lib/textmate_grammar/linters/unused_unresolved.rb +9 -0
  14. data/lib/textmate_grammar/pattern_extensions/look_ahead_for.rb +32 -0
  15. data/lib/textmate_grammar/pattern_extensions/look_ahead_to_avoid.rb +31 -0
  16. data/lib/textmate_grammar/pattern_extensions/look_behind_for.rb +31 -0
  17. data/lib/textmate_grammar/pattern_extensions/look_behind_to_avoid.rb +31 -0
  18. data/lib/textmate_grammar/pattern_extensions/lookaround_pattern.rb +169 -0
  19. data/lib/textmate_grammar/pattern_extensions/match_result_of.rb +67 -0
  20. data/lib/textmate_grammar/pattern_extensions/maybe.rb +50 -0
  21. data/lib/textmate_grammar/pattern_extensions/one_of.rb +107 -0
  22. data/lib/textmate_grammar/pattern_extensions/one_or_more_of.rb +42 -0
  23. data/lib/textmate_grammar/pattern_extensions/or_pattern.rb +55 -0
  24. data/lib/textmate_grammar/pattern_extensions/placeholder.rb +102 -0
  25. data/lib/textmate_grammar/pattern_extensions/recursively_match.rb +76 -0
  26. data/lib/textmate_grammar/pattern_extensions/zero_or_more_of.rb +50 -0
  27. data/lib/textmate_grammar/pattern_variations/base_pattern.rb +870 -0
  28. data/lib/textmate_grammar/pattern_variations/legacy_pattern.rb +61 -0
  29. data/lib/textmate_grammar/pattern_variations/pattern.rb +9 -0
  30. data/lib/textmate_grammar/pattern_variations/pattern_range.rb +233 -0
  31. data/lib/textmate_grammar/pattern_variations/repeatable_pattern.rb +204 -0
  32. data/lib/textmate_grammar/regex_operator.rb +182 -0
  33. data/lib/textmate_grammar/regex_operators/alternation.rb +24 -0
  34. data/lib/textmate_grammar/regex_operators/concat.rb +23 -0
  35. data/lib/textmate_grammar/stdlib/common.rb +20 -0
  36. data/lib/textmate_grammar/tokens.rb +110 -0
  37. data/lib/textmate_grammar/transforms/add_ending.rb +25 -0
  38. data/lib/textmate_grammar/transforms/bailout.rb +92 -0
  39. data/lib/textmate_grammar/transforms/fix_repeated_tag_as.rb +75 -0
  40. data/lib/textmate_grammar/transforms/resolve_placeholders.rb +121 -0
  41. data/lib/textmate_grammar/util.rb +198 -0
  42. data/lib/textmate_grammar.rb +4 -0
  43. metadata +85 -0
@@ -0,0 +1,75 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # tag_as: inside a quantifier does not work as expected, this fixes it
5
+ #
6
+ class FixRepeatedTagAs < GrammarTransform
7
+ #
8
+ # Does pattern or any of its children / siblings have a tag_as
9
+ #
10
+ # @param [PatternBase, String] pattern the pattern to check
11
+ #
12
+ # @return [Boolean] if any of the patterns have a tag_as
13
+ #
14
+ def tag_as?(pattern)
15
+ return false unless pattern.is_a? PatternBase
16
+
17
+ pattern.each do |s|
18
+ return true if s.arguments[:tag_as]
19
+ end
20
+
21
+ false
22
+ end
23
+
24
+ #
25
+ # fixes tag_as when it is inside a quantifier
26
+ # see https://github.com/jeff-hykin/cpp-textmate-grammar/issues/339#issuecomment-543285390
27
+ # for an explanation of why and how
28
+ #
29
+ def pre_transform(pattern, options)
30
+ return pattern.map { |v| pre_transform(v, options) } if pattern.is_a? Array
31
+ return pattern unless pattern.is_a? PatternBase
32
+ return pattern if pattern.is_a? PatternRange
33
+
34
+ pattern.map do |pat|
35
+ next pat unless pat.respond_to? :self_capture_group_rematch
36
+ next pat unless pat.self_capture_group_rematch
37
+ next pat unless tag_as?(pat.match)
38
+
39
+ unless pat.arguments[:includes].nil? || pat.arguments[:includes].empty?
40
+ raise "Cannot transform a Repeated pattern that has non empty includes"
41
+ end
42
+
43
+ pat.arguments[:includes] = [pat.match.__deep_clone__]
44
+ pat.match.map! do |pm|
45
+ pm.arguments.delete(:tag_as)
46
+ pm.arguments.delete(:includes)
47
+ next unless options[:preserve_references?] || pm.arguments[:preserve_references?]
48
+
49
+ pm.self_scramble_references
50
+ end
51
+ end
52
+ end
53
+
54
+ #
55
+ # Contributes the option :preserve_references?
56
+ #
57
+ # :preserve_references? disables the scrambling of references
58
+ #
59
+ # @return (see GrammarPlugin.options)
60
+ #
61
+ def self.options
62
+ [:preserve_references?]
63
+ end
64
+
65
+ #
66
+ # Displays the state of the options
67
+ #
68
+ # @return (see GrammarPlugin.display_options)
69
+ #
70
+ def self.display_options(indent, options)
71
+ ",\n#{indent}preserve_references?: #{options[:preserve_references?]}"
72
+ end
73
+ end
74
+
75
+ Grammar.register_transform(FixRepeatedTagAs.new, 99)
@@ -0,0 +1,121 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Resolves any embedded placeholders
5
+ #
6
+ class ResolvePlaceholders < GrammarTransform
7
+ def pre_transform(pattern, options)
8
+ return pattern unless pattern.is_a? PatternBase
9
+ pattern_copy = pattern.__deep_clone__
10
+ pattern_copy.map!(true) do |each_pattern_like|
11
+
12
+ arguments = each_pattern_like.arguments
13
+ repository = options[:repository]
14
+
15
+ if each_pattern_like.is_a?(PlaceholderPattern)
16
+
17
+ unless repository[arguments[:placeholder]].is_a? PatternBase
18
+ raise ":#{arguments[:placeholder]} is not a pattern and cannot be substituted"
19
+ end
20
+
21
+ each_pattern_like.match = repository[arguments[:placeholder]].__deep_clone__
22
+ #
23
+ # token pattern
24
+ #
25
+ elsif each_pattern_like.is_a?(TokenPattern)
26
+ qualifying_patterns = []
27
+ for each_key, each_value in repository
28
+ next unless each_value.is_a?(PatternBase)
29
+ qualifying_patterns << each_value if arguments[:pattern_filter][each_value]
30
+ end
31
+ if qualifying_patterns.size == 0
32
+ raise <<-HEREDOC.remove_indent
33
+
34
+
35
+ When creating a token filter #{arguments[:pattern_filter]}
36
+ all the patterns that are in the grammar repository were searched
37
+ but none of thier adjective lists matched the token filter
38
+ HEREDOC
39
+ end
40
+
41
+
42
+ # change this pattern right before the grammar is generated
43
+ each_pattern_like.match = oneOf(qualifying_patterns)
44
+ end
45
+ each_pattern_like
46
+ end
47
+ pattern_copy.freeze
48
+ end
49
+ end
50
+
51
+ # resolving placeholders has no dependencies and makes analyzing patterns much nicer
52
+ # so it happens fairly early
53
+ Grammar.register_transform(ResolvePlaceholders.new, 0)
54
+
55
+
56
+
57
+
58
+
59
+ # # frozen_string_literal: true
60
+
61
+ # #
62
+ # # Resolves any embedded placeholders
63
+ # #
64
+ # class ResolvePlaceholders < GrammarTransform
65
+ # def pre_transform(pattern, options)
66
+ # # skip past anything that isn't a pattern
67
+ # return pattern unless pattern.is_a? PatternBase
68
+
69
+ # pattern_copy = pattern.__deep_clone__
70
+ # # recursively fill in all of the placeholders by looking them up
71
+ # repository = options[:repository]
72
+ # pattern_copy.map!(true) do |each_pattern_like|
73
+ # arguments = each_pattern_like.arguments
74
+ # name_of_placeholder = arguments[:placeholder]
75
+ # #
76
+ # # placeholder pattern
77
+ # #
78
+ # if each_pattern_like.is_a?(PlaceholderPattern)
79
+ # # error if can't find thing the placeholder is reffering to
80
+ # if !repository[name_of_placeholder].is_a?(PatternBase)
81
+ # raise "\n#{arguments[:placeholder]} is not a pattern and cannot be substituted"
82
+ # end
83
+
84
+ # # if the pattern exists though, make the substitution
85
+ # arguments = { match:repository[arguments[:placeholder]].__deep_clone__ }
86
+ # for each_key, each_value in each_pattern_like.arguments
87
+ # arguments[each_key] = each_value
88
+ # end
89
+ # each_pattern_like = Pattern.new(arguments)
90
+ # #
91
+ # # token pattern
92
+ # #
93
+ # elsif each_pattern_like.is_a?(TokenPattern)
94
+ # qualifying_patterns = []
95
+ # for each_key, each_value in repository
96
+ # next unless each_value.is_a?(PatternBase)
97
+ # qualifying_patterns << each_value if arguments[:pattern_filter][each_value]
98
+ # end
99
+ # if qualifying_patterns.size == 0
100
+ # raise <<-HEREDOC.remove_indent
101
+
102
+
103
+ # When creating a token filter #{arguments[:pattern_filter]}
104
+ # all the patterns that are in the grammar repository were searched
105
+ # but none of thier adjective lists matched the token filter
106
+ # HEREDOC
107
+ # end
108
+
109
+
110
+ # # change this pattern right before the grammar is generated
111
+ # each_pattern_like.match = oneOf(qualifying_patterns)
112
+ # end
113
+ # each_pattern_like
114
+ # end
115
+ # return pattern_copy
116
+ # end
117
+ # end
118
+
119
+ # # resolving placeholders has no dependencies and makes analyzing patterns much nicer
120
+ # # so it happens fairly early
121
+ # Grammar.register_transform(ResolvePlaceholders.new, 0)
@@ -0,0 +1,198 @@
1
+ # frozen_string_literal: true
2
+
3
+ #
4
+ # Disables warnings for the block
5
+ #
6
+ # @return [void]
7
+ #
8
+ def with_no_warnings
9
+ old_verbose = $VERBOSE
10
+ $VERBOSE = nil
11
+ yield
12
+ ensure
13
+ $VERBOSE = old_verbose
14
+ end
15
+
16
+ # Add remove indent to the String class
17
+ class String
18
+ # a helper for writing multi-line strings for error messages
19
+ # example usage
20
+ # puts <<-HEREDOC.remove_indent
21
+ # This command does such and such.
22
+ # this part is extra indented
23
+ # HEREDOC
24
+ # @return [String]
25
+ def remove_indent
26
+ gsub(/^[ \t]{#{match(/^[ \t]*/)[0].length}}/, '')
27
+ end
28
+ end
29
+
30
+ #
31
+ # Provides to_s
32
+ #
33
+ class Enumerator
34
+ #
35
+ # Converts Enumerator to a string representing Integer.times
36
+ #
37
+ # @return [String] the Enumerator as a string
38
+ #
39
+ def to_s
40
+ size.to_s + ".times"
41
+ end
42
+ end
43
+
44
+ # determines if a regex string is a single entity
45
+ #
46
+ # @note single entity means that for the purposes of modification, the expression is
47
+ # atomic, for example if appending a +*+ to the end of +regex_string+ matches only
48
+ # a part of regex string multiple times then it is not a single_entity
49
+ # @param regex_string [String] a string representing a regular expression, without the
50
+ # forward slash "/" at the beginning and
51
+ # @return [Boolean] if the string represents an single regex entity
52
+ def string_single_entity?(regex_string)
53
+ escaped = false
54
+ in_set = false
55
+ depth = 0
56
+ regex_string.each_char.with_index do |c, index|
57
+ # allow the first character to be at depth 0
58
+ # NOTE: this automatically makes a single char regexp a single entity
59
+ return false if depth == 0 && index != 0
60
+
61
+ if escaped
62
+ escaped = false
63
+ next
64
+ end
65
+ if c == '\\'
66
+ escaped = true
67
+ next
68
+ end
69
+ if in_set
70
+ if c == ']'
71
+ in_set = false
72
+ depth -= 1
73
+ end
74
+ next
75
+ end
76
+ case c
77
+ when "(" then depth += 1
78
+ when ")" then depth -= 1
79
+ when "["
80
+ depth += 1
81
+ in_set = true
82
+ end
83
+ end
84
+ # sanity check
85
+ if depth != 0 or escaped or in_set
86
+ puts "Internal error: when determining if a Regexp is a single entity"
87
+ puts "an unexpected sequence was found. This is a bug with the gem."
88
+ puts "This will not effect the validity of the produced grammar"
89
+ puts "Regexp: #{inspect} depth: #{depth} escaped: #{escaped} in_set: #{in_set}"
90
+ return false
91
+ end
92
+ true
93
+ end
94
+
95
+ #
96
+ # Wraps a pattern in start and end anchors
97
+ #
98
+ # @param [PatternBase] pat the pattern to wrap
99
+ #
100
+ # @return [PatternBase] the wrapped pattern
101
+ #
102
+ def wrap_with_anchors(pat)
103
+ Pattern.new(/^/).then(Pattern.new(pat)).then(/$/)
104
+ end
105
+
106
+ #
107
+ # Fixes value to be either a PatternBase, Symbol, or Array of either
108
+ #
109
+ # @param [*] value The value to fixup
110
+ #
111
+ # @return [PatternBase,Symbol,Array<PatternBase,Symbol>] the fixed value
112
+ #
113
+ def fixup_value(value)
114
+ is_array = value.is_a? Array
115
+ # ensure array is flat and only contains patterns or symbols
116
+ value = [value].flatten.map do |v|
117
+ next v if v.is_a? Symbol
118
+
119
+ if v.is_a? String
120
+ next v if v.start_with?("source.", "text.", "$")
121
+ end
122
+
123
+ if v.is_a? Hash
124
+ # check for an implicit legacy pattern
125
+ legacy_keys = [
126
+ :name,
127
+ :contentName,
128
+ :begin,
129
+ :end,
130
+ :while,
131
+ :comment,
132
+ :disabled,
133
+ :patterns,
134
+ ]
135
+ v = LegacyPattern.new(v) unless (v.keys.map(&:to_sym) & legacy_keys).empty?
136
+ end
137
+
138
+ v = Pattern.new(v) unless v.is_a? PatternBase
139
+ v
140
+ end
141
+
142
+ value = value[0] unless is_array
143
+ value
144
+ end
145
+
146
+ #
147
+ # Determine the absolute path that a require statement resolves to
148
+ #
149
+ # @note this assumes path was successfully required previously
150
+ #
151
+ # @param [String] path the path to resolve
152
+ #
153
+ # @return [String] the resolved path
154
+ #
155
+ def resolve_require(path)
156
+ path = Pathname.new path
157
+ return path.to_s if path.absolute? && path.extname != ""
158
+
159
+ return path.dirname.glob("#{path.basename}.{rb,so,dll}")[0].to_s if path.absolute?
160
+
161
+ $LOAD_PATH.each do |p|
162
+ test_path = Pathname.new(p).join(path)
163
+ return test_path.to_s if path.extname != "" && test_path.exist?
164
+
165
+ test_paths = test_path.dirname.glob("#{test_path.basename}.{rb,so,dll}")
166
+ return test_paths[0].to_s unless test_paths.empty?
167
+ end
168
+
169
+ ""
170
+ end
171
+
172
+ #
173
+ # Converts an output into a set of tags
174
+ #
175
+ # @param [Hash] output output of Grammar#generate
176
+ #
177
+ # @return [Set<String>] The tags in the grammar
178
+ #
179
+ def get_tags(output)
180
+ repository = output[:repository]
181
+ repository[:$initial_context] = {patterns: output[:patterns]}
182
+ tags = Set.new
183
+ add_tags = lambda do |rule|
184
+ rule = rule.transform_keys(&:to_sym)
185
+ tags.merge(rule[:name].split(" ")) if rule[:name]
186
+ tags.merge(rule[:contentName].split(" ")) if rule[:contentName]
187
+
188
+ rule[:patterns]&.each { |p| add_tags.call(p) }
189
+ rule[:captures]&.values&.each { |p| add_tags.call(p) }
190
+ rule[:beginCaptures]&.values&.each { |p| add_tags.call(p) }
191
+ rule[:endCaptures]&.values&.each { |p| add_tags.call(p) }
192
+ rule[:whileCaptures]&.values&.each { |p| add_tags.call(p) }
193
+ end
194
+
195
+ repository.values.each { |p| add_tags.call(p) }
196
+
197
+ tags
198
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'ruby_grammar_builder/grammar'
4
+ require 'ruby_grammar_builder/stdlib/common'
metadata ADDED
@@ -0,0 +1,85 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ruby_grammar_builder
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Jeff Hykin
8
+ - Matthew Fosdick
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2019-09-16 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description:
15
+ email:
16
+ executables: []
17
+ extensions: []
18
+ extra_rdoc_files: []
19
+ files:
20
+ - LICENSE
21
+ - lib/textmate_grammar.rb
22
+ - lib/textmate_grammar/generated/grammar.rb
23
+ - lib/textmate_grammar/generated/rule.rb
24
+ - lib/textmate_grammar/grammar.rb
25
+ - lib/textmate_grammar/grammar_plugin.rb
26
+ - lib/textmate_grammar/import_patterns.rb
27
+ - lib/textmate_grammar/linters/flat_includes.rb
28
+ - lib/textmate_grammar/linters/includes_then_tag_as.rb
29
+ - lib/textmate_grammar/linters/standard_naming.rb
30
+ - lib/textmate_grammar/linters/start_match_empty.rb
31
+ - lib/textmate_grammar/linters/tests.rb
32
+ - lib/textmate_grammar/linters/unused_unresolved.rb
33
+ - lib/textmate_grammar/pattern_extensions/look_ahead_for.rb
34
+ - lib/textmate_grammar/pattern_extensions/look_ahead_to_avoid.rb
35
+ - lib/textmate_grammar/pattern_extensions/look_behind_for.rb
36
+ - lib/textmate_grammar/pattern_extensions/look_behind_to_avoid.rb
37
+ - lib/textmate_grammar/pattern_extensions/lookaround_pattern.rb
38
+ - lib/textmate_grammar/pattern_extensions/match_result_of.rb
39
+ - lib/textmate_grammar/pattern_extensions/maybe.rb
40
+ - lib/textmate_grammar/pattern_extensions/one_of.rb
41
+ - lib/textmate_grammar/pattern_extensions/one_or_more_of.rb
42
+ - lib/textmate_grammar/pattern_extensions/or_pattern.rb
43
+ - lib/textmate_grammar/pattern_extensions/placeholder.rb
44
+ - lib/textmate_grammar/pattern_extensions/recursively_match.rb
45
+ - lib/textmate_grammar/pattern_extensions/zero_or_more_of.rb
46
+ - lib/textmate_grammar/pattern_variations/base_pattern.rb
47
+ - lib/textmate_grammar/pattern_variations/legacy_pattern.rb
48
+ - lib/textmate_grammar/pattern_variations/pattern.rb
49
+ - lib/textmate_grammar/pattern_variations/pattern_range.rb
50
+ - lib/textmate_grammar/pattern_variations/repeatable_pattern.rb
51
+ - lib/textmate_grammar/regex_operator.rb
52
+ - lib/textmate_grammar/regex_operators/alternation.rb
53
+ - lib/textmate_grammar/regex_operators/concat.rb
54
+ - lib/textmate_grammar/stdlib/common.rb
55
+ - lib/textmate_grammar/tokens.rb
56
+ - lib/textmate_grammar/transforms/add_ending.rb
57
+ - lib/textmate_grammar/transforms/bailout.rb
58
+ - lib/textmate_grammar/transforms/fix_repeated_tag_as.rb
59
+ - lib/textmate_grammar/transforms/resolve_placeholders.rb
60
+ - lib/textmate_grammar/util.rb
61
+ homepage: https://github.com/jeff-hykin/cpp-textmate-grammar
62
+ licenses:
63
+ - MIT
64
+ metadata:
65
+ yard.run: yri
66
+ post_install_message:
67
+ rdoc_options: []
68
+ require_paths:
69
+ - lib
70
+ required_ruby_version: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - ">="
73
+ - !ruby/object:Gem::Version
74
+ version: 2.5.0
75
+ required_rubygems_version: !ruby/object:Gem::Requirement
76
+ requirements:
77
+ - - ">="
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubygems_version: 3.2.16
82
+ signing_key:
83
+ specification_version: 4
84
+ summary: A library to generate textmate grammars
85
+ test_files: []