1281 lines
28 KiB
Text
1281 lines
28 KiB
Text
%{
|
|
/******************************************************************************
|
|
Copyright (c) 1992, 1995, 1996 Xerox Corporation. All rights reserved.
|
|
Portions of this code were written by Stephen White, aka ghond.
|
|
Use and copying of this software and preparation of derivative works based
|
|
upon this software are permitted. Any distribution of this software or
|
|
derivative works must comply with all applicable United States export
|
|
control laws. This software is made available AS IS, and Xerox Corporation
|
|
makes no warranty about the software, its performance or its conformity to
|
|
any specification. Any person obtaining a copy of this software is requested
|
|
to send their name and post office or electronic mail address to:
|
|
Pavel Curtis
|
|
Xerox PARC
|
|
3333 Coyote Hill Rd.
|
|
Palo Alto, CA 94304
|
|
Pavel@Xerox.Com
|
|
*****************************************************************************/
|
|
|
|
/*************************************************************************/
|
|
/* NOTE: If you add an #include here, make sure you properly update the */
|
|
/* parser.o dependency line in the Makefile. */
|
|
/*************************************************************************/
|
|
|
|
#include <ctype.h>
|
|
#include <math.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include "ast.h"
|
|
#include "pconfig.h"
|
|
#include "functions.h"
|
|
#include "keywords.h"
|
|
#include "list.h"
|
|
#include "parser.h"
|
|
#include "parsen.h"
|
|
#include "storage.h"
|
|
#include "streams.h"
|
|
#include "structures.h"
|
|
#include "utils.h"
|
|
#include "version.h"
|
|
#include "../gm-support.h"
|
|
|
|
static Stmt *prog_start;
|
|
static int dollars_ok;
|
|
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 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 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);
|
|
%}
|
|
|
|
%union {
|
|
Stmt *stmt;
|
|
Expr *expr;
|
|
int integer;
|
|
Objid object;
|
|
double *real;
|
|
char *string;
|
|
enum error error;
|
|
Arg_List *args;
|
|
Cond_Arm *arm;
|
|
Except_Arm *except;
|
|
Scatter *scatter;
|
|
}
|
|
|
|
%type <stmt> statements statement elsepart
|
|
%type <arm> elseifs
|
|
%type <expr> expr default
|
|
%type <args> arglist ne_arglist codes
|
|
%type <except> except excepts
|
|
%type <string> opt_id
|
|
%type <scatter> scatter scatter_item
|
|
|
|
%token <integer> tINTEGER
|
|
%token <object> tOBJECT
|
|
%token <real> tFLOAT
|
|
%token <string> tSTRING tID
|
|
%token <error> tERROR
|
|
%token tIF tELSE tELSEIF tENDIF tFOR tIN tENDFOR tRETURN tFORK tENDFORK
|
|
%token tWHILE tENDWHILE tTRY tENDTRY tEXCEPT tFINALLY tANY tBREAK tCONTINUE
|
|
|
|
%token tTO tARROW
|
|
|
|
%right '='
|
|
%nonassoc '?' '|'
|
|
%left tOR tAND
|
|
%left tEQ tNE '<' tLE '>' tGE tIN
|
|
%left '+' '-'
|
|
%left '*' '/' '%'
|
|
%right '^'
|
|
%left '!' tUNARYMINUS
|
|
%nonassoc '.' ':' '[' '$'
|
|
|
|
%%
|
|
|
|
program: statements
|
|
{ prog_start = $1; }
|
|
;
|
|
|
|
statements:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| statements statement
|
|
{
|
|
if ($1) {
|
|
Stmt *tmp = $1;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = $2;
|
|
$$ = $1;
|
|
} else
|
|
$$ = $2;
|
|
}
|
|
;
|
|
|
|
statement:
|
|
tIF '(' expr ')' statements elseifs elsepart tENDIF
|
|
{
|
|
|
|
$$ = alloc_stmt(STMT_COND);
|
|
$$->s.cond.arms = alloc_cond_arm($3, $5);
|
|
$$->s.cond.arms->next = $6;
|
|
$$->s.cond.otherwise = $7;
|
|
}
|
|
| tFOR tID tIN '(' expr ')'
|
|
{
|
|
push_loop_name($2);
|
|
}
|
|
statements tENDFOR
|
|
{
|
|
$$ = alloc_stmt(STMT_LIST);
|
|
$$->s.list.id = find_id($2);
|
|
$$->s.list.expr = $5;
|
|
$$->s.list.body = $8;
|
|
pop_loop_name();
|
|
}
|
|
| tFOR tID tIN '[' expr tTO expr ']'
|
|
{
|
|
push_loop_name($2);
|
|
}
|
|
statements tENDFOR
|
|
{
|
|
$$ = alloc_stmt(STMT_RANGE);
|
|
$$->s.range.id = find_id($2);
|
|
$$->s.range.from = $5;
|
|
$$->s.range.to = $7;
|
|
$$->s.range.body = $10;
|
|
pop_loop_name();
|
|
}
|
|
| tWHILE '(' expr ')'
|
|
{
|
|
push_loop_name(0);
|
|
}
|
|
statements tENDWHILE
|
|
{
|
|
$$ = alloc_stmt(STMT_WHILE);
|
|
$$->s.loop.id = -1;
|
|
$$->s.loop.condition = $3;
|
|
$$->s.loop.body = $6;
|
|
pop_loop_name();
|
|
}
|
|
| tWHILE tID '(' expr ')'
|
|
{
|
|
push_loop_name($2);
|
|
}
|
|
statements tENDWHILE
|
|
{
|
|
$$ = alloc_stmt(STMT_WHILE);
|
|
$$->s.loop.id = find_id($2);
|
|
$$->s.loop.condition = $4;
|
|
$$->s.loop.body = $7;
|
|
pop_loop_name();
|
|
}
|
|
| tFORK '(' expr ')'
|
|
{
|
|
suspend_loop_scope();
|
|
}
|
|
statements tENDFORK
|
|
{
|
|
$$ = alloc_stmt(STMT_FORK);
|
|
$$->s.fork.id = -1;
|
|
$$->s.fork.time = $3;
|
|
$$->s.fork.body = $6;
|
|
resume_loop_scope();
|
|
}
|
|
| tFORK tID '(' expr ')'
|
|
{
|
|
suspend_loop_scope();
|
|
}
|
|
statements tENDFORK
|
|
{
|
|
$$ = alloc_stmt(STMT_FORK);
|
|
$$->s.fork.id = find_id($2);
|
|
$$->s.fork.time = $4;
|
|
$$->s.fork.body = $7;
|
|
resume_loop_scope();
|
|
}
|
|
| expr ';'
|
|
{
|
|
$$ = alloc_stmt(STMT_EXPR);
|
|
$$->s.expr = $1;
|
|
}
|
|
| tBREAK ';'
|
|
{
|
|
check_loop_name(0, LOOP_BREAK);
|
|
$$ = alloc_stmt(STMT_BREAK);
|
|
$$->s.exit = -1;
|
|
}
|
|
| tBREAK tID ';'
|
|
{
|
|
check_loop_name($2, LOOP_BREAK);
|
|
$$ = alloc_stmt(STMT_BREAK);
|
|
$$->s.exit = find_id($2);
|
|
}
|
|
| tCONTINUE ';'
|
|
{
|
|
check_loop_name(0, LOOP_CONTINUE);
|
|
$$ = alloc_stmt(STMT_CONTINUE);
|
|
$$->s.exit = -1;
|
|
}
|
|
| tCONTINUE tID ';'
|
|
{
|
|
check_loop_name($2, LOOP_CONTINUE);
|
|
$$ = alloc_stmt(STMT_CONTINUE);
|
|
$$->s.exit = find_id($2);
|
|
}
|
|
| tRETURN expr ';'
|
|
{
|
|
$$ = alloc_stmt(STMT_RETURN);
|
|
$$->s.expr = $2;
|
|
}
|
|
| tRETURN ';'
|
|
{
|
|
$$ = alloc_stmt(STMT_RETURN);
|
|
$$->s.expr = 0;
|
|
}
|
|
| ';'
|
|
{ $$ = 0; }
|
|
| tTRY statements excepts tENDTRY
|
|
{
|
|
$$ = alloc_stmt(STMT_TRY_EXCEPT);
|
|
$$->s.catch.body = $2;
|
|
$$->s.catch.excepts = $3;
|
|
}
|
|
| tTRY statements tFINALLY statements tENDTRY
|
|
{
|
|
$$ = alloc_stmt(STMT_TRY_FINALLY);
|
|
$$->s.finally.body = $2;
|
|
$$->s.finally.handler = $4;
|
|
}
|
|
;
|
|
|
|
elseifs:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| elseifs tELSEIF '(' expr ')' statements
|
|
{
|
|
Cond_Arm *this_arm = alloc_cond_arm($4, $6);
|
|
|
|
if ($1) {
|
|
Cond_Arm *tmp = $1;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = this_arm;
|
|
$$ = $1;
|
|
} else
|
|
$$ = this_arm;
|
|
}
|
|
;
|
|
|
|
elsepart:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| tELSE statements
|
|
{ $$ = $2; }
|
|
;
|
|
|
|
excepts:
|
|
tEXCEPT except
|
|
{ $$ = $2; }
|
|
| excepts tEXCEPT
|
|
{
|
|
Except_Arm *tmp = $1;
|
|
int count = 1;
|
|
|
|
while (tmp->next) {
|
|
tmp = tmp->next;
|
|
count++;
|
|
}
|
|
if (!tmp->codes)
|
|
yyerror(_("Unreachable EXCEPT clause"));
|
|
else if (count > 255)
|
|
yyerror(_("Too many EXCEPT clauses (max. 255)"));
|
|
}
|
|
except
|
|
{
|
|
Except_Arm *tmp = $1;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
|
|
tmp->next = $4;
|
|
$$ = $1;
|
|
}
|
|
;
|
|
except:
|
|
opt_id '(' codes ')' statements
|
|
{ $$ = alloc_except($1 ? find_id($1) : -1, $3, $5); }
|
|
;
|
|
|
|
opt_id:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| tID
|
|
{ $$ = $1; }
|
|
;
|
|
|
|
expr:
|
|
tINTEGER
|
|
{
|
|
$$ = alloc_var(TYPE_INT);
|
|
$$->e.var.v.num = $1;
|
|
}
|
|
| tFLOAT
|
|
{
|
|
$$ = alloc_var(TYPE_FLOAT);
|
|
$$->e.var.v.fnum = $1;
|
|
}
|
|
| tSTRING
|
|
{
|
|
$$ = alloc_var(TYPE_STR);
|
|
$$->e.var.v.str = $1;
|
|
}
|
|
| tOBJECT
|
|
{
|
|
$$ = alloc_var(TYPE_OBJ);
|
|
$$->e.var.v.obj = $1;
|
|
}
|
|
| tERROR
|
|
{
|
|
$$ = alloc_var(TYPE_ERR);
|
|
$$->e.var.v.err = $1;
|
|
}
|
|
| tID
|
|
{
|
|
$$ = alloc_expr(EXPR_ID);
|
|
$$->e.id = find_id($1);
|
|
}
|
|
| '$' tID
|
|
{
|
|
/* Treat $foo like #0.("foo") */
|
|
Expr *obj = alloc_var(TYPE_OBJ);
|
|
Expr *prop = alloc_var(TYPE_STR);
|
|
obj->e.var.v.obj = 0;
|
|
prop->e.var.v.str = $2;
|
|
$$ = alloc_binary(EXPR_PROP, obj, prop);
|
|
}
|
|
| expr '.' tID
|
|
{
|
|
/* Treat foo.bar like foo.("bar") for simplicity */
|
|
Expr *prop = alloc_var(TYPE_STR);
|
|
prop->e.var.v.str = $3;
|
|
$$ = alloc_binary(EXPR_PROP, $1, prop);
|
|
}
|
|
| expr '.' '(' expr ')'
|
|
{
|
|
$$ = alloc_binary(EXPR_PROP, $1, $4);
|
|
}
|
|
| expr ':' tID '(' arglist ')'
|
|
{
|
|
/* treat foo:bar(args) like foo:("bar")(args) */
|
|
Expr *verb = alloc_var(TYPE_STR);
|
|
verb->e.var.v.str = $3;
|
|
$$ = alloc_verb($1, verb, $5);
|
|
}
|
|
| '$' tID '(' arglist ')'
|
|
{
|
|
/* treat $bar(args) like #0:("bar")(args) */
|
|
Expr *obj = alloc_var(TYPE_OBJ);
|
|
Expr *verb = alloc_var(TYPE_STR);
|
|
obj->e.var.v.obj = 0;
|
|
verb->e.var.v.str = $2;
|
|
$$ = alloc_verb(obj, verb, $4);
|
|
}
|
|
| expr ':' '(' expr ')' '(' arglist ')'
|
|
{
|
|
$$ = alloc_verb($1, $4, $7);
|
|
}
|
|
| expr '[' dollars_up expr ']'
|
|
{
|
|
dollars_ok--;
|
|
$$ = alloc_binary(EXPR_INDEX, $1, $4);
|
|
}
|
|
| expr '[' dollars_up expr tTO expr ']'
|
|
{
|
|
dollars_ok--;
|
|
$$ = alloc_expr(EXPR_RANGE);
|
|
$$->e.range.base = $1;
|
|
$$->e.range.from = $4;
|
|
$$->e.range.to = $6;
|
|
}
|
|
| '$'
|
|
{
|
|
if (!dollars_ok)
|
|
yyerror(_("Illegal context for `$' expression."));
|
|
$$ = alloc_expr(EXPR_LENGTH);
|
|
}
|
|
| expr '=' expr
|
|
{
|
|
Expr *e = $1;
|
|
|
|
if (e->kind == EXPR_LIST) {
|
|
e->kind = EXPR_SCATTER;
|
|
if (e->e.list) {
|
|
e->e.scatter = scatter_from_arglist(e->e.list);
|
|
vet_scatter(e->e.scatter);
|
|
} else
|
|
yyerror(_("Empty list in scattering assignment."));
|
|
} else {
|
|
if (e->kind == EXPR_RANGE)
|
|
e = e->e.range.base;
|
|
while (e->kind == EXPR_INDEX)
|
|
e = e->e.bin.lhs;
|
|
if (e->kind != EXPR_ID && e->kind != EXPR_PROP)
|
|
yyerror(_("Illegal expression on left side of"
|
|
" assignment."));
|
|
}
|
|
$$ = alloc_binary(EXPR_ASGN, $1, $3);
|
|
}
|
|
| '{' scatter '}' '=' expr
|
|
{
|
|
Expr *e = alloc_expr(EXPR_SCATTER);
|
|
|
|
e->e.scatter = $2;
|
|
vet_scatter($2);
|
|
$$ = alloc_binary(EXPR_ASGN, e, $5);
|
|
}
|
|
| tID '(' arglist ')'
|
|
{
|
|
unsigned f_no;
|
|
|
|
$$ = alloc_expr(EXPR_CALL);
|
|
if ((f_no = number_func_by_name($1)) == FUNC_NOT_FOUND) {
|
|
/* Replace with call_function("$1", @args) */
|
|
Expr *fname = alloc_var(TYPE_STR);
|
|
Arg_List *a = alloc_arg_list(ARG_NORMAL, fname);
|
|
|
|
fname->e.var.v.str = $1;
|
|
a->next = $3;
|
|
warning(_("Unknown built-in function: "), $1);
|
|
$$->e.call.func = number_func_by_name("call_function");
|
|
$$->e.call.args = a;
|
|
} else {
|
|
$$->e.call.func = f_no;
|
|
$$->e.call.args = $3;
|
|
dealloc_string($1);
|
|
}
|
|
}
|
|
| expr '+' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_PLUS, $1, $3);
|
|
}
|
|
| expr '-' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_MINUS, $1, $3);
|
|
}
|
|
| expr '*' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_TIMES, $1, $3);
|
|
}
|
|
| expr '/' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_DIVIDE, $1, $3);
|
|
}
|
|
| expr '%' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_MOD, $1, $3);
|
|
}
|
|
| expr '^' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_EXP, $1, $3);
|
|
}
|
|
| expr tAND expr
|
|
{
|
|
$$ = alloc_binary(EXPR_AND, $1, $3);
|
|
}
|
|
| expr tOR expr
|
|
{
|
|
$$ = alloc_binary(EXPR_OR, $1, $3);
|
|
}
|
|
| expr tEQ expr
|
|
{
|
|
$$ = alloc_binary(EXPR_EQ, $1, $3);
|
|
}
|
|
| expr tNE expr
|
|
{
|
|
$$ = alloc_binary(EXPR_NE, $1, $3);
|
|
}
|
|
| expr '<' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_LT, $1, $3);
|
|
}
|
|
| expr tLE expr
|
|
{
|
|
$$ = alloc_binary(EXPR_LE, $1, $3);
|
|
}
|
|
| expr '>' expr
|
|
{
|
|
$$ = alloc_binary(EXPR_GT, $1, $3);
|
|
}
|
|
| expr tGE expr
|
|
{
|
|
$$ = alloc_binary(EXPR_GE, $1, $3);
|
|
}
|
|
| expr tIN expr
|
|
{
|
|
$$ = alloc_binary(EXPR_IN, $1, $3);
|
|
}
|
|
| '-' expr %prec tUNARYMINUS
|
|
{
|
|
if ($2->kind == EXPR_VAR
|
|
&& ($2->e.var.type == TYPE_INT
|
|
|| $2->e.var.type == TYPE_FLOAT)) {
|
|
switch ($2->e.var.type) {
|
|
case TYPE_INT:
|
|
$2->e.var.v.num = -$2->e.var.v.num;
|
|
break;
|
|
case TYPE_FLOAT:
|
|
*($2->e.var.v.fnum) = - (*($2->e.var.v.fnum));
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
$$ = $2;
|
|
} else {
|
|
$$ = alloc_expr(EXPR_NEGATE);
|
|
$$->e.expr = $2;
|
|
}
|
|
}
|
|
| '!' expr
|
|
{
|
|
$$ = alloc_expr(EXPR_NOT);
|
|
$$->e.expr = $2;
|
|
}
|
|
| '(' expr ')'
|
|
{ $$ = $2; }
|
|
| '{' arglist '}'
|
|
{
|
|
$$ = alloc_expr(EXPR_LIST);
|
|
$$->e.list = $2;
|
|
}
|
|
| expr '?' expr '|' expr
|
|
{
|
|
$$ = alloc_expr(EXPR_COND);
|
|
$$->e.cond.condition = $1;
|
|
$$->e.cond.consequent = $3;
|
|
$$->e.cond.alternate = $5;
|
|
}
|
|
| '`' expr '!' codes default '\''
|
|
{
|
|
$$ = alloc_expr(EXPR_CATCH);
|
|
$$->e.catch.try = $2;
|
|
$$->e.catch.codes = $4;
|
|
$$->e.catch.except = $5;
|
|
}
|
|
;
|
|
|
|
dollars_up:
|
|
/* NOTHING */
|
|
{ dollars_ok++; }
|
|
;
|
|
codes:
|
|
tANY
|
|
{ $$ = 0; }
|
|
| ne_arglist
|
|
{ $$ = $1; }
|
|
;
|
|
default:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| tARROW expr
|
|
{ $$ = $2; }
|
|
;
|
|
arglist:
|
|
/* NOTHING */
|
|
{ $$ = 0; }
|
|
| ne_arglist
|
|
{ $$ = $1; }
|
|
;
|
|
|
|
ne_arglist:
|
|
expr
|
|
{ $$ = alloc_arg_list(ARG_NORMAL, $1); }
|
|
| '@' expr
|
|
{ $$ = alloc_arg_list(ARG_SPLICE, $2); }
|
|
| ne_arglist ',' expr
|
|
{
|
|
Arg_List *this_arg = alloc_arg_list(ARG_NORMAL, $3);
|
|
|
|
if ($1) {
|
|
Arg_List *tmp = $1;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = this_arg;
|
|
$$ = $1;
|
|
} else
|
|
$$ = this_arg;
|
|
}
|
|
| ne_arglist ',' '@' expr
|
|
{
|
|
Arg_List *this_arg = alloc_arg_list(ARG_SPLICE, $4);
|
|
|
|
if ($1) {
|
|
Arg_List *tmp = $1;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = this_arg;
|
|
$$ = $1;
|
|
} else
|
|
$$ = this_arg;
|
|
}
|
|
;
|
|
|
|
scatter:
|
|
ne_arglist ',' scatter_item
|
|
{
|
|
Scatter *sc = scatter_from_arglist($1);
|
|
|
|
if (sc)
|
|
$$ = add_scatter_item(sc, $3);
|
|
else
|
|
$$ = $3;
|
|
}
|
|
| scatter ',' scatter_item
|
|
{
|
|
$$ = add_scatter_item($1, $3);
|
|
}
|
|
| scatter ',' tID
|
|
{
|
|
$$ = add_scatter_item($1, alloc_scatter(SCAT_REQUIRED,
|
|
find_id($3), 0));
|
|
}
|
|
| scatter ',' '@' tID
|
|
{
|
|
$$ = add_scatter_item($1, alloc_scatter(SCAT_REST,
|
|
find_id($4), 0));
|
|
}
|
|
| scatter_item
|
|
{ $$ = $1; }
|
|
;
|
|
|
|
scatter_item:
|
|
'?' tID
|
|
{
|
|
$$ = alloc_scatter(SCAT_OPTIONAL, find_id($2), 0);
|
|
}
|
|
| '?' tID '=' expr
|
|
{
|
|
$$ = alloc_scatter(SCAT_OPTIONAL, find_id($2), $4);
|
|
}
|
|
;
|
|
|
|
%%
|
|
|
|
static int lineno, nerrors, must_rename_keywords;
|
|
static Parser_Client client;
|
|
static void *client_data;
|
|
static Names *local_names;
|
|
|
|
static int
|
|
find_id(char *name)
|
|
{
|
|
int slot = find_or_add_name(&local_names, name);
|
|
|
|
dealloc_string(name);
|
|
return slot;
|
|
}
|
|
|
|
static void
|
|
yyerror(const char *s)
|
|
{
|
|
error(s, 0);
|
|
}
|
|
|
|
static const char *
|
|
fmt_error(const char *s, const char *t)
|
|
{
|
|
static Stream *str = 0;
|
|
|
|
if (str == 0)
|
|
str = new_stream(100);
|
|
if (t)
|
|
stream_printf(str, "%s%s", s, t);
|
|
else
|
|
stream_printf(str, "%s", s);
|
|
return reset_stream(str);
|
|
}
|
|
|
|
static void
|
|
error(const char *s, const char *t)
|
|
{
|
|
nerrors++;
|
|
(*(client.error))(client_data, fmt_error(s, t));
|
|
}
|
|
|
|
static void
|
|
warning(const char *s, const char *t)
|
|
{
|
|
if (client.warning)
|
|
(*(client.warning))(client_data, fmt_error(s, t));
|
|
else
|
|
error(s, t);
|
|
}
|
|
|
|
static int unget_buffer[5], unget_count;
|
|
|
|
static int
|
|
lex_getc(void)
|
|
{
|
|
if (unget_count > 0)
|
|
return unget_buffer[--unget_count];
|
|
else
|
|
return (*(client.getch))(client_data);
|
|
}
|
|
|
|
static void
|
|
lex_ungetc(int c)
|
|
{
|
|
unget_buffer[unget_count++] = c;
|
|
}
|
|
|
|
static int
|
|
follow(int expect, int ifyes, int ifno) /* look ahead for >=, etc. */
|
|
{
|
|
int c = lex_getc();
|
|
|
|
if (c == expect)
|
|
return ifyes;
|
|
lex_ungetc(c);
|
|
return ifno;
|
|
}
|
|
|
|
static Stream *token_stream = 0;
|
|
|
|
static int
|
|
yylex(void)
|
|
{
|
|
int c;
|
|
|
|
reset_stream(token_stream);
|
|
|
|
start_over:
|
|
|
|
do {
|
|
c = lex_getc();
|
|
if (c == '\n') lineno++;
|
|
} while (isspace(c));
|
|
|
|
if (c == '/') {
|
|
c = lex_getc();
|
|
if (c == '*') {
|
|
for (;;) {
|
|
c = lex_getc();
|
|
if (c == '*') {
|
|
c = lex_getc();
|
|
if (c == '/')
|
|
goto start_over;
|
|
}
|
|
if (c == EOF) {
|
|
yyerror(_("End of program while in a comment"));
|
|
return c;
|
|
}
|
|
}
|
|
} else {
|
|
lex_ungetc(c);
|
|
return '/';
|
|
}
|
|
}
|
|
|
|
if (c == '#') {
|
|
int negative = 0;
|
|
Objid oid = 0;
|
|
|
|
c = lex_getc();
|
|
if (c == '-') {
|
|
negative = 1;
|
|
c = lex_getc();
|
|
}
|
|
if (!isdigit(c)) {
|
|
yyerror(_("Malformed object number"));
|
|
lex_ungetc(c);
|
|
return 0;
|
|
}
|
|
do {
|
|
oid = oid * 10 + (c - '0');
|
|
c = lex_getc();
|
|
} while (isdigit(c));
|
|
lex_ungetc(c);
|
|
|
|
yylval.object = negative ? -oid : oid;
|
|
return tOBJECT;
|
|
}
|
|
|
|
if (isdigit(c) || (c == '.' && language_version >= DBV_Float)) {
|
|
int n = 0;
|
|
int type = tINTEGER;
|
|
|
|
while (isdigit(c)) {
|
|
n = n * 10 + (c - '0');
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
}
|
|
|
|
if (language_version >= DBV_Float && c == '.') {
|
|
/* maybe floating-point (but maybe `..') */
|
|
int cc;
|
|
|
|
lex_ungetc(cc = lex_getc()); /* peek ahead */
|
|
if (isdigit(cc)) { /* definitely floating-point */
|
|
type = tFLOAT;
|
|
do {
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
} while (isdigit(c));
|
|
} else if (stream_length(token_stream) == 0)
|
|
/* no digits before or after `.'; not a number at all */
|
|
goto normal_dot;
|
|
else if (cc != '.') {
|
|
/* Some digits before dot, not `..' */
|
|
type = tFLOAT;
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
}
|
|
}
|
|
|
|
if (language_version >= DBV_Float && (c == 'e' || c == 'E')) {
|
|
/* better be an exponent */
|
|
type = tFLOAT;
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
if (c == '+' || c == '-') {
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
}
|
|
if (!isdigit(c)) {
|
|
yyerror(_("Malformed floating-point literal"));
|
|
lex_ungetc(c);
|
|
return 0;
|
|
}
|
|
do {
|
|
stream_add_char(token_stream, c);
|
|
c = lex_getc();
|
|
} while (isdigit(c));
|
|
}
|
|
|
|
lex_ungetc(c);
|
|
|
|
if (type == tINTEGER)
|
|
yylval.integer = n;
|
|
else {
|
|
double d;
|
|
|
|
d = strtod(reset_stream(token_stream), 0);
|
|
if (!IS_REAL(d)) {
|
|
yyerror(_("Floating-point literal out of range"));
|
|
d = 0.0;
|
|
}
|
|
yylval.real = alloc_float(d);
|
|
}
|
|
return type;
|
|
}
|
|
|
|
if (isalpha(c) || c == '_') {
|
|
char *buf;
|
|
Keyword *k;
|
|
|
|
stream_add_char(token_stream, c);
|
|
while (isalnum(c = lex_getc()) || c == '_')
|
|
stream_add_char(token_stream, c);
|
|
lex_ungetc(c);
|
|
buf = reset_stream(token_stream);
|
|
|
|
k = find_keyword(buf);
|
|
if (k) {
|
|
if (k->version <= language_version) {
|
|
int t = k->token;
|
|
|
|
if (t == tERROR)
|
|
yylval.error = k->error;
|
|
return t;
|
|
} else { /* New keyword being used as an identifier */
|
|
if (!must_rename_keywords)
|
|
warning(_("Renaming old use of new keyword: "), buf);
|
|
must_rename_keywords = 1;
|
|
}
|
|
}
|
|
|
|
yylval.string = alloc_string(buf);
|
|
return tID;
|
|
}
|
|
|
|
if (c == '"') {
|
|
while(1) {
|
|
c = lex_getc();
|
|
if (c == '"')
|
|
break;
|
|
if (c == '\\')
|
|
c = lex_getc();
|
|
if (c == '\n' || c == EOF) {
|
|
yyerror(_("Missing quote"));
|
|
break;
|
|
}
|
|
stream_add_char(token_stream, c);
|
|
}
|
|
yylval.string = alloc_string(reset_stream(token_stream));
|
|
return tSTRING;
|
|
}
|
|
|
|
switch(c) {
|
|
case '>': return follow('=', tGE, '>');
|
|
case '<': return follow('=', tLE, '<');
|
|
case '=': return ((c = follow('=', tEQ, 0))
|
|
? c
|
|
: follow('>', tARROW, '='));
|
|
case '!': return follow('=', tNE, '!');
|
|
case '|': return follow('|', tOR, '|');
|
|
case '&': return follow('&', tAND, '&');
|
|
normal_dot:
|
|
case '.': return follow('.', tTO, '.');
|
|
default: return c;
|
|
}
|
|
}
|
|
|
|
static Scatter *
|
|
add_scatter_item(Scatter *first, Scatter *last)
|
|
{
|
|
Scatter *tmp = first;
|
|
|
|
while (tmp->next)
|
|
tmp = tmp->next;
|
|
tmp->next = last;
|
|
|
|
return first;
|
|
}
|
|
|
|
static Scatter *
|
|
scatter_from_arglist(Arg_List *a)
|
|
{
|
|
Scatter *sc = 0, **scp;
|
|
Arg_List *anext;
|
|
|
|
for (scp = ≻ a; a = anext, scp = &((*scp)->next)) {
|
|
if (a->expr->kind == EXPR_ID) {
|
|
*scp = alloc_scatter(a->kind == ARG_NORMAL ? SCAT_REQUIRED
|
|
: SCAT_REST,
|
|
a->expr->e.id, 0);
|
|
anext = a->next;
|
|
dealloc_node(a->expr);
|
|
dealloc_node(a);
|
|
} else {
|
|
yyerror(_("Scattering assignment targets must be simple variables."));
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
return sc;
|
|
}
|
|
|
|
static void
|
|
vet_scatter(Scatter *sc)
|
|
{
|
|
int seen_rest = 0, count = 0;
|
|
|
|
for (; sc; sc = sc->next) {
|
|
if (sc->kind == SCAT_REST) {
|
|
if (seen_rest)
|
|
yyerror(_("More than one `@' target in scattering assignment."));
|
|
else
|
|
seen_rest = 1;
|
|
}
|
|
count++;
|
|
}
|
|
|
|
if (count > 255)
|
|
yyerror(_("Too many targets in scattering assignment."));
|
|
}
|
|
|
|
struct loop_entry {
|
|
struct loop_entry *next;
|
|
const char *name;
|
|
int is_barrier;
|
|
};
|
|
|
|
static struct loop_entry *loop_stack;
|
|
|
|
static void
|
|
push_loop_name(const char *name)
|
|
{
|
|
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;
|
|
loop_stack = entry;
|
|
}
|
|
|
|
static void
|
|
pop_loop_name(void)
|
|
{
|
|
if (loop_stack && loop_stack->is_barrier) {
|
|
struct loop_entry *entry = loop_stack;
|
|
|
|
loop_stack = loop_stack->next;
|
|
if (entry->name)
|
|
free_str(entry->name);
|
|
myfree(entry, M_AST);
|
|
}
|
|
}
|
|
|
|
static void
|
|
suspend_loop_scope(void)
|
|
{
|
|
struct loop_entry *entry = mymalloc(sizeof(struct loop_entry), M_AST);
|
|
|
|
entry->next = loop_stack;
|
|
entry->name = 0;
|
|
entry->is_barrier = 1;
|
|
loop_stack = entry;
|
|
}
|
|
|
|
static void
|
|
resume_loop_scope(void)
|
|
{
|
|
if (loop_stack && loop_stack->is_barrier){
|
|
struct loop_entry *entry = loop_stack;
|
|
|
|
loop_stack = loop_stack->next;
|
|
myfree(entry, M_AST);
|
|
}
|
|
}
|
|
|
|
static void
|
|
check_loop_name(const char *name, enum loop_exit_kind kind)
|
|
{
|
|
struct loop_entry *entry;
|
|
|
|
if (!name) {
|
|
if (!loop_stack || loop_stack->is_barrier) {
|
|
if (kind == LOOP_BREAK)
|
|
yyerror(_("No enclosing loop for `break' statement"));
|
|
else
|
|
yyerror(_("No enclosing loop for `continue' statement"));
|
|
}
|
|
return;
|
|
}
|
|
|
|
for (entry = loop_stack; entry && !entry->is_barrier; entry = entry->next)
|
|
if (entry->name && mystrcasecmp(entry->name, name) == 0)
|
|
return;
|
|
|
|
if (kind == LOOP_BREAK)
|
|
error("Invalid loop name in `break' statement: ", name);
|
|
else
|
|
error("Invalid loop name in `continue' statement: ", name);
|
|
}
|
|
|
|
void
|
|
parse_program(DB_Version version, Parser_Client c, void *data)
|
|
{
|
|
extern int yyparse();
|
|
|
|
if (token_stream == 0)
|
|
token_stream = new_stream(1024);
|
|
unget_count = 0;
|
|
nerrors = 0;
|
|
must_rename_keywords = 0;
|
|
lineno = 1;
|
|
client = c;
|
|
client_data = data;
|
|
local_names = new_builtin_names(version);
|
|
dollars_ok = 0;
|
|
loop_stack = 0;
|
|
language_version = version;
|
|
|
|
register_bi_functions();
|
|
|
|
begin_code_allocation();
|
|
yyparse();
|
|
end_code_allocation(nerrors > 0);
|
|
if (loop_stack) {
|
|
while (loop_stack) {
|
|
struct loop_entry *entry = loop_stack;
|
|
|
|
loop_stack = loop_stack->next;
|
|
if (entry->name)
|
|
free_str(entry->name);
|
|
myfree(entry, M_AST);
|
|
}
|
|
}
|
|
}
|
|
|
|
struct parser_state {
|
|
Var code; /* a list of strings */
|
|
int cur_string; /* which string? */
|
|
int cur_char; /* which character in that string? */
|
|
Var errors; /* a list of strings */
|
|
};
|
|
|
|
static void
|
|
my_error(void *data, const char *msg)
|
|
{
|
|
struct parser_state *state = (struct parser_state *) data;
|
|
Var v;
|
|
|
|
v.type = TYPE_STR;
|
|
v.v.str = str_dup(msg);
|
|
state->errors = listappend(state->errors, v);
|
|
}
|
|
|
|
static int
|
|
my_getc(void *data)
|
|
{
|
|
struct parser_state *state = (struct parser_state *) data;
|
|
Var code;
|
|
unsigned char c;
|
|
|
|
code = state->code;
|
|
if (state->cur_string > code.v.list[0].v.num)
|
|
return EOF;
|
|
else if (!(c = code.v.list[state->cur_string].v.str[state->cur_char])) {
|
|
state->cur_string++;
|
|
state->cur_char = 0;
|
|
return '\n';
|
|
} else {
|
|
state->cur_char++;
|
|
return c;
|
|
}
|
|
}
|
|
|
|
static Parser_Client list_parser_client = { my_error, 0, my_getc };
|
|
|
|
void
|
|
parse_list_as_program(Var code, Var *errors)
|
|
{
|
|
struct parser_state state;
|
|
state.code = code;
|
|
state.cur_string = 1;
|
|
state.cur_char = 0;
|
|
state.errors = new_list(0);
|
|
parse_program(current_version, list_parser_client, &state);
|
|
*errors = state.errors;
|
|
}
|
|
|
|
char rcsid_parser[] = "$Id: parser.y,v 1.1.1.1 2004/02/26 13:13:58 jesse Exp $";
|
|
|
|
/*
|
|
* $Log: parser.y,v $
|
|
* Revision 1.1.1.1 2004/02/26 13:13:58 jesse
|
|
* Initial import into CVS
|
|
*
|
|
* Revision 1.8.1fa 2004/01/02
|
|
* my_getc, changed char c to unsigned;
|
|
*
|
|
* Revision 1.2 1998/12/14 13:18:45 nop
|
|
* Merge UNSAFE_OPTS (ref fixups); fix Log tag placement to fit CVS whims
|
|
*
|
|
* Revision 1.1.1.1 1997/03/03 03:45:02 nop
|
|
* LambdaMOO 1.8.0p5
|
|
*
|
|
* Revision 2.11 1996/05/12 21:28:37 pavel
|
|
* Removed non-backward-compatible parse error for negating a non-numeric
|
|
* literal. Made certain aspects of floating-point literal parsing be
|
|
* language-version-dependent, to maintain backward compatibility with DBs
|
|
* written by pre-float servers. Release 1.8.0p5.
|
|
*
|
|
* Revision 2.10 1996/04/19 01:26:38 pavel
|
|
* Fixed potential memory smash when `$' expression occurs in an illegal
|
|
* context. Release 1.8.0p4.
|
|
*
|
|
* Revision 2.9 1996/03/10 01:08:01 pavel
|
|
* Removed bogus `call_function("foo", ...)' --> `foo(...)' conversion.
|
|
* Fixed line numbers of renamed-variable warnings. Release 1.8.0.
|
|
*
|
|
* Revision 2.8 1996/02/18 23:14:54 pavel
|
|
* Liberalized parsing of floating-point numbers to accept `3.' and `3.e1'.
|
|
* Changed parsing to replace call_function("foo", @args) with foo(@args) if
|
|
* foo is a known function. Release 1.8.0beta3.
|
|
*
|
|
* Revision 2.7 1996/02/11 00:46:01 pavel
|
|
* Liberalized parsing of floating-point literals to accept `.02' and the
|
|
* like. Fixed a bug where `1E6' and the like would be parsed as an integer.
|
|
* Release 1.8.0beta2.
|
|
*
|
|
* Revision 2.6 1996/02/08 06:32:48 pavel
|
|
* Added support for floating-point literals, exponentiation expression, named
|
|
* WHILE loops, and BREAK and CONTINUE statements. Generalized support for
|
|
* version-dependent compilation. Renamed TYPE_NUM to TYPE_INT. Removed use
|
|
* of ungetch() method on Parser_Client. Updated copyright notice for 1996.
|
|
* Release 1.8.0beta1.
|
|
*
|
|
* Revision 2.5 1996/01/16 07:17:05 pavel
|
|
* Add support for scattering assignment. Release 1.8.0alpha6.
|
|
*
|
|
* Revision 2.4 1996/01/11 07:50:49 pavel
|
|
* Added missing #include "my-string.h". Release 1.8.0alpha5.
|
|
*
|
|
* Revision 2.3 1995/12/31 00:09:40 pavel
|
|
* Removed unused tokens tRAISE and tFORLIST (huh?). Added `$' expression.
|
|
* Release 1.8.0alpha4.
|
|
*
|
|
* Revision 2.2 1995/12/28 00:43:25 pavel
|
|
* Added some support for MOO-compilation warnings. Changed `unknown built-in
|
|
* function' error into a warning with recovery. Changed identifier-renaming
|
|
* log message into a warning. Release 1.8.0alpha3.
|
|
*
|
|
* Revision 2.1 1995/12/11 08:01:26 pavel
|
|
* Added support for `$foo(...)' syntax. Release 1.8.0alpha2.
|
|
*
|
|
* Revision 2.0 1995/11/30 04:48:11 pavel
|
|
* New baseline version, corresponding to release 1.8.0alpha1.
|
|
*
|
|
* Revision 1.13 1992/10/23 23:03:47 pavel
|
|
* Added copyright notice.
|
|
*
|
|
* Revision 1.12 1992/10/23 22:21:43 pavel
|
|
* Eliminated all uses of the useless macro NULL.
|
|
*
|
|
* Revision 1.11 1992/10/21 03:02:35 pavel
|
|
* Converted to use new automatic configuration system.
|
|
*
|
|
* Revision 1.10 1992/08/31 22:25:51 pjames
|
|
* Changed some `char *'s to `const char *'
|
|
*
|
|
* Revision 1.9 1992/08/28 23:18:38 pjames
|
|
* Added ASGN_RANGE parsing code. Fixed tiny memory leak.
|
|
*
|
|
* Revision 1.8 1992/08/28 16:16:52 pjames
|
|
* Changed `ak_dealloc_string()' to `free_str()'.
|
|
*
|
|
* Revision 1.7 1992/08/12 01:49:58 pjames
|
|
* Negative literals are now accepted (instead of negated positive literals).
|
|
*
|
|
* Revision 1.6 1992/08/10 16:55:59 pjames
|
|
* Updated #includes.
|
|
*
|
|
* Revision 1.5 1992/07/30 21:22:08 pjames
|
|
* Removed max_stack calculations (moved to vector.c).
|
|
*
|
|
* Revision 1.4 1992/07/29 18:05:12 pjames
|
|
* $$ no longer = NULL when there is an illegal left hand side of an
|
|
* expression.
|
|
*
|
|
* Revision 1.3 1992/07/27 18:12:41 pjames
|
|
* Changed name of ct_env to var_names and const_env to literals.
|
|
* Modified call to vectorize to use new argument format.
|
|
* Assignment statements now check left hand side properly for allowable
|
|
* expressions, and now frees memory from reused expressions.
|
|
*
|
|
* Revision 1.2 1992/07/21 00:05:16 pavel
|
|
* Added rcsid_<filename-root> declaration to hold the RCS ident. string.
|
|
*
|
|
* Revision 1.1 1992/07/20 23:23:12 pavel
|
|
* Initial RCS-controlled version.
|
|
*/
|