Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions ChangeLog.txt
Original file line number Diff line number Diff line change
Expand Up @@ -2295,3 +2295,7 @@ Version 1.8.0p5, 12 May 1996
36789.foo
(an understandable typo for #6789.foo) stopped compiling when found in a
database made by a pre-1.8.0 server.

Version X, in progress
**** Changes relevant to programmers / wizards:
-- Added support for scattering in `for' statements.
96 changes: 87 additions & 9 deletions parser.y
Original file line number Diff line number Diff line change
Expand Up @@ -51,19 +51,21 @@ static DB_Version language_version;
static void error(const char *, const char *);
static void warning(const char *, const char *);
static int find_id(char *name);
static const char *get_name_from_id(int);
static void yyerror(const char *s);
static int yylex(void);
static Scatter *scatter_from_arglist(Arg_List *);
static Scatter *add_scatter_item(Scatter *, Scatter *);
static void vet_scatter(Scatter *);
static void push_loop_name(const char *);
static void push_loop_name_with_id(const char *, int);
static void pop_loop_name(void);
static void suspend_loop_scope(void);
static void resume_loop_scope(void);

enum loop_exit_kind { LOOP_BREAK, LOOP_CONTINUE };

static void check_loop_name(const char *, enum loop_exit_kind);
static int check_loop_name(char *, enum loop_exit_kind);
%}

%union {
Expand All @@ -86,7 +88,7 @@ static void check_loop_name(const char *, enum loop_exit_kind);
%type <args> arglist ne_arglist codes
%type <except> except excepts
%type <string> opt_id
%type <scatter> scatter scatter_item
%type <scatter> scatter_any scatter scatter_item

%token <integer> tINTEGER
%token <object> tOBJECT
Expand Down Expand Up @@ -140,6 +142,47 @@ statement:
$$->s.cond.arms->next = $6;
$$->s.cond.otherwise = $7;
}
| tFOR '{' scatter_any '}' tIN '(' expr ')'
{
const char *vname;
Stream *s;
int id;

if (!$3) {
yyerror("Empty list in scattering for.");
return 0;
}

vet_scatter($3);
vname = get_name_from_id($3->id);
s = new_stream(8);
stream_printf(s, "@%s", vname);
id = find_id(alloc_string(stream_contents(s)));
free_stream(s);
push_loop_name_with_id(vname, id);

$<integer>$ = id;
}
statements tENDFOR
{
Stmt *s;
Expr *lhs, *rhs;
int id = $<integer>9;

lhs = alloc_expr(EXPR_SCATTER);
lhs->e.scatter = $3;
rhs = alloc_expr(EXPR_ID);
rhs->e.id = id;
s = alloc_stmt(STMT_EXPR);
s->s.expr = alloc_binary(EXPR_ASGN, lhs, rhs);
s->next = $10;

$$ = alloc_stmt(STMT_LIST);
$$->s.list.id = id;
$$->s.list.expr = $7;
$$->s.list.body = s;
pop_loop_name();
}
| tFOR tID tIN '(' expr ')'
{
push_loop_name($2);
Expand Down Expand Up @@ -226,9 +269,10 @@ statement:
}
| tBREAK tID ';'
{
int id =
check_loop_name($2, LOOP_BREAK);
$$ = alloc_stmt(STMT_BREAK);
$$->s.exit = find_id($2);
$$->s.exit = id;
}
| tCONTINUE ';'
{
Expand All @@ -238,9 +282,10 @@ statement:
}
| tCONTINUE tID ';'
{
int id =
check_loop_name($2, LOOP_CONTINUE);
$$ = alloc_stmt(STMT_CONTINUE);
$$->s.exit = find_id($2);
$$->s.exit = id;
}
| tRETURN expr ';'
{
Expand Down Expand Up @@ -640,6 +685,17 @@ ne_arglist:
}
;

scatter_any:
arglist
{
$$ = scatter_from_arglist($1);
}
| scatter
{
$$ = $1;
}
;

scatter:
ne_arglist ',' scatter_item
{
Expand Down Expand Up @@ -695,6 +751,12 @@ find_id(char *name)
return slot;
}

static const char*
get_name_from_id(int id)
{
return local_names->names[id];
}

static void
yyerror(const char *s)
{
Expand Down Expand Up @@ -1008,21 +1070,29 @@ struct loop_entry {
struct loop_entry *next;
const char *name;
int is_barrier;
int id;
};

static struct loop_entry *loop_stack;

static void
push_loop_name(const char *name)
push_loop_name_with_id(const char *name, int id)
{
struct loop_entry *entry = mymalloc(sizeof(struct loop_entry), M_AST);

entry->next = loop_stack;
entry->name = (name ? str_dup(name) : 0);
entry->is_barrier = 0;
entry->id = id;
loop_stack = entry;
}

static void
push_loop_name(const char *name)
{
push_loop_name_with_id(name, -1);
}

static void
pop_loop_name(void)
{
Expand Down Expand Up @@ -1066,8 +1136,8 @@ resume_loop_scope(void)
}
}

static void
check_loop_name(const char *name, enum loop_exit_kind kind)
static int
check_loop_name(char *name, enum loop_exit_kind kind)
{
struct loop_entry *entry;

Expand All @@ -1078,17 +1148,25 @@ check_loop_name(const char *name, enum loop_exit_kind kind)
else
yyerror("No enclosing loop for `continue' statement");
}
return;
return -1;
}

for (entry = loop_stack; entry && !entry->is_barrier; entry = entry->next)
if (entry->name && mystrcasecmp(entry->name, name) == 0)
return;
{
if (entry->id == -1)
entry->id = find_id(name);
else
dealloc_string(name);
return entry->id;
}

if (kind == LOOP_BREAK)
error("Invalid loop name in `break' statement: ", name);
else
error("Invalid loop name in `continue' statement: ", name);
dealloc_string(name);
return -1;
}

Program *
Expand Down
32 changes: 30 additions & 2 deletions unparse.c
Original file line number Diff line number Diff line change
Expand Up @@ -271,12 +271,40 @@ unparse_stmt_cond(Stream *str, struct Stmt_Cond cond, int indent)
static void
unparse_stmt_list(Stream *str, struct Stmt_List list, int indent)
{
stream_printf(str, "for %s in (", prog->var_names[list.id]);
const char *vname = prog->var_names[list.id];
Stmt *body = list.body;

stream_add_string(str, "for ");
if (vname[0] == '@' &&
body->kind == STMT_EXPR &&
body->s.expr->kind == EXPR_ASGN &&
body->s.expr->e.bin.rhs->kind == EXPR_ID &&
body->s.expr->e.bin.rhs->e.id == list.id &&
body->s.expr->e.bin.lhs->kind == EXPR_SCATTER) {
unparse_expr(str, body->s.expr->e.bin.lhs);
/* NOTE: if programmers are ever allowed to put '@' in their own
* break/continue statements, this could protentially lead to a
* decompile-then-recompile inconsistency. For example:
* for {a, b} in (c)
* for a in (a) would continue the *outer* loop
* continue @a; <-- initially, but decompile as "continue
* endfor a" and then recompile to continue the
* endfor inner loop instead
* This could also happen without extending the syntax, by
* allowing programmers to modify the bytecode other ways (eg,
* an assembler/debugger).
*/
prog->var_names[list.id] = &prog->var_names[list.id][1];
body = body->next;
} else
stream_add_string(str, vname);
stream_add_string(str, " in (");
unparse_expr(str, list.expr);
stream_add_char(str, ')');
output(str);
unparse_stmt(list.body, indent + 2);
unparse_stmt(body, indent + 2);
indent_stmt(str, indent);
prog->var_names[list.id] = vname;
stream_add_string(str, "endfor");
output(str);
}
Expand Down