arpaka 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.
- checksums.yaml +7 -0
- data/CHANGELOG.md +5 -0
- data/LICENSE.txt +21 -0
- data/README.md +302 -0
- data/Rakefile +39 -0
- data/assets/arpaka-logo.svg +209 -0
- data/examples/calculator.y +13 -0
- data/examples/calculator_ast.y +13 -0
- data/exe/arpaka +6 -0
- data/lib/arpaka/BSDL +22 -0
- data/lib/arpaka/COPYING +58 -0
- data/lib/arpaka/actions.rb +656 -0
- data/lib/arpaka/cli.rb +60 -0
- data/lib/arpaka/generator.rb +43 -0
- data/lib/arpaka/ruby_grammar.rb +170 -0
- data/lib/arpaka/runtime/ast.rb.erb +35 -0
- data/lib/arpaka/runtime/builder.rb.erb +247 -0
- data/lib/arpaka/runtime/frontend.rb.erb +37 -0
- data/lib/arpaka/runtime/lexer.rb.erb +1371 -0
- data/lib/arpaka/source.json +11 -0
- data/lib/arpaka/upstream_rules.json +10202 -0
- data/lib/arpaka.rb +20 -0
- data/lib/lrama/ruby/action_code.rb +55 -0
- data/lib/lrama/ruby/action_scanner.rb +353 -0
- data/lib/lrama/ruby/backend.rb +103 -0
- data/lib/lrama/ruby/generator.rb +38 -0
- data/lib/lrama/ruby/parser.rb.erb +127 -0
- data/lib/lrama/ruby/version.rb +7 -0
- data/lib/lrama/ruby.rb +28 -0
- data/sig/arpaka.rbs +15 -0
- data/sig/lrama/ruby.rbs +17 -0
- metadata +86 -0
checksums.yaml
ADDED
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
---
|
|
2
|
+
SHA256:
|
|
3
|
+
metadata.gz: 3b5b641fb92742a1368aa7449797f7d3f5dd756bb66ac7a169c4b8975cde4209
|
|
4
|
+
data.tar.gz: 4fabb68f5aada69502fe0468cafd8560f5a586953b258ebba68f91b58fe73c73
|
|
5
|
+
SHA512:
|
|
6
|
+
metadata.gz: 0b1300a4517df7fbe9b77eeff694aea8c99d280dc4ee5b877a262413f44f399e5d27ed8b98ac3f898804fa72a0b265bd862f4d7780445ec8516301b79f77287c
|
|
7
|
+
data.tar.gz: 1625c54504452c354b20a8b83229ce79388f6b2d420a7a084a9733946d68f325e3ce74844ef4497f04f5bd6d49b2bf50dded6f3f23d28504409f2386bacbb94c
|
data/CHANGELOG.md
ADDED
data/LICENSE.txt
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
The MIT License (MIT)
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 MATSUMOTO, Katsuyoshi
|
|
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
|
|
13
|
+
all 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
|
|
21
|
+
THE SOFTWARE.
|
data/README.md
ADDED
|
@@ -0,0 +1,302 @@
|
|
|
1
|
+
# Arpaka
|
|
2
|
+
|
|
3
|
+
<p align="center">
|
|
4
|
+
<img src="assets/arpaka-logo.svg" alt="Arpaka" width="720">
|
|
5
|
+
</p>
|
|
6
|
+
|
|
7
|
+
<p align="center">
|
|
8
|
+
Generate Ruby parsers with Lrama, and use their ASTs from Ruby.
|
|
9
|
+
</p>
|
|
10
|
+
|
|
11
|
+
Arpaka provides a Ruby output backend for
|
|
12
|
+
[Lrama](https://github.com/ruby/lrama) and a tool for generating a Ruby language
|
|
13
|
+
frontend. Its goal is to make Ruby syntax accessible as an AST from Ruby code.
|
|
14
|
+
|
|
15
|
+
`Lrama::Ruby` turns Yacc-style grammars with Ruby semantic actions into
|
|
16
|
+
standalone Ruby parsers using Lrama's LALR/IELR tables. Arpaka applies its Ruby
|
|
17
|
+
Action mappings to a Ruby source tree supplied by the user and emits a single
|
|
18
|
+
Ruby file containing the parser, lexer, AST and builder. Users choose the output
|
|
19
|
+
location and regenerate when updating the grammar or Actions.
|
|
20
|
+
|
|
21
|
+
The Ruby frontend is experimental and partial. The gem contains generation
|
|
22
|
+
support, not a bundled `parse.y` or a pre-generated Ruby frontend.
|
|
23
|
+
|
|
24
|
+
## Requirements
|
|
25
|
+
|
|
26
|
+
Ruby 4.0 or later. Start the generator with `RUBY_BOX=1`: Ruby Box isolates the
|
|
27
|
+
Ruby-specific extensions to Lrama. `Lrama::Ruby.compile` also loads each generated
|
|
28
|
+
parser in a separate box, so parsers with the same class name can coexist.
|
|
29
|
+
|
|
30
|
+
Generated `.rb` files have no gem dependencies and can also be required normally
|
|
31
|
+
without Ruby Box. Support for Ruby 3.4 and earlier remains undecided.
|
|
32
|
+
|
|
33
|
+
## Installation
|
|
34
|
+
|
|
35
|
+
For local development:
|
|
36
|
+
|
|
37
|
+
```sh
|
|
38
|
+
bin/setup
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
To use this unreleased gem from another application's Gemfile:
|
|
42
|
+
|
|
43
|
+
```ruby
|
|
44
|
+
gem "arpaka", git: "https://github.com/katsyoshi/arpaka"
|
|
45
|
+
```
|
|
46
|
+
|
|
47
|
+
## Usage
|
|
48
|
+
|
|
49
|
+
Write a Yacc-style grammar with Ruby actions, such as
|
|
50
|
+
[examples/calculator.y](examples/calculator.y):
|
|
51
|
+
|
|
52
|
+
```yacc
|
|
53
|
+
%token NUMBER
|
|
54
|
+
%left '+'
|
|
55
|
+
%left '*'
|
|
56
|
+
%%
|
|
57
|
+
expression: NUMBER
|
|
58
|
+
| expression '+' expression { $$ = $1 + $3 }
|
|
59
|
+
| expression '*' expression { $$ = $1 * $3 }
|
|
60
|
+
;
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
Run the following Ruby code with `RUBY_BOX=1 bundle exec ruby your_script.rb`:
|
|
64
|
+
|
|
65
|
+
```ruby
|
|
66
|
+
require "arpaka"
|
|
67
|
+
|
|
68
|
+
path = "examples/calculator.y"
|
|
69
|
+
grammar = File.read(path)
|
|
70
|
+
parser_class = Lrama::Ruby.compile(grammar, filename: path, class_name: "Calculator")
|
|
71
|
+
parser = parser_class.new
|
|
72
|
+
p parser.parse([[:NUMBER, 2], ["+", nil], [:NUMBER, 3], ["*", nil], [:NUMBER, 4]])
|
|
73
|
+
# => 14
|
|
74
|
+
|
|
75
|
+
# Generate source without executing its semantic actions.
|
|
76
|
+
File.write("calculator.rb",
|
|
77
|
+
Lrama::Ruby.generate(grammar, filename: path, class_name: "Calculator"))
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
`class_name` defaults to `Parser` and must be a single Ruby constant name.
|
|
81
|
+
The generated class exposes `parse(tokens)`. Supply an enumerable of
|
|
82
|
+
`[token, value]` pairs; tokens may be names (symbols or strings), string aliases,
|
|
83
|
+
character literals such as `"+"`, or numeric token IDs. `TOKENS` maps names to IDs.
|
|
84
|
+
Exhausting the enumerable signals EOF; `[0, nil]` also signals EOF explicitly.
|
|
85
|
+
Provide your own tokenizer to convert source text into these pairs.
|
|
86
|
+
|
|
87
|
+
`$$` is the result of an action. `$1`, `$2`, etc. refer to values on the rule's
|
|
88
|
+
right-hand side; named references such as `$left` and midrule actions are also
|
|
89
|
+
supported. An omitted action returns the first value, or `nil` for an empty rule.
|
|
90
|
+
Strings, comments, regular expressions and Ruby instance variables are preserved.
|
|
91
|
+
Dollar-prefixed names in executable action code are reserved for grammar
|
|
92
|
+
references, including inside Ruby interpolation. Typed references, bracketed
|
|
93
|
+
references, `$0`, and special Ruby globals are not supported.
|
|
94
|
+
|
|
95
|
+
Unexpected input raises the generated class's `ParseError`, exposing `token` and
|
|
96
|
+
`state`. Exceptions from token enumeration and semantic actions propagate to the
|
|
97
|
+
caller. There is no error recovery yet.
|
|
98
|
+
|
|
99
|
+
`allow_error_rules: true` permits grammars containing the reserved `error`
|
|
100
|
+
symbol without enabling error recovery. The default is `false`. With either
|
|
101
|
+
setting the parser stops at the first syntax error; supplying the reserved
|
|
102
|
+
error token from a lexer is rejected. This option is useful when incrementally
|
|
103
|
+
porting a grammar whose recovery rules must remain present.
|
|
104
|
+
|
|
105
|
+
### Building an AST
|
|
106
|
+
|
|
107
|
+
[examples/calculator_ast.y](examples/calculator_ast.y) uses Ruby actions to
|
|
108
|
+
build an abstract syntax tree instead of evaluating arithmetic. This example
|
|
109
|
+
represents a number as `[:number, value]` and a binary operation as
|
|
110
|
+
`[:binary, operator, left, right]`. The grammar defines this AST format;
|
|
111
|
+
the backend does not impose a node type.
|
|
112
|
+
|
|
113
|
+
```ruby
|
|
114
|
+
path = "examples/calculator_ast.y"
|
|
115
|
+
parser = Lrama::Ruby.compile(File.read(path), filename: path,
|
|
116
|
+
class_name: "CalculatorAst").new
|
|
117
|
+
|
|
118
|
+
tree = parser.parse([[:NUMBER, 2], ["+", nil], [:NUMBER, 3], ["*", nil], [:NUMBER, 4]])
|
|
119
|
+
p tree
|
|
120
|
+
# => [:binary, :+, [:number, 2], [:binary, :*, [:number, 3], [:number, 4]]]
|
|
121
|
+
```
|
|
122
|
+
|
|
123
|
+
Run this after requiring `arpaka`, with `RUBY_BOX=1`. Supply tokens from
|
|
124
|
+
your own lexer, or pass a token array as above. The default parser mode runs
|
|
125
|
+
the actions and returns the root node. Precedence and associativity determine
|
|
126
|
+
the tree's shape. Parentheses affect grouping but are omitted from the AST by
|
|
127
|
+
the action `$$ = $2`. Evaluation or compilation of the resulting tree belongs
|
|
128
|
+
to the application; even `1 / 0` produces a tree without performing division.
|
|
129
|
+
|
|
130
|
+
### Generating a Ruby frontend
|
|
131
|
+
|
|
132
|
+
Provide a Ruby source tree containing `parse.y`, `tool/id2token.rb`,
|
|
133
|
+
`defs/id.def`, `COPYING` and `BSDL`. Arpaka does not download Ruby sources.
|
|
134
|
+
The first supported source profile is ruby/ruby revision
|
|
135
|
+
`37d60dd3241d5fdb10ca43f36077f5d556ae1b8d`; other revisions are not automatically
|
|
136
|
+
supported. Input checksums and expanded rule mappings are checked before
|
|
137
|
+
applying Actions. Changed input files are reported and generation stops.
|
|
138
|
+
Even comment-only changes currently require updating the source profile.
|
|
139
|
+
|
|
140
|
+
```sh
|
|
141
|
+
RUBY_BOX=1 bundle exec arpaka generate \
|
|
142
|
+
--ruby-source /path/to/ruby \
|
|
143
|
+
--class-name MyRubyParser \
|
|
144
|
+
--output lib/my_library/ruby_parser.rb
|
|
145
|
+
```
|
|
146
|
+
|
|
147
|
+
The output directory must already exist. `--class-name` defaults to
|
|
148
|
+
`RubyParser` and must be a single Ruby constant name. Existing output files are
|
|
149
|
+
preserved unless `--force` is supplied. A failed generation leaves the previous
|
|
150
|
+
file intact. The output includes its source profile, generator version and
|
|
151
|
+
copyright/license notices.
|
|
152
|
+
|
|
153
|
+
The consumer only needs the generated file and Ruby 4.0 or later:
|
|
154
|
+
|
|
155
|
+
```ruby
|
|
156
|
+
require_relative "lib/my_library/ruby_parser"
|
|
157
|
+
|
|
158
|
+
tree = MyRubyParser.parse("a = 1\na + 2", filename: "example.rb")
|
|
159
|
+
# MyRubyParser::AST::Program containing the assignment and addition
|
|
160
|
+
```
|
|
161
|
+
|
|
162
|
+
`parse` takes Ruby source and returns an AST. The parser, lexer and builder are
|
|
163
|
+
internal; callers do not need to supply tokens. Each call has fresh parsing and
|
|
164
|
+
local-variable state. No Arpaka/Lrama gem, Ruby Box, grammar files, network access
|
|
165
|
+
or filesystem writes are needed at runtime.
|
|
166
|
+
|
|
167
|
+
For generation from Ruby, `Arpaka.generate` returns that same standalone source.
|
|
168
|
+
To try a frontend in memory, use `Arpaka.compile`; both require `RUBY_BOX=1`:
|
|
169
|
+
|
|
170
|
+
```ruby
|
|
171
|
+
require "arpaka"
|
|
172
|
+
|
|
173
|
+
frontend = Arpaka.compile(ruby_source: "/path/to/ruby", class_name: "MyRubyParser")
|
|
174
|
+
tree = frontend.parse("a = 1")
|
|
175
|
+
|
|
176
|
+
code = Arpaka.generate(ruby_source: "/path/to/ruby", class_name: "MyRubyParser")
|
|
177
|
+
File.write("ruby_parser.rb", code)
|
|
178
|
+
```
|
|
179
|
+
|
|
180
|
+
The former `Arpaka.parse`, `Arpaka.parse_source` and `Lrama::Ruby.parse`
|
|
181
|
+
APIs have been replaced by explicitly generating or compiling a frontend.
|
|
182
|
+
The generic `Lrama::Ruby.generate` and `Lrama::Ruby.compile` APIs are unchanged.
|
|
183
|
+
|
|
184
|
+
This is not a complete Ruby frontend. Existing support includes literals,
|
|
185
|
+
assignments, calls, collections and portions of control flow and definitions.
|
|
186
|
+
Some constructs parse without preserving all their semantics in the current
|
|
187
|
+
AST, and nodes do not have source locations. Known unported rules raise
|
|
188
|
+
`MyRubyParser::UnsupportedSyntax`, exposing `rule` and upstream `line`.
|
|
189
|
+
Syntax errors raise `MyRubyParser::ParseError`, exposing `token` and `state`.
|
|
190
|
+
Lexer failures raise `MyRubyParser::LexerError` with filename, line and byte
|
|
191
|
+
column. These inherit from the generated `MyRubyParser::Error`.
|
|
192
|
+
|
|
193
|
+
The generated frontend has also been exercised against real-world source. On
|
|
194
|
+
Ruby 4.0.6, it parsed all 3,436 Ruby files in a local Rails checkout. This is
|
|
195
|
+
the practical compatibility signal for the current frontend. Ruby's Trick
|
|
196
|
+
contest programs are kept as a separate language-compatibility challenge: the
|
|
197
|
+
frontend currently parses 12 of 23 Ruby files in the `trick18` checkout, while
|
|
198
|
+
the remaining files use deliberately adversarial syntax that is not yet
|
|
199
|
+
represented by the current Action mappings. These are smoke-test results, not
|
|
200
|
+
a claim of complete Ruby language coverage.
|
|
201
|
+
|
|
202
|
+
## Current scope
|
|
203
|
+
|
|
204
|
+
Supported: precedence and associativity, `%prec`, `%empty`, `%start`, `%expect`,
|
|
205
|
+
LALR/IELR tables, and Lrama's standard parameterized rules. Standard list rules
|
|
206
|
+
retain Lrama's default semantic actions; they do not automatically build arrays.
|
|
207
|
+
Unresolved conflicts fail generation unless the shift/reduce count matches
|
|
208
|
+
`%expect`; reduce/reduce conflicts always fail.
|
|
209
|
+
|
|
210
|
+
Locations, typed values/`%union`, `%code`, prologues, epilogues, parse/lex parameters,
|
|
211
|
+
initial actions, hooks, printers, destructors and error recovery are not implemented.
|
|
212
|
+
The generator rejects these features rather than dropping their behavior.
|
|
213
|
+
Use plain Ruby values in actions; C actions are not translated into Ruby.
|
|
214
|
+
A Ruby action scanner handles semantic action tokens; Bison is not required.
|
|
215
|
+
|
|
216
|
+
### Inspecting a C grammar
|
|
217
|
+
|
|
218
|
+
Use `mode: :recognizer` to generate a standalone recognizer from a grammar with
|
|
219
|
+
C actions. This mode uses Lrama's C action lexer and deliberately omits all
|
|
220
|
+
semantic actions, prologues, epilogues, hooks, printers and destructors. Type and
|
|
221
|
+
location declarations do not produce runtime values. Error recovery is not
|
|
222
|
+
performed: unexpected input still raises `ParseError`. Conflicts are checked
|
|
223
|
+
as in the default `mode: :parser`.
|
|
224
|
+
|
|
225
|
+
```ruby
|
|
226
|
+
code = Lrama::Ruby.generate(File.read("parse.preprocessed.y"),
|
|
227
|
+
class_name: "RubyRecognizer", mode: :recognizer)
|
|
228
|
+
File.write("ruby_recognizer.rb", code)
|
|
229
|
+
```
|
|
230
|
+
|
|
231
|
+
`parse(tokens)` returns `true` when the token stream is accepted. This mode is
|
|
232
|
+
for inspecting grammar tables; it does not build an AST or preserve checks and
|
|
233
|
+
lexer state changes performed by C actions. You must supply the token stream.
|
|
234
|
+
It does not by itself parse Ruby source text.
|
|
235
|
+
|
|
236
|
+
For Ruby's upstream `parse.y`, first run Ruby's `tool/id2token.rb` with
|
|
237
|
+
`defs/id.def` from the same revision. This is the preprocessing step used by
|
|
238
|
+
Ruby's build to replace `RUBY_TOKEN(...)` with numeric token IDs:
|
|
239
|
+
|
|
240
|
+
```sh
|
|
241
|
+
ruby /path/to/ruby/tool/id2token.rb /path/to/ruby/parse.y > parse.preprocessed.y
|
|
242
|
+
```
|
|
243
|
+
|
|
244
|
+
## Development
|
|
245
|
+
|
|
246
|
+
```sh
|
|
247
|
+
RUBY_BOX=1 bundle exec rake
|
|
248
|
+
RUBY_BOX=1 bundle exec rake test
|
|
249
|
+
RUBY_BOX=1 bundle exec rake ruby:check
|
|
250
|
+
RUBY_BOX=1 OUTPUT=/path/to/frontend.rb bundle exec rake ruby:generate
|
|
251
|
+
RUBY_BOX=1 bundle exec rake benchmark:parse
|
|
252
|
+
```
|
|
253
|
+
|
|
254
|
+
Development commands default to the pinned Ruby source fixture in `vendor/ruby`.
|
|
255
|
+
In an uninstalled checkout, invoke the CLI with
|
|
256
|
+
`RUBY_BOX=1 bundle exec ruby -Ilib exe/arpaka generate ...`.
|
|
257
|
+
Set `RUBY_SOURCE` to select another source directory. Normal CLI/API usage always
|
|
258
|
+
requires an explicit Ruby source tree. `ruby:check` verifies the source profile,
|
|
259
|
+
Action inventory, grammar transformation and generated frontend without editing
|
|
260
|
+
artifacts. `ruby:generate` writes the standalone frontend to `OUTPUT`.
|
|
261
|
+
|
|
262
|
+
`lib/arpaka/actions.rb` holds the Ruby Action mappings;
|
|
263
|
+
`lib/arpaka/upstream_rules.json` records the expected expanded upstream rules.
|
|
264
|
+
Update these and the source checksums together when adding support for a changed
|
|
265
|
+
Ruby grammar. Runtime templates live under `lib/arpaka/runtime/`.
|
|
266
|
+
|
|
267
|
+
Tests exercise the generated frontend and the generic backend. Package checks
|
|
268
|
+
install the gem, generate from external Ruby sources, and execute the resulting
|
|
269
|
+
file with gems and Ruby Box disabled. `Gemfile.lock` stays local and untracked.
|
|
270
|
+
Developer fixtures and tools are excluded from the gem.
|
|
271
|
+
|
|
272
|
+
### Benchmark
|
|
273
|
+
|
|
274
|
+
`benchmark:parse` measures frontend generation separately from loading the
|
|
275
|
+
generated file and parsing in a fresh process with Ruby Box disabled.
|
|
276
|
+
Set `ITERATIONS` to change the measured parsing iterations (default: 100).
|
|
277
|
+
Each warm measurement follows ten warmup calls. File reading/class definition
|
|
278
|
+
is measured separately; process startup and `require "prism"` are not timed.
|
|
279
|
+
|
|
280
|
+
The RubyVM and Prism measurements use the same source but return different AST
|
|
281
|
+
representations. They are reference measurements, not equivalent-work guarantees.
|
|
282
|
+
|
|
283
|
+
Example results on Ruby 4.0.6, x86_64 Linux, YJIT disabled, Prism 1.9.0
|
|
284
|
+
(10 measured iterations per input, ten warmup calls):
|
|
285
|
+
|
|
286
|
+
| Input | Arpaka (warm) | RubyVM AST | Prism |
|
|
287
|
+
| ---: | ---: | ---: | ---: |
|
|
288
|
+
| 10 bytes | 0.054 ms | 0.003 ms | 0.003 ms |
|
|
289
|
+
| 1,697 bytes | 5.901 ms | 0.060 ms | 0.128 ms |
|
|
290
|
+
| 18,800 bytes | 61.475 ms | 0.850 ms | 1.859 ms |
|
|
291
|
+
|
|
292
|
+
Frontend generation took 2.190s and loading the generated file took 20.571ms
|
|
293
|
+
in the same run. The first Arpaka parse took 0.130ms, 6.707ms and 73.381ms
|
|
294
|
+
for the small, medium and large inputs respectively.
|
|
295
|
+
|
|
296
|
+
Generation is a one-time build step per regeneration, not a cost of parsing an
|
|
297
|
+
input file. The generated frontend is loaded and measured with Ruby Box disabled.
|
|
298
|
+
Results vary with input and environment.
|
|
299
|
+
|
|
300
|
+
## License
|
|
301
|
+
|
|
302
|
+
[MIT](LICENSE.txt).
|
data/Rakefile
ADDED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require "bundler/gem_tasks"
|
|
4
|
+
require "rake/testtask"
|
|
5
|
+
|
|
6
|
+
Rake::TestTask.new(:test) do |t|
|
|
7
|
+
t.libs << "test"
|
|
8
|
+
t.libs << "lib"
|
|
9
|
+
t.test_files = FileList["test/**/*_test.rb"]
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
namespace :ruby do
|
|
13
|
+
desc "Generate a standalone Ruby frontend (requires OUTPUT; optional RUBY_SOURCE)"
|
|
14
|
+
task :generate do
|
|
15
|
+
abort "Set OUTPUT to the destination Ruby file" unless ENV["OUTPUT"]
|
|
16
|
+
ruby "tool/ruby/grammar.rb", "--output", ENV.fetch("OUTPUT")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
desc "Verify the Ruby source profile, Action mappings and generated frontend"
|
|
20
|
+
task :check do
|
|
21
|
+
ruby "tool/ruby/grammar.rb", "--check"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
namespace :package do
|
|
26
|
+
desc "Verify installed frontend generation and standalone execution"
|
|
27
|
+
task :check do
|
|
28
|
+
ruby "tool/check_package.rb"
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
|
|
32
|
+
task default: ["ruby:check", :test, "package:check"]
|
|
33
|
+
|
|
34
|
+
namespace :benchmark do
|
|
35
|
+
desc "Benchmark Ruby source and token parsing"
|
|
36
|
+
task :parse do
|
|
37
|
+
ruby "benchmark/parse.rb"
|
|
38
|
+
end
|
|
39
|
+
end
|
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
|
|
2
|
+
<svg
|
|
3
|
+
width="1200"
|
|
4
|
+
height="440"
|
|
5
|
+
viewBox="0 0 600 220"
|
|
6
|
+
role="img"
|
|
7
|
+
aria-labelledby="title desc"
|
|
8
|
+
version="1.1"
|
|
9
|
+
id="svg9"
|
|
10
|
+
sodipodi:docname="arpaka-logo.svg"
|
|
11
|
+
inkscape:version="1.4.4 (dcaf3e7d9e, 2026-05-05)"
|
|
12
|
+
xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape"
|
|
13
|
+
xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd"
|
|
14
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
15
|
+
xmlns:svg="http://www.w3.org/2000/svg">
|
|
16
|
+
<defs
|
|
17
|
+
id="defs9" />
|
|
18
|
+
<sodipodi:namedview
|
|
19
|
+
id="namedview9"
|
|
20
|
+
pagecolor="#ffffff"
|
|
21
|
+
bordercolor="#666666"
|
|
22
|
+
borderopacity="1.0"
|
|
23
|
+
inkscape:showpageshadow="2"
|
|
24
|
+
inkscape:pageopacity="0.0"
|
|
25
|
+
inkscape:pagecheckerboard="0"
|
|
26
|
+
inkscape:deskcolor="#d1d1d1"
|
|
27
|
+
inkscape:zoom="1.7451172"
|
|
28
|
+
inkscape:cx="591.36429"
|
|
29
|
+
inkscape:cy="516.58422"
|
|
30
|
+
inkscape:window-width="3832"
|
|
31
|
+
inkscape:window-height="2110"
|
|
32
|
+
inkscape:window-x="0"
|
|
33
|
+
inkscape:window-y="0"
|
|
34
|
+
inkscape:window-maximized="1"
|
|
35
|
+
inkscape:current-layer="svg9" />
|
|
36
|
+
<title
|
|
37
|
+
id="title">Arpaka</title>
|
|
38
|
+
<desc
|
|
39
|
+
id="desc">A cute front-facing alpaca with ruby-red drool</desc>
|
|
40
|
+
<path
|
|
41
|
+
fill="#fffaf0"
|
|
42
|
+
d="m 74.285576,148.91435 c -10.808162,13.20997 -12.009069,33.62539 -9.607255,68.45169 h 96.072559 c 2.40181,-34.8263 1.20091,-55.24172 -9.60726,-68.45169 -12.00907,9.60725 -26.41995,14.41088 -38.42902,14.41088 -12.00907,0 -26.419955,-4.80363 -38.429024,-14.41088 z"
|
|
43
|
+
id="path3"
|
|
44
|
+
style="stroke:#20252b;stroke-width:4.80363;stroke-linecap:round;stroke-linejoin:round" />
|
|
45
|
+
<g
|
|
46
|
+
id="g16"
|
|
47
|
+
transform="matrix(1.2009069,0,0,1.2009069,-19.976543,-7.0557052)">
|
|
48
|
+
<path
|
|
49
|
+
fill="#fffaf0"
|
|
50
|
+
d="m 61.492445,71.876889 -16,-54 c -3,-10.0000004 10,-14.0000004 15,-5 l 25,35 m 74.000005,24 16,-54 c 3,-10.0000004 -10,-14.0000004 -15,-5 l -25,35"
|
|
51
|
+
id="path1"
|
|
52
|
+
style="stroke:#20252b;stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
53
|
+
<g
|
|
54
|
+
id="g12"
|
|
55
|
+
transform="matrix(-0.85223362,0.08323429,-0.59125268,-0.78836262,250.77326,163.82889)"
|
|
56
|
+
style="fill:#ff2a2a">
|
|
57
|
+
<path
|
|
58
|
+
fill="none"
|
|
59
|
+
stroke="#b9efff"
|
|
60
|
+
d="m 103.10384,143.75115 -1.58766,7.84088"
|
|
61
|
+
id="path9"
|
|
62
|
+
style="fill:#ff2a2a;stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
63
|
+
<path
|
|
64
|
+
fill="#45a9c7"
|
|
65
|
+
stroke="#24728c"
|
|
66
|
+
d="m 101.13626,127.60028 c -3.682972,24.38722 -2.796844,38.28295 2.363,37.05529 5.15985,-1.22766 10.46555,-16.59657 14.14852,-40.98379 1.47318,-9.75489 -15.03834,-5.82639 -16.51152,3.9285 z"
|
|
67
|
+
id="path15"
|
|
68
|
+
style="fill:#ffaaaa;stroke:#ff8080;stroke-width:5.08725;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
|
69
|
+
</g>
|
|
70
|
+
<g
|
|
71
|
+
id="g12-6"
|
|
72
|
+
transform="matrix(-0.84570239,0.13422949,-0.63749913,-0.75145872,317.85438,149.44723)"
|
|
73
|
+
style="fill:#ff2a2a">
|
|
74
|
+
<path
|
|
75
|
+
fill="none"
|
|
76
|
+
stroke="#b9efff"
|
|
77
|
+
d="m 103.10384,143.75115 -1.58766,7.84088"
|
|
78
|
+
id="path9-7"
|
|
79
|
+
style="fill:#ff2a2a;stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
80
|
+
<path
|
|
81
|
+
fill="#45a9c7"
|
|
82
|
+
stroke="#24728c"
|
|
83
|
+
d="m 113.31614,123.12934 c -29.895387,19.07124 -47.555417,32.77189 -47.093418,36.53508 0.461998,3.76319 18.676408,-5.42162 48.571798,-24.49285 11.95816,-7.62851 10.47978,-19.67073 -1.47838,-12.04223 z"
|
|
84
|
+
id="path15-5"
|
|
85
|
+
style="fill:#ffaaaa;stroke:#ff8080;stroke-width:5.08725;stroke-linecap:round;stroke-linejoin:round;stroke-opacity:1" />
|
|
86
|
+
</g>
|
|
87
|
+
</g>
|
|
88
|
+
<g
|
|
89
|
+
id="g15"
|
|
90
|
+
transform="matrix(1.2009069,0,0,1.2009069,-23.30055,-6.9325939)">
|
|
91
|
+
<path
|
|
92
|
+
fill="#fffaf0"
|
|
93
|
+
d="m 109.26134,44.848908 c 7,-12 22,-10 26,2 12,-7 25,2 23,15 10,3 15,12 13,21 8,8 7,20.000002 0,28.000002 2,14 -8,25 -22,27 -10,12 -25,13 -35,4 -10,9 -25.000009,8 -34.000009,-4 -14,-2 -24,-13 -22,-27 -7,-8 -8,-20.000002 0,-28.000002 -2,-9 3,-18 13,-21 -2,-13 10,-23 22,-20 4,-11 17.000009,-16 26.000009,-9 z"
|
|
94
|
+
id="path2"
|
|
95
|
+
style="stroke:#20252b;stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
96
|
+
<g
|
|
97
|
+
id="g14"
|
|
98
|
+
transform="translate(-8.6177952,1.8466704)">
|
|
99
|
+
<circle
|
|
100
|
+
fill="#20252b"
|
|
101
|
+
stroke="none"
|
|
102
|
+
cx="83.492447"
|
|
103
|
+
cy="82.876892"
|
|
104
|
+
r="9"
|
|
105
|
+
id="circle3"
|
|
106
|
+
style="stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
107
|
+
<circle
|
|
108
|
+
fill="#fffaf0"
|
|
109
|
+
stroke="none"
|
|
110
|
+
cx="86.492447"
|
|
111
|
+
cy="79.876892"
|
|
112
|
+
r="3"
|
|
113
|
+
id="circle5"
|
|
114
|
+
style="stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
115
|
+
</g>
|
|
116
|
+
<g
|
|
117
|
+
id="g13"
|
|
118
|
+
transform="translate(8.9871293,1.6004477)">
|
|
119
|
+
<circle
|
|
120
|
+
fill="#20252b"
|
|
121
|
+
stroke="none"
|
|
122
|
+
cx="143.40179"
|
|
123
|
+
cy="82.138222"
|
|
124
|
+
r="9"
|
|
125
|
+
id="circle4"
|
|
126
|
+
style="stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
127
|
+
<circle
|
|
128
|
+
fill="#fffaf0"
|
|
129
|
+
stroke="none"
|
|
130
|
+
cx="140.49245"
|
|
131
|
+
cy="79.876892"
|
|
132
|
+
r="3"
|
|
133
|
+
id="circle6"
|
|
134
|
+
style="stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
135
|
+
</g>
|
|
136
|
+
<path
|
|
137
|
+
fill="#20252b"
|
|
138
|
+
d="m 103.59962,118.89259 c 0,-4.38819 4.45779,-6.58227 10.4015,-6.58227 5.94371,0 10.40149,2.19408 10.40149,6.58227 0,5.11953 -4.45778,7.31361 -10.40149,7.31361 -5.94371,0 -10.4015,-2.19408 -10.4015,-7.31361 z"
|
|
139
|
+
id="path6"
|
|
140
|
+
style="stroke:#20252b;stroke-width:2.94856;stroke-linecap:round;stroke-linejoin:round" />
|
|
141
|
+
<g
|
|
142
|
+
id="g9"
|
|
143
|
+
transform="matrix(0.83067695,0.05806705,-0.05806705,0.83067695,23.681495,3.4124346)">
|
|
144
|
+
<path
|
|
145
|
+
fill="#c51f35"
|
|
146
|
+
stroke="#8c1526"
|
|
147
|
+
d="m 109.7214,156.26763 c 0,18.01361 3.60272,28.82177 8.40635,28.82177 4.80363,0 8.40635,-10.80816 8.40635,-28.82177 0,-7.20544 -16.8127,-7.20544 -16.8127,0 z"
|
|
148
|
+
id="path4"
|
|
149
|
+
style="stroke-width:1.20091" />
|
|
150
|
+
<path
|
|
151
|
+
fill="none"
|
|
152
|
+
stroke="#ef7180"
|
|
153
|
+
d="m 116.92684,163.47307 v 9.60726"
|
|
154
|
+
id="path5"
|
|
155
|
+
style="stroke-width:1.20091" />
|
|
156
|
+
</g>
|
|
157
|
+
<path
|
|
158
|
+
fill="none"
|
|
159
|
+
d="m 113.57023,127.75826 v 8 m -9,-3 c 5,5 13,5 18,0"
|
|
160
|
+
id="path7"
|
|
161
|
+
style="stroke:#20252b;stroke-width:4;stroke-linecap:round;stroke-linejoin:round" />
|
|
162
|
+
</g>
|
|
163
|
+
<g
|
|
164
|
+
id="g8"
|
|
165
|
+
transform="translate(-6.1555679,17.728036)">
|
|
166
|
+
<path
|
|
167
|
+
fill="#c51f35"
|
|
168
|
+
stroke="#8c1526"
|
|
169
|
+
d="m 138.54317,167.0758 c 0,7.20544 3.60272,12.00906 8.40635,12.00906 4.80362,0 8.40634,-4.80362 8.40634,-12.00906 0,-4.80363 -16.81269,-4.80363 -16.81269,0 z"
|
|
170
|
+
id="path8"
|
|
171
|
+
style="stroke-width:1.20091" />
|
|
172
|
+
<circle
|
|
173
|
+
fill="#ef7180"
|
|
174
|
+
stroke="none"
|
|
175
|
+
cx="145.74861"
|
|
176
|
+
cy="170.67851"
|
|
177
|
+
r="2.4018137"
|
|
178
|
+
id="circle8"
|
|
179
|
+
style="stroke-width:1.20091" />
|
|
180
|
+
</g>
|
|
181
|
+
<path
|
|
182
|
+
d="m 260.67431,135.13716 35,-67.00001 35,67.00001 m -49,-18 h 38"
|
|
183
|
+
id="path10"
|
|
184
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round" />
|
|
185
|
+
<path
|
|
186
|
+
d="M 344.07459,135.90398 V 92.903984 m 0,13.999996 c 8,-13.999996 18,-15.999996 28,-9.999996"
|
|
187
|
+
id="path11"
|
|
188
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round" />
|
|
189
|
+
<path
|
|
190
|
+
d="m 386.04791,92.39779 -0.57303,61.55623 m 0,-50 c 8,-10.999993 26,-10.999993 32,3 7,17 -2,27 -16,27 -8,0 -13,-5 -16,-12"
|
|
191
|
+
id="path12"
|
|
192
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round"
|
|
193
|
+
sodipodi:nodetypes="ccccsc" />
|
|
194
|
+
<path
|
|
195
|
+
transform="translate(0,-5)"
|
|
196
|
+
d="m 467.92974,110.38253 c -5,-10 -22,-12.000004 -30,-3 -10,12 -3,31 12,31 9,0 15,-5 18,-12 m 0,-19 v 31"
|
|
197
|
+
id="path13"
|
|
198
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round" />
|
|
199
|
+
<path
|
|
200
|
+
transform="translate(0,-3)"
|
|
201
|
+
d="m 481.33002,72.275322 v 66.999998 m 0,-27 30,-15.999998 m -17,7.999998 21,35"
|
|
202
|
+
id="path14"
|
|
203
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round" />
|
|
204
|
+
<path
|
|
205
|
+
transform="translate(0,-6)"
|
|
206
|
+
d="m 563.39899,111.27532 c -5,-10 -22,-11.999998 -30,-3 -10,12 -3,31 12,31 9,0 15,-5 18,-12 m 0,-19 v 31"
|
|
207
|
+
id="path16"
|
|
208
|
+
style="fill:none;stroke:#20252b;stroke-width:12;stroke-linecap:round;stroke-linejoin:round" />
|
|
209
|
+
</svg>
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
%token NUMBER
|
|
2
|
+
%left '+' '-'
|
|
3
|
+
%left '*' '/'
|
|
4
|
+
|
|
5
|
+
%%
|
|
6
|
+
|
|
7
|
+
expression: NUMBER
|
|
8
|
+
| expression '+' expression { $$ = $1 + $3 }
|
|
9
|
+
| expression '-' expression { $$ = $1 - $3 }
|
|
10
|
+
| expression '*' expression { $$ = $1 * $3 }
|
|
11
|
+
| expression '/' expression { $$ = $1 / $3 }
|
|
12
|
+
| '(' expression ')' { $$ = $2 }
|
|
13
|
+
;
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
%token NUMBER
|
|
2
|
+
%left '+' '-'
|
|
3
|
+
%left '*' '/'
|
|
4
|
+
|
|
5
|
+
%%
|
|
6
|
+
|
|
7
|
+
expression: NUMBER { $$ = [:number, $1] }
|
|
8
|
+
| expression '+' expression { $$ = [:binary, :+, $1, $3] }
|
|
9
|
+
| expression '-' expression { $$ = [:binary, :-, $1, $3] }
|
|
10
|
+
| expression '*' expression { $$ = [:binary, :*, $1, $3] }
|
|
11
|
+
| expression '/' expression { $$ = [:binary, :/, $1, $3] }
|
|
12
|
+
| '(' expression ')' { $$ = $2 }
|
|
13
|
+
;
|
data/exe/arpaka
ADDED
data/lib/arpaka/BSDL
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
Copyright (C) 1993-2013 Yukihiro Matsumoto. All rights reserved.
|
|
2
|
+
|
|
3
|
+
Redistribution and use in source and binary forms, with or without
|
|
4
|
+
modification, are permitted provided that the following conditions
|
|
5
|
+
are met:
|
|
6
|
+
1. Redistributions of source code must retain the above copyright
|
|
7
|
+
notice, this list of conditions and the following disclaimer.
|
|
8
|
+
2. Redistributions in binary form must reproduce the above copyright
|
|
9
|
+
notice, this list of conditions and the following disclaimer in the
|
|
10
|
+
documentation and/or other materials provided with the distribution.
|
|
11
|
+
|
|
12
|
+
THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
|
|
13
|
+
ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
14
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
15
|
+
ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
|
|
16
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
17
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
|
|
18
|
+
OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
|
|
19
|
+
HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
|
|
20
|
+
LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
|
|
21
|
+
OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
|
|
22
|
+
SUCH DAMAGE.
|