You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
ravi/ravicomp/src/ast_printer.c

562 lines
17 KiB

/******************************************************************************
* Copyright (C) 2018-2021 Dibyendu Majumdar
*
* Permission is hereby granted, free of charge, to any person obtaining
* a copy of this software and associated documentation files (the
* "Software"), to deal in the Software without restriction, including
* without limitation the rights to use, copy, modify, merge, publish,
* distribute, sublicense, and/or sell copies of the Software, and to
* permit persons to whom the Software is furnished to do so, subject to
* the following conditions:
*
* The above copyright notice and this permission notice shall be
* included in all copies or substantial portions of the Software.
*
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
* EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
* MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
* IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
* CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
* TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
* SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
******************************************************************************/
#include <parser.h>
const char *raviX_get_type_name(ravitype_t tt)
{
switch (tt) {
case RAVI_TANY:
return "any";
case RAVI_TNIL:
return "nil";
case RAVI_TBOOLEAN:
return "boolean";
case RAVI_TNUMFLT:
return "number";
case RAVI_TNUMINT:
return "integer";
case RAVI_TTABLE:
return "table";
case RAVI_TSTRING:
return "string";
case RAVI_TARRAYINT:
return "integer[]";
case RAVI_TARRAYFLT:
return "number[]";
case RAVI_TFUNCTION:
return "closure";
case RAVI_TUSERDATA:
return "userdata";
default:
return "?";
}
}
static void printf_buf(TextBuffer *buf, const char *format, ...)
{
static const char *PADDING = " ";
char tbuf[128] = {0};
va_list ap;
const char *cp;
va_start(ap, format);
for (cp = format; *cp; cp++) {
if (cp[0] == '%' && cp[1] == 'p') { /* padding */
int level = va_arg(ap, int);
snprintf(tbuf, sizeof tbuf, "%.*s", level, PADDING);
raviX_buffer_add_string(buf, tbuf);
cp++;
} else if (cp[0] == '%' && cp[1] == 't') { /* string_object */
const StringObject *s = va_arg(ap, const StringObject *);
raviX_buffer_add_string(buf, s->str);
cp++;
} else if (cp[0] == '%' && cp[1] == 'T') { /* VariableType */
const VariableType *type;
type = va_arg(ap, const VariableType *);
if (type->type_code == RAVI_TUSERDATA) {
const StringObject *s = type->type_name;
raviX_buffer_add_string(buf, s->str);
} else {
raviX_buffer_add_string(buf, raviX_get_type_name(type->type_code));
}
cp++;
} else if (cp[0] == '%' && cp[1] == 's') { /* const char * */
const char *s;
s = va_arg(ap, const char *);
raviX_buffer_add_string(buf, s);
cp++;
} else if (cp[0] == '%' && cp[1] == 'c') { /* comment */
const char *s;
s = va_arg(ap, const char *);
raviX_buffer_add_fstring(buf, "--%s", s);
cp++;
} else if (cp[0] == '%' && cp[1] == 'i') { /* integer */
lua_Integer i;
i = va_arg(ap, lua_Integer);
raviX_buffer_add_longlong(buf, i);
cp++;
} else if (cp[0] == '%' && cp[1] == 'f') { /* float */
double d;
d = va_arg(ap, double);
raviX_buffer_add_fstring(buf, "%.16f", d);
cp++;
} else if (cp[0] == '%' && cp[1] == 'b') { /* boolean */
lua_Integer i;
i = va_arg(ap, lua_Integer);
raviX_buffer_add_bool(buf, i != 0);
cp++;
} else {
raviX_buffer_add_char(buf, *cp);
}
}
va_end(ap);
}
static void print_ast_node_list(TextBuffer *buf, AstNodeList *list, int level, const char *delimiter)
{
AstNode *node;
bool is_first = true;
FOR_EACH_PTR(list, AstNode, node)
{
if (is_first)
is_first = false;
else if (delimiter)
printf_buf(buf, "%p%s\n", level, delimiter);
raviX_print_ast_node(buf, node, level + 1);
}
END_FOR_EACH_PTR(node);
}
static void print_statement_list(TextBuffer *buf, AstNodeList *statement_list, int level)
{
print_ast_node_list(buf, statement_list, level + 1, NULL);
}
static inline const char *get_as_str(const StringObject *ts) { return ts ? ts->str : ""; }
static void print_symbol(TextBuffer *buf, LuaSymbol *sym, int level)
{
switch (sym->symbol_type) {
case SYM_ENV: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "_ENV",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_GLOBAL: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "global symbol",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_LOCAL: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->variable.var_name, "local symbol",
raviX_get_type_name(sym->variable.value_type.type_code), get_as_str(sym->variable.value_type.type_name));
break;
}
case SYM_UPVALUE: {
printf_buf(buf, "%p%t %c %s %s\n", level, sym->upvalue.target_variable->variable.var_name, "upvalue",
raviX_get_type_name(sym->upvalue.target_variable->variable.value_type.type_code),
get_as_str(sym->upvalue.target_variable->variable.value_type.type_name));
break;
}
default:
assert(0);
}
}
static void print_symbol_name(TextBuffer *buf, LuaSymbol *sym)
{
switch (sym->symbol_type) {
case SYM_LOCAL:
case SYM_ENV:
case SYM_GLOBAL: {
printf_buf(buf, "%t", sym->variable.var_name);
break;
}
case SYM_UPVALUE: {
if (sym->upvalue.target_variable->symbol_type == SYM_ENV) {
printf_buf(buf, "%t*", sym->upvalue.target_variable->variable.var_name);
}
else {
printf_buf(buf, "%t", sym->upvalue.target_variable->variable.var_name);
}
break;
}
default:
assert(0);
}
}
static void print_symbol_list(TextBuffer *buf, LuaSymbolList *list, int level, const char *delimiter)
{
LuaSymbol *node;
bool is_first = true;
FOR_EACH_PTR(list, LuaSymbol, node)
{
if (is_first)
is_first = false;
else if (delimiter)
printf_buf(buf, "%p%s\n", level, delimiter);
print_symbol(buf, node, level + 1);
}
END_FOR_EACH_PTR(node);
}
static void print_symbol_names(TextBuffer *buf, LuaSymbolList *list)
{
LuaSymbol *node;
bool is_first = true;
FOR_EACH_PTR(list, LuaSymbol, node)
{
if (is_first)
is_first = false;
else
printf_buf(buf, ", ");
print_symbol_name(buf, node);
}
END_FOR_EACH_PTR(node);
}
const char *raviX_get_unary_opr_str(UnaryOperatorType op)
{
switch (op) {
case UNOPR_NOT:
return "not";
case UNOPR_MINUS:
return "-";
case UNOPR_BNOT:
return "~";
case UNOPR_LEN:
return "#";
case UNOPR_TO_INTEGER:
return "@integer";
case UNOPR_TO_NUMBER:
return "@number";
case UNOPR_TO_INTARRAY:
return "@integer[]";
case UNOPR_TO_NUMARRAY:
return "@number[]";
case UNOPR_TO_TABLE:
return "@table";
case UNOPR_TO_CLOSURE:
return "@closure";
case UNOPR_TO_STRING:
return "@string";
case UNOPR_TO_TYPE:
return "@<usertype>";
default:
return "";
}
}
const char *raviX_get_binary_opr_str(BinaryOperatorType op)
{
switch (op) {
case BINOPR_ADD:
return "+";
case BINOPR_SUB:
return "-";
case BINOPR_MUL:
return "*";
case BINOPR_MOD:
return "%";
case BINOPR_POW:
return "^";
case BINOPR_DIV:
return "/";
case BINOPR_IDIV:
return "//";
case BINOPR_BAND:
return "&";
case BINOPR_BOR:
return "|";
case BINOPR_BXOR:
return "~";
case BINOPR_SHL:
return "<<";
case BINOPR_SHR:
return ">>";
case BINOPR_CONCAT:
return "..";
case BINOPR_NE:
return "~=";
case BINOPR_EQ:
return "==";
case BINOPR_LT:
return "<";
case BINOPR_LE:
return "<=";
case BINOPR_GT:
return ">";
case BINOPR_GE:
return ">=";
case BINOPR_AND:
return "and";
case BINOPR_OR:
return "or";
default:
return "";
}
}
void raviX_print_ast_node(TextBuffer *buf, AstNode *node, int level)
{
switch (node->type) {
case EXPR_FUNCTION: {
if (node->function_expr.args) {
printf_buf(buf, "%pfunction(\n", level);
print_symbol_list(buf, node->function_expr.args, level + 1, ",");
printf_buf(buf, "%p)\n", level);
} else {
printf_buf(buf, "%pfunction()\n", level);
}
if (node->function_expr.locals) {
printf_buf(buf, "%p%c ", level, "locals ");
print_symbol_names(buf, node->function_expr.locals);
printf_buf(buf, "\n");
}
if (node->function_expr.upvalues) {
printf_buf(buf, "%p%c ", level, "upvalues ");
print_symbol_names(buf, node->function_expr.upvalues);
printf_buf(buf, "\n");
}
print_statement_list(buf, node->function_expr.function_statement_list, level);
printf_buf(buf, "%pend\n", level);
break;
}
case AST_NONE:
break;
case STMT_RETURN: {
printf_buf(buf, "%preturn\n", level);
print_ast_node_list(buf, node->return_stmt.expr_list, level + 1, ",");
break;
}
case STMT_LOCAL: {
printf_buf(buf, "%plocal\n", level);
printf_buf(buf, "%p%c\n", level, "[symbols]");
print_symbol_list(buf, node->local_stmt.var_list, level + 1, ",");
if (node->local_stmt.expr_list) {
printf_buf(buf, "%p%c\n", level, "[expressions]");
print_ast_node_list(buf, node->local_stmt.expr_list, level + 1, ",");
}
break;
}
case STMT_FUNCTION: {
raviX_print_ast_node(buf, node->function_stmt.name, level);
if (node->function_stmt.selectors) {
printf_buf(buf, "%p%c\n", level + 1, "[selectors]");
print_ast_node_list(buf, node->function_stmt.selectors, level + 2, NULL);
}
if (node->function_stmt.method_name) {
printf_buf(buf, "%p%c\n", level + 1, "[method name]");
raviX_print_ast_node(buf, node->function_stmt.method_name, level + 2);
}
printf_buf(buf, "%p=\n", level + 1);
raviX_print_ast_node(buf, node->function_stmt.function_expr, level + 2);
break;
}
case STMT_LABEL: {
printf_buf(buf, "%p::%t::\n", level, node->label_stmt.symbol->label.label_name);
break;
}
case STMT_GOTO: {
printf_buf(buf, "%pgoto %t\n", level, node->goto_stmt.name);
break;
}
case STMT_DO: {
printf_buf(buf, "%pdo\n", level);
print_ast_node_list(buf, node->do_stmt.do_statement_list, level + 1, NULL);
printf_buf(buf, "%pend\n", level);
break;
}
case STMT_EXPR: {
printf_buf(buf, "%p%c\n", level, "[expression statement start]");
if (node->expression_stmt.var_expr_list) {
printf_buf(buf, "%p%c\n", level + 1, "[var list start]");
print_ast_node_list(buf, node->expression_stmt.var_expr_list, level + 2, ",");
printf_buf(buf, "%p= %c\n", level + 1, "[var list end]");
}
printf_buf(buf, "%p%c\n", level + 1, "[expression list start]");
print_ast_node_list(buf, node->expression_stmt.expr_list, level + 2, ",");
printf_buf(buf, "%p%c\n", level + 1, "[expression list end]");
printf_buf(buf, "%p%c\n", level, "[expression statement end]");
break;
}
case STMT_IF: {
AstNode *test_then_block;
bool is_first = true;
FOR_EACH_PTR(node->if_stmt.if_condition_list, AstNode, test_then_block)
{
if (is_first) {
is_first = false;
printf_buf(buf, "%pif\n", level);
} else
printf_buf(buf, "%pelseif\n", level);
raviX_print_ast_node(buf, test_then_block->test_then_block.condition, level + 1);
printf_buf(buf, "%pthen\n", level);
print_ast_node_list(buf, test_then_block->test_then_block.test_then_statement_list, level + 1,
NULL);
}
END_FOR_EACH_PTR(node);
if (node->if_stmt.else_block) {
printf_buf(buf, "%pelse\n", level);
print_ast_node_list(buf, node->if_stmt.else_statement_list, level + 1, NULL);
}
printf_buf(buf, "%pend\n", level);
break;
}
case STMT_WHILE: {
printf_buf(buf, "%pwhile\n", level);
raviX_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
printf_buf(buf, "%pdo\n", level);
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
printf_buf(buf, "%pend\n", level);
break;
}
case STMT_REPEAT: {
printf_buf(buf, "%prepeat\n", level);
print_ast_node_list(buf, node->while_or_repeat_stmt.loop_statement_list, level + 1, NULL);
printf_buf(buf, "%puntil\n", level);
raviX_print_ast_node(buf, node->while_or_repeat_stmt.condition, level + 1);
printf_buf(buf, "%p%c\n", level, "[repeat end]");
break;
}
case STMT_FOR_IN: {
printf_buf(buf, "%pfor\n", level);
print_symbol_list(buf, node->for_stmt.symbols, level + 1, ",");
printf_buf(buf, "%pin\n", level);
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
printf_buf(buf, "%pdo\n", level);
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
printf_buf(buf, "%pend\n", level);
break;
}
case STMT_FOR_NUM: {
printf_buf(buf, "%pfor\n", level);
print_symbol_list(buf, node->for_stmt.symbols, level + 1, NULL);
printf_buf(buf, "%p=\n", level);
print_ast_node_list(buf, node->for_stmt.expr_list, level + 1, ",");
printf_buf(buf, "%pdo\n", level);
print_statement_list(buf, node->for_stmt.for_statement_list, level + 1);
printf_buf(buf, "%pend\n", level);
break;
}
case EXPR_SUFFIXED: {
printf_buf(buf, "%p%c %T\n", level, "[suffixed expr start]", &node->suffixed_expr.type);
printf_buf(buf, "%p%c %T\n", level + 1, "[primary start]",
&node->suffixed_expr.primary_expr->common_expr.type);
raviX_print_ast_node(buf, node->suffixed_expr.primary_expr, level + 2);
printf_buf(buf, "%p%c\n", level + 1, "[primary end]");
if (node->suffixed_expr.suffix_list) {
printf_buf(buf, "%p%c\n", level + 1, "[suffix list start]");
print_ast_node_list(buf, node->suffixed_expr.suffix_list, level + 2, NULL);
printf_buf(buf, "%p%c\n", level + 1, "[suffix list end]");
}
printf_buf(buf, "%p%c\n", level, "[suffixed expr end]");
break;
}
case EXPR_FUNCTION_CALL: {
printf_buf(buf, "%p%c %T\n", level, "[function call start]", &node->function_call_expr.type);
if (node->function_call_expr.method_name) {
printf_buf(buf, "%p: %t (\n", level + 1, node->function_call_expr.method_name);
} else {
printf_buf(buf, "%p(\n", level + 1);
}
print_ast_node_list(buf, node->function_call_expr.arg_list, level + 2, ",");
printf_buf(buf, "%p)\n", level + 1);
printf_buf(buf, "%p%c\n", level, "[function call end]");
break;
}
case EXPR_SYMBOL: {
print_symbol(buf, node->symbol_expr.var, level + 1);
break;
}
case EXPR_BINARY: {
printf_buf(buf, "%p%c %T\n", level, "[binary expr start]", &node->binary_expr.type);
raviX_print_ast_node(buf, node->binary_expr.expr_left, level + 1);
printf_buf(buf, "%p%s\n", level, raviX_get_binary_opr_str(node->binary_expr.binary_op));
raviX_print_ast_node(buf, node->binary_expr.expr_right, level + 1);
printf_buf(buf, "%p%c\n", level, "[binary expr end]");
break;
}
case EXPR_UNARY: {
printf_buf(buf, "%p%c %T\n", level, "[unary expr start]", &node->unary_expr.type);
printf_buf(buf, "%p%s\n", level, raviX_get_unary_opr_str(node->unary_expr.unary_op));
raviX_print_ast_node(buf, node->unary_expr.expr, level + 1);
printf_buf(buf, "%p%c\n", level, "[unary expr end]");
break;
}
case EXPR_LITERAL: {
printf_buf(buf, "%p", level);
switch (node->literal_expr.type.type_code) {
case RAVI_TNIL:
printf_buf(buf, "nil");
break;
case RAVI_TBOOLEAN:
printf_buf(buf, "%b", node->literal_expr.u.i);
break;
case RAVI_TNUMINT:
printf_buf(buf, "%i", node->literal_expr.u.i);
break;
case RAVI_TNUMFLT:
printf_buf(buf, "%f", node->literal_expr.u.r);
break;
case RAVI_TSTRING:
printf_buf(buf, "'%t'", node->literal_expr.u.ts);
break;
case RAVI_TVARARGS:
printf_buf(buf, "...");
break;
default:
assert(0);
}
printf_buf(buf, "\n");
break;
}
case EXPR_FIELD_SELECTOR: {
printf_buf(buf, "%p%c %T\n", level, "[field selector start]", &node->index_expr.type);
printf_buf(buf, "%p.\n", level + 1);
raviX_print_ast_node(buf, node->index_expr.expr, level + 2);
printf_buf(buf, "%p%c\n", level, "[field selector end]");
break;
}
case EXPR_Y_INDEX: {
printf_buf(buf, "%p%c %T\n", level, "[Y index start]", &node->index_expr.type);
printf_buf(buf, "%p[\n", level + 1);
raviX_print_ast_node(buf, node->index_expr.expr, level + 2);
printf_buf(buf, "%p]\n", level + 1);
printf_buf(buf, "%p%c\n", level, "[Y index end]");
break;
}
case EXPR_TABLE_ELEMENT_ASSIGN: {
printf_buf(buf, "%p%c %T\n", level, "[indexed assign start]", &node->table_elem_assign_expr.type);
if (node->table_elem_assign_expr.key_expr) {
printf_buf(buf, "%p%c\n", level, "[index start]");
raviX_print_ast_node(buf, node->table_elem_assign_expr.key_expr, level + 1);
printf_buf(buf, "%p%c\n", level, "[index end]");
}
printf_buf(buf, "%p%c\n", level, "[value start]");
raviX_print_ast_node(buf, node->table_elem_assign_expr.value_expr, level + 1);
printf_buf(buf, "%p%c\n", level, "[value end]");
printf_buf(buf, "%p%c\n", level, "[indexed assign end]");
break;
}
case EXPR_TABLE_LITERAL: {
printf_buf(buf, "%p{ %c %T\n", level, "[table constructor start]", &node->table_expr.type);
print_ast_node_list(buf, node->table_expr.expr_list, level + 1, ",");
printf_buf(buf, "%p} %c\n", level, "[table constructor end]");
break;
}
default:
printf_buf(buf, "%pUnsupported node type %d\n", level, node->type);
assert(0);
}
}
void raviX_output_ast(CompilerState *container, FILE *fp)
{
TextBuffer mbuf;
raviX_buffer_init(&mbuf, 1024);
raviX_print_ast_node(&mbuf, container->main_function, 0);
fputs(mbuf.buf, fp);
raviX_buffer_free(&mbuf);
}