grongigo 1.0.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/MIT +21 -0
- data/README.md +266 -0
- data/Rakefile +8 -0
- data/bin/grongigo +157 -0
- data/examples/calc.grg +17 -0
- data/examples/factorial.grg +21 -0
- data/examples/fizzbuzz.grg +86 -0
- data/examples/hello.grg +5 -0
- data/grongigo.gemspec +30 -0
- data/lib/grongigo/ast/assign_expr.rb +18 -0
- data/lib/grongigo/ast/binary_expr.rb +19 -0
- data/lib/grongigo/ast/block_stmt.rb +17 -0
- data/lib/grongigo/ast/break_stmt.rb +14 -0
- data/lib/grongigo/ast/call_expr.rb +18 -0
- data/lib/grongigo/ast/case_clause.rb +18 -0
- data/lib/grongigo/ast/char_literal.rb +17 -0
- data/lib/grongigo/ast/continue_stmt.rb +14 -0
- data/lib/grongigo/ast/expr_stmt.rb +17 -0
- data/lib/grongigo/ast/for_stmt.rb +20 -0
- data/lib/grongigo/ast/function_decl.rb +20 -0
- data/lib/grongigo/ast/identifier.rb +17 -0
- data/lib/grongigo/ast/if_stmt.rb +19 -0
- data/lib/grongigo/ast/index_expr.rb +18 -0
- data/lib/grongigo/ast/node.rb +15 -0
- data/lib/grongigo/ast/number_literal.rb +17 -0
- data/lib/grongigo/ast/parameter.rb +18 -0
- data/lib/grongigo/ast/program.rb +17 -0
- data/lib/grongigo/ast/return_stmt.rb +17 -0
- data/lib/grongigo/ast/string_literal.rb +17 -0
- data/lib/grongigo/ast/switch_stmt.rb +19 -0
- data/lib/grongigo/ast/unary_expr.rb +19 -0
- data/lib/grongigo/ast/var_decl.rb +19 -0
- data/lib/grongigo/ast/while_stmt.rb +18 -0
- data/lib/grongigo/ast.rb +30 -0
- data/lib/grongigo/codegen.rb +357 -0
- data/lib/grongigo/compiler.rb +156 -0
- data/lib/grongigo/constants.rb +129 -0
- data/lib/grongigo/jp2grg.rb +117 -0
- data/lib/grongigo/lexer.rb +349 -0
- data/lib/grongigo/parse_error.rb +13 -0
- data/lib/grongigo/parser.rb +572 -0
- data/lib/grongigo/token.rb +23 -0
- data/lib/grongigo.rb +12 -0
- metadata +90 -0
|
@@ -0,0 +1,572 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'lexer'
|
|
4
|
+
require_relative 'parse_error'
|
|
5
|
+
require_relative 'ast'
|
|
6
|
+
|
|
7
|
+
module Grongigo
|
|
8
|
+
# Parser
|
|
9
|
+
class Parser
|
|
10
|
+
def initialize(tokens)
|
|
11
|
+
@tokens = tokens
|
|
12
|
+
@pos = 0
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def parse
|
|
16
|
+
declarations = []
|
|
17
|
+
until check(:eof)
|
|
18
|
+
decl = parse_declaration
|
|
19
|
+
declarations << decl if decl
|
|
20
|
+
end
|
|
21
|
+
AST::Program.new(declarations)
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def current
|
|
27
|
+
@tokens[@pos]
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def previous
|
|
31
|
+
@tokens[@pos - 1]
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
def check(type)
|
|
35
|
+
# Special handling for EOF check
|
|
36
|
+
return current.type == :eof if type == :eof
|
|
37
|
+
return false if eof?
|
|
38
|
+
|
|
39
|
+
current.type == type
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
def check_value(value)
|
|
43
|
+
return false if eof?
|
|
44
|
+
|
|
45
|
+
current.value == value
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def eof?
|
|
49
|
+
current.type == :eof
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def advance
|
|
53
|
+
@pos += 1 unless eof?
|
|
54
|
+
previous
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
def match(*types)
|
|
58
|
+
types.each do |type|
|
|
59
|
+
if check(type)
|
|
60
|
+
advance
|
|
61
|
+
return true
|
|
62
|
+
end
|
|
63
|
+
end
|
|
64
|
+
false
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def match_value(*values)
|
|
68
|
+
values.each do |value|
|
|
69
|
+
if check_value(value)
|
|
70
|
+
advance
|
|
71
|
+
return true
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
false
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def consume(type, message)
|
|
78
|
+
return advance if check(type)
|
|
79
|
+
|
|
80
|
+
raise ParseError.new("#{message} at line #{current.line}, column #{current.column}", current)
|
|
81
|
+
end
|
|
82
|
+
|
|
83
|
+
def consume_value(value, message)
|
|
84
|
+
return advance if check_value(value)
|
|
85
|
+
|
|
86
|
+
raise ParseError.new("#{message} at line #{current.line}, column #{current.column}", current)
|
|
87
|
+
end
|
|
88
|
+
|
|
89
|
+
# Parse declaration
|
|
90
|
+
def parse_declaration
|
|
91
|
+
# EOF check
|
|
92
|
+
return nil if check(:eof)
|
|
93
|
+
|
|
94
|
+
# Function definition: パザ type name parameters block
|
|
95
|
+
return parse_function_declaration if check(:other_keyword) && current.value == 'パザ'
|
|
96
|
+
|
|
97
|
+
# Variable or function declaration starting with type
|
|
98
|
+
return parse_var_or_func_declaration if check(:type_keyword)
|
|
99
|
+
|
|
100
|
+
parse_statement
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def parse_function_declaration
|
|
104
|
+
advance # Skip パザ
|
|
105
|
+
line = current.line
|
|
106
|
+
column = current.column
|
|
107
|
+
|
|
108
|
+
return_type = consume(:type_keyword, 'Expected return type').value
|
|
109
|
+
name = parse_identifier_name
|
|
110
|
+
|
|
111
|
+
# Parameters
|
|
112
|
+
params = []
|
|
113
|
+
|
|
114
|
+
# Parameters with parentheses
|
|
115
|
+
if match(:open_paren)
|
|
116
|
+
unless check(:close_paren)
|
|
117
|
+
# No parameters if void only
|
|
118
|
+
if check(:type_keyword) && current.value == 'void'
|
|
119
|
+
advance
|
|
120
|
+
else
|
|
121
|
+
loop do
|
|
122
|
+
param_type = consume(:type_keyword, 'Expected parameter type').value
|
|
123
|
+
param_name = parse_identifier_name
|
|
124
|
+
params << AST::Parameter.new(param_type, param_name)
|
|
125
|
+
break unless match(:comma)
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
consume(:close_paren, 'Expected )')
|
|
130
|
+
elsif check(:type_keyword) && current.value == 'void'
|
|
131
|
+
# Without parentheses: if only type name then no arguments, otherwise parameter list
|
|
132
|
+
# Example: パザ ゲギグウ ゴロ バサ ザジレ ... → main(void)
|
|
133
|
+
advance # Skip void
|
|
134
|
+
elsif check(:type_keyword)
|
|
135
|
+
# If there are parameters
|
|
136
|
+
loop do
|
|
137
|
+
param_type = advance.value
|
|
138
|
+
break if check(:open_brace) # End if block starts
|
|
139
|
+
|
|
140
|
+
param_name = parse_identifier_name
|
|
141
|
+
params << AST::Parameter.new(param_type, param_name)
|
|
142
|
+
break unless match(:comma)
|
|
143
|
+
end
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
body = parse_block
|
|
147
|
+
AST::FunctionDecl.new(return_type, name, params, body, line, column)
|
|
148
|
+
end
|
|
149
|
+
|
|
150
|
+
def parse_var_or_func_declaration
|
|
151
|
+
line = current.line
|
|
152
|
+
column = current.column
|
|
153
|
+
type = advance.value
|
|
154
|
+
name = parse_identifier_name
|
|
155
|
+
|
|
156
|
+
# Check for function declaration (whether there is a parameter list)
|
|
157
|
+
if match(:open_paren)
|
|
158
|
+
params = []
|
|
159
|
+
unless check(:close_paren)
|
|
160
|
+
loop do
|
|
161
|
+
param_type = consume(:type_keyword, 'Expected parameter type').value
|
|
162
|
+
param_name = parse_identifier_name
|
|
163
|
+
params << AST::Parameter.new(param_type, param_name)
|
|
164
|
+
break unless match(:comma)
|
|
165
|
+
end
|
|
166
|
+
end
|
|
167
|
+
consume(:close_paren, 'Expected )')
|
|
168
|
+
body = parse_block
|
|
169
|
+
return AST::FunctionDecl.new(type, name, params, body, line, column)
|
|
170
|
+
end
|
|
171
|
+
|
|
172
|
+
# Variable declaration
|
|
173
|
+
initializer = nil
|
|
174
|
+
initializer = parse_expression if match(:operator) && previous.value == '='
|
|
175
|
+
|
|
176
|
+
AST::VarDecl.new(type, name, initializer, line, column)
|
|
177
|
+
end
|
|
178
|
+
|
|
179
|
+
def parse_identifier_name
|
|
180
|
+
if check(:identifier)
|
|
181
|
+
advance.value
|
|
182
|
+
elsif check(:other_keyword)
|
|
183
|
+
advance.value
|
|
184
|
+
else
|
|
185
|
+
raise ParseError.new("Expected identifier at line #{current.line}", current)
|
|
186
|
+
end
|
|
187
|
+
end
|
|
188
|
+
|
|
189
|
+
# Parse statement
|
|
190
|
+
def parse_statement
|
|
191
|
+
# EOFチェック
|
|
192
|
+
return nil if check(:eof)
|
|
193
|
+
|
|
194
|
+
line = current.line
|
|
195
|
+
column = current.column
|
|
196
|
+
|
|
197
|
+
# Block
|
|
198
|
+
return parse_block if check(:open_brace)
|
|
199
|
+
|
|
200
|
+
# if statement
|
|
201
|
+
return parse_if_statement if check(:control_keyword) && current.value == 'if'
|
|
202
|
+
|
|
203
|
+
# while statement
|
|
204
|
+
return parse_while_statement if check(:control_keyword) && current.value == 'while'
|
|
205
|
+
|
|
206
|
+
# for statement
|
|
207
|
+
return parse_for_statement if check(:control_keyword) && current.value == 'for'
|
|
208
|
+
|
|
209
|
+
# switch statement
|
|
210
|
+
return parse_switch_statement if check(:control_keyword) && current.value == 'switch'
|
|
211
|
+
|
|
212
|
+
# return statement
|
|
213
|
+
return parse_return_statement if check(:control_keyword) && current.value == 'return'
|
|
214
|
+
|
|
215
|
+
# break statement
|
|
216
|
+
if check(:control_keyword) && current.value == 'break'
|
|
217
|
+
advance
|
|
218
|
+
return AST::BreakStmt.new(line, column)
|
|
219
|
+
end
|
|
220
|
+
|
|
221
|
+
# continue statement
|
|
222
|
+
if check(:control_keyword) && current.value == 'continue'
|
|
223
|
+
advance
|
|
224
|
+
return AST::ContinueStmt.new(line, column)
|
|
225
|
+
end
|
|
226
|
+
|
|
227
|
+
# Variable declaration
|
|
228
|
+
return parse_var_declaration if check(:type_keyword)
|
|
229
|
+
|
|
230
|
+
# Expression statement
|
|
231
|
+
parse_expression_statement
|
|
232
|
+
end
|
|
233
|
+
|
|
234
|
+
def parse_block
|
|
235
|
+
line = current.line
|
|
236
|
+
column = current.column
|
|
237
|
+
consume(:open_brace, 'Expected {')
|
|
238
|
+
|
|
239
|
+
statements = []
|
|
240
|
+
until check(:close_brace) || eof?
|
|
241
|
+
stmt = parse_statement
|
|
242
|
+
statements << stmt if stmt
|
|
243
|
+
end
|
|
244
|
+
|
|
245
|
+
consume(:close_brace, 'Expected }')
|
|
246
|
+
AST::BlockStmt.new(statements, line, column)
|
|
247
|
+
end
|
|
248
|
+
|
|
249
|
+
def parse_if_statement
|
|
250
|
+
line = current.line
|
|
251
|
+
column = current.column
|
|
252
|
+
advance # Skip if
|
|
253
|
+
|
|
254
|
+
condition = parse_expression
|
|
255
|
+
then_branch = parse_block_or_statement
|
|
256
|
+
|
|
257
|
+
else_branch = nil
|
|
258
|
+
if check(:control_keyword) && current.value == 'else'
|
|
259
|
+
advance
|
|
260
|
+
# Check for else if
|
|
261
|
+
else_branch = if check(:control_keyword) && current.value == 'if'
|
|
262
|
+
parse_if_statement
|
|
263
|
+
else
|
|
264
|
+
parse_block_or_statement
|
|
265
|
+
end
|
|
266
|
+
end
|
|
267
|
+
|
|
268
|
+
AST::IfStmt.new(condition, then_branch, else_branch, line, column)
|
|
269
|
+
end
|
|
270
|
+
|
|
271
|
+
def parse_while_statement
|
|
272
|
+
line = current.line
|
|
273
|
+
column = current.column
|
|
274
|
+
advance # Skip while
|
|
275
|
+
|
|
276
|
+
condition = parse_expression
|
|
277
|
+
body = parse_block_or_statement
|
|
278
|
+
|
|
279
|
+
AST::WhileStmt.new(condition, body, line, column)
|
|
280
|
+
end
|
|
281
|
+
|
|
282
|
+
def parse_for_statement
|
|
283
|
+
line = current.line
|
|
284
|
+
column = current.column
|
|
285
|
+
advance # Skip for
|
|
286
|
+
|
|
287
|
+
# Simple for statement: for init condition update block
|
|
288
|
+
# Or with parentheses: for (init, condition, update) block
|
|
289
|
+
|
|
290
|
+
init = nil
|
|
291
|
+
condition = nil
|
|
292
|
+
update = nil
|
|
293
|
+
|
|
294
|
+
if match(:open_paren)
|
|
295
|
+
init = parse_for_init unless check(:comma)
|
|
296
|
+
consume(:comma, 'Expected ,')
|
|
297
|
+
condition = parse_expression unless check(:comma)
|
|
298
|
+
consume(:comma, 'Expected ,')
|
|
299
|
+
update = parse_expression unless check(:close_paren)
|
|
300
|
+
consume(:close_paren, 'Expected )')
|
|
301
|
+
else
|
|
302
|
+
# Without parentheses: condition only
|
|
303
|
+
condition = parse_expression
|
|
304
|
+
end
|
|
305
|
+
|
|
306
|
+
body = parse_block_or_statement
|
|
307
|
+
AST::ForStmt.new(init, condition, update, body, line, column)
|
|
308
|
+
end
|
|
309
|
+
|
|
310
|
+
def parse_for_init
|
|
311
|
+
if check(:type_keyword)
|
|
312
|
+
parse_var_declaration
|
|
313
|
+
else
|
|
314
|
+
parse_expression
|
|
315
|
+
end
|
|
316
|
+
end
|
|
317
|
+
|
|
318
|
+
def parse_switch_statement
|
|
319
|
+
line = current.line
|
|
320
|
+
column = current.column
|
|
321
|
+
advance # Skip switch
|
|
322
|
+
|
|
323
|
+
expression = parse_expression
|
|
324
|
+
consume(:open_brace, 'Expected {')
|
|
325
|
+
|
|
326
|
+
cases = []
|
|
327
|
+
default_case = nil
|
|
328
|
+
|
|
329
|
+
until check(:close_brace) || eof?
|
|
330
|
+
if check(:control_keyword) && current.value == 'case'
|
|
331
|
+
advance
|
|
332
|
+
value = parse_expression
|
|
333
|
+
consume(:colon, 'Expected :')
|
|
334
|
+
statements = []
|
|
335
|
+
until check(:control_keyword) && %w[case default].include?(current.value) || check(:close_brace)
|
|
336
|
+
statements << parse_statement
|
|
337
|
+
end
|
|
338
|
+
cases << AST::CaseClause.new(value, statements)
|
|
339
|
+
elsif check(:control_keyword) && current.value == 'default'
|
|
340
|
+
advance
|
|
341
|
+
consume(:colon, 'Expected :')
|
|
342
|
+
statements = []
|
|
343
|
+
statements << parse_statement until check(:control_keyword) && current.value == 'case' || check(:close_brace)
|
|
344
|
+
default_case = statements
|
|
345
|
+
else
|
|
346
|
+
break
|
|
347
|
+
end
|
|
348
|
+
end
|
|
349
|
+
|
|
350
|
+
consume(:close_brace, 'Expected }')
|
|
351
|
+
AST::SwitchStmt.new(expression, cases, default_case, line, column)
|
|
352
|
+
end
|
|
353
|
+
|
|
354
|
+
def parse_return_statement
|
|
355
|
+
line = current.line
|
|
356
|
+
column = current.column
|
|
357
|
+
advance # Skip return
|
|
358
|
+
|
|
359
|
+
value = nil
|
|
360
|
+
# Parse value if not block end or EOF
|
|
361
|
+
unless check(:close_brace) || check(:eof) || check(:control_keyword) || check(:type_keyword)
|
|
362
|
+
value = parse_expression
|
|
363
|
+
end
|
|
364
|
+
|
|
365
|
+
AST::ReturnStmt.new(value, line, column)
|
|
366
|
+
end
|
|
367
|
+
|
|
368
|
+
def parse_var_declaration
|
|
369
|
+
line = current.line
|
|
370
|
+
column = current.column
|
|
371
|
+
type = advance.value
|
|
372
|
+
name = parse_identifier_name
|
|
373
|
+
|
|
374
|
+
# Array declaration
|
|
375
|
+
if match(:open_bracket)
|
|
376
|
+
# Array size
|
|
377
|
+
parse_expression unless check(:close_bracket)
|
|
378
|
+
consume(:close_bracket, 'Expected ]')
|
|
379
|
+
type = "#{type}[]"
|
|
380
|
+
end
|
|
381
|
+
|
|
382
|
+
initializer = nil
|
|
383
|
+
if check(:operator) && current.value == '='
|
|
384
|
+
advance
|
|
385
|
+
initializer = parse_expression
|
|
386
|
+
end
|
|
387
|
+
|
|
388
|
+
AST::VarDecl.new(type, name, initializer, line, column)
|
|
389
|
+
end
|
|
390
|
+
|
|
391
|
+
def parse_block_or_statement
|
|
392
|
+
if check(:open_brace)
|
|
393
|
+
parse_block
|
|
394
|
+
else
|
|
395
|
+
parse_statement
|
|
396
|
+
end
|
|
397
|
+
end
|
|
398
|
+
|
|
399
|
+
def parse_expression_statement
|
|
400
|
+
line = current.line
|
|
401
|
+
column = current.column
|
|
402
|
+
expr = parse_expression
|
|
403
|
+
AST::ExprStmt.new(expr, line, column)
|
|
404
|
+
end
|
|
405
|
+
|
|
406
|
+
# Parse expression(優先順位順)
|
|
407
|
+
def parse_expression
|
|
408
|
+
parse_assignment
|
|
409
|
+
end
|
|
410
|
+
|
|
411
|
+
def parse_assignment
|
|
412
|
+
expr = parse_or
|
|
413
|
+
|
|
414
|
+
if check(:operator) && current.value == '='
|
|
415
|
+
advance
|
|
416
|
+
value = parse_assignment
|
|
417
|
+
return AST::AssignExpr.new(expr, value, expr.line, expr.column)
|
|
418
|
+
end
|
|
419
|
+
|
|
420
|
+
expr
|
|
421
|
+
end
|
|
422
|
+
|
|
423
|
+
def parse_or
|
|
424
|
+
expr = parse_and
|
|
425
|
+
|
|
426
|
+
while check(:operator) && current.value == '||'
|
|
427
|
+
op = advance.value
|
|
428
|
+
right = parse_and
|
|
429
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
430
|
+
end
|
|
431
|
+
|
|
432
|
+
expr
|
|
433
|
+
end
|
|
434
|
+
|
|
435
|
+
def parse_and
|
|
436
|
+
expr = parse_equality
|
|
437
|
+
|
|
438
|
+
while check(:operator) && current.value == '&&'
|
|
439
|
+
op = advance.value
|
|
440
|
+
right = parse_equality
|
|
441
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
442
|
+
end
|
|
443
|
+
|
|
444
|
+
expr
|
|
445
|
+
end
|
|
446
|
+
|
|
447
|
+
def parse_equality
|
|
448
|
+
expr = parse_comparison
|
|
449
|
+
|
|
450
|
+
while check(:operator) && %w[== !=].include?(current.value)
|
|
451
|
+
op = advance.value
|
|
452
|
+
right = parse_comparison
|
|
453
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
454
|
+
end
|
|
455
|
+
|
|
456
|
+
expr
|
|
457
|
+
end
|
|
458
|
+
|
|
459
|
+
def parse_comparison
|
|
460
|
+
expr = parse_term
|
|
461
|
+
|
|
462
|
+
while check(:operator) && %w[< > <= >=].include?(current.value)
|
|
463
|
+
op = advance.value
|
|
464
|
+
right = parse_term
|
|
465
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
466
|
+
end
|
|
467
|
+
|
|
468
|
+
expr
|
|
469
|
+
end
|
|
470
|
+
|
|
471
|
+
def parse_term
|
|
472
|
+
expr = parse_factor
|
|
473
|
+
|
|
474
|
+
while check(:operator) && %w[+ -].include?(current.value)
|
|
475
|
+
op = advance.value
|
|
476
|
+
right = parse_factor
|
|
477
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
478
|
+
end
|
|
479
|
+
|
|
480
|
+
expr
|
|
481
|
+
end
|
|
482
|
+
|
|
483
|
+
def parse_factor
|
|
484
|
+
expr = parse_unary
|
|
485
|
+
|
|
486
|
+
while check(:operator) && %w[* / %].include?(current.value)
|
|
487
|
+
op = advance.value
|
|
488
|
+
right = parse_unary
|
|
489
|
+
expr = AST::BinaryExpr.new(expr, op, right, expr.line, expr.column)
|
|
490
|
+
end
|
|
491
|
+
|
|
492
|
+
expr
|
|
493
|
+
end
|
|
494
|
+
|
|
495
|
+
def parse_unary
|
|
496
|
+
if check(:operator) && %w[! - ++ --].include?(current.value)
|
|
497
|
+
op = advance.value
|
|
498
|
+
operand = parse_unary
|
|
499
|
+
return AST::UnaryExpr.new(op, operand, true, operand.line, operand.column)
|
|
500
|
+
end
|
|
501
|
+
|
|
502
|
+
parse_postfix
|
|
503
|
+
end
|
|
504
|
+
|
|
505
|
+
def parse_postfix
|
|
506
|
+
expr = parse_primary
|
|
507
|
+
|
|
508
|
+
loop do
|
|
509
|
+
if match(:open_paren)
|
|
510
|
+
# Function call
|
|
511
|
+
args = []
|
|
512
|
+
unless check(:close_paren)
|
|
513
|
+
loop do
|
|
514
|
+
args << parse_expression
|
|
515
|
+
break unless match(:comma)
|
|
516
|
+
end
|
|
517
|
+
end
|
|
518
|
+
consume(:close_paren, 'Expected )')
|
|
519
|
+
expr = AST::CallExpr.new(expr, args, expr.line, expr.column)
|
|
520
|
+
elsif match(:open_bracket)
|
|
521
|
+
# Array access
|
|
522
|
+
index = parse_expression
|
|
523
|
+
consume(:close_bracket, 'Expected ]')
|
|
524
|
+
expr = AST::IndexExpr.new(expr, index, expr.line, expr.column)
|
|
525
|
+
elsif check(:operator) && %w[++ --].include?(current.value)
|
|
526
|
+
op = advance.value
|
|
527
|
+
expr = AST::UnaryExpr.new(op, expr, false, expr.line, expr.column)
|
|
528
|
+
else
|
|
529
|
+
break
|
|
530
|
+
end
|
|
531
|
+
end
|
|
532
|
+
|
|
533
|
+
expr
|
|
534
|
+
end
|
|
535
|
+
|
|
536
|
+
def parse_primary
|
|
537
|
+
line = current.line
|
|
538
|
+
column = current.column
|
|
539
|
+
|
|
540
|
+
# Number literal
|
|
541
|
+
if check(:number)
|
|
542
|
+
advance
|
|
543
|
+
return AST::NumberLiteral.new(previous.value, line, column)
|
|
544
|
+
end
|
|
545
|
+
|
|
546
|
+
# String literal
|
|
547
|
+
return AST::StringLiteral.new(previous.value, line, column) if match(:string_literal)
|
|
548
|
+
|
|
549
|
+
# Character literal
|
|
550
|
+
return AST::CharLiteral.new(previous.value, line, column) if match(:char_literal)
|
|
551
|
+
|
|
552
|
+
# Identifier
|
|
553
|
+
return AST::Identifier.new(previous.value, line, column) if match(:identifier)
|
|
554
|
+
|
|
555
|
+
# Keyword used as identifier
|
|
556
|
+
return AST::Identifier.new(previous.value, line, column) if match(:other_keyword)
|
|
557
|
+
|
|
558
|
+
# Control keyword (function name like printf)
|
|
559
|
+
return AST::Identifier.new(previous.value, line, column) if match(:control_keyword)
|
|
560
|
+
|
|
561
|
+
# Parentheses
|
|
562
|
+
if match(:open_paren)
|
|
563
|
+
expr = parse_expression
|
|
564
|
+
consume(:close_paren, 'Expected )')
|
|
565
|
+
return expr
|
|
566
|
+
end
|
|
567
|
+
|
|
568
|
+
raise ParseError.new("Unexpected token: #{current.value.inspect} (type: #{current.type}) at line #{line}",
|
|
569
|
+
current)
|
|
570
|
+
end
|
|
571
|
+
end
|
|
572
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Grongigo
|
|
4
|
+
# Token type
|
|
5
|
+
class Token
|
|
6
|
+
attr_reader :type, :value, :line, :column
|
|
7
|
+
|
|
8
|
+
def initialize(type, value, line = 0, column = 0)
|
|
9
|
+
@type = type
|
|
10
|
+
@value = value
|
|
11
|
+
@line = line
|
|
12
|
+
@column = column
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def to_s
|
|
16
|
+
"Token(#{@type}, #{@value.inspect}, L#{@line}:#{@column})"
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def inspect
|
|
20
|
+
to_s
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
end
|
data/lib/grongigo.rb
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
require_relative 'grongigo/constants'
|
|
4
|
+
require_relative 'grongigo/lexer'
|
|
5
|
+
require_relative 'grongigo/parser'
|
|
6
|
+
require_relative 'grongigo/codegen'
|
|
7
|
+
require_relative 'grongigo/compiler'
|
|
8
|
+
require_relative 'grongigo/jp2grg'
|
|
9
|
+
|
|
10
|
+
module Grongigo
|
|
11
|
+
VERSION = '1.0.0'
|
|
12
|
+
end
|
metadata
ADDED
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
|
2
|
+
name: grongigo
|
|
3
|
+
version: !ruby/object:Gem::Version
|
|
4
|
+
version: 1.0.0
|
|
5
|
+
platform: ruby
|
|
6
|
+
authors:
|
|
7
|
+
- Yudai Takada
|
|
8
|
+
bindir: bin
|
|
9
|
+
cert_chain: []
|
|
10
|
+
date: 1980-01-02 00:00:00.000000000 Z
|
|
11
|
+
dependencies: []
|
|
12
|
+
description: Grongigo is a programming language compiler that transpiles Grongi language
|
|
13
|
+
code to C language. Features include complete Grongigo syntax, base-9 number system,
|
|
14
|
+
and C language transpilation.
|
|
15
|
+
email:
|
|
16
|
+
- t.yudai92@gmail.com
|
|
17
|
+
executables:
|
|
18
|
+
- grongigo
|
|
19
|
+
extensions: []
|
|
20
|
+
extra_rdoc_files: []
|
|
21
|
+
files:
|
|
22
|
+
- MIT
|
|
23
|
+
- README.md
|
|
24
|
+
- Rakefile
|
|
25
|
+
- bin/grongigo
|
|
26
|
+
- examples/calc.grg
|
|
27
|
+
- examples/factorial.grg
|
|
28
|
+
- examples/fizzbuzz.grg
|
|
29
|
+
- examples/hello.grg
|
|
30
|
+
- grongigo.gemspec
|
|
31
|
+
- lib/grongigo.rb
|
|
32
|
+
- lib/grongigo/ast.rb
|
|
33
|
+
- lib/grongigo/ast/assign_expr.rb
|
|
34
|
+
- lib/grongigo/ast/binary_expr.rb
|
|
35
|
+
- lib/grongigo/ast/block_stmt.rb
|
|
36
|
+
- lib/grongigo/ast/break_stmt.rb
|
|
37
|
+
- lib/grongigo/ast/call_expr.rb
|
|
38
|
+
- lib/grongigo/ast/case_clause.rb
|
|
39
|
+
- lib/grongigo/ast/char_literal.rb
|
|
40
|
+
- lib/grongigo/ast/continue_stmt.rb
|
|
41
|
+
- lib/grongigo/ast/expr_stmt.rb
|
|
42
|
+
- lib/grongigo/ast/for_stmt.rb
|
|
43
|
+
- lib/grongigo/ast/function_decl.rb
|
|
44
|
+
- lib/grongigo/ast/identifier.rb
|
|
45
|
+
- lib/grongigo/ast/if_stmt.rb
|
|
46
|
+
- lib/grongigo/ast/index_expr.rb
|
|
47
|
+
- lib/grongigo/ast/node.rb
|
|
48
|
+
- lib/grongigo/ast/number_literal.rb
|
|
49
|
+
- lib/grongigo/ast/parameter.rb
|
|
50
|
+
- lib/grongigo/ast/program.rb
|
|
51
|
+
- lib/grongigo/ast/return_stmt.rb
|
|
52
|
+
- lib/grongigo/ast/string_literal.rb
|
|
53
|
+
- lib/grongigo/ast/switch_stmt.rb
|
|
54
|
+
- lib/grongigo/ast/unary_expr.rb
|
|
55
|
+
- lib/grongigo/ast/var_decl.rb
|
|
56
|
+
- lib/grongigo/ast/while_stmt.rb
|
|
57
|
+
- lib/grongigo/codegen.rb
|
|
58
|
+
- lib/grongigo/compiler.rb
|
|
59
|
+
- lib/grongigo/constants.rb
|
|
60
|
+
- lib/grongigo/jp2grg.rb
|
|
61
|
+
- lib/grongigo/lexer.rb
|
|
62
|
+
- lib/grongigo/parse_error.rb
|
|
63
|
+
- lib/grongigo/parser.rb
|
|
64
|
+
- lib/grongigo/token.rb
|
|
65
|
+
homepage: https://github.com/ydah/grongigo
|
|
66
|
+
licenses:
|
|
67
|
+
- MIT
|
|
68
|
+
metadata:
|
|
69
|
+
homepage_uri: https://github.com/ydah/grongigo
|
|
70
|
+
source_code_uri: https://github.com/ydah/grongigo
|
|
71
|
+
changelog_uri: https://github.com/ydah/grongigo/blob/main/CHANGELOG.md
|
|
72
|
+
rdoc_options: []
|
|
73
|
+
require_paths:
|
|
74
|
+
- lib
|
|
75
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
|
76
|
+
requirements:
|
|
77
|
+
- - ">="
|
|
78
|
+
- !ruby/object:Gem::Version
|
|
79
|
+
version: 3.0.0
|
|
80
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
|
81
|
+
requirements:
|
|
82
|
+
- - ">="
|
|
83
|
+
- !ruby/object:Gem::Version
|
|
84
|
+
version: '0'
|
|
85
|
+
requirements: []
|
|
86
|
+
rubygems_version: 3.6.9
|
|
87
|
+
specification_version: 4
|
|
88
|
+
summary: Grongigo programming language compiler - A programming language based on
|
|
89
|
+
Grongi language from Kamen Rider Kuuga (Masked Rider Kuuga)
|
|
90
|
+
test_files: []
|