Mercurial > repo
view src/ploki/transmogrify.c @ 8876:87d57c2a2071
<oerjan> slwd markdown//s/rasp.*/walnut./
author | HackBot |
---|---|
date | Fri, 29 Jul 2016 00:50:35 +0000 |
parents | ac0403686959 |
children |
line wrap: on
line source
#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); } } }