/* * (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands. * See the copyright notice in the ACK home directory, in the file "Copyright". * * Author: Ceriel J.H. Jacobs */ /* O V E R A L L S T R U C T U R E */ /* stripped down version of the one in the Modula-2 compiler */ /* $Id$ */ /* The grammar as given by Wirth is already almost LL(1); the main problem is that the full form of a qualified designator may be: [ module_ident '.' ]* IDENT [ '.' field_ident ]* which is quite confusing to an LL(1) parser. Rather than resorting to context-sensitive techniques, I have decided to render this as: IDENT [ '.' IDENT ]* on the grounds that it is quite natural to consider the first IDENT to be the name of the object and regard the others as field identifiers. */ { #include "main.h" #include "idf.h" #include "f_info.h" #include "LLlex.h" #include struct lnk * new_lnk() { static struct lnk *p; static int cnt; if (cnt-- <= 0) { p = (struct lnk *)Malloc(50*sizeof(struct lnk)); cnt = 49; } p->lnk_next = 0; p->lnk_imp = 0; return p++; } } %lexical LLlex; %start CompUnit, CompilationUnit; %start DefModule, DefinitionModule; ModuleDeclaration : MODULE IDENT priority ';' import((struct lnk **) 0)* export? block IDENT ; priority: [ '[' ConstExpression ']' | /* empty */ ] ; export : EXPORT [ QUALIFIED | /* empty */ ] IdentList ';' ; import(register struct lnk **p;) { register struct idf *fromid = 0; struct idf *id; } : { if (p) while (*p) p = &((*p)->lnk_next); } [ FROM identifier(&id) { fromid = id; if (p) { if (AddToList(fromid->id_text, ".def")) { *p = new_lnk(); (*p)->lnk_imp = fromid; } } } ]? IMPORT identifier(&id) { if (! fromid && p) { if (AddToList(id->id_text, ".def")) { *p = new_lnk(); (*p)->lnk_imp = id; p = &((*p)->lnk_next); } } } [ ',' identifier(&id) { if (! fromid && p) { if (AddToList(id->id_text, ".def")) { *p = new_lnk(); (*p)->lnk_imp = id; p = &((*p)->lnk_next); } } } ]* ';' /* When parsing a global module, this is the place where we must read already compiled definition modules. If the FROM clause is present, the identifier in it is a module name, otherwise the names in the import list are module names. */ ; DefinitionModule { struct idf *id; extern char *strrchr(); } : DEFINITION MODULE identifier(&id) { if (! ForeignFlag) { AddToList(id->id_text, ".mod"); } if (! id->id_type) { id->id_type = DEFINITION; } else if (id->id_type != IMPLEMENTATION) { error("multiple declaration for module %s", id->id_text); } if (! id->id_dir) { id->id_dir = WorkingDir; } else if (strcmp(id->id_dir, WorkingDir)) { Gerror("definition and implementation of module %s reside in different directories", id->id_text); } id->id_def = strrchr(FileName, '/'); if (! id->id_def) id->id_def = FileName; else (id->id_def)++; CurrentArg->a_idf = id; } ';' import(&(id->id_defimports))* [ export | /* empty */ ] definition* END IDENT '.' ; definition : CONST [ %persistent ConstantDeclaration ';' ]* | TYPE [ %persistent IDENT [ '=' type | /* empty */ /* Here, the exported type has a hidden implementation. The export is said to be opaque. It is restricted to pointer types. */ ] ';' ]* | VAR [ %persistent VariableDeclaration ';' ]* | ProcedureHeading ';' ; ProgramModule { struct idf *id; } : MODULE identifier(&id) { if (! id->id_type) { id->id_type = state; } else if (id->id_type != DEFINITION || state != IMPLEMENTATION) { error("multiple declaration for module %s", id->id_text); } if (! id->id_dir) { id->id_dir = WorkingDir; } else if (strcmp(id->id_dir, WorkingDir)) { Gerror("definition and implementation of module %s reside in different directories", id->id_text); } CurrentArg->a_idf = id; } priority ';' import(&(id->id_modimports))* block IDENT '.' ; Module: DEFINITION { fatal("Definition module in .mod file"); } | %default [ IMPLEMENTATION { state = IMPLEMENTATION; } | /* empty */ { state = PROGRAM; } ] ProgramModule ; CompilationUnit: Module ; identifier(struct idf **id;): IDENT { extern char idfbuf[]; *id = str2idf(idfbuf, 1); } ;