123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732 |
- /*************************************************************************
- * Copyright (c) 2011 AT&T Intellectual Property
- * All rights reserved. This program and the accompanying materials
- * are made available under the terms of the Eclipse Public License v1.0
- * which accompanies this distribution, and is available at
- * https://www.eclipse.org/legal/epl-v10.html
- *
- * Contributors: Details at https://graphviz.org
- *************************************************************************/
- /*
- * Glenn Fowler
- * AT&T Research
- *
- * expression library C program generator
- */
- #include <expr/exlib.h>
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
- #include <util/agxbuf.h>
- #include <util/exit.h>
- typedef struct Excc_s Excc_t;
- typedef struct Exccdisc_s Exccdisc_t;
- struct Exccdisc_s /* excc() discipline */
- {
- agxbuf *text; // text output buffer
- char* id; /* symbol prefix */
- uint64_t flags; /* EXCC_* flags */
- int (*ccf)(Excc_t*, Exnode_t*, Exid_t*, Exref_t*, Exnode_t*, Exccdisc_t*);
- /* program generator function */
- };
- struct Excc_s /* excc() state */
- {
- Expr_t* expr; /* exopen() state */
- Exdisc_t* disc; /* exopen() discipline */
- char* id; /* prefix + _ */
- int tmp; /* temp var index */
- Exccdisc_t* ccdisc; /* excc() discipline */
- };
- #define EX_CC_DUMP 0x8000
- static const char quote[] = "\"";
- static void gen(Excc_t*, Exnode_t*);
- /*
- * return C name for op
- */
- char *exopname(long op) {
- static char buf[16];
- switch (op)
- {
- case '!':
- return "!";
- case '%':
- return "%";
- case '&':
- return "&";
- case '(':
- return "(";
- case '*':
- return "*";
- case '+':
- return "+";
- case ',':
- return ",";
- case '-':
- return "-";
- case '/':
- return "/";
- case ':':
- return ":";
- case '<':
- return "<";
- case '=':
- return "=";
- case '>':
- return ">";
- case '?':
- return "?";
- case '^':
- return "^";
- case '|':
- return "|";
- case '~':
- return "~";
- case AND:
- return "&&";
- case EQ:
- return "==";
- case GE:
- return ">=";
- case LE:
- return "<=";
- case LSH:
- return "<<";
- case NE:
- return "!=";
- case OR:
- return "||";
- case RSH:
- return ">>";
- default:
- break;
- }
- snprintf(buf, sizeof(buf) - 1, "(OP=%03lo)", op);
- return buf;
- }
- /*
- * generate printf()
- */
- static void print(Excc_t *cc, Exnode_t *exnode) {
- Print_t* x;
- if ((x = exnode->data.print.args))
- {
- char *quoted = fmtesq(x->format, quote);
- agxbprint(cc->ccdisc->text, "sfprintf(%s, \"%s", exnode->data.print.descriptor->op == CONSTANT && exnode->data.print.descriptor->data.constant.value.integer == 2 ? "sfstderr" : "sfstdout", quoted);
- free(quoted);
- while ((x = x->next)) {
- quoted = fmtesq(x->format, quote);
- agxbput(cc->ccdisc->text, quoted);
- free(quoted);
- }
- agxbputc(cc->ccdisc->text, '"');
- for (x = exnode->data.print.args; x; x = x->next)
- {
- if (x->arg)
- {
- for (size_t i = 0; i < elementsof(x->param) && x->param[i]; i++)
- {
- agxbput(cc->ccdisc->text, ", (");
- gen(cc, x->param[i]);
- agxbputc(cc->ccdisc->text, ')');
- }
- agxbput(cc->ccdisc->text, ", (");
- gen(cc, x->arg);
- agxbputc(cc->ccdisc->text, ')');
- }
- }
- agxbput(cc->ccdisc->text, ");\n");
- }
- }
- /*
- * generate scanf()
- */
- static void scan(Excc_t *cc, Exnode_t *exnode) {
- Print_t* x;
- if ((x = exnode->data.print.args))
- {
- char *quoted = fmtesq(x->format, quote);
- agxbprint(cc->ccdisc->text, "sfscanf(sfstdin, \"%s", quoted);
- free(quoted);
- while ((x = x->next)) {
- quoted = fmtesq(x->format, quote);
- agxbput(cc->ccdisc->text, quoted);
- free(quoted);
- }
- agxbputc(cc->ccdisc->text, '"');
- for (x = exnode->data.print.args; x; x = x->next)
- {
- if (x->arg)
- {
- for (size_t i = 0; i < elementsof(x->param) && x->param[i]; i++)
- {
- agxbput(cc->ccdisc->text, ", &(");
- gen(cc, x->param[i]);
- agxbputc(cc->ccdisc->text, ')');
- }
- agxbput(cc->ccdisc->text, ", &(");
- gen(cc, x->arg);
- agxbputc(cc->ccdisc->text, ')');
- }
- }
- agxbput(cc->ccdisc->text, ");\n");
- }
- }
- /*
- * internal excc
- */
- static void gen(Excc_t *cc, Exnode_t *exnode) {
- Exnode_t* x;
- Exnode_t* y;
- int n;
- int m;
- char* s;
- Extype_t* v;
- Extype_t** p;
- if (!exnode)
- return;
- if (exnode->op == CALL) {
- agxbprint(cc->ccdisc->text, "%s(", exnode->data.call.procedure->name);
- if (exnode->data.call.args)
- gen(cc, exnode->data.call.args);
- agxbputc(cc->ccdisc->text, ')');
- return;
- }
- x = exnode->data.operand.left;
- switch (exnode->op)
- {
- case BREAK:
- agxbput(cc->ccdisc->text, "break;\n");
- return;
- case CONTINUE:
- agxbput(cc->ccdisc->text, "continue;\n");
- return;
- case CONSTANT:
- switch (exnode->type)
- {
- case FLOATING:
- agxbprint(cc->ccdisc->text, "%g", exnode->data.constant.value.floating);
- break;
- case STRING: {
- char *quoted = fmtesq(exnode->data.constant.value.string, quote);
- agxbprint(cc->ccdisc->text, "\"%s\"", quoted);
- free(quoted);
- break;
- }
- case UNSIGNED:
- agxbprint(cc->ccdisc->text, "%llu",
- (long long unsigned)exnode->data.constant.value.integer);
- break;
- default:
- agxbprint(cc->ccdisc->text, "%lld", exnode->data.constant.value.integer);
- break;
- }
- return;
- case DEC:
- agxbprint(cc->ccdisc->text, "%s--", x->data.variable.symbol->name);
- return;
- case DYNAMIC:
- agxbput(cc->ccdisc->text, exnode->data.variable.symbol->name);
- return;
- case EXIT:
- agxbput(cc->ccdisc->text, "exit(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ");\n");
- return;
- case FUNCTION:
- gen(cc, x);
- agxbputc(cc->ccdisc->text, '(');
- if ((y = exnode->data.operand.right)) {
- gen(cc, y);
- }
- agxbputc(cc->ccdisc->text, ')');
- return;
- case RAND:
- agxbput(cc->ccdisc->text, "rand();\n");
- return;
- case SRAND:
- if (exnode->binary) {
- agxbput(cc->ccdisc->text, "srand(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ");\n");
- } else
- agxbput(cc->ccdisc->text, "srand();\n");
- return;
- case GSUB:
- case SUB:
- case SUBSTR:
- s = (exnode->op == GSUB ? "gsub(" : exnode->op == SUB ? "sub(" : "substr(");
- agxbput(cc->ccdisc->text, s);
- gen(cc, exnode->data.string.base);
- agxbput(cc->ccdisc->text, ", ");
- gen(cc, exnode->data.string.pat);
- if (exnode->data.string.repl) {
- agxbput(cc->ccdisc->text, ", ");
- gen(cc, exnode->data.string.repl);
- }
- agxbputc(cc->ccdisc->text, ')');
- return;
- case IN_OP:
- gen(cc, exnode->data.variable.index);
- agxbprint(cc->ccdisc->text, " in %s", exnode->data.variable.symbol->name);
- return;
- case IF:
- agxbput(cc->ccdisc->text, "if (");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ") {\n");
- gen(cc, exnode->data.operand.right->data.operand.left);
- if (exnode->data.operand.right->data.operand.right)
- {
- agxbput(cc->ccdisc->text, "} else {\n");
- gen(cc, exnode->data.operand.right->data.operand.right);
- }
- agxbput(cc->ccdisc->text, "}\n");
- return;
- case FOR:
- agxbput(cc->ccdisc->text, "for (;");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ");");
- if (exnode->data.operand.left)
- {
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, exnode->data.operand.left);
- agxbputc(cc->ccdisc->text, ')');
- }
- agxbput(cc->ccdisc->text, ") {");
- if (exnode->data.operand.right)
- gen(cc, exnode->data.operand.right);
- agxbputc(cc->ccdisc->text, '}');
- return;
- case ID:
- if (cc->ccdisc->ccf)
- cc->ccdisc->ccf(cc, exnode, exnode->data.variable.symbol, exnode->data.variable.reference, exnode->data.variable.index, cc->ccdisc);
- else
- agxbput(cc->ccdisc->text, exnode->data.variable.symbol->name);
- return;
- case INC:
- agxbprint(cc->ccdisc->text, "%s++", x->data.variable.symbol->name);
- return;
- case ITERATE:
- case ITERATOR:
- if (exnode->op == DYNAMIC)
- {
- agxbprint(cc->ccdisc->text, "{ Exassoc_t* %stmp_%d;", cc->id, ++cc->tmp);
- agxbprint(cc->ccdisc->text, "for (%stmp_%d = (Exassoc_t*)dtfirst(%s); %stmp_%d && (%s = %stmp_%d->name); %stmp_%d = (Exassoc_t*)dtnext(%s, %stmp_%d)) {", cc->id, cc->tmp, exnode->data.generate.array->data.variable.symbol->name, cc->id, cc->tmp, exnode->data.generate.index->name, cc->id, cc->tmp, cc->id, cc->tmp, exnode->data.generate.array->data.variable.symbol->name, cc->id, cc->tmp);
- gen(cc, exnode->data.generate.statement);
- agxbput(cc->ccdisc->text, "} }");
- }
- return;
- case PRINT:
- agxbput(cc->ccdisc->text, "print");
- if (x)
- gen(cc, x);
- else
- agxbput(cc->ccdisc->text, "()");
- return;
- case PRINTF:
- print(cc, exnode);
- return;
- case RETURN:
- agxbput(cc->ccdisc->text, "return(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ");\n");
- return;
- case SCANF:
- scan(cc, exnode);
- return;
- case SPLIT:
- case TOKENS:
- if (exnode->op == SPLIT)
- agxbput(cc->ccdisc->text, "split (");
- else
- agxbput(cc->ccdisc->text, "tokens (");
- gen(cc, exnode->data.split.string);
- agxbprint(cc->ccdisc->text, ", %s", exnode->data.split.array->name);
- if (exnode->data.split.seps) {
- agxbputc(cc->ccdisc->text, ',');
- gen(cc, exnode->data.split.seps);
- }
- agxbputc(cc->ccdisc->text, ')');
- return;
- case SWITCH: {
- long t = x->type;
- agxbprint(cc->ccdisc->text, "{ %s %stmp_%d = ", extype(t), cc->id, ++cc->tmp);
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ';');
- x = exnode->data.operand.right;
- y = x->data.select.statement;
- n = 0;
- while ((x = x->data.select.next))
- {
- if (n)
- agxbput(cc->ccdisc->text, "else ");
- if (!(p = x->data.select.constant))
- y = x->data.select.statement;
- else
- {
- m = 0;
- while ((v = *p++))
- {
- if (m)
- agxbput(cc->ccdisc->text, "||");
- else
- {
- m = 1;
- agxbput(cc->ccdisc->text, "if (");
- }
- if (t == STRING) {
- char *quoted = fmtesq(v->string, quote);
- agxbprint(cc->ccdisc->text, "strmatch(%stmp_%d, \"%s\")", cc->id, cc->tmp, quoted);
- free(quoted);
- } else {
- agxbprint(cc->ccdisc->text, "%stmp_%d == ", cc->id, cc->tmp);
- switch (t)
- {
- case INTEGER:
- case UNSIGNED:
- agxbprint(cc->ccdisc->text, "%llu",
- (unsigned long long)v->integer);
- break;
- default:
- agxbprint(cc->ccdisc->text, "%g", v->floating);
- break;
- }
- }
- }
- agxbput(cc->ccdisc->text, ") {");
- gen(cc, x->data.select.statement);
- agxbputc(cc->ccdisc->text, '}');
- }
- }
- if (y)
- {
- if (n)
- agxbput(cc->ccdisc->text, "else ");
- agxbputc(cc->ccdisc->text, '{');
- gen(cc, y);
- agxbputc(cc->ccdisc->text, '}');
- }
- agxbputc(cc->ccdisc->text, '}');
- return;
- }
- case UNSET:
- agxbprint(cc->ccdisc->text, "unset(%s", exnode->data.variable.symbol->name);
- if (exnode->data.variable.index) {
- agxbputc(cc->ccdisc->text, ',');
- gen(cc, exnode->data.variable.index);
- }
- agxbputc(cc->ccdisc->text, ')');
- return;
- case WHILE:
- agxbput(cc->ccdisc->text, "while (");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ") {");
- if (exnode->data.operand.right)
- gen(cc, exnode->data.operand.right);
- agxbputc(cc->ccdisc->text, '}');
- return;
- case '#':
- agxbprint(cc->ccdisc->text, "# %s", exnode->data.variable.symbol->name);
- return;
- case '=':
- agxbprint(cc->ccdisc->text, "(%s%s=", x->data.variable.symbol->name, exnode->subop == '=' ? "" : exopname(exnode->subop));
- gen(cc, exnode->data.operand.right);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case ';':
- for (;;)
- {
- if (!(x = exnode->data.operand.right))
- switch (exnode->data.operand.left->op)
- {
- case FOR:
- case IF:
- case PRINTF:
- case PRINT:
- case RETURN:
- case WHILE:
- break;
- default:
- agxbprint(cc->ccdisc->text, "_%svalue=", cc->id);
- break;
- }
- gen(cc, exnode->data.operand.left);
- agxbput(cc->ccdisc->text, ";\n");
- if (!(exnode = x))
- break;
- switch (exnode->op)
- {
- case ';':
- continue;
- case FOR:
- case IF:
- case PRINTF:
- case PRINT:
- case RETURN:
- case WHILE:
- break;
- default:
- agxbprint(cc->ccdisc->text, "_%svalue=", cc->id);
- break;
- }
- gen(cc, exnode);
- agxbput(cc->ccdisc->text, ";\n");
- break;
- }
- return;
- case ',':
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, x);
- while ((exnode = exnode->data.operand.right) && exnode->op == ',')
- {
- agxbput(cc->ccdisc->text, "), (");
- gen(cc, exnode->data.operand.left);
- }
- if (exnode)
- {
- agxbput(cc->ccdisc->text, "), (");
- gen(cc, exnode);
- }
- agxbputc(cc->ccdisc->text, ')');
- return;
- case '?':
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, x);
- agxbput(cc->ccdisc->text, ") ? (");
- gen(cc, exnode->data.operand.right->data.operand.left);
- agxbput(cc->ccdisc->text, ") : (");
- gen(cc, exnode->data.operand.right->data.operand.right);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case AND:
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, x);
- agxbput(cc->ccdisc->text, ") && (");
- gen(cc, exnode->data.operand.right);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case OR:
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, x);
- agxbput(cc->ccdisc->text, ") || (");
- gen(cc, exnode->data.operand.right);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case F2I:
- agxbprint(cc->ccdisc->text, "(%s)(", extype(INTEGER));
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case I2F:
- agxbprint(cc->ccdisc->text, "(%s)(", extype(FLOATING));
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case S2I:
- agxbput(cc->ccdisc->text, "strtoll(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ",(char**)0,0)");
- return;
- case X2I:
- agxbput(cc->ccdisc->text, "X2I(");
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case X2X:
- agxbput(cc->ccdisc->text, "X2X(");
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ')');
- return;
- }
- y = exnode->data.operand.right;
- if (x->type == STRING)
- {
- switch (exnode->op)
- {
- case S2B:
- agxbput(cc->ccdisc->text, "*(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ")!=0");
- return;
- case S2F:
- agxbput(cc->ccdisc->text, "strtod(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ",0)");
- return;
- case S2I:
- agxbput(cc->ccdisc->text, "strtol(");
- gen(cc, x);
- agxbput(cc->ccdisc->text, ",0,0)");
- return;
- case S2X:
- agxbput(cc->ccdisc->text, "** cannot convert string value to external **");
- return;
- case NE:
- agxbputc(cc->ccdisc->text, '!');
- /*FALLTHROUGH*/
- case EQ:
- agxbput(cc->ccdisc->text, "strmatch(");
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ',');
- gen(cc, y);
- agxbputc(cc->ccdisc->text, ')');
- return;
- case '+':
- case '|':
- case '&':
- case '^':
- case '%':
- case '*':
- agxbput(cc->ccdisc->text, "** string bits not supported **");
- return;
- }
- switch (exnode->op)
- {
- case '<':
- s = "<0";
- break;
- case LE:
- s = "<=0";
- break;
- case GE:
- s = ">=0";
- break;
- case '>':
- s = ">0";
- break;
- default:
- s = "** unknown string op **";
- break;
- }
- agxbput(cc->ccdisc->text, "strcoll(");
- gen(cc, x);
- agxbputc(cc->ccdisc->text, ',');
- gen(cc, y);
- agxbprint(cc->ccdisc->text, ")%s", s);
- return;
- }
- else
- {
- if (!y)
- agxbput(cc->ccdisc->text, exopname(exnode->op));
- agxbputc(cc->ccdisc->text, '(');
- gen(cc, x);
- if (y)
- {
- agxbprint(cc->ccdisc->text, ")%s(", exopname(exnode->op));
- gen(cc, y);
- }
- agxbputc(cc->ccdisc->text, ')');
- }
- return;
- }
- /*
- * generate global declarations
- */
- static int global(void *object, void *handle) {
- agxbuf *stream = handle;
- Exid_t* sym = object;
- if (sym->lex == DYNAMIC)
- agxbprint(stream, "static %s %s;\n", extype(sym->type), sym->name);
- return 0;
- }
- /*
- * open C program generator context
- */
- static Excc_t *exccopen(Expr_t *ex, Exccdisc_t *disc) {
- Excc_t* cc;
- char* id;
- if (!(id = disc->id))
- id = "";
- if (!(cc = calloc(1, sizeof(Excc_t) + strlen(id) + 2)))
- return 0;
- cc->expr = ex;
- cc->disc = ex->disc;
- cc->id = (char*)(cc + 1);
- cc->ccdisc = disc;
- if (!(disc->flags & EX_CC_DUMP))
- {
- agxbprint(disc->text, "/* : : generated by %s : : */\n", exversion);
- agxbput(disc->text, "\n#include <ast/ast.h>\n");
- if (*id)
- snprintf(cc->id, strlen(id) + 2, "%s_", id);
- agxbputc(disc->text, '\n');
- dtwalk(ex->symbols, global, disc->text);
- }
- return cc;
- }
- /*
- * close C program generator context
- */
- static int exccclose(Excc_t *cc) {
- int r = 0;
- if (!cc)
- r = -1;
- else
- {
- if (!(cc->ccdisc->flags & EX_CC_DUMP))
- {
- if (cc->ccdisc->text)
- agxbuse(cc->ccdisc->text);
- else
- r = -1;
- }
- free(cc);
- }
- return r;
- }
- /*
- * dump an expression tree to a buffer
- */
- int exdump(Expr_t *ex, Exnode_t *node, agxbuf *xb) {
- Excc_t* cc;
- Exccdisc_t ccdisc;
- Exid_t* sym;
- memset(&ccdisc, 0, sizeof(ccdisc));
- ccdisc.flags = EX_CC_DUMP;
- ccdisc.text = xb;
- if (!(cc = exccopen(ex, &ccdisc)))
- return -1;
- if (node)
- gen(cc, node);
- else
- for (sym = dtfirst(ex->symbols); sym; sym = dtnext(ex->symbols, sym))
- if (sym->lex == PROCEDURE && sym->value)
- {
- agxbprint(xb, "%s:\n", sym->name);
- gen(cc, sym->value->data.procedure.body);
- }
- agxbputc(xb, '\n');
- return exccclose(cc);
- }
|