Introduce basic methodmaps (PR #38).

commit 1e5213d43fdd170bb0c30af914a4e40610014b2b
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 04:09:27 2014 -0700

    Quell MSVC C99 bugs.

commit f2e166c5925fda49b5abeadc0aa0f9156b99cf11
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 03:59:23 2014 -0700

    Fix varying levels of stupid memory errors.

commit b0773d7be45345351ab1c1738681d5215a97f3f3
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 03:36:39 2014 -0700

    Fix memory leak in parsing some control flow structures.

commit 5aca55713cfc2dd09c5900132fc4a6be51e3e309
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 03:35:17 2014 -0700

    Fix memory leak in struct parsing.

commit b46ec5cd281b46177e83c4f0a4acac9cc1065c53
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 03:32:03 2014 -0700

    Fix build.

commit 17bbbb9a46bfc00862adca7d3e15369a48e9ac0f
Merge: c083409 2107599
Author: David Anderson <[email protected]>
Date:   Sat Jun 21 01:26:27 2014 -0700

    Merge branch 'master' into methodmaps

commit c083409b569abff13f24d3b8c47f8ff199036840
Author: David Anderson <[email protected]>
Date:   Fri Jun 20 23:49:36 2014 -0700

    Add VS2k13 support.

commit b7993778494d538cb1c1965116030142a7f7765b
Author: David Anderson <[email protected]>
Date:   Fri Jun 20 01:28:08 2014 -0700

    Implement destructors.

commit 1a340dec260d079ed1b79351ed7b50b58a997cea
Author: David Anderson <[email protected]>
Date:   Fri Jun 20 00:08:04 2014 -0700

    Add some tests.

commit 12db52ee64eb009ead9294495e9034c63ab75b09
Author: David Anderson <[email protected]>
Date:   Fri Jun 20 00:05:49 2014 -0700

    Initial implementation of constructors.

commit 074669a658caa2822aa864164b615a244c00a0bc
Author: David Anderson <[email protected]>
Date:   Thu Jun 19 22:42:35 2014 -0700

    Add simple test harness.

commit 27c1e3cf14e1e6c5cf35c80c792cce2744b804d7
Author: David Anderson <[email protected]>
Date:   Thu Jun 19 22:15:42 2014 -0700

    Big refactoring for new syntax.

commit f3c37fdc919e76ee0815e2394cbe8d221f9fc0ca
Author: David Anderson <[email protected]>
Date:   Thu Jun 19 22:12:54 2014 -0700

    Refactor tests for the new syntax.

commit 6211f392f8e722b907474cf380cfac4347e46b8e
Author: David Anderson <[email protected]>
Date:   Wed Jun 18 22:25:48 2014 -0700

    Make lexer tokens an enum.

commit 5210b013756b0b00de3a61c6490685c768ff8cbd
Author: David Anderson <[email protected]>
Date:   Tue Jun 17 06:48:15 2014 -0700

    Add comment.

commit 06688ff4aced14077dd21a9cc1db4c26c7420ff3
Author: David Anderson <[email protected]>
Date:   Tue Jun 17 06:46:10 2014 -0700

    Allow |this| to be a base type of the methodmap.

commit 05cf3682020e0e6d9f47b1a0a6727b9edbfe7622
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 22:11:58 2014 -0700

    Unify duplicate typesymbol checking.

commit 09161bf2691c8c1ed25b9b70fda01c336f21aa0b
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 19:53:36 2014 -0700

    Close loophole that allowed methodmaps for enums.

commit 5bb4aeba89fec47a4de7a7532d27830999d1fcb4
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:50:42 2014 -0700

    Add tests and dbi/handle changes.

commit b9203e2491daec2a8073874d6375949483778d14
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:38:29 2014 -0700

    Ensure methodmap tags are fixed.

commit 878b80fd87a2ea500d3a28ce2d53f616d1efe5e8
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:36:04 2014 -0700

    Implement inheritance.

commit 6ba9e004fbae18ad68056368ddd0affdc78659f1
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:31:00 2014 -0700

    Refactor matchtag() to not be insane.

commit 4ede6343b0682c6df98fa869153828e92f891bcc
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:20:50 2014 -0700

    Fix indenting.

commit e3ddef8916e3dd5f4ff0fe571d6e1c3acd163352
Author: David Anderson <[email protected]>
Date:   Mon Jun 16 01:20:27 2014 -0700

    Initial prototype.
This commit is contained in:
David Anderson
2014-06-21 04:10:15 -07:00
parent 210759907d
commit 63ad5eff18
47 changed files with 1956 additions and 731 deletions
+498 -20
View File
@@ -1,3 +1,4 @@
/* vim: set sts=2 ts=8 sw=2 tw=99 et: */
/* Pawn compiler
*
* Function and variable definition and declaration, statement parser.
@@ -75,6 +76,7 @@
int pc_anytag = 0;
int pc_functag = 0;
int pc_tag_string = 0;
int pc_tag_void = 0;
static void resetglobals(void);
static void initglobals(void);
@@ -93,6 +95,7 @@ static void declglb(char *firstname,int firsttag,int fpublic,int fstatic,
int stock,int fconst);
static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct);
static int declloc(int fstatic);
static void dodelete();
static void decl_const(int table);
static void declstruct();
static void decl_enum(int table);
@@ -107,7 +110,7 @@ static cell initvector(int ident,int tag,cell size,int fillzero,
static cell init(int ident,int *tag,int *errorfound);
static int getstates(const char *funcname);
static void attachstatelist(symbol *sym, int state_id);
static void funcstub(int fnative);
static symbol *funcstub(int fnative);
static int newfunc(char *firstname,int firsttag,int fpublic,int fstatic,int stock);
static int declargs(symbol *sym,int chkshadow);
static void doarg(char *name,int ident,int offset,int tags[],int numtags,
@@ -137,6 +140,7 @@ static void dogoto(void);
static void dolabel(void);
static void doreturn(void);
static void dofuncenum(int listmode);
static void domethodmap(LayoutSpec spec);
static void dobreak(void);
static void docont(void);
static void dosleep(void);
@@ -345,7 +349,8 @@ int pc_compile(int argc, char *argv[])
#endif
resetglobals();
pstructs_free();
funcenums_free();
funcenums_free();
methodmaps_free();
sc_ctrlchar=sc_ctrlchar_org;
sc_packstr=lcl_packstr;
sc_needsemicolon=lcl_needsemicolon;
@@ -407,6 +412,7 @@ int pc_compile(int argc, char *argv[])
reduce_referrers(&glbtab);
delete_symbols(&glbtab,0,TRUE,FALSE);
funcenums_free();
methodmaps_free();
pstructs_free();
#if !defined NO_DEFINE
delete_substtable();
@@ -529,6 +535,7 @@ cleanup:
delete_sourcefiletable();
delete_dbgstringtable();
funcenums_free();
methodmaps_free();
pstructs_free();
#if !defined NO_DEFINE
delete_substtable();
@@ -628,14 +635,33 @@ static void inst_datetime_defines(void)
insert_subst("__TIME__", ltime, 8);
}
const char *pc_tagname(int tag)
{
constvalue *ptr=tagname_tab.next;
for (; ptr; ptr=ptr->next) {
if ((int)(ptr->value & TAGMASK) == (tag & TAGMASK))
return ptr->name;
}
return "<unknown>";
}
int pc_findtag(const char *name)
{
constvalue *ptr=tagname_tab.next;
for (; ptr; ptr=ptr->next) {
if (strcmp(name,ptr->name)==0)
return (int)(ptr->value & TAGMASK);
}
return -1;
}
#if defined __cplusplus
extern "C"
#endif
int pc_addtag(char *name)
{
cell val;
constvalue *ptr;
int last,tag;
int val;
int flags = 0;
if (name==NULL) {
/* no tagname was given, check for one */
@@ -645,6 +671,17 @@ int pc_addtag(char *name)
} /* if */
} /* if */
if (isupper(*name))
flags |= FIXEDTAG;
return pc_addtag_flags(name, flags);
}
int pc_addtag_flags(char *name, int flags)
{
constvalue *ptr;
int last,tag;
assert(strchr(name,':')==NULL); /* colon should already have been stripped */
last=0;
ptr=tagname_tab.next;
@@ -661,8 +698,7 @@ int pc_addtag(char *name)
/* tagname currently unknown, add it */
tag=last+1; /* guaranteed not to exist already */
if (isupper(*name))
tag |= (int)FIXEDTAG;
tag|=flags;
append_constval(&tagname_tab,name,(cell)tag,0);
return tag;
}
@@ -1326,6 +1362,8 @@ static void setconstants(void)
pc_anytag = pc_addtag("any");
pc_functag = pc_addfunctag("Function");
pc_tag_string = pc_addtag("String");
pc_tag_void = pc_addtag_flags("void", FIXEDTAG);
sc_rationaltag = pc_addtag("Float");
add_constant("true",1,sGLOBAL,1); /* boolean flags */
add_constant("false",0,sGLOBAL,1);
@@ -1488,6 +1526,9 @@ static void parse(void)
case tFUNCENUM:
dofuncenum(TRUE);
break;
case tMETHODMAP:
domethodmap(Layout_MethodMap);
break;
case tFUNCTAG:
dofuncenum(FALSE);
break;
@@ -1826,6 +1867,8 @@ static void declstructvar(char *firstname,int fpublic, pstruct_t *pstruct)
matchtoken(';');
/* Mark it as undefined instead */
mysym->usage = uSTOCK|uSTRUCT;
free(found);
free(values);
return;
} else {
mysym->usage = usage;
@@ -3110,6 +3153,13 @@ static void decl_const(int vclass)
needtoken(tTERM);
}
static void check_struct_name(const char *name)
{
LayoutSpec spec = deduce_layout_spec_by_name(name);
if (!can_redef_layout_spec(spec, Layout_PawnStruct))
error(110, name, layout_spec_name(spec));
}
/*
* declstruct - declare a struct type
*/
@@ -3128,10 +3178,7 @@ static void declstruct(void)
error(93);
}
if (pstructs_find(str) != NULL)
{
error(98);
}
check_struct_name(str);
pstruct = pstructs_add(str);
@@ -3202,13 +3249,423 @@ static void declstruct(void)
}
if (pstructs_addarg(pstruct, &arg) == NULL)
{
error(99, arg.name, pstruct->name);
error(103, arg.name, layout_spec_name(Layout_PawnStruct));
}
} while (matchtoken(','));
needtoken('}');
matchtoken(';'); /* eat up optional semicolon */
}
int parse_typeexpr(declinfo_t *decl, const token_t *first, int flags)
{
token_t tok;
if (first) {
tok = *first;
} else {
lextok(&tok);
}
if (tok.id == tCONST) {
decl->usage |= uCONST;
lextok(&tok);
}
if (tok.id == tLABEL || tok.id == '[')
return FALSE;
switch (tok.id) {
case tINT:
strcpy(decl->type, "int");
decl->tag = 0;
break;
case tCHAR:
strcpy(decl->type, "char");
decl->tag = pc_tag_string;
break;
case tVOID:
strcpy(decl->type, "void");
decl->tag = pc_tag_void;
break;
case tSYMBOL:
strcpy(decl->type, tok.str);
if (strcmp(decl->type, "float") == 0) {
decl->tag = sc_rationaltag;
} else {
decl->tag = pc_findtag(decl->type);
if (decl->tag == sc_rationaltag)
error(98, "Float", "float");
else if (decl->tag == pc_tag_string)
error(98, "String", "char");
else if (decl->tag == 0)
error(98, "_", "int");
}
default:
return FALSE;
}
return TRUE;
}
// Parse a new-style declaration. If the name was already fetched (because we
// didn't have enough lookahead), it can be given ahead of time.
int parse_decl(declinfo_t *decl, const token_t *first, int flags)
{
memset(decl, 0, sizeof(*decl));
if (!parse_typeexpr(decl, first, flags))
return FALSE;
return TRUE;
}
void define_constructor(methodmap_t *map, methodmap_method_t *method)
{
symbol *sym = findglb(map->name, sGLOBAL);
if (sym && sym->ident == iPROXY && sym->parent == method->target)
return;
if (sym) {
const char *type = "<unknown>";
switch (sym->ident) {
case iVARIABLE:
case iARRAY:
case iARRAYCELL:
case iARRAYCHAR:
type = "variable";
break;
case iFUNCTN:
type = "function";
break;
}
error(113, map->name, type);
return;
}
sym = addsym(map->name, 0, iPROXY, sGLOBAL, 0, 0);
sym->target = method->target;
}
methodmap_method_t *parse_method(methodmap_t *map)
{
int is_dtor = 0;
int is_bind = 0;
int is_native = 0;
const char *spectype = layout_spec_name(map->spec);
// We keep a wider buffer since we do name munging.
char ident[sNAMEMAX * 3 + 1] = "<unknown>";
char bindname[sNAMEMAX * 3 + 1] = "<unknown>";
needtoken(tPUBLIC);
token_t tok;
declinfo_t decl;
if (matchtoken('~')) {
// We got something like "public ~Blah = X"
is_bind = 1;
is_dtor = 1;
if (needtoken(tSYMBOL)) {
tokeninfo(&tok.val, &tok.str);
strcpy(ident, tok.str);
}
needtoken('(');
needtoken(')');
needtoken('=');
if (!expecttoken(tSYMBOL, &tok))
return NULL;
strcpy(bindname, tok.str);
} else {
is_native = matchtoken(tNATIVE);
if (is_native) {
// If we have a native, we should always get a type expression next.
parse_decl(&decl, NULL, 0);
} else {
// Parsing "public Clone =" and "public Handle Clone = " requires two tokens
// of lookahead. By the time we see the '=' in the first example, we'd have
// expected a function name, but it's too late to back up - _lexpush is only
// one token deep.
//
// If we see a symbol and a '=', we know it's a simple binding. Otherwise,
// we have to take the token we got from lextok() and ask parse_decl() to
// start parsing it as a type expression.
int is_symbol = (lextok(&tok) == tSYMBOL);
if (is_symbol) {
// Save the string because matchtoken() will overwrite the token buffer.
// Note we also have to repoint tok so parse_decl will point at our
// local copy.
strcpy(ident, tok.str);
tok.str = ident;
if (matchtoken('(')) {
needtoken(')');
needtoken('=');
// Grab the name we're binding to.
is_bind = 1;
if (!expecttoken(tSYMBOL, &tok))
return NULL;
strcpy(bindname, tok.str);
}
}
if (!is_bind) {
// We didn't find an '=', so proceed with a normal function signature.
parse_decl(&decl, &tok, 0);
is_dtor = matchtoken('~');
if (lextok(&tok) != tSYMBOL) {
// Error, and if EOF, return. The lexpush is so we don't accidentally
// skip over a terminator or something, since we scan to the end of the
// line.
lexpush();
error(111);
if (tok.id == 0)
return NULL;
}
strcpy(ident, tok.str);
} // if (tok == symbol && matchtoken('='))
} // if (is_native)
} // if (matchtoken('~'))
symbol *target = NULL;
if (is_bind) {
target = findglb(bindname, sGLOBAL);
if (!target)
error(17, ident);
else if (target->ident != iFUNCTN)
error(10);
} else {
error(10);
}
if (!target)
return NULL;
if (is_dtor) {
// Make sure the dtor has the right name.
if (strcmp(ident, map->name) != 0)
error(114, spectype, map->name);
if (!(target->usage & uNATIVE)) {
// Must be a native.
error(118);
return NULL;
}
if (target->tag != 0 && target->tag != pc_tag_void) {
// Cannot return a value.
error(99);
return NULL;
}
if (target->dim.arglist[0].ident && target->dim.arglist[1].ident) {
// Cannot have extra arguments.
error(119);
return NULL;
}
// Make sure the final name includes the ~.
strcpy(ident, "~");
strcat(ident, map->name);
}
// Check that a method with this name doesn't already exist.
for (size_t i = 0; i < map->nummethods; i++) {
if (strcmp(map->methods[i]->name, ident) == 0) {
error(103, ident, spectype);
return NULL;
}
}
methodmap_method_t *method = (methodmap_method_t *)calloc(1, sizeof(methodmap_method_t));
strcpy(method->name, ident);
method->target = target;
if (is_dtor)
map->dtor = method;
// If the symbol is a constructor, we bypass the initial argument checks,
// and instead require that it returns something with the same tag.
if (strcmp(ident, map->name) == 0) {
if (target->tag != map->tag)
error(112, map->name);
define_constructor(map, method);
return method;
}
// Check the implicit this parameter. Currently we only allow scalars. As
// to not encourage enum-structs, we will not allow those either.
const arginfo *first_arg = &target->dim.arglist[0];
if (first_arg->ident == 0 ||
first_arg->ident != iVARIABLE ||
(first_arg->usage & uCONST) ||
first_arg->hasdefault ||
first_arg->numtags != 1)
{
free(method);
error(108, spectype, map->name);
return NULL;
}
// Ensure the methodmap tag is compatible with |this|.
int ok = 0;
for (methodmap_t *mapptr = map; mapptr; mapptr = mapptr->parent) {
if (first_arg->tags[0] == mapptr->tag) {
ok = 1;
break;
}
}
if (!ok)
error(108, spectype, map->name);
return method;
}
static int consume_line()
{
int val;
char *str;
// First check for EOF.
if (lex(&val, &str) == 0)
return FALSE;
lexpush();
while (!matchtoken(tTERM)) {
// Check for EOF.
if (lex(&val, &str) == 0)
return FALSE;
}
return TRUE;
}
/**
* domethodmap - declare a method map for OO-ish syntax.
*
*/
static void domethodmap(LayoutSpec spec)
{
int val;
char *str;
LayoutSpec old_spec;
methodmap_t *parent = NULL;
const char *spectype = layout_spec_name(spec);
// methodmap ::= "methodmap" symbol ("<" symbol)? "{" methodmap-body "}"
char mapname[sNAMEMAX + 1];
if (lex(&val, &str) != tSYMBOL)
error(93);
strcpy(mapname, str);
if (!isupper(*mapname))
error(109, spectype);
old_spec = deduce_layout_spec_by_name(mapname);
if (!can_redef_layout_spec(spec, old_spec))
error(110, mapname, layout_spec_name(old_spec));
if (matchtoken('<')) {
if (lex(&val, &str) != tSYMBOL) {
error(93);
return;
}
if ((parent = methodmap_find_by_name(str)) == NULL) {
error(102, str);
}
}
methodmap_t *map = (methodmap_t *)calloc(1, sizeof(methodmap_t));
map->parent = parent;
map->tag = pc_addtag(mapname);
map->spec = spec;
strcpy(map->name, mapname);
methodmap_add(map);
needtoken('{');
while (!matchtoken('}')) {
methodmap_method_t *method;
methodmap_method_t **methods;
if ((method = parse_method(map)) == NULL) {
if (!consume_line())
return;
continue;
}
needtoken(tTERM);
methods = (methodmap_method_t **)realloc(map->methods, sizeof(methodmap_method_t *) * (map->nummethods + 1));
if (!methods) {
error(123);
return;
}
map->methods = methods;
map->methods[map->nummethods++] = method;
}
needtoken(tTERM);
}
// delete ::= "delete" expr
static void dodelete()
{
int tag;
symbol *sym;
int ident = doexpr(TRUE, FALSE, TRUE, FALSE, &tag, &sym, TRUE);
needtoken(tTERM);
switch (ident) {
case iFUNCTN:
case iREFFUNC:
error(115, "functions");
return;
case iARRAY:
case iREFARRAY:
case iARRAYCELL:
case iARRAYCHAR:
error(115, "arrays");
return;
}
if (tag == 0) {
error(115, "primitive types or enums");
return;
}
methodmap_t *map = methodmap_find_by_tag(tag);
if (!map) {
error(115, pc_tagname(tag));
return;
}
if (!map->dtor) {
error(115, layout_spec_name(map->spec), map->name);
return;
}
// For some reason, we don't get a sysreq.n once this passes through the
// peephole optimizer. I can't tell why. -dvander
//
// push pri
// push.c 1
// sysreq.c N 1
// stack 8
pushreg(sPRI);
markexpr(sPARM,NULL,0);
{
pushval(1);
ffcall(map->dtor->target, NULL, 1);
map->dtor->target->usage |= uREAD;
}
markexpr(sEXPR,NULL,0);
}
/**
* dofuncenum - declare function enumerations
@@ -3449,13 +3906,17 @@ static void decl_enum(int vclass)
cell increment,multiplier;
constvalue *enumroot;
symbol *enumsym;
LayoutSpec spec;
/* get an explicit tag, if any (we need to remember whether an explicit
* tag was passed, even if that explicit tag was "_:", so we cannot call
* pc_addtag() here
*/
if (lex(&val,&str)==tLABEL) {
tag=pc_addtag(str);
tag = pc_addtag(str);
spec = deduce_layout_spec_by_tag(tag);
if (!can_redef_layout_spec(spec, Layout_Enum))
error(110, str, layout_spec_name(spec));
explicittag=TRUE;
} else {
lexpush();
@@ -3466,8 +3927,16 @@ static void decl_enum(int vclass)
/* get optional enum name (also serves as a tag if no explicit tag was set) */
if (lex(&val,&str)==tSYMBOL) { /* read in (new) token */
strcpy(enumname,str); /* save enum name (last constant) */
if (!explicittag)
if (!explicittag) {
tag=pc_addtag(enumname);
spec = deduce_layout_spec_by_tag(tag);
if (!can_redef_layout_spec(spec, Layout_Enum))
error(110, enumname, layout_spec_name(spec));
} else {
spec = deduce_layout_spec_by_name(enumname);
if (!can_redef_layout_spec(spec, Layout_Enum))
error(110, enumname, layout_spec_name(spec));
}
} else {
lexpush(); /* analyze again */
enumname[0]='\0';
@@ -3997,7 +4466,7 @@ SC_FUNC char *funcdisplayname(char *dest,char *funcname)
return dest;
}
static void funcstub(int fnative)
static symbol *funcstub(int fnative)
{
int tok,tag,fpublic;
char *str;
@@ -4022,7 +4491,7 @@ static void funcstub(int fnative)
*/
if (numdim == sDIMEN_MAX) {
error(53); /* exceeding maximum number of dimensions */
return;
return NULL;
} /* if */
size=needsub(&idxtag[numdim],NULL); /* get size; size==0 for "var[]" */
if (size==0)
@@ -4050,12 +4519,12 @@ static void funcstub(int fnative)
if (tok==tOPERATOR) {
opertok=operatorname(symbolname);
if (opertok==0)
return; /* error message already given */
return NULL; /* error message already given */
check_operatortag(opertok,tag,symbolname);
} else {
if (tok!=tSYMBOL && freading) {
error(10); /* illegal function or declaration */
return;
return NULL;
} /* if */
strcpy(symbolname,str);
} /* if */
@@ -4063,7 +4532,7 @@ static void funcstub(int fnative)
sym=fetchfunc(symbolname,tag);/* get a pointer to the function entry */
if (sym==NULL)
return;
return NULL;
if (fnative) {
sym->usage=(char)(uNATIVE | uRETVALUE | uDEFINE | (sym->usage & uPROTOTYPED));
sym->x.lib=curlibrary;
@@ -4122,6 +4591,8 @@ static void funcstub(int fnative)
litidx=0; /* clear the literal pool */
delete_symbols(&loctab,0,TRUE,TRUE);/* clear local variables queue */
return sym;
}
/* newfunc - begin a function
@@ -5364,6 +5835,9 @@ static int testsymbols(symbol *root,int level,int testlabs,int testconst)
error(203,sym->name); /* symbol isn't used: ... */
} /* if */
break;
case iPROXY:
// Ignore usage on proxies.
break;
default:
/* a variable */
if (sym->parent!=NULL)
@@ -5651,6 +6125,10 @@ static void statement(int *lastindent,int allow_decl)
error(3); /* declaration only valid in a block */
} /* if */
break;
case tDELETE:
dodelete();
lastst=tDELETE;
break;
case tDECL:
if (allow_decl) {
autozero=0;
@@ -5675,7 +6153,7 @@ static void statement(int *lastindent,int allow_decl)
compound(save==fline,tok);
} else {
lastst = tEMPTYBLOCK;
}
}
/* lastst (for "last statement") does not change
you're not my father, don't tell me what to do */
break;