collie 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.
Files changed (58) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +12 -0
  3. data/Gemfile +10 -0
  4. data/LICENSE.txt +21 -0
  5. data/README.md +333 -0
  6. data/Rakefile +9 -0
  7. data/collie.gemspec +37 -0
  8. data/docs/TUTORIAL.md +588 -0
  9. data/docs/index.html +56 -0
  10. data/docs/playground/README.md +134 -0
  11. data/docs/playground/build-collie-bundle.rb +85 -0
  12. data/docs/playground/css/styles.css +402 -0
  13. data/docs/playground/index.html +146 -0
  14. data/docs/playground/js/app.js +231 -0
  15. data/docs/playground/js/collie-bridge.js +186 -0
  16. data/docs/playground/js/editor.js +129 -0
  17. data/docs/playground/js/examples.js +80 -0
  18. data/docs/playground/js/ruby-runner.js +75 -0
  19. data/docs/playground/test-server.sh +18 -0
  20. data/exe/collie +15 -0
  21. data/lib/collie/analyzer/conflict.rb +114 -0
  22. data/lib/collie/analyzer/reachability.rb +83 -0
  23. data/lib/collie/analyzer/recursion.rb +96 -0
  24. data/lib/collie/analyzer/symbol_table.rb +67 -0
  25. data/lib/collie/ast.rb +183 -0
  26. data/lib/collie/cli.rb +249 -0
  27. data/lib/collie/config.rb +91 -0
  28. data/lib/collie/formatter/formatter.rb +196 -0
  29. data/lib/collie/formatter/options.rb +23 -0
  30. data/lib/collie/linter/base.rb +62 -0
  31. data/lib/collie/linter/registry.rb +34 -0
  32. data/lib/collie/linter/rules/ambiguous_precedence.rb +87 -0
  33. data/lib/collie/linter/rules/circular_reference.rb +89 -0
  34. data/lib/collie/linter/rules/consistent_tag_naming.rb +69 -0
  35. data/lib/collie/linter/rules/duplicate_token.rb +38 -0
  36. data/lib/collie/linter/rules/empty_action.rb +52 -0
  37. data/lib/collie/linter/rules/factorizable_rules.rb +67 -0
  38. data/lib/collie/linter/rules/left_recursion.rb +34 -0
  39. data/lib/collie/linter/rules/long_rule.rb +37 -0
  40. data/lib/collie/linter/rules/missing_start_symbol.rb +38 -0
  41. data/lib/collie/linter/rules/nonterminal_naming.rb +34 -0
  42. data/lib/collie/linter/rules/prec_improvement.rb +54 -0
  43. data/lib/collie/linter/rules/redundant_epsilon.rb +44 -0
  44. data/lib/collie/linter/rules/right_recursion.rb +35 -0
  45. data/lib/collie/linter/rules/token_naming.rb +39 -0
  46. data/lib/collie/linter/rules/trailing_whitespace.rb +46 -0
  47. data/lib/collie/linter/rules/undefined_symbol.rb +55 -0
  48. data/lib/collie/linter/rules/unreachable_rule.rb +49 -0
  49. data/lib/collie/linter/rules/unused_nonterminal.rb +93 -0
  50. data/lib/collie/linter/rules/unused_token.rb +82 -0
  51. data/lib/collie/parser/lexer.rb +349 -0
  52. data/lib/collie/parser/parser.rb +416 -0
  53. data/lib/collie/reporter/github.rb +35 -0
  54. data/lib/collie/reporter/json.rb +52 -0
  55. data/lib/collie/reporter/text.rb +97 -0
  56. data/lib/collie/version.rb +5 -0
  57. data/lib/collie.rb +52 -0
  58. metadata +145 -0
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c8b86b09838d7091834f3243ae717931e2085c16013aa17e0dff5e8569e8975b
4
+ data.tar.gz: 4d09ba7a44de27ff966cf4edc0da4b9824749d719f1051d7fce6d8c39e36c97d
5
+ SHA512:
6
+ metadata.gz: fbf34be5a91e5a0fb0d5bf334e0bffe92ad289720b059022e9e6c050facbba9e0956ea49d4820eec03fc06e81138a4beb06a1e872031472eaffb2f55ec770f18
7
+ data.tar.gz: 7d323a0aaca55514da222f2ba48f8bd436fb1139a43d83d7bac90845f06844bd29f9ab280d1859f42e54dc260d9e142af410260042546848f6f7693bf4a9bd11
data/CHANGELOG.md ADDED
@@ -0,0 +1,12 @@
1
+ # Changelog
2
+
3
+ All notable changes to this project will be documented in this file.
4
+
5
+ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
6
+ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
7
+
8
+ ## Unreleased
9
+
10
+ ## 0.1.0 - 2025-12-17
11
+
12
+ - Initial commit
data/Gemfile ADDED
@@ -0,0 +1,10 @@
1
+ # frozen_string_literal: true
2
+
3
+ source 'https://rubygems.org'
4
+
5
+ gemspec
6
+
7
+ gem 'rake'
8
+ gem 'rspec'
9
+ gem 'simplecov'
10
+ gem 'yard'
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2025 Yudai Takada
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,333 @@
1
+ # Collie
2
+
3
+ A linter and formatter for Lrama Style BNF grammar files (.y files). Collie helps you write clean, maintainable, and error-free grammar files for parser generators like Lrama, Yacc, and Bison.
4
+
5
+ [![CI](https://github.com/ydah/collie/workflows/CI/badge.svg)](https://github.com/ydah/collie/actions)
6
+ [![Gem Version](https://badge.fury.io/rb/collie.svg)](https://badge.fury.io/rb/collie)
7
+
8
+ ## Features
9
+
10
+ - 18 Built-in Lint Rules - Catch common errors and suggest improvements
11
+ - Lrama Extension Support - Full support for parameterized rules, named references, and inline rules
12
+ - Smart Formatting - Consistent indentation, alignment, and spacing
13
+ - Configurable - Customize rules and formatting options via `.collie.yml`
14
+ - Multiple Output Formats - Text, JSON, and GitHub Actions annotations
15
+ - Auto-correction - Automatically fix certain issues
16
+
17
+ ## Installation
18
+
19
+ ```bash
20
+ gem install collie
21
+ ```
22
+
23
+ Or add to your Gemfile:
24
+
25
+ ```ruby
26
+ gem 'collie', require: false
27
+ ```
28
+
29
+ ## Quick Start
30
+
31
+ ### Try Online
32
+
33
+ Try Collie in your browser without installing anything:
34
+
35
+ [Open Playground](https://ydah.github.io/collie/playground/) (Coming soon)
36
+
37
+ ### Lint a grammar file
38
+
39
+ ```bash
40
+ # Check for issues
41
+ collie lint parse.y
42
+
43
+ # Auto-fix issues where possible
44
+ collie lint -a parse.y
45
+ ```
46
+
47
+ ### Format a grammar file
48
+
49
+ ```bash
50
+ # Check formatting
51
+ collie fmt --check parse.y
52
+
53
+ # Format in-place
54
+ collie fmt parse.y
55
+
56
+ # Show diff
57
+ collie fmt --diff parse.y
58
+ ```
59
+
60
+ ### List all available rules
61
+
62
+ ```bash
63
+ collie rules
64
+ ```
65
+
66
+ ## Configuration
67
+
68
+ Create a `.collie.yml` file in your project root:
69
+
70
+ ```yaml
71
+ # Inherit from another config (optional)
72
+ inherit_from: .collie_base.yml
73
+
74
+ # Rule configuration
75
+ rules:
76
+ DuplicateToken:
77
+ enabled: true
78
+ severity: error
79
+
80
+ TokenNaming:
81
+ enabled: true
82
+ severity: convention
83
+ pattern: '^[A-Z][A-Z0-9_]*$'
84
+
85
+ LongRule:
86
+ enabled: true
87
+ max_alternatives: 10
88
+
89
+ # Disable specific rules
90
+ LeftRecursion:
91
+ enabled: false
92
+
93
+ # Formatter options
94
+ formatter:
95
+ indent_size: 4
96
+ align_tokens: true
97
+ align_alternatives: true
98
+ blank_lines_around_sections: 2
99
+ max_line_length: 120
100
+
101
+ # File patterns
102
+ include:
103
+ - '**/*.y'
104
+ exclude:
105
+ - 'vendor/**/*'
106
+ - 'tmp/**/*'
107
+ ```
108
+
109
+ ## Available Rules
110
+
111
+ ### Validation Rules (Error)
112
+
113
+ | Rule | Description | Auto-fix |
114
+ |------|-------------|----------|
115
+ | `DuplicateToken` | Token defined multiple times | No |
116
+ | `UndefinedSymbol` | Reference to undeclared token/nonterminal | No |
117
+ | `UnreachableRule` | Rule not derivable from start symbol | No |
118
+ | `CircularReference` | Infinite recursion in grammar | No |
119
+ | `MissingStartSymbol` | No `%start` declaration with ambiguous default | No |
120
+
121
+ ### Warning Rules
122
+
123
+ | Rule | Description | Auto-fix |
124
+ |------|-------------|----------|
125
+ | `UnusedNonterminal` | Nonterminal defined but never referenced | No |
126
+ | `UnusedToken` | Token declared but never used | No |
127
+ | `LeftRecursion` | Detects left recursion (informational) | No |
128
+ | `RightRecursion` | Suggests left recursion conversion | No |
129
+ | `AmbiguousPrecedence` | Operators without explicit precedence | No |
130
+
131
+ ### Style Rules (Convention)
132
+
133
+ | Rule | Description | Auto-fix |
134
+ |------|-------------|----------|
135
+ | `TokenNaming` | Tokens should be UPPER_CASE | No |
136
+ | `NonterminalNaming` | Nonterminals should be snake_case | No |
137
+ | `ConsistentTagNaming` | Type tags should be consistent | No |
138
+ | `TrailingWhitespace` | No trailing whitespace at end of lines | Yes |
139
+ | `EmptyAction` | Warns on empty `{ }` actions | Yes |
140
+ | `LongRule` | Rule with too many alternatives | No |
141
+
142
+ ### Optimization Rules (Info)
143
+
144
+ | Rule | Description | Auto-fix |
145
+ |------|-------------|----------|
146
+ | `FactorizableRules` | Suggests factoring common prefixes | No |
147
+ | `RedundantEpsilon` | Unnecessary epsilon productions | No |
148
+ | `PrecImprovement` | Suggests `%prec` improvements | No |
149
+
150
+ ## Usage Examples
151
+
152
+ ### Example Grammar File
153
+
154
+ ```yacc
155
+ %token <node> CLASS MODULE DEF
156
+ %token <id> IDENTIFIER CONSTANT
157
+ %token <num> INTEGER FLOAT
158
+
159
+ %left '+' '-'
160
+ %left '*' '/'
161
+ %right '^'
162
+
163
+ %%
164
+
165
+ program
166
+ : class_definition
167
+ | module_definition
168
+ ;
169
+
170
+ class_definition
171
+ : CLASS CONSTANT '{' class_body '}'
172
+ { $$ = make_class($2, $4); }
173
+ ;
174
+
175
+ expr
176
+ : expr '+' expr { $$ = add($1, $3); }
177
+ | expr '-' expr { $$ = sub($1, $3); }
178
+ | expr '*' expr { $$ = mul($1, $3); }
179
+ | '(' expr ')' { $$ = $2; }
180
+ | IDENTIFIER { $$ = var($1); }
181
+ | INTEGER { $$ = num($1); }
182
+ ;
183
+
184
+ %%
185
+ ```
186
+
187
+ ### Lrama Extensions
188
+
189
+ Collie fully supports Lrama-specific syntax:
190
+
191
+ ```yacc
192
+ # Parameterized Rules
193
+ %rule pair(X, Y): X COMMA Y ;
194
+
195
+ number_pair
196
+ : pair(NUMBER, NUMBER)
197
+ { $$ = make_pair($1, $3); }
198
+ ;
199
+
200
+ # Named References
201
+ assignment
202
+ : IDENTIFIER[var] EQUALS NUMBER[value]
203
+ { assign($var, $value); }
204
+ ;
205
+
206
+ # Inline Rules
207
+ %inline opt(X): /* empty */ | X ;
208
+ ```
209
+
210
+ ### CI Integration (GitHub Actions)
211
+
212
+ Use the reusable workflow in your project:
213
+
214
+ ```yaml
215
+ # .github/workflows/lint.yml
216
+ name: Lint Grammar Files
217
+
218
+ on: [push, pull_request]
219
+
220
+ jobs:
221
+ lint:
222
+ uses: ydah/collie/.github/workflows/lint.yml@main
223
+ with:
224
+ files: 'src/**/*.y'
225
+ config: '.collie.yml'
226
+ fail-on-warnings: true
227
+ ```
228
+
229
+ ### Programmatic Usage
230
+
231
+ ```ruby
232
+ require 'collie'
233
+
234
+ # Parse a grammar file
235
+ parser = Collie::Parser::Parser.new(source_code)
236
+ ast = parser.parse
237
+
238
+ # Analyze the grammar
239
+ symbol_table = Collie::Analyzer::SymbolTable.new(ast)
240
+ symbol_table.build
241
+
242
+ # Run linter
243
+ config = Collie::Config.new
244
+ linter = Collie::Linter.new(config)
245
+ offenses = linter.lint(ast)
246
+
247
+ # Format the grammar
248
+ formatter = Collie::Formatter::Formatter.new(config.formatter_options)
249
+ formatted_code = formatter.format(ast)
250
+ ```
251
+
252
+ ## Command Line Options
253
+
254
+ ### `collie lint`
255
+
256
+ ```bash
257
+ collie lint [OPTIONS] FILES
258
+
259
+ Options:
260
+ --config PATH Path to config file (default: .collie.yml)
261
+ --format FORMAT Output format: text, json, github (default: text)
262
+ -a, --autocorrect Auto-fix offenses where possible
263
+ --only RULES Run only specified rules (comma-separated)
264
+ --except RULES Exclude specified rules (comma-separated)
265
+ ```
266
+
267
+ ### `collie fmt`
268
+
269
+ ```bash
270
+ collie fmt [OPTIONS] FILES
271
+
272
+ Options:
273
+ --config PATH Path to config file
274
+ --check Check only, don't modify files
275
+ --diff Show diff of changes
276
+ ```
277
+
278
+ ### `collie rules`
279
+
280
+ ```bash
281
+ collie rules [OPTIONS]
282
+
283
+ Options:
284
+ --format FORMAT Output format: text, json (default: text)
285
+ ```
286
+
287
+ ## Development
288
+
289
+ ```bash
290
+ # Install dependencies
291
+ bundle install
292
+
293
+ # Run tests
294
+ bundle exec rspec
295
+
296
+ # Run linter
297
+ bundle exec rubocop
298
+
299
+ # Run all checks
300
+ bundle exec rake
301
+ ```
302
+
303
+ ## Contributing
304
+
305
+ 1. Fork it
306
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
307
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
308
+ 4. Push to the branch (`git push origin my-new-feature`)
309
+ 5. Create new Pull Request
310
+
311
+ ## License
312
+
313
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
314
+
315
+ ## Credits
316
+
317
+ Developed for improving the development experience with [Lrama](https://github.com/ruby/lrama), the next-generation parser generator for Ruby.
318
+
319
+ ## Related Projects
320
+
321
+ ### Editor Integration (Planned)
322
+
323
+ - collie-lsp - LSP (Language Server Protocol) implementation for Collie
324
+ - vscode-collie - VS Code extension for Collie
325
+
326
+ ### Parser Generators
327
+
328
+ - [Lrama](https://github.com/ruby/lrama) - LALR (1) parser generator
329
+ - [Bison](https://www.gnu.org/software/bison/) - GNU parser generator
330
+
331
+ ### Inspiration
332
+
333
+ - [RuboCop](https://github.com/rubocop/rubocop) - Ruby static code analyzer (inspiration for architecture)
data/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require "bundler/gem_tasks"
4
+ require "rspec/core/rake_task"
5
+
6
+ RSpec::Core::RakeTask.new(:spec)
7
+
8
+ desc "Run tests"
9
+ task default: [:spec]
data/collie.gemspec ADDED
@@ -0,0 +1,37 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/collie/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "collie"
7
+ spec.version = Collie::VERSION
8
+ spec.authors = ["Yudai Takada"]
9
+ spec.email = ["t.yudai92@gmail.com"]
10
+
11
+ spec.summary = "A linter and formatter for Lrama Style BNF grammar files"
12
+ spec.description = "Collie is a linter and formatter for Lrama Style BNF grammar files (.y files). " \
13
+ "It helps establish best practices for grammar file development and improves " \
14
+ "maintainability of complex parsers."
15
+ spec.homepage = "https://github.com/ydah/collie"
16
+ spec.license = "MIT"
17
+ spec.required_ruby_version = ">= 3.2.0"
18
+
19
+ spec.metadata["homepage_uri"] = spec.homepage
20
+ spec.metadata["source_code_uri"] = spec.homepage
21
+ spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/CHANGELOG.md"
22
+ spec.metadata["rubygems_mfa_required"] = "true"
23
+
24
+ spec.files = Dir.chdir(__dir__) do
25
+ `git ls-files -z`.split("\x0").reject do |f|
26
+ (File.expand_path(f) == __FILE__) ||
27
+ f.start_with?(*%w[bin/ test/ spec/ features/ .git .circleci appveyor])
28
+ end
29
+ end
30
+ spec.bindir = "exe"
31
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
32
+ spec.require_paths = ["lib"]
33
+
34
+ spec.add_dependency "pastel", "~> 0.8"
35
+ spec.add_dependency "thor", "~> 1.0"
36
+ spec.add_dependency "tty-table", "~> 0.12"
37
+ end