array unset function.
Add split function as in awk.
}
sfprintf(cc->ccdisc->text, ")");
return;
+ case IN:
+ gen(cc, expr->data.variable.index);
+ sfprintf(cc->ccdisc->text, " in %s", expr->data.variable.symbol->name);
+ return;
case IF:
sfprintf(cc->ccdisc->text, "if (");
gen(cc, x);
sfprintf(cc->ccdisc->text, "}\n");
return;
case FOR:
- sfprintf(cc->ccdisc->text, "for (;");
+ case FORR:
+ if (expr->op == FOR)
+ sfprintf(cc->ccdisc->text, "for (;");
+ else
+ sfprintf(cc->ccdisc->text, "forr (;");
gen(cc, x);
sfprintf(cc->ccdisc->text, ");");
if (expr->data.operand.left) {
case SCANF:
scan(cc, expr);
return;
+ case SPLIT:
+ sfprintf(cc->ccdisc->text, "split (");
+ gen(cc, expr->data.split.string);
+ sfprintf(cc->ccdisc->text, ", %s", expr->data.split.array->name);
+ if (expr->data.split.seps) {
+ sfprintf(cc->ccdisc->text, ",");
+ gen(cc, expr->data.split.seps);
+ }
+ sfprintf(cc->ccdisc->text, ")");
+ return;
case SWITCH:
t = x->type;
sfprintf(cc->ccdisc->text, "{ %s %stmp_%d = ", extype(t), cc->id,
}
sfprintf(cc->ccdisc->text, "}");
return;
+ case UNSET:
+ sfprintf(cc->ccdisc->text, "unset(%s", expr->data.variable.symbol->name);
+ if (expr->data.variable.index) {
+ sfprintf(cc->ccdisc->text, ",");
+ gen(cc, expr->data.variable.index);
+ }
+ sfprintf(cc->ccdisc->text, ")");
+ return;
case WHILE:
sfprintf(cc->ccdisc->text, "while (");
gen(cc, x);
gen(cc, expr->data.operand.right);
sfprintf(cc->ccdisc->text, "}");
return;
+ case '#':
+ sfprintf(cc->ccdisc->text, "# %s",
+ expr->data.variable.symbol->name);
+ return;
case '=':
sfprintf(cc->ccdisc->text, "(%s%s=", x->data.variable.symbol->name,
expr->subop == '=' ? "" : exopname(expr->subop));
if (!(x = expr->data.operand.right))
switch (cc->lastop = expr->data.operand.left->op) {
case FOR:
+ case FORR:
case IF:
case PRINTF:
case PRINT:
case ';':
continue;
case FOR:
+ case FORR:
case IF:
case PRINTF:
case PRINT:
EX_ID("else", ELSE, ELSE, 0, 0),
EX_ID("exit", EXIT, EXIT, INTEGER, 0),
EX_ID("for", FOR, FOR, 0, 0),
+ EX_ID("forr", FORR, FORR, 0, 0),
EX_ID("float", DECLARE, FLOATING, FLOATING, 0),
EX_ID("gsub", GSUB, GSUB, STRING, 0),
EX_ID("if", IF, IF, 0, 0),
+ EX_ID("in", IN, IN, 0, 0),
EX_ID("int", DECLARE, INTEGER, INTEGER, 0),
EX_ID("long", DECLARE, INTEGER, INTEGER, 0),
EX_ID("print", PRINT, PRINT, INTEGER, 0),
EX_ID("return", RETURN, RETURN, 0, 0),
EX_ID("scanf", SCANF, SCANF, INTEGER, 0),
EX_ID("sscanf", SSCANF, SSCANF, INTEGER, 0),
+ EX_ID("split", SPLIT, SPLIT, INTEGER, 0),
EX_ID("sprintf", SPRINTF, SPRINTF, STRING, 0),
EX_ID("srand", SRAND, SRAND, INTEGER, 0),
EX_ID("sub", SUB, SUB, STRING, 0),
EX_ID("switch", SWITCH, SWITCH, 0, 0),
EX_ID("unsigned", DECLARE, UNSIGNED, UNSIGNED, 0),
EX_ID("void", DECLARE, VOIDTYPE, 0, 0),
+ EX_ID("unset", UNSET, UNSET, 0, 0),
EX_ID("while", WHILE, WHILE, 0, 0),
EX_ID({0}, 0, 0, 0, 0)
#include "exlib.h"
#include "exop.h"
#include <string.h>
+#include <assert.h>
#include <time.h>
#ifdef WIN32
#include <stdlib.h>
return b;
}
+/* evaldyn:
+ * Evaluate item from array given key.
+ * Returns 1 if item existed, zero otherwise
+ *
+ */
+static int
+evaldyn (Expr_t * ex, register Exnode_t * expr, void *env, int delete)
+{
+ Exassoc_t *b;
+ Extype_t v;
+ char buf[32];
+ Extype_t key;
+ char *keyname;
+
+ v = eval(ex, expr->data.variable.index, env);
+ if (expr->data.variable.symbol->index_type == INTEGER) {
+ if (!(b = (Exassoc_t *) dtmatch((Dt_t *) expr->data.variable.
+ symbol->local.pointer, &v))) {
+ return 0;
+ }
+ }
+ else {
+ int type = expr->data.variable.index->type;
+ if (type != STRING) {
+ if (!BUILTIN(type)) {
+ key = (*ex->disc->keyf) (ex, v, type, ex->disc);
+ } else
+ key.integer = v.integer;
+ sfsprintf(buf, sizeof(buf), "0x%I*x", sizeof(v.integer),
+ key.integer);
+ keyname = buf;
+ } else
+ keyname = v.string;
+ if (!(b = (Exassoc_t *) dtmatch((Dt_t *) expr->data.variable.
+ symbol->local.pointer, keyname))) {
+ return 0;
+ }
+ }
+ if (delete) {
+ dtdelete ((Dt_t*)expr->data.variable.symbol->local.pointer, b);
+ free (b);
+ }
+ return 1;
+}
+
/*
* return dynamic (associative array) variable value
* assoc will point to the associative array bucket
#define MCNT(s) (sizeof(s)/(2*sizeof(int)))
+/* exsplit:
+ * tokenize string and store in array
+ * return number of tokens
+ */
+Extype_t
+exsplit(Expr_t * ex, register Exnode_t * expr, void *env)
+{
+ Extype_t v;
+ char *str;
+ char *seps;
+ char *tok;
+ Exassoc_t* b;
+ size_t sz;
+ Sfio_t* fp = ex->tmp;
+ int cnt = 0;
+ Dt_t* arr = (Dt_t*)expr->data.split.array->local.pointer;
+
+ str = (eval(ex, expr->data.split.string, env)).string;
+ if (expr->data.split.seps)
+ seps = (eval(ex, expr->data.string.pat, env)).string;
+ else
+ seps = " \t\n";
+
+ v.integer = 0;
+ while (*str) {
+ sz = strspn (str, seps);
+ str += sz;
+ if (*str == '\0')
+ break;
+ sz = strcspn (str, seps);
+ assert (sz);
+ sfwrite (fp, str, sz);
+ tok = vmstrdup(ex->vc, sfstruse(fp));
+
+ if (!(b = (Exassoc_t *) dtmatch(arr, &v))) {
+ if (!(b = newof(0, Exassoc_t, 1, 0)))
+ exerror("out of space [assoc]");
+ b->key = v;
+ dtinsert(arr, b);
+ }
+ b->value.string = tok;
+
+ v.integer++;
+ str += sz;
+ }
+
+ v.integer = cnt;
+ return v;
+
+}
+
/* exsub:
* return string after pattern substitution
*/
return r;
case DYNAMIC:
return getdyn(ex, expr, env, &assoc);
+ case SPLIT:
+ return exsplit(ex, expr, env);
case GSUB:
return exsub(ex, expr, env, 1);
case SUB:
}
}
return v;
+ case ITERATER:
+ v.integer = 0;
+ if (expr->data.generate.array->op == DYNAMIC) {
+ n = expr->data.generate.index->type == STRING;
+ for (assoc =
+ (Exassoc_t *) dtlast((Dt_t *) expr->data.generate.array->
+ data.variable.symbol->local.
+ pointer); assoc;
+ assoc =
+ (Exassoc_t *) dtprev((Dt_t *) expr->data.generate.array->
+ data.variable.symbol->local.pointer,
+ assoc)) {
+ v.integer++;
+ if (n)
+ expr->data.generate.index->value->data.constant.value.
+ string = assoc->name;
+ else
+ expr->data.generate.index->value->data.constant.value =
+ assoc->key;
+ eval(ex, expr->data.generate.statement, env);
+ if (ex->loopcount > 0
+ && (--ex->loopcount > 0 || ex->loopop != CONTINUE)) {
+ v.integer = 0;
+ break;
+ }
+ }
+ } else {
+ r = (*ex->disc->getf) (ex, expr,
+ expr->data.generate.array->data.
+ variable.symbol,
+ expr->data.generate.array->data.
+ variable.reference, env, 0, ex->disc);
+ for (v.integer = r.integer-1; 0 <= v.integer; v.integer--) {
+ expr->data.generate.index->value->data.constant.value.
+ integer = v.integer;
+ eval(ex, expr->data.generate.statement, env);
+ if (ex->loopcount > 0
+ && (--ex->loopcount > 0 || ex->loopop != CONTINUE)) {
+ v.integer = 0;
+ break;
+ }
+ }
+ }
+ return v;
+ case '#':
+ v.integer = dtsize ((Dt_t*)expr->data.variable.symbol->local.pointer);
+ return v;
+ case IN:
+ v.integer = evaldyn (ex, expr, env, 0);
+ return v;
+ case UNSET:
+ if (expr->data.variable.index) {
+ v.integer = evaldyn (ex, expr, env, 1);
+ }
+ else {
+ dtclear ((Dt_t*)expr->data.variable.symbol->local.pointer);
+ v.integer = 0;
+ }
+ return v;
case CALL:
x = expr->data.call.args;
for (n = 0, a =
x->data.variable.symbol->local.pointer = 0;
}
break;
+ case '#':
+ if (x->data.variable.symbol->local.pointer) {
+ dtclose((Dt_t *) x->data.variable.symbol->local.pointer);
+ x->data.variable.symbol->local.pointer = 0;
+ }
+ break;
+ case IN:
+ case UNSET:
+ if (x->data.variable.index)
+ exfreenode(p, x->data.variable.index);
+ if (x->data.variable.symbol->local.pointer) {
+ dtclose((Dt_t *) x->data.variable.symbol->local.pointer);
+ x->data.variable.symbol->local.pointer = 0;
+ }
+ break;
case ITERATE:
+ case ITERATER:
if (x->data.generate.statement)
exfreenode(p, x->data.generate.statement);
break;
vmfree(p->vm, r);
}
if (x->data.variable.index)
- exfreenode(p, x->data.variable.index);
break;
case GSUB:
case SUB:
if (x->data.string.repl)
exfreenode(p, x->data.string.repl);
break;
+ case SPLIT:
+ if (x->data.split.seps)
+ exfreenode(p, x->data.split.seps);
+ exfreenode(p, x->data.split.string);
+ if (x->data.split.array->local.pointer) {
+ dtclose((Dt_t *) x->data.split.array->local.pointer);
+ x->data.split.array->local.pointer = 0;
+ }
+ break;
case PRINT:
exfreenode(p, x->data.operand.left);
break;
return left;
}
+/* exnewsplit:
+ * Generate split node.
+ * Fourth argument is optional.
+ */
+ static Exnode_t *exnewsplit(Expr_t * p, Exid_t* dyn, Exnode_t * s, Exnode_t* seps) {
+ Exnode_t *ss = 0;
+
+ if (dyn->local.pointer == 0)
+ exerror("cannot use non-array %s in split", dyn->name);
+ if ((dyn->index_type > 0) && (dyn->index_type != INTEGER))
+ exerror("in split, array %s must have integer index type, not %s",
+ dyn->name, extypename(p, s->type));
+ if (dyn->type != STRING)
+ exerror("in split, array %s entries must have string type, not %s",
+ dyn->name, extypename(p, s->type));
+ if (s->type != STRING)
+ exerror("first argument to split must have string type, not %s",
+ extypename(p, s->type));
+ if (seps && (seps->type != STRING))
+ exerror("third argument to split must have string type, not %s",
+ extypename(p, seps->type));
+ ss = exnewnode(p, SPLIT, 0, INTEGER, NiL, NiL);
+ ss->data.split.array = dyn;
+ ss->data.split.string = s;
+ ss->data.split.seps = seps;
+ return ss;
+ }
+
/* exnewsub:
* Generate sub node.
* Third argument is optional.
} generate; /* associative array generator */ \
struct \
{ \
+ Exid_t* array; /* array */ \
+ Exnode_t* string; /* string */ \
+ Exnode_t* seps; /* optional separators */ \
+ } split; /* string split */ \
+ struct \
+ { \
Exnode_t* descriptor; /* Expr_t.file index */ \
Print_t* args; /* compiler printf args */ \
} print; /* printf */ \
%token ELSE
%token EXIT
%token FOR
+%token FORR
%token FUNCTION
%token GSUB
%token ITERATE
+%token ITERATER
%token ID
%token IF
%token LABEL
%token RAND
%token RETURN
%token SCANF
+%token SPLIT
%token SPRINTF
%token SRAND
%token SSCANF
%token SUB
%token SUBSTR
%token SWITCH
+%token UNSET
%token WHILE
%token F2I
%binary <op> EQ NE
%binary <op> '<' '>' LE GE
%left <op> LS RS
-%left <op> '+' '-'
+%left <op> '+' '-' IN
%left <op> '*' '/' '%'
-%right <op> '!' '~' UNARY
+%right <op> '!' '~' '#' UNARY
%right <op> INC DEC
-%right <op> CAST
+%right <op> CAST
%left <op> '('
%type <expr> statement statement_list arg_list
%type <expr> formals formal_list formal_item
%type <reference> members
%type <id> ID LABEL NAME
-%type <id> CONSTANT ARRAY FUNCTION DECLARE
-%type <id> EXIT PRINT PRINTF QUERY
+%type <id> CONSTANT ARRAY FUNCTION DECLARE
+%type <id> EXIT PRINT PRINTF QUERY
%type <id> RAND SRAND
-%type <id> SPRINTF GSUB SUB SUBSTR PROCEDURE name dcl_name
-%type <id> IF WHILE FOR
-%type <id> BREAK CONTINUE print member
-%type <id> RETURN DYNAMIC SWITCH
+%type <id> SPRINTF GSUB SUB SPLIT
+%type <id> SUBSTR PROCEDURE name dcl_name
+%type <id> IF WHILE FOR FORR
+%type <id> BREAK CONTINUE print member
+%type <id> RETURN DYNAMIC SWITCH UNSET
%type <id> SCANF SSCANF scan
%type <floating> FLOATING
%type <integer> INTEGER UNSIGNED array
if ($3)
$$ = exnewnode(expr.program, ';', 1, INTEGER, $3, $$);
}
+ | FORR '(' variable ')' statement
+ {
+ $$ = exnewnode(expr.program, ITERATER, 0, INTEGER, NiL, NiL);
+ $$->data.generate.array = $3;
+ if (!$3->data.variable.index || $3->data.variable.index->op != DYNAMIC)
+ exerror("simple index variable expected");
+ $$->data.generate.index = $3->data.variable.index->data.variable.symbol;
+ if ($3->op == ID && $$->data.generate.index->type != INTEGER)
+ exerror("integer index variable expected");
+ exfreenode(expr.program, $3->data.variable.index);
+ $3->data.variable.index = 0;
+ $$->data.generate.statement = $5;
+ }
+ | UNSET '(' DYNAMIC ')'
+ {
+ if ($3->local.pointer == 0)
+ exerror("cannot apply unset to non-array %s", $3->name);
+ $$ = exnewnode(expr.program, UNSET, 0, INTEGER, NiL, NiL);
+ $$->data.variable.symbol = $3;
+ $$->data.variable.index = NiL;
+ }
+ | UNSET '(' DYNAMIC ',' expr ')'
+ {
+ if ($3->local.pointer == 0)
+ exerror("cannot apply unset to non-array %s", $3->name);
+ if (($3->index_type > 0) && ($5->type != $3->index_type))
+ exerror("%s indices must have type %s, not %s",
+ $3->name, extypename(expr.program, $3->index_type),extypename(expr.program, $5->type));
+ $$ = exnewnode(expr.program, UNSET, 0, INTEGER, NiL, NiL);
+ $$->data.variable.symbol = $3;
+ $$->data.variable.index = $5;
+ }
| WHILE '(' expr ')' statement
{
if (exisAssign ($3))
checkBinary(expr.program, $2, $$, 0);
}
}
+ | '#' DYNAMIC
+ {
+ if ($2->local.pointer == 0)
+ exerror("cannot apply '#' operator to non-array %s", $2->name);
+ $$ = exnewnode(expr.program, '#', 0, INTEGER, NiL, NiL);
+ $$->data.variable.symbol = $2;
+ }
| '~' expr
{
goto iunary;
{
$$ = exnewsubstr (expr.program, $3);
}
+ | SPLIT '(' expr ',' DYNAMIC ')'
+ {
+ $$ = exnewsplit (expr.program, $5, $3, NiL);
+ }
+ | SPLIT '(' expr ',' DYNAMIC ',' expr ')'
+ {
+ $$ = exnewsplit (expr.program, $5, $3, $7);
+ }
| EXIT '(' expr ')'
{
if (!INTEGRAL($3->type))
$$ = exnewnode(expr.program, $2, 0, $1->type, $1, NiL);
$$->subop = POS;
}
+ | expr IN DYNAMIC
+ {
+ if ($3->local.pointer == 0)
+ exerror("cannot apply IN to non-array %s", $3->name);
+ if (($3->index_type > 0) && ($1->type != $3->index_type))
+ exerror("%s indices must have type %s, not %s",
+ $3->name, extypename(expr.program, $3->index_type),extypename(expr.program, $1->type));
+ $$ = exnewnode(expr.program, IN, 0, INTEGER, NiL, NiL);
+ $$->data.variable.symbol = $3;
+ $$->data.variable.index = $1;
+ }
| DEC variable
{
goto pre;
case FOR:
s = " for";
break;
+ case FORR:
+ s = " forr";
+ break;
case GSUB:
s = " gsub";
break;
case IF:
s = " if";
break;
+ case IN:
+ s = " in";
+ break;
case PRAGMA:
s = " pragma";
break;
case RETURN:
s = " return";
break;
+ case SPLIT:
+ s = " split";
+ break;
case SPRINTF:
s = " sprintf";
break;
case SWITCH:
s = " switch";
break;
+ case UNSET:
+ s = " unset";
+ break;
case WHILE:
s = " while";
break;