add --diagram-ast option to dump AST to graphviz
This commit is contained in:
+54
-2
@@ -35,13 +35,14 @@
|
|||||||
#include <stdbool.h>
|
#include <stdbool.h>
|
||||||
#include "../lib/run.h"
|
#include "../lib/run.h"
|
||||||
#include "../lib/runtime.h"
|
#include "../lib/runtime.h"
|
||||||
|
#include "../lib/diagram.h"
|
||||||
|
|
||||||
static void usage(FILE *stream, const char *name)
|
static void usage(FILE *stream, const char *name)
|
||||||
{
|
{
|
||||||
fprintf(stream,
|
fprintf(stream,
|
||||||
"\n"
|
"\n"
|
||||||
"Usage:\n"
|
"Usage:\n"
|
||||||
" $ %s [-h | -o <file> | -p | -H <heap size> | {-d | -a}] <file>\n", name);
|
" $ %s ..OPTIONS <file>\n", name);
|
||||||
}
|
}
|
||||||
|
|
||||||
static void help(FILE *stream, const char *name)
|
static void help(FILE *stream, const char *name)
|
||||||
@@ -54,12 +55,14 @@ static void help(FILE *stream, const char *name)
|
|||||||
" -i, --inline Execute a string of code instead of a file\n"
|
" -i, --inline Execute a string of code instead of a file\n"
|
||||||
" -a, --assembly Specify that the source is bytecode and not noja code\n"
|
" -a, --assembly Specify that the source is bytecode and not noja code\n"
|
||||||
" -p, --profile Profile the execution of the source (can't be used with -d)\n"
|
" -p, --profile Profile the execution of the source (can't be used with -d)\n"
|
||||||
" -o, --output <file> Specify the output file of -p\n"
|
" -o, --output <file> Specify the output file of -p or --diagram-ast\n"
|
||||||
" -H, --heap <size> Specify the heap size of the runtime\n"
|
" -H, --heap <size> Specify the heap size of the runtime\n"
|
||||||
|
" --diagram-ast Generate a GraphViz view of the AST\n"
|
||||||
"\n");
|
"\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef enum {
|
typedef enum {
|
||||||
|
Mode_DIAGRAM,
|
||||||
Mode_ASSEMBLY,
|
Mode_ASSEMBLY,
|
||||||
Mode_DEFAULT,
|
Mode_DEFAULT,
|
||||||
Mode_HELP,
|
Mode_HELP,
|
||||||
@@ -97,6 +100,10 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
mode = Mode_ASSEMBLY;
|
mode = Mode_ASSEMBLY;
|
||||||
|
|
||||||
|
} else if (!strcmp(argv[i], "--diagram-ast")) {
|
||||||
|
|
||||||
|
mode = Mode_DIAGRAM;
|
||||||
|
|
||||||
} else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--profile")) {
|
} else if (!strcmp(argv[i], "-p") || !strcmp(argv[i], "--profile")) {
|
||||||
|
|
||||||
profile = true;
|
profile = true;
|
||||||
@@ -138,6 +145,51 @@ int main(int argc, char **argv)
|
|||||||
code = 0;
|
code = 0;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
|
case Mode_DIAGRAM:
|
||||||
|
{
|
||||||
|
if (input == NULL) {
|
||||||
|
fprintf(stderr, "Error: No input file\n");
|
||||||
|
usage(stderr, argv[0]);
|
||||||
|
code = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
Error error;
|
||||||
|
Error_Init(&error);
|
||||||
|
|
||||||
|
char *result;
|
||||||
|
if (no_file)
|
||||||
|
result = diagramStringAST(input, &error, NULL);
|
||||||
|
else
|
||||||
|
result = diagramFileAST(input, &error, NULL);
|
||||||
|
|
||||||
|
if (!result) {
|
||||||
|
Error_Print(&error, ErrorType_UNSPECIFIED, stderr);
|
||||||
|
Error_Free(&error);
|
||||||
|
code = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE *stream;
|
||||||
|
if (output == NULL)
|
||||||
|
stream = stdout;
|
||||||
|
else {
|
||||||
|
stream = fopen(output, "wb");
|
||||||
|
if (!stream) {
|
||||||
|
fprintf(stderr, "Error: Couldn't open '%s'\n", output);
|
||||||
|
free(result);
|
||||||
|
code = -1;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
fwrite(result, 1, strlen(result), stream);
|
||||||
|
if (stream != stdout)
|
||||||
|
fclose(stream);
|
||||||
|
free(result);
|
||||||
|
}
|
||||||
|
code = 0;
|
||||||
|
break;
|
||||||
|
|
||||||
case Mode_DEFAULT:
|
case Mode_DEFAULT:
|
||||||
case Mode_ASSEMBLY:
|
case Mode_ASSEMBLY:
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,544 @@
|
|||||||
|
|
||||||
|
/* +--------------------------------------------------------------------------+
|
||||||
|
** | _ _ _ |
|
||||||
|
** | | \ | | (_) |
|
||||||
|
** | | \| | ___ _ __ _ |
|
||||||
|
** | | . ` |/ _ \| |/ _` | |
|
||||||
|
** | | |\ | (_) | | (_| | |
|
||||||
|
** | |_| \_|\___/| |\__,_| |
|
||||||
|
** | _/ | |
|
||||||
|
** | |__/ |
|
||||||
|
** +--------------------------------------------------------------------------+
|
||||||
|
** | Copyright (c) 2022 Francesco Cozzuto <francesco.cozzuto@gmail.com> |
|
||||||
|
** +--------------------------------------------------------------------------+
|
||||||
|
** | This file is part of The Noja Interpreter. |
|
||||||
|
** | |
|
||||||
|
** | The Noja Interpreter is free software: you can redistribute it and/or |
|
||||||
|
** | modify it under the terms of the GNU General Public License as published |
|
||||||
|
** | by the Free Software Foundation, either version 3 of the License, or (at |
|
||||||
|
** | your option) any later version. |
|
||||||
|
** | |
|
||||||
|
** | The Noja Interpreter is distributed in the hope that it will be useful, |
|
||||||
|
** | but WITHOUT ANY WARRANTY; without even the implied warranty of |
|
||||||
|
** | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General |
|
||||||
|
** | Public License for more details. |
|
||||||
|
** | |
|
||||||
|
** | You should have received a copy of the GNU General Public License along |
|
||||||
|
** | with The Noja Interpreter. If not, see <http://www.gnu.org/licenses/>. |
|
||||||
|
** +--------------------------------------------------------------------------+
|
||||||
|
** | WHAT IS THIS FILE? |
|
||||||
|
** | |
|
||||||
|
** | This file implements the routines that transform the AST into a list of |
|
||||||
|
** | bytecodes. The functionalities of this file are exposed through the |
|
||||||
|
** | `compile` function, that takes as input an `AST` and outputs an |
|
||||||
|
** | `Executable`. |
|
||||||
|
** | |
|
||||||
|
** | The function that does the heavy lifting is `emitInstrForNode` which |
|
||||||
|
** | walks the tree and writes instructions to the `ExeBuilder`. |
|
||||||
|
** | |
|
||||||
|
** | Some semantic errors are catched at this phase, in which case, they are |
|
||||||
|
** | reported by filling out the `error` structure and aborting. It's also |
|
||||||
|
** | possible that the compilation fails bacause of internal errors (which |
|
||||||
|
** | usually means "out of memory"). |
|
||||||
|
** +--------------------------------------------------------------------------+
|
||||||
|
*/
|
||||||
|
|
||||||
|
#include <assert.h>
|
||||||
|
#include <stdlib.h>
|
||||||
|
#include <string.h>
|
||||||
|
#include <stdbool.h>
|
||||||
|
#include "../utils/defs.h"
|
||||||
|
#include "graphviz.h"
|
||||||
|
#include "ASTi.h"
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
char *data;
|
||||||
|
size_t size;
|
||||||
|
size_t used;
|
||||||
|
} Buffer;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
Error *error;
|
||||||
|
Buffer head, body;
|
||||||
|
} GraphViz;
|
||||||
|
|
||||||
|
static void Buffer_Init(Buffer *buf)
|
||||||
|
{
|
||||||
|
buf->data = NULL;
|
||||||
|
buf->size = 0;
|
||||||
|
buf->used = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void Buffer_Free(Buffer *buf)
|
||||||
|
{
|
||||||
|
free(buf->data);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GraphViz_Init(GraphViz *gv, Error *error)
|
||||||
|
{
|
||||||
|
Buffer_Init(&gv->head);
|
||||||
|
Buffer_Init(&gv->body);
|
||||||
|
gv->error = error;
|
||||||
|
gv->error->occurred = false;
|
||||||
|
}
|
||||||
|
static void GraphViz_Free(GraphViz *gv)
|
||||||
|
{
|
||||||
|
Buffer_Free(&gv->head);
|
||||||
|
Buffer_Free(&gv->body);
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Buffer_EnsureFreeSpace(Buffer *buf, Error *error, size_t num)
|
||||||
|
{
|
||||||
|
if (buf->used + num > buf->size) {
|
||||||
|
|
||||||
|
// Need to add space
|
||||||
|
|
||||||
|
size_t oldsize = buf->size;
|
||||||
|
size_t newsize = buf->size * 2;
|
||||||
|
|
||||||
|
if (newsize < oldsize + num)
|
||||||
|
newsize = oldsize + num;
|
||||||
|
|
||||||
|
void *p = realloc(buf->data, newsize);
|
||||||
|
if (p == NULL) {
|
||||||
|
Error_Report(error, ErrorType_INTERNAL, "Out of memory");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
buf->size = newsize;
|
||||||
|
buf->data = p;
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static bool Buffer_VAppendF(Buffer *buf, Error *error, const char *fmt, va_list args)
|
||||||
|
{
|
||||||
|
va_list args2;
|
||||||
|
|
||||||
|
// Calculate the string length required to hold
|
||||||
|
// the evaluated format
|
||||||
|
va_copy(args2, args);
|
||||||
|
int required = vsnprintf(NULL, 0, fmt, args2);
|
||||||
|
va_end(args2);
|
||||||
|
if (required < 0) {
|
||||||
|
Error_Report(error, ErrorType_INTERNAL, "Bad format");
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!Buffer_EnsureFreeSpace(buf, error, required+1))
|
||||||
|
return false;
|
||||||
|
|
||||||
|
vsnprintf(buf->data + buf->used, buf->size - buf->used, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
|
||||||
|
buf->used += required;
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GraphViz_AppendHead(GraphViz *gv, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
if (gv->error->occurred)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Buffer_VAppendF(&gv->head, gv->error, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void GraphViz_AppendBody(GraphViz *gv, const char *fmt, ...)
|
||||||
|
{
|
||||||
|
if (gv->error->occurred)
|
||||||
|
return;
|
||||||
|
|
||||||
|
va_list args;
|
||||||
|
va_start(args, fmt);
|
||||||
|
Buffer_VAppendF(&gv->body, gv->error, fmt, args);
|
||||||
|
va_end(args);
|
||||||
|
}
|
||||||
|
|
||||||
|
static char *GraphViz_Complete(GraphViz *gv, size_t *len)
|
||||||
|
{
|
||||||
|
if (gv->error->occurred)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *result;
|
||||||
|
size_t reslen;
|
||||||
|
|
||||||
|
reslen = gv->head.used + gv->body.used;
|
||||||
|
result = malloc(reslen+1);
|
||||||
|
if (!result) {
|
||||||
|
Error_Report(gv->error, ErrorType_INTERNAL, "Out of memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
memcpy(result, gv->head.data, gv->head.used);
|
||||||
|
memcpy(result + gv->head.used, gv->body.data, gv->body.used);
|
||||||
|
result[reslen] = '\0';
|
||||||
|
|
||||||
|
if (len) *len = reslen;
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nodeToGraphViz(GraphViz *ctx, Node *node, int *count, char *name);
|
||||||
|
|
||||||
|
static void flattenTupleTree(ExprNode *root, ExprNode *tuple[], int max, int *count)
|
||||||
|
{
|
||||||
|
if(root->kind == EXPR_PAIR)
|
||||||
|
{
|
||||||
|
flattenTupleTree((ExprNode*) ((OperExprNode*) root)->head, tuple, max, count);
|
||||||
|
flattenTupleTree((ExprNode*) ((OperExprNode*) root)->head->next, tuple, max, count);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
|
||||||
|
if(max == *count)
|
||||||
|
{
|
||||||
|
assert(0); // TODO: Do something smart instead
|
||||||
|
}
|
||||||
|
|
||||||
|
tuple[(*count)++] = root;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void generateName(int *count, char *dst, size_t max)
|
||||||
|
{
|
||||||
|
snprintf(dst, max, "node_%d", ++(*count));
|
||||||
|
}
|
||||||
|
|
||||||
|
static const char *exprkind_to_label(ExprKind kind)
|
||||||
|
{
|
||||||
|
switch(kind)
|
||||||
|
{
|
||||||
|
case EXPR_NULLABLETYPE: return "Nullable Type";
|
||||||
|
case EXPR_SUMTYPE: return "Sum Type";
|
||||||
|
case EXPR_NOT: return "!";
|
||||||
|
case EXPR_POS: return "+";
|
||||||
|
case EXPR_NEG: return "-";
|
||||||
|
case EXPR_ADD: return "+";
|
||||||
|
case EXPR_SUB: return "-";
|
||||||
|
case EXPR_MUL: return "×";
|
||||||
|
case EXPR_DIV: return "/";
|
||||||
|
case EXPR_MOD: return "%";
|
||||||
|
case EXPR_EQL: return "==";
|
||||||
|
case EXPR_NQL: return "!=";
|
||||||
|
case EXPR_LSS: return "<";
|
||||||
|
case EXPR_LEQ: return "<=";
|
||||||
|
case EXPR_GRT: return ">";
|
||||||
|
case EXPR_GEQ: return ">=";
|
||||||
|
case EXPR_AND: return "and";
|
||||||
|
case EXPR_OR : return "or";
|
||||||
|
case EXPR_ASS: return "=";
|
||||||
|
case EXPR_ARW: return "->";
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
return "???";
|
||||||
|
}
|
||||||
|
|
||||||
|
static void childToGraphViz(GraphViz *ctx, Node *child, int *count, char *parent)
|
||||||
|
{
|
||||||
|
char name2[128];
|
||||||
|
generateName(count, name2, sizeof(name2));
|
||||||
|
nodeToGraphViz(ctx, child, count, name2);
|
||||||
|
GraphViz_AppendBody(ctx, "\t%s -> %s;\n", parent, name2);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void funcExprToGraphViz(GraphViz *ctx, FuncExprNode *func, char *func_name, int *count, char *name)
|
||||||
|
{
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%s\"];\n", name, func_name);
|
||||||
|
|
||||||
|
char name2[128];
|
||||||
|
generateName(count, name2, sizeof(name2));
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"args\"];\n", name2);
|
||||||
|
GraphViz_AppendBody(ctx, "\t%s -> %s;\n", name, name2);
|
||||||
|
|
||||||
|
ArgumentNode *arg = (ArgumentNode*) func->argv;
|
||||||
|
while(arg)
|
||||||
|
{
|
||||||
|
char name3[128];
|
||||||
|
generateName(count, name3, sizeof(name3));
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%s\"];\n", name3, arg->name);
|
||||||
|
GraphViz_AppendBody(ctx, "\t%s -> %s;\n", name2, name3);
|
||||||
|
if (arg->value != NULL) childToGraphViz(ctx, arg->value, count, name3);
|
||||||
|
if (arg->type != NULL) childToGraphViz(ctx, arg->type, count, name3);
|
||||||
|
arg = (ArgumentNode*) arg->base.next;
|
||||||
|
}
|
||||||
|
|
||||||
|
childToGraphViz(ctx, func->body, count, name);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void exprToGraphViz(GraphViz *ctx, ExprNode *expr, int *count, char *name)
|
||||||
|
{
|
||||||
|
switch(expr->kind)
|
||||||
|
{
|
||||||
|
case EXPR_PAIR:
|
||||||
|
UNREACHABLE;
|
||||||
|
return; // For the compiler warning.
|
||||||
|
|
||||||
|
case EXPR_NULLABLETYPE:
|
||||||
|
case EXPR_SUMTYPE:
|
||||||
|
case EXPR_ASS: case EXPR_ARW:
|
||||||
|
case EXPR_NOT: case EXPR_MOD:
|
||||||
|
case EXPR_POS: case EXPR_NEG:
|
||||||
|
case EXPR_ADD: case EXPR_SUB:
|
||||||
|
case EXPR_MUL: case EXPR_DIV:
|
||||||
|
case EXPR_EQL: case EXPR_NQL:
|
||||||
|
case EXPR_LSS: case EXPR_LEQ:
|
||||||
|
case EXPR_GRT: case EXPR_GEQ:
|
||||||
|
case EXPR_AND: case EXPR_OR:
|
||||||
|
{
|
||||||
|
OperExprNode *oper = (OperExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%s\"];\n", name, exprkind_to_label(expr->kind));
|
||||||
|
for(Node *operand = oper->head; operand; operand = operand->next)
|
||||||
|
childToGraphViz(ctx, operand, count, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_INT:
|
||||||
|
{
|
||||||
|
IntExprNode *p = (IntExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%d\"];\n", name, p->val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_FLOAT:
|
||||||
|
{
|
||||||
|
FloatExprNode *p = (FloatExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%g\"];\n", name, p->val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_STRING:
|
||||||
|
{
|
||||||
|
StringExprNode *p = (StringExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"", name);
|
||||||
|
char *s = p->val;
|
||||||
|
size_t i = 0;
|
||||||
|
size_t len = strlen(s);
|
||||||
|
while (i < len) {
|
||||||
|
|
||||||
|
size_t start = i;
|
||||||
|
while (i < len && (s[i] != '"' && s[i] != '\\' && s[i] != '\n' && s[i] != '\t'))
|
||||||
|
i++;
|
||||||
|
size_t end = i;
|
||||||
|
GraphViz_AppendHead(ctx, "%.*s", end - start, s + start);
|
||||||
|
if (i == len)
|
||||||
|
break;
|
||||||
|
switch (s[i]) {
|
||||||
|
case '"': GraphViz_AppendHead(ctx, "\\\""); break;
|
||||||
|
case '\\': GraphViz_AppendHead(ctx, "\\\\"); break;
|
||||||
|
case '\t': GraphViz_AppendHead(ctx, "\\t"); break;
|
||||||
|
case '\n': GraphViz_AppendHead(ctx, "\\n"); break;
|
||||||
|
}
|
||||||
|
i++;
|
||||||
|
}
|
||||||
|
GraphViz_AppendHead(ctx, "\"];\n");
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_IDENT:
|
||||||
|
{
|
||||||
|
IdentExprNode *p = (IdentExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"%s\"];\n", name, p->val);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_LIST:
|
||||||
|
{
|
||||||
|
ListExprNode *l = (ListExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"List\"];\n", name);
|
||||||
|
for (Node *item = l->items; item; item = item->next)
|
||||||
|
childToGraphViz(ctx, item, count, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_MAP:
|
||||||
|
{
|
||||||
|
MapExprNode *m = (MapExprNode*) expr;
|
||||||
|
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"Map\"];\n", name);
|
||||||
|
|
||||||
|
Node *key = m->keys;
|
||||||
|
Node *item = m->items;
|
||||||
|
|
||||||
|
while(item)
|
||||||
|
{
|
||||||
|
char name2[128];
|
||||||
|
generateName(count, name2, sizeof(name2));
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"Entry\"];\n", name2);
|
||||||
|
GraphViz_AppendBody(ctx, "\t%s -> %s;\n", name, name2);
|
||||||
|
|
||||||
|
childToGraphViz(ctx, key, count, name2);
|
||||||
|
childToGraphViz(ctx, item, count, name2);
|
||||||
|
|
||||||
|
key = key->next;
|
||||||
|
item = item->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_CALL:
|
||||||
|
{
|
||||||
|
CallExprNode *call = (CallExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"Call\"];\n", name);
|
||||||
|
|
||||||
|
childToGraphViz(ctx, call->func, count, name);
|
||||||
|
|
||||||
|
Node *arg = call->argv;
|
||||||
|
while(arg)
|
||||||
|
{
|
||||||
|
childToGraphViz(ctx, arg, count, name);
|
||||||
|
arg = arg->next;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXPR_FUNC:
|
||||||
|
funcExprToGraphViz(ctx, (FuncExprNode*) expr, "???", count, name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXPR_SELECT:
|
||||||
|
{
|
||||||
|
IndexSelectionExprNode *sel = (IndexSelectionExprNode*) expr;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"Select\"];\n", name);
|
||||||
|
childToGraphViz(ctx, sel->set, count, name);
|
||||||
|
childToGraphViz(ctx, sel->idx, count, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case EXPR_NONE:
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"none\"];\n", name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXPR_TRUE:
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"true\"];\n", name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case EXPR_FALSE:
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"false\"];\n", name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static void nodeToGraphViz(GraphViz *ctx, Node *node, int *count, char *name)
|
||||||
|
{
|
||||||
|
assert(node != NULL);
|
||||||
|
|
||||||
|
switch(node->kind)
|
||||||
|
{
|
||||||
|
case NODE_EXPR:
|
||||||
|
exprToGraphViz(ctx, (ExprNode*) node, count, name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NODE_BREAK:
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"break\"];\n", name);
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NODE_IFELSE:
|
||||||
|
{
|
||||||
|
IfElseNode *ifelse = (IfElseNode*) node;
|
||||||
|
|
||||||
|
if (ifelse->false_branch)
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"if-else\"];\n", name);
|
||||||
|
else
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"if\"];\n", name);
|
||||||
|
|
||||||
|
childToGraphViz(ctx, ifelse->condition, count, name);
|
||||||
|
childToGraphViz(ctx, ifelse->true_branch, count, name);
|
||||||
|
if (ifelse->false_branch)
|
||||||
|
childToGraphViz(ctx, ifelse->false_branch, count, name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NODE_WHILE:
|
||||||
|
{
|
||||||
|
WhileNode *while_ = (WhileNode*) node;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"while\"];\n", name);
|
||||||
|
childToGraphViz(ctx, while_->condition, count, name);
|
||||||
|
childToGraphViz(ctx, while_->body, count, name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NODE_DOWHILE:
|
||||||
|
{
|
||||||
|
DoWhileNode *dowhile = (DoWhileNode*) node;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"do-while\"];\n", name);
|
||||||
|
childToGraphViz(ctx, dowhile->condition, count, name);
|
||||||
|
childToGraphViz(ctx, dowhile->body, count, name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
case NODE_COMP:
|
||||||
|
{
|
||||||
|
CompoundNode *comp = (CompoundNode*) node;
|
||||||
|
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"compound\"];\n", name);
|
||||||
|
|
||||||
|
Node *stmt = comp->head;
|
||||||
|
while(stmt)
|
||||||
|
{
|
||||||
|
childToGraphViz(ctx, stmt, count, name);
|
||||||
|
stmt = stmt->next;
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NODE_RETURN:
|
||||||
|
{
|
||||||
|
ReturnNode *ret = (ReturnNode*) node;
|
||||||
|
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"return\"];\n", name);
|
||||||
|
|
||||||
|
ExprNode *tuple[32];
|
||||||
|
int return_count = 0;
|
||||||
|
|
||||||
|
flattenTupleTree((ExprNode*) ret->val, tuple, sizeof(tuple)/sizeof(tuple[0]), &return_count);
|
||||||
|
|
||||||
|
for(int i = 0; i < return_count; i += 1)
|
||||||
|
childToGraphViz(ctx, (Node*) tuple[i], count, name);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
case NODE_FUNC:
|
||||||
|
{
|
||||||
|
FuncDeclNode *func = (FuncDeclNode*) node;
|
||||||
|
GraphViz_AppendHead(ctx, "\t%s [label=\"func\"];\n", name);
|
||||||
|
funcExprToGraphViz(ctx, func->expr, func->name->val, count, name);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
|
||||||
|
default:
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
UNREACHABLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *graphviz(AST *ast, Error *err, size_t *len)
|
||||||
|
{
|
||||||
|
err->occurred = false;
|
||||||
|
|
||||||
|
GraphViz gv;
|
||||||
|
GraphViz_Init(&gv, err);
|
||||||
|
GraphViz_AppendHead(&gv, "digraph G {\n");
|
||||||
|
|
||||||
|
int count = 0;
|
||||||
|
char name[128];
|
||||||
|
generateName(&count, name, sizeof(name));
|
||||||
|
nodeToGraphViz(&gv, ast->root, &count, name);
|
||||||
|
|
||||||
|
GraphViz_AppendHead(&gv, "\n");
|
||||||
|
GraphViz_AppendBody(&gv, "}\n");
|
||||||
|
|
||||||
|
char *res = GraphViz_Complete(&gv, len);
|
||||||
|
|
||||||
|
GraphViz_Free(&gv);
|
||||||
|
return res;
|
||||||
|
}
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
|
||||||
|
#include "AST.h"
|
||||||
|
#include "../utils/error.h"
|
||||||
|
|
||||||
|
char *graphviz(AST *ast, Error *err, size_t *len);
|
||||||
@@ -0,0 +1,59 @@
|
|||||||
|
#include <assert.h>
|
||||||
|
#include "diagram.h"
|
||||||
|
#include "utils/source.h"
|
||||||
|
#include "utils/bpalloc.h"
|
||||||
|
#include "compiler/parse.h"
|
||||||
|
#include "compiler/graphviz.h"
|
||||||
|
|
||||||
|
static char *diagramSourceAST(Source *source, Error *error,
|
||||||
|
int *error_offset)
|
||||||
|
{
|
||||||
|
BPAlloc *alloc = BPAlloc_Init(-1);
|
||||||
|
if(alloc == NULL)
|
||||||
|
{
|
||||||
|
*error_offset = -1;
|
||||||
|
Error_Report(error, ErrorType_INTERNAL, "No memory");
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
// NOTE: The AST is stored in the BPAlloc. Its
|
||||||
|
// lifetime is the same as the pool.
|
||||||
|
AST *ast = parse(source, alloc, error, error_offset);
|
||||||
|
if(ast == NULL)
|
||||||
|
{
|
||||||
|
assert(error->occurred);
|
||||||
|
BPAlloc_Free(alloc);
|
||||||
|
return NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *out = graphviz(ast, error, NULL);
|
||||||
|
|
||||||
|
BPAlloc_Free(alloc);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *diagramFileAST(const char *file, Error *error,
|
||||||
|
int *error_offset)
|
||||||
|
{
|
||||||
|
Source *source = Source_FromFile(file, error);
|
||||||
|
if (source == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *out = diagramSourceAST(source, error, error_offset);
|
||||||
|
|
||||||
|
Source_Free(source);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
|
|
||||||
|
char *diagramStringAST(const char *str, Error *error,
|
||||||
|
int *error_offset)
|
||||||
|
{
|
||||||
|
Source *source = Source_FromString(NULL, str, -1, error);
|
||||||
|
if (source == NULL)
|
||||||
|
return NULL;
|
||||||
|
|
||||||
|
char *out = diagramSourceAST(source, error, error_offset);
|
||||||
|
|
||||||
|
Source_Free(source);
|
||||||
|
return out;
|
||||||
|
}
|
||||||
@@ -0,0 +1,4 @@
|
|||||||
|
#include "utils/error.h"
|
||||||
|
|
||||||
|
char *diagramFileAST(const char *file, Error *error, int *error_offset);
|
||||||
|
char *diagramStringAST(const char *str, Error *error, int *error_offset);
|
||||||
Reference in New Issue
Block a user