Mercurial > repo
diff src/ploki/transmogrify.c @ 4223:ac0403686959
<oerjan> rm -rf src/ploki; mv ploki src
author | HackBot |
---|---|
date | Fri, 20 Dec 2013 22:18:50 +0000 |
parents | |
children |
line wrap: on
line diff
--- /dev/null Thu Jan 01 00:00:00 1970 +0000 +++ b/src/ploki/transmogrify.c Fri Dec 20 22:18:50 2013 +0000 @@ -0,0 +1,598 @@ +#include "config.h" +#include "Str.h" +#include "expr.h" +#include "kork.h" +#include "main_label.h" +#include "op.h" +#include "text.h" +#include "transmogrify.h" +#include "val.h" +#include "venus.h" +#include "xmalloc.h" +#include "zz.h" + +#include <ctype.h> +#include <math.h> +#include <stddef.h> +#include <assert.h> + +ATTR_PURE +static int liter_expr(const struct expr *const e) { + return + e->type == literE || + ( + e->type == symbolE && + e->op != S_ARG && e->op != S_ARGC && e->op != S_ARGV && + e->op != S_ERR && e->op != S_MATCH && e->op != S_RAND && + e->op != S_RESULT + ) || + ( + e->type == listE && + (e->right == NULL || liter_expr(e->right)) && + e->left.expr == NULL + ) + ; +} + +#define const_unop(op) \ +( \ + (op) == F_ABS || (op) == F_ACOS || (op) == F_ASIN || \ + (op) == F_ATAN || (op) == F_ATAN2 || (op) == F_CHR || \ + (op) == F_CHR || (op) == F_COS || (op) == F_DEFINED || \ + (op) == F_EXP || (op) == F_GETENV || (op) == F_INT || \ + (op) == F_IO || (op) == F_LENGTH || (op) == F_LOG || \ + (op) == F_LOG10 || (op) == F_LOWER || (op) == F_NEG || \ + (op) == F_NOT || (op) == F_NUM || (op) == F_ORD || \ + (op) == F_QUOTE || (op) == F_RE_ESC || (op) == F_REVERSE || \ + (op) == F_SIN || (op) == F_TAN || (op) == F_SQRT || \ + (op) == F_STR || (op) == F_TYPEOF || (op) == F_UPPER \ +) + +#define const_binop(op) \ +( \ + !((op) == B_SPOT || (op) == B_SQIGGLE || (op) == B_XMATCH) \ +) + +static int hp_expr(const struct expr *e) { + switch (e->type) { + case literE: + return 1; + + case varE: + return 1; + + case varhashE: + return hp_expr(e->right); + + case symbolE: + if (e->op != S_RAND) { + return 1; + } + return 0; + + case unopE: + if (!const_unop(e->op)) { + return 0; + } + return hp_expr(e->right); + + case binopE: + if (!const_binop(e->op)) { + return 0; + } + if (!hp_expr(e->left.expr)) { + return 0; + } + return hp_expr(e->right); + + case listE: + if (e->right) { + if (!hp_expr(e->right)) { + return 0; + } + if (!e->left.expr) { + return 1; + } + return hp_expr(e->left.expr); + } + return 1; + } + + NOTREACHED; +} + +void trans_fold(struct expr **e) { + struct val *v; + + switch ((*e)->type) { + case literE: + break; + + case varE: + break; + + case varhashE: + trans_fold(&(*e)->right); + break; + + case symbolE: + break; + + case unopE: + trans_fold(&(*e)->right); + if (liter_expr((*e)->right)) { + if ((*e)->op == F_NUL) { + v = eval_expr((*e)->right); + free_expr((*e)->right); + (*e)->right = xmalloc(1, sizeof *(*e)->right); + (*e)->right->type = literE; + (*e)->right->left.expr = (*e)->right->right = NULL; + (*e)->right->v.val = v_undef(); + TOLABEL(v); + if (((*e)->left.op = ve_findnext(&Venus, ko_str(v->ko), (*e)->left.bonus))) { + (*e)->op = F_CALL; + } else { + (*e)->op = F_HANG; + } + v_delete(v); + } else if (const_unop((*e)->op)) { + (*e)->v.val = eval_expr(*e); + free_expr((*e)->right); + (*e)->right = NULL; + (*e)->type = literE; + } + } else if ( + (*e)->right->type == unopE && + (*e)->op == (*e)->right->op && + ((*e)->op == F_STR || (*e)->op == F_NUM) + ) { + struct expr *const tmp = (*e)->right; + (*e)->right = (*e)->right->right; + tmp->right = NULL; + free_expr(tmp); + } + break; + + case binopE: + trans_fold(&(*e)->left.expr); + trans_fold(&(*e)->right); + if (liter_expr((*e)->left.expr) && liter_expr((*e)->right) && const_binop((*e)->op)) { + (*e)->v.val = eval_expr(*e); + free_expr((*e)->left.expr); + (*e)->left.expr = NULL; + free_expr((*e)->right); + (*e)->right = NULL; + (*e)->type = literE; + } else switch ((*e)->op) { + case B_INTERSECTION: + if ((*e)->left.expr->type == literE) { + V_NUM((*e)->left.expr->v.val); + if ((*e)->left.expr->v.val->num == 0.0) { + free_expr((*e)->left.expr); + (*e)->left.expr = NULL; + (*e)->op = F_NUM; + (*e)->type = unopE; + } + } else if ((*e)->right->type == literE) { + V_NUM((*e)->right->v.val); + if ((*e)->right->v.val->num == 0.0) { + free_expr((*e)->right); + (*e)->right = (*e)->left.expr; + (*e)->left.expr = NULL; + (*e)->op = F_NUM; + (*e)->type = unopE; + } + } + break; + + case B_TAIL: + if (hp_expr((*e)->left.expr)) { + struct expr *tmp; + + free_expr((*e)->left.expr); + tmp = *e; + *e = (*e)->right; + xfree(tmp); + } + break; + + case B_WORM: + if ((*e)->left.expr->type == literE) { + V_NUM((*e)->left.expr->v.val); + if ((*e)->left.expr->v.val->num == 0.0) { + free_expr((*e)->left.expr); + (*e)->left.expr = NULL; + (*e)->op = F_NEG; + (*e)->type = unopE; + } + } else if ((*e)->right->type == literE) { + V_NUM((*e)->right->v.val); + if ((*e)->right->v.val->num == 0.0) { + free_expr((*e)->right); + (*e)->right = (*e)->left.expr; + (*e)->left.expr = NULL; + (*e)->op = F_NUM; + (*e)->type = unopE; + } + } + break; + + case B_SHARK_FIN: + if ((*e)->left.expr->type == symbolE) { + if ((*e)->left.expr->op == S_EULER) { + free_expr((*e)->left.expr); + (*e)->left.expr = NULL; + (*e)->op = F_EXP; + (*e)->type = unopE; + } + } else if ((*e)->right->type == literE) { + V_NUM((*e)->right->v.val); + if ((*e)->right->v.val->num == 0.5) { + free_expr((*e)->right); + (*e)->right = (*e)->left.expr; + (*e)->left.expr = NULL; + (*e)->op = F_SQRT; + (*e)->type = unopE; + } + } + break; + + case B_FLATWORM: + if ((*e)->left.expr->type == literE) { + V_STR((*e)->left.expr->v.val); + if (!ko_length((*e)->left.expr->v.val->ko)) { + free_expr((*e)->left.expr); + (*e)->left.expr = NULL; + (*e)->op = F_STR; + (*e)->type = unopE; + } + } else if ((*e)->right->type == literE) { + V_STR((*e)->right->v.val); + if (!ko_length((*e)->right->v.val->ko)) { + free_expr((*e)->right); + (*e)->right = (*e)->left.expr; + (*e)->left.expr = NULL; + (*e)->op = F_STR; + (*e)->type = unopE; + } + } + break; + + case B_SQIGGLE: + case B_XMATCH: + if ((*e)->right->type == literE) { + t_regex *rx; + V_STR((*e)->right->v.val); + rx = re_compile(ko_str((*e)->right->v.val->ko)); + free_expr((*e)->right); + (*e)->right = (*e)->left.expr; + (*e)->left.rx = rx; + (*e)->op = F_MATCH; + (*e)->type = unopE; + } + break; + } + break; + + case listE: + if ((*e)->right) { + trans_fold(&(*e)->right); + if ((*e)->left.expr) { + trans_fold(&(*e)->left.expr); + } + } + break; + } +} + +#define SNAP(p) \ +do { \ + while ( \ + (p) && \ + (p)->type == OP_NOP && \ + (p) != (p)->next \ + ) { \ + (p) = (p)->next; \ + } \ +} while (0) + +static void walk(struct expr *e) { + for (;;) { + switch (e->type) { + case literE: + case symbolE: + return; + + case varE: + return; + + case varhashE: + e = e->right; + continue; + + case unopE: + if (e->op == F_CALL) { + SNAP(e->left.op); + if ( + e->left.op && + e->left.op->type == OP_RETURN && + hp_expr(e->right) && + liter_expr(e->left.op->arg) + ) { + free_expr(e->right); + e->right = NULL; + switch (e->left.op->arg->type) { + case literE: + e->v.val = v_undef(); + v_set(e->v.val, e->left.op->arg->v.val); + e->type = literE; + break; + + case symbolE: + e->op = e->left.op->arg->op; + e->type = symbolE; + break; + + case listE: + assert(!e->left.op->arg->left.expr && !e->left.op->arg->right); + e->type = listE; + break; + + default: + NOTREACHED; + } + e->left.expr = NULL; + return; + } + } + e = e->right; + continue; + + case binopE: + walk(e->left.expr); + e = e->right; + continue; + + case listE: + if (e->right) { + if (e->left.expr) { + walk(e->left.expr); + } + e = e->right; + continue; + } + return; + } + } +} + +void transmogrify(struct text *code) { + size_t i; + struct op *hang = NULL; + + for (i = 0; i < code->length; ++i) { + struct op *op = code->start[i]; + + switch (op->type) { + case OP_CALL: + case OP_CALL_DYN: + case OP_CLOSE: + case OP_EXIT: + case OP_FLUSH: + case OP_GOBACK: + case OP_GOTO: + case OP_IF: + case OP_RESET: + case OP_RETURN: + case OP_SYSTEM: + case OP_THROW: + trans_fold(&op->arg); + break; + + case OP_ASSIGN: + trans_fold(&op->arg); + if (!op->arh.expr) { + if (hp_expr(op->arg)) { + free_expr(op->arg); + op->arg = NULL; + op->type = OP_NOP; + } + } else { + trans_fold(&op->arh.expr); + if ( + op->arh.expr->type == varE && + op->arg->type == binopE && + op->arg->left.expr->type == varE && + op->arh.expr->v.val == op->arg->left.expr->v.val + ) { + struct expr *const tmp = op->arg; + op->arh.expr->op = op->arg->op; + op->arg = op->arg->right; + xfree(tmp->left.expr); + xfree(tmp); + op->type = OP_MODIFY; + } else if (op->arg->type == literE) { + op->type = OP_SET_VAL; + } + } + break; + + case OP_CALL_BACK: + case OP_MODIFY: + trans_fold(&op->arh.expr); + trans_fold(&op->arg); + break; + + case OP_PRINT: + case OP_PUTC: + case OP_TEMP: + trans_fold(&op->arg); + if (op->arh.expr) { + trans_fold(&op->arh.expr); + } + break; + + default: + break; + } + } + + for (i = 0; i < code->length; ++i) { + struct op *op = code->start[i]; + + switch (op->type) { + case OP_CALL_BACK: + if (liter_expr(op->arh.expr)) { + struct val *v; + + v = eval_expr(op->arh.expr); + TOLABEL(v); + free_expr(op->arh.expr); + if ((op->arh.op = ve_findprev(&Venus, ko_str(v->ko), op->line))) { + op->type = OP_CALL; + } else { + free_expr(op->arg); + op->arg = NULL; + op->type = OP_HANG; + hang = op; + } + v_end(v); + xfree(v); + } + break; + + case OP_CALL_DYN: + case OP_GOBACK: + case OP_GOTO: + if (liter_expr(op->arg)) { + struct val *v; + + v = eval_expr(op->arg); + TOLABEL(v); + free_expr(op->arg); + op->arg = NULL; + if ((op->arh.op = (op->type == OP_GOBACK ? ve_findprev : ve_findnext)(&Venus, ko_str(v->ko), op->line))) { + if (op->type == OP_CALL_DYN) { + op->type = OP_CALL; + op->arg = xmalloc(1, sizeof *op->arg); + op->arg->left.expr = op->arg->right = NULL; + op->arg->type = literE; + op->arg->v.val = v_undef(); + } else { + op->next = op->arh.op; + op->arh.op = NULL; + op->type = OP_NOP; + } + } else { + op->type = OP_HANG; + hang = op; + } + v_end(v); + xfree(v); + } + break; + + case OP_HANG: + hang = op; + break; + + case OP_IF: + if (liter_expr(op->arg)) { + struct val *v; + + v = eval_expr(op->arg); + if (v_true(v)) { + op->next = op->arh.op; + } + v_delete(v); + op->type = OP_NOP; + op->arh.expr = NULL; + free_expr(op->arg); + op->arg = NULL; + } + break; + + case OP_PRINT: + if ( + op->arg->type == literE && + ( + ( + op->arg->v.val->type == V_STR_K && + ko_length(op->arg->v.val->ko) == 0 + ) || + op->arg->v.val->type == V_UNDEF + ) + ) { + op->type = OP_NOP; + free_expr(op->arg); + op->arg = NULL; + if (op->arh.expr) { + free_expr(op->arh.expr); + op->arh.expr = NULL; + } + break; + } + if ( + op->arh.expr && + op->arh.expr->type == symbolE && + op->arh.expr->op == S_STDOUT + ) { + free_expr(op->arh.expr); + op->arh.expr = NULL; + } + break; + + default: + break; + } + } + + if (!hang) { + struct op tmp; + + tmp.type = OP_HANG; + tmp.arg = tmp.arh.expr = NULL; + tmp.next = NULL; + St_init(&tmp.txt); + St_clear(&tmp.txt); + tmp.line = -1; + + hang = text_push(code, &tmp); + } + + for (i = 0; i < code->length; ++i) { + struct op *op = code->start[i]; + + SNAP(op->next); + if (op->type == OP_IF) { + SNAP(op->arh.op); + } else if (op->type == OP_CALL) { + SNAP(op->arh.op); + if (!op->arh.op) { + struct expr *tmp = xmalloc(1, sizeof *tmp); + tmp->type = binopE; + tmp->op = B_TAIL; + tmp->left.expr = op->arg; + tmp->right = xmalloc(1, sizeof *tmp->right); + tmp->right->type = literE; + tmp->right->v.val = v_undef(); + tmp->right->left.expr = tmp->right->right = NULL; + op->arg = tmp; + op->type = OP_EXIT; + trans_fold(&op->arg); + } + } + + if (op == op->next) { + op->next = hang; + } + if (OP_1ARG_P(op->type)) { + walk(op->arg); + } else if (OP_2ARG_P(op->type)) { + if (op->arh.expr) { + walk(op->arh.expr); + } + walk(op->arg); + } + } +}