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 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
@@ -0,0 +1,5 @@
1
+ ## [Unreleased]
2
+
3
+ ## [0.1.0] - 2026-09-07
4
+
5
+ - Initial release
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
@@ -0,0 +1,6 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "arpaka/cli"
5
+
6
+ exit Arpaka::CLI.run(ARGV)
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.