Skip to content

Commit 28c9f12

Browse files
committed
feat(semantic): add codegen_lua code
1 parent 0e65da4 commit 28c9f12

File tree

2 files changed

+342
-0
lines changed

2 files changed

+342
-0
lines changed

src/codegen_lua.c

Lines changed: 331 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,331 @@
1+
#include "codegen_lua.h"
2+
3+
#include <stdio.h>
4+
#include <string.h>
5+
6+
static void emit_program(FILE *out, const AstProgram *program, const FunctionTable *functions);
7+
static void emit_function(FILE *out, const AstFunction *fn, const FunctionSignature *signature, const FunctionTable *functions);
8+
static void emit_main_wrapper(FILE *out, const AstFunction *fn, const FunctionSignature *signature, const FunctionTable *functions);
9+
static void emit_block(FILE *out, const AstBlock *block, const FunctionTable *functions, const FunctionSignature *signature, int indent, int wrap_with_do);
10+
static void emit_statement(FILE *out, const AstStmt *stmt, const FunctionTable *functions, const FunctionSignature *signature, int indent);
11+
static void emit_expression_raw(FILE *out, const AstExpr *expr, const FunctionTable *functions);
12+
static void emit_expression_expected(FILE *out, const AstExpr *expr, const FunctionTable *functions, TypeKind expected_type);
13+
static void emit_call(FILE *out, const AstExpr *expr, const FunctionTable *functions);
14+
static const FunctionSignature *lookup_signature(const FunctionTable *functions, const char *name);
15+
static const char *binary_op_token(AstBinaryOp op);
16+
static void emit_indent(FILE *out, int indent);
17+
18+
void codegen_lua_emit(FILE *out, const AstProgram *program, const FunctionTable *functions)
19+
{
20+
if (!out || !program || !functions)
21+
{
22+
return;
23+
}
24+
emit_program(out, program, functions);
25+
}
26+
27+
static void emit_program(FILE *out, const AstProgram *program, const FunctionTable *functions)
28+
{
29+
const AstFunction *main_function = NULL;
30+
const FunctionSignature *main_signature = NULL;
31+
32+
for (size_t i = 0; i < program->functions.count; ++i)
33+
{
34+
const AstFunction *fn = program->functions.items[i];
35+
if (fn && strcmp(fn->name, "main") == 0)
36+
{
37+
main_function = fn;
38+
main_signature = lookup_signature(functions, fn->name);
39+
continue;
40+
}
41+
const FunctionSignature *signature = lookup_signature(functions, fn->name);
42+
emit_function(out, fn, signature, functions);
43+
fputc('\n', out);
44+
}
45+
46+
if (main_function && main_signature)
47+
{
48+
emit_main_wrapper(out, main_function, main_signature, functions);
49+
}
50+
}
51+
52+
static void emit_function(FILE *out, const AstFunction *fn, const FunctionSignature *signature, const FunctionTable *functions)
53+
{
54+
(void)signature;
55+
emit_indent(out, 0);
56+
fprintf(out, "local function %s(", fn->name);
57+
for (size_t i = 0; i < fn->params.count; ++i)
58+
{
59+
if (i > 0)
60+
{
61+
fputs(", ", out);
62+
}
63+
fputs(fn->params.items[i].name, out);
64+
}
65+
fputs(")\n", out);
66+
emit_block(out, &fn->body, functions, signature, 1, 0);
67+
emit_indent(out, 0);
68+
fputs("end\n", out);
69+
}
70+
71+
static void emit_main_wrapper(FILE *out, const AstFunction *fn, const FunctionSignature *signature, const FunctionTable *functions)
72+
{
73+
emit_indent(out, 0);
74+
fputs("os.exit((function(args)\n", out);
75+
76+
if (fn->params.count > 0)
77+
{
78+
emit_indent(out, 1);
79+
fputs("local args_table = args\n", out);
80+
for (size_t i = 0; i < fn->params.count; ++i)
81+
{
82+
const AstParam *param = &fn->params.items[i];
83+
emit_indent(out, 1);
84+
switch (param->type)
85+
{
86+
case TYPE_INT:
87+
fprintf(out, "local %s = args_table and tonumber(args_table[%zu]) or 0\n", param->name, i + 1);
88+
break;
89+
case TYPE_FLOAT:
90+
fprintf(out, "local %s = args_table and tonumber(args_table[%zu]) or 0.0\n", param->name, i + 1);
91+
break;
92+
case TYPE_BOOL:
93+
fprintf(out, "local %s = args_table and args_table[%zu] ~= nil or false\n", param->name, i + 1);
94+
break;
95+
default:
96+
fprintf(out, "local %s = args_table and args_table[%zu] or nil\n", param->name, i + 1);
97+
break;
98+
}
99+
}
100+
}
101+
102+
emit_block(out, &fn->body, functions, signature, 1, 0);
103+
104+
emit_indent(out, 0);
105+
fputs("end)(arg))\n", out);
106+
}
107+
108+
static void emit_block(FILE *out, const AstBlock *block, const FunctionTable *functions, const FunctionSignature *signature, int indent, int wrap_with_do)
109+
{
110+
if (!block)
111+
{
112+
return;
113+
}
114+
115+
int current_indent = indent;
116+
if (wrap_with_do)
117+
{
118+
emit_indent(out, indent);
119+
fputs("do\n", out);
120+
current_indent = indent + 1;
121+
}
122+
123+
for (size_t i = 0; i < block->statements.count; ++i)
124+
{
125+
const AstStmt *stmt = block->statements.items[i];
126+
emit_statement(out, stmt, functions, signature, current_indent);
127+
}
128+
129+
if (wrap_with_do)
130+
{
131+
emit_indent(out, indent);
132+
fputs("end\n", out);
133+
}
134+
}
135+
136+
static void emit_statement(FILE *out, const AstStmt *stmt, const FunctionTable *functions, const FunctionSignature *signature, int indent)
137+
{
138+
if (!stmt)
139+
{
140+
return;
141+
}
142+
143+
switch (stmt->kind)
144+
{
145+
case STMT_BLOCK:
146+
emit_block(out, &stmt->data.block, functions, signature, indent, 1);
147+
break;
148+
case STMT_DECL:
149+
emit_indent(out, indent);
150+
fprintf(out, "local %s", stmt->data.decl.name);
151+
if (stmt->data.decl.init)
152+
{
153+
fputs(" = ", out);
154+
emit_expression_expected(out, stmt->data.decl.init, functions, stmt->data.decl.type);
155+
}
156+
fputc('\n', out);
157+
break;
158+
case STMT_ASSIGN:
159+
emit_indent(out, indent);
160+
fprintf(out, "%s = ", stmt->data.assign.name);
161+
emit_expression_expected(out, stmt->data.assign.value, functions, stmt->data.assign.type);
162+
fputc('\n', out);
163+
break;
164+
case STMT_EXPR:
165+
if (stmt->data.expr)
166+
{
167+
emit_indent(out, indent);
168+
emit_expression_raw(out, stmt->data.expr, functions);
169+
fputc('\n', out);
170+
}
171+
break;
172+
case STMT_RETURN:
173+
emit_indent(out, indent);
174+
fputs("return", out);
175+
if (stmt->data.expr)
176+
{
177+
fputc(' ', out);
178+
TypeKind expected = signature ? signature->return_type : TYPE_UNKNOWN;
179+
emit_expression_expected(out, stmt->data.expr, functions, expected);
180+
}
181+
fputc('\n', out);
182+
break;
183+
}
184+
}
185+
186+
static void emit_expression_expected(FILE *out, const AstExpr *expr, const FunctionTable *functions, TypeKind expected_type)
187+
{
188+
if (!expr)
189+
{
190+
fputs("nil", out);
191+
return;
192+
}
193+
194+
TypeKind actual = expr->type;
195+
if (expected_type == TYPE_UNKNOWN || actual == TYPE_UNKNOWN || expected_type == actual)
196+
{
197+
emit_expression_raw(out, expr, functions);
198+
return;
199+
}
200+
201+
if (expected_type == TYPE_INT && actual == TYPE_FLOAT)
202+
{
203+
fputs("math.floor(", out);
204+
emit_expression_raw(out, expr, functions);
205+
fputc(')', out);
206+
return;
207+
}
208+
209+
emit_expression_raw(out, expr, functions);
210+
}
211+
212+
static void emit_expression_raw(FILE *out, const AstExpr *expr, const FunctionTable *functions)
213+
{
214+
if (!expr)
215+
{
216+
fputs("nil", out);
217+
return;
218+
}
219+
220+
switch (expr->kind)
221+
{
222+
case EXPR_INT_LITERAL:
223+
fprintf(out, "%lld", expr->data.int_value);
224+
break;
225+
case EXPR_FLOAT_LITERAL:
226+
fprintf(out, "%g", expr->data.float_value);
227+
break;
228+
case EXPR_BOOL_LITERAL:
229+
fputs(expr->data.bool_value ? "true" : "false", out);
230+
break;
231+
case EXPR_IDENTIFIER:
232+
fputs(expr->data.identifier, out);
233+
break;
234+
case EXPR_BINARY:
235+
fputc('(', out);
236+
emit_expression_raw(out, expr->data.binary.left, functions);
237+
fprintf(out, " %s ", binary_op_token(expr->data.binary.op));
238+
emit_expression_raw(out, expr->data.binary.right, functions);
239+
fputc(')', out);
240+
break;
241+
case EXPR_UNARY:
242+
if (expr->data.unary.op == UN_OP_NEG)
243+
{
244+
fputc('-', out);
245+
fputc('(', out);
246+
emit_expression_raw(out, expr->data.unary.operand, functions);
247+
fputc(')', out);
248+
}
249+
else if (expr->data.unary.op == UN_OP_NOT)
250+
{
251+
fputs("not ", out);
252+
emit_expression_raw(out, expr->data.unary.operand, functions);
253+
}
254+
else
255+
{
256+
emit_expression_raw(out, expr->data.unary.operand, functions);
257+
}
258+
break;
259+
case EXPR_CALL:
260+
emit_call(out, expr, functions);
261+
break;
262+
}
263+
}
264+
265+
static void emit_call(FILE *out, const AstExpr *expr, const FunctionTable *functions)
266+
{
267+
const FunctionSignature *signature = lookup_signature(functions, expr->data.call.callee);
268+
fputs(expr->data.call.callee, out);
269+
fputc('(', out);
270+
for (size_t i = 0; i < expr->data.call.args.count; ++i)
271+
{
272+
if (i > 0)
273+
{
274+
fputs(", ", out);
275+
}
276+
TypeKind expected = TYPE_UNKNOWN;
277+
if (signature && i < signature->params.count)
278+
{
279+
expected = signature->params.items[i].type;
280+
}
281+
emit_expression_expected(out, expr->data.call.args.items[i], functions, expected);
282+
}
283+
fputc(')', out);
284+
}
285+
286+
static const FunctionSignature *lookup_signature(const FunctionTable *functions, const char *name)
287+
{
288+
if (!functions || !name)
289+
{
290+
return NULL;
291+
}
292+
return function_table_find(functions, name);
293+
}
294+
295+
static const char *binary_op_token(AstBinaryOp op)
296+
{
297+
switch (op)
298+
{
299+
case BIN_OP_ADD:
300+
return "+";
301+
case BIN_OP_SUB:
302+
return "-";
303+
case BIN_OP_MUL:
304+
return "*";
305+
case BIN_OP_DIV:
306+
return "/";
307+
case BIN_OP_MOD:
308+
return "%";
309+
case BIN_OP_EQ:
310+
return "==";
311+
case BIN_OP_NEQ:
312+
return "~=";
313+
case BIN_OP_LT:
314+
return "<";
315+
case BIN_OP_LE:
316+
return "<=";
317+
case BIN_OP_GT:
318+
return ">";
319+
case BIN_OP_GE:
320+
return ">=";
321+
}
322+
return "?";
323+
}
324+
325+
static void emit_indent(FILE *out, int indent)
326+
{
327+
for (int i = 0; i < indent; ++i)
328+
{
329+
fputs(" ", out);
330+
}
331+
}

src/codegen_lua.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,11 @@
1+
#ifndef CODEGEN_LUA_H
2+
#define CODEGEN_LUA_H
3+
4+
#include <stdio.h>
5+
6+
#include "ast.h"
7+
#include "symbol_table.h"
8+
9+
void codegen_lua_emit(FILE *out, const AstProgram *program, const FunctionTable *functions);
10+
11+
#endif

0 commit comments

Comments
 (0)