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);
+		}
+	}
+}