diff options
Diffstat (limited to 'ghc/compiler/parser/syntax.c')
-rw-r--r-- | ghc/compiler/parser/syntax.c | 720 |
1 files changed, 720 insertions, 0 deletions
diff --git a/ghc/compiler/parser/syntax.c b/ghc/compiler/parser/syntax.c new file mode 100644 index 0000000000..ad5d3d68b1 --- /dev/null +++ b/ghc/compiler/parser/syntax.c @@ -0,0 +1,720 @@ +/********************************************************************** +* * +* * +* Syntax-related Utility Functions * +* * +* * +**********************************************************************/ + +#include <stdio.h> +#include <ctype.h> + +#include "hspincl.h" +#include "constants.h" +#include "utils.h" +#include "tree.h" + +#include "hsparser.tab.h" + +/* Imported values */ +extern short icontexts; +extern list Lnil; +extern unsigned endlineno, startlineno; +extern BOOLEAN hashIds, etags; + +/* Forward Declarations */ + +char *ineg PROTO((char *)); +static tree unparen PROTO((tree)); +static void is_conapp_patt PROTO((int, tree, tree)); +static void rearrangeprec PROTO((tree, tree)); +static void error_if_expr_wanted PROTO((int, char *)); +static void error_if_patt_wanted PROTO((int, char *)); + +qid fns[MAX_CONTEXTS] = { NULL }; +BOOLEAN samefn[MAX_CONTEXTS] = { FALSE }; +tree prevpatt[MAX_CONTEXTS] = { NULL }; + +BOOLEAN inpat = FALSE; + +static BOOLEAN checkorder2 PROTO((binding, BOOLEAN)); +static BOOLEAN checksig PROTO((BOOLEAN, binding)); + +/* + check infix value in range 0..9 +*/ + + +int +checkfixity(vals) + char *vals; +{ + int value; + sscanf(vals,"%d",&value); + + if (value < 0 || value > 9) + { + int oldvalue = value; + value = value < 0 ? 0 : 9; + fprintf(stderr,"Precedence must be between 0 and 9 (value given: %d, changed to %d)\n", + oldvalue,value); + } + return(value); +} + + +/* + Check Previous Pattern usage +*/ + +void +checksamefn(fn) + qid fn; +{ + char *this = qid_to_string(fn); + char *was = (FN==NULL) ? NULL : qid_to_string(FN); + + SAMEFN = (was != NULL && strcmp(this,was) == 0); + + if(!SAMEFN && etags) +#if 1/*etags*/ + printf("%u\n",startlineno); +#else + fprintf(stderr,"%u\tchecksamefn:%s\n",startlineno,this); +#endif +} + + +void +checkinpat() +{ + if(!inpat) + hsperror("pattern syntax used in expression"); +} + +/* ------------------------------------------------------------------------ +*/ + +void +expORpat(int wanted, tree e) +{ + switch(ttree(e)) + { + case ident: /* a pattern or expr */ + break; + + case wildp: + error_if_expr_wanted(wanted, "wildcard in expression"); + break; + + case as: + error_if_expr_wanted(wanted, "`as'-pattern instead of an expression"); + expORpat(wanted, gase(e)); + break; + + case lazyp: + error_if_expr_wanted(wanted, "irrefutable pattern instead of an expression"); + expORpat(wanted, glazyp(e)); + break; + + case lit: + switch (tliteral(glit(e))) { + case integer: + case intprim: + case floatr: + case doubleprim: + case floatprim: + case string: + case stringprim: + case charr: + case charprim: + break; /* pattern or expr */ + + case clitlit: + error_if_patt_wanted(wanted, "``literal-literal'' in pattern"); + + default: /* the others only occur in pragmas */ + hsperror("not a valid literal pattern or expression"); + } + break; + + case negate: + { tree sub = gnexp(e); + if (ttree(sub) != lit) { + error_if_patt_wanted(wanted, "\"-\" applied to a non-literal"); + } else { + literal l = glit(sub); + + if (tliteral(l) != integer && tliteral(l) != floatr) { + error_if_patt_wanted(wanted, "\"-\" applied to a non-number"); + } + } + expORpat(wanted, sub); + } + break; + + case ap: + { + tree f = gfun(e); + tree a = garg(e); + + is_conapp_patt(wanted, f, a); /* does nothing unless wanted == LEGIT_PATT */ + expORpat(wanted, f); + expORpat(wanted, a); + } + break; + + case infixap: + { + qid f = ginffun ((struct Sinfixap *)e); + tree a1 = ginfarg1((struct Sinfixap *)e); + tree a2 = ginfarg2((struct Sinfixap *)e); + + expORpat(wanted, a1); + expORpat(wanted, a2); + + if (wanted == LEGIT_PATT && !isconstr(qid_to_string(f))) + hsperror("variable application in pattern"); + } + break; + + case record: + { + list field; + for (field = grbinds(e); tlist(field) == lcons; field = ltl(field)) { + expORpat(wanted, lhd(field)); + } + } + break; + + case rbind: + if (tmaybe(grbindexp(e)) == just) + expORpat(wanted, gthing(grbindexp(e))); + break; + + case tuple: + { + list tup; + for (tup = gtuplelist(e); tlist(tup) == lcons; tup = ltl(tup)) { + expORpat(wanted, lhd(tup)); + } + } + break; + + case llist: + { + list l; + for (l = gllist(e); tlist(l) == lcons; l = ltl(l)) { + expORpat(wanted, lhd(l)); + } + } + break; + + case par: /* parenthesised */ + expORpat(wanted, gpare(e)); + break; + + case restr: + case lambda: + case let: + case casee: + case ife: + case doe: + case ccall: + case scc: + case rupdate: + case comprh: + case eenum: + case lsection: + case rsection: + error_if_patt_wanted(wanted, "unexpected construct in a pattern"); + break; + + default: + hsperror("not a pattern or expression"); + } +} + +static void +is_conapp_patt(int wanted, tree f, tree a) +{ + if (wanted == LEGIT_EXPR) + return; /* that was easy */ + + switch(ttree(f)) + { + case ident: + if (isconstr(qid_to_string(gident(f)))) + { + expORpat(wanted, a); + return; + } + { + char errbuf[ERR_BUF_SIZE]; + sprintf(errbuf,"not a constructor application -- %s",qid_to_string(gident(f))); + hsperror(errbuf); + } + + case ap: + is_conapp_patt(wanted, gfun(f), garg(f)); + expORpat(wanted, a); + return; + + case par: + is_conapp_patt(wanted, gpare(f), a); + break; + + case tuple: + { + char errbuf[ERR_BUF_SIZE]; + sprintf(errbuf,"tuple pattern `applied' to arguments (missing comma?)"); + hsperror(errbuf); + } + break; + + default: + hsperror("not a constructor application"); + } +} + +static void +error_if_expr_wanted(int wanted, char *msg) +{ + if (wanted == LEGIT_EXPR) + hsperror(msg); +} + +static void +error_if_patt_wanted(int wanted, char *msg) +{ + if (wanted == LEGIT_PATT) + hsperror(msg); +} + +/* ---------------------------------------------------------------------- */ + +BOOLEAN /* return TRUE if LHS is a pattern */ +lhs_is_patt(tree e) +{ + switch(ttree(e)) + { + case lit: + switch (tliteral(glit(e))) { + case integer: + case intprim: + case floatr: + case doubleprim: + case floatprim: + case string: + case charr: + case charprim: + case stringprim: + return TRUE; + default: + hsperror("Literal is not a valid LHS"); + } + + case wildp: + return TRUE; + + case as: + case lazyp: + case llist: + case tuple: + case negate: + expORpat(LEGIT_PATT, e); + return TRUE; + + case ident: + return(TRUE); + /* This change might break ap infixop below. BEWARE. + return (isconstr(qid_to_string(gident(e)))); + */ + + case ap: + { + tree f = function(e); + tree a = garg(e); /* do not "unparen", otherwise the error + fromInteger ((x,y) {-no comma-} z) + will be missed. + */ + + /* definitions must have pattern arguments */ + expORpat(LEGIT_PATT, a); + + if(ttree(f) == ident) + return(isconstr(qid_to_string(gident(f)))); + + else if(ttree(f) == infixap) + return(lhs_is_patt(f)); + + else + hsperror("Not a legal pattern binding in LHS"); + } + + case infixap: + { + qid f = ginffun((struct Sinfixap *)e); + tree a1 = unparen(ginfarg1((struct Sinfixap *)e)), + a2 = unparen(ginfarg2((struct Sinfixap *)e)); + + /* definitions must have pattern arguments */ + expORpat(LEGIT_PATT, a1); + expORpat(LEGIT_PATT, a2); + + return(isconstr(qid_to_string(f))); + } + + case par: + return(lhs_is_patt(gpare(e))); + + /* Anything else must be an illegal LHS */ + default: + hsperror("Not a valid LHS"); + } + + abort(); /* should never get here */ + return(FALSE); +} + + +/* + Return the function at the root of a series of applications. +*/ + +tree +function(e) + tree e; +{ + switch (ttree(e)) + { + case ap: + expORpat(LEGIT_PATT, garg(e)); + return(function(gfun(e))); + + case par: + return(function(gpare(e))); + + default: + return(e); + } +} + + +static tree +unparen(e) + tree e; +{ + while (ttree(e) == par) + e = gpare(e); + + return(e); +} + + +/* + Extend a function by adding a new definition to its list of bindings. +*/ + +void +extendfn(bind,rule) +binding bind; +binding rule; +{ +/* fprintf(stderr,"extending binding (%d)\n",tbinding(bind));*/ + if(tbinding(bind) == abind) + bind = gabindsnd(bind); + + if(tbinding(bind) == pbind) + gpbindl(bind) = lconc(gpbindl(bind), gpbindl(rule)); + else if(tbinding(bind) == fbind) + gfbindl(bind) = lconc(gfbindl(bind), gfbindl(rule)); + else + fprintf(stderr,"bind error in decl (%d)\n",tbinding(bind)); +} + +/* + + Precedence Parser for Haskell. By default operators are left-associative, + so it is only necessary to rearrange the parse tree where the new operator + has a greater precedence than the existing one, or where two operators have + the same precedence and are both right-associative. Error conditions are + handled. + + Note: Prefix negation has the same precedence as infix minus. + The algorithm must thus take account of explicit negates. +*/ + +void +precparse(tree t) +{ + if(ttree(t) == infixap) + { + tree left = ginfarg1(t); + + if(ttree(left) == negate) + { + struct infix *ttabpos = infixlookup(ginffun(t)); + struct infix *ntabpos = infixlookup(mknoqual(install_literal("-"))); + + if(pprecedence(ntabpos) < pprecedence(ttabpos)) + { + /* (-x)*y ==> -(x*y) */ + qid lop = ginffun(t); + tree arg1 = gnexp(left); + tree arg2 = ginfarg2(t); + + t->tag = negate; + gnexp(t) = left; + gnxxx1(t) = NULL; + gnxxx2(t) = NULL; + + left->tag = infixap; + ginffun(left) = lop; + ginfarg1(left) = arg1; + ginfarg2(left) = arg2; + + precparse(left); + } + } + + else if(ttree(left) == infixap) + { + struct infix *ttabpos = infixlookup(ginffun(t)); + struct infix *lefttabpos = infixlookup(ginffun(left)); + + if(pprecedence(lefttabpos) < pprecedence(ttabpos)) + rearrangeprec(left,t); + + else if(pprecedence(lefttabpos) == pprecedence(ttabpos)) + { + if(pfixity(lefttabpos) == INFIXR && pfixity(ttabpos) == INFIXR) + rearrangeprec(left,t); + + else if(pfixity(lefttabpos) == INFIXL && pfixity(ttabpos) == INFIXL) + /* SKIP */; + + else + { + char errbuf[ERR_BUF_SIZE]; + sprintf(errbuf,"Cannot mix %s and %s in the same infix expression", + qid_to_string(ginffun(left)), qid_to_string(ginffun(t))); + hsperror(errbuf); + } + } + } + } +} + + +/* + Rearrange a tree to effectively insert an operator in the correct place. + + x+y*z ==parsed== (x+y)*z ==> x+(y*z) + + The recursive call to precparse ensures this filters down as necessary. +*/ + +static void +rearrangeprec(tree left, tree t) +{ + qid top = ginffun(left); + qid lop = ginffun(t); + tree arg1 = ginfarg1(left); + tree arg2 = ginfarg2(left); + tree arg3 = ginfarg2(t); + + ginffun(t) = top; + ginfarg1(t) = arg1; + ginfarg2(t) = left; + + ginffun(left) = lop; + ginfarg1(left) = arg2; + ginfarg2(left) = arg3; + + precparse(left); +} + +pbinding +createpat(guards,where) + pbinding guards; + binding where; +{ + qid func; + + if(FN != NULL) + func = FN; + else + func = mknoqual(install_literal("")); + + return(mkpgrhs(PREVPATT,guards,where,func,endlineno)); +} + +char * +ineg(i) + char *i; +{ + char *p = xmalloc(strlen(i)+2); + + *p = '-'; + strcpy(p+1,i); + return(p); +} + +#if 0 +/* UNUSED: at the moment */ +void +checkmodname(import,interface) + id import, interface; +{ + if(strcmp(import,interface) != 0) + { + char errbuf[ERR_BUF_SIZE]; + sprintf(errbuf,"interface name (%s) does not agree with import name (%s)",interface,import); + hsperror(errbuf); + } +} +#endif /* 0 */ + +/* + Check the ordering of declarations in a cbody. + All signatures must appear before any declarations. +*/ + +void +checkorder(decls) + binding decls; +{ + /* The ordering must be correct for a singleton */ + if(tbinding(decls)!=abind) + return; + + checkorder2(decls,TRUE); +} + +static BOOLEAN +checkorder2(decls,sigs) + binding decls; + BOOLEAN sigs; +{ + while(tbinding(decls)==abind) + { + /* Perform a left-traversal if necessary */ + binding left = gabindfst(decls); + if(tbinding(left)==abind) + sigs = checkorder2(left,sigs); + else + sigs = checksig(sigs,left); + decls = gabindsnd(decls); + } + + return(checksig(sigs,decls)); +} + + +static BOOLEAN +checksig(sig,decl) + BOOLEAN sig; + binding decl; +{ + BOOLEAN issig = tbinding(decl) == sbind || tbinding(decl) == nullbind; + if(!sig && issig) + hsperror("Signature appears after definition in class body"); + + return(issig); +} + + +/* + Check the last expression in a list of do statements. +*/ + +void +checkdostmts(stmts) + list stmts; +{ + if (tlist(stmts) == lnil) + hsperror("do expression with no statements"); + + for(; tlist(ltl(stmts)) != lnil; stmts = ltl(stmts)) + ; + if (ttree(lhd(stmts)) != doexp) + hsperror("do statements must end with expression"); +} + + +/* + Check the precedence of a pattern or expression to ensure that + sections and function definitions have the correct parse. +*/ + +void +checkprec(exp,qfn,right) + tree exp; + qid qfn; + BOOLEAN right; +{ + if(ttree(exp) == infixap) + { + struct infix *ftabpos = infixlookup(qfn); + struct infix *etabpos = infixlookup(ginffun(exp)); + + if (pprecedence(etabpos) > pprecedence(ftabpos) || + (pprecedence(etabpos) == pprecedence(ftabpos) && + ((pfixity(etabpos) == INFIXR && pfixity(ftabpos) == INFIXR && right) || + ((pfixity(etabpos) == INFIXL && pfixity(ftabpos) == INFIXL && !right))))) + /* SKIP */; + else + { + char errbuf[ERR_BUF_SIZE]; + sprintf(errbuf,"Cannot mix %s and %s on a LHS or in a section", + qid_to_string(qfn), qid_to_string(ginffun(exp))); + hsperror(errbuf); + } + } +} + + +/* + Checks there are no bangs in a tycon application. +*/ + +void +checknobangs(app) + ttype app; +{ + if(tttype(app) == tapp) + { + if(tttype(gtarg((struct Stapp *)app)) == tbang) + hsperror("syntax error: unexpected ! in type"); + + checknobangs(gtapp((struct Stapp *)app)); + } +} + + +/* + Splits a tycon application into its constructor and a list of types. +*/ + +void +splittyconapp(app, tyc, tys) + ttype app; + qid *tyc; + list *tys; +{ + if(tttype(app) == tapp) + { + splittyconapp(gtapp((struct Stapp *)app), tyc, tys); + *tys = lapp(*tys, gtarg((struct Stapp *)app)); + } + else if(tttype(app) == tname) + { + *tyc = gtypeid((struct Stname *)app); + *tys = Lnil; + } + else + { + hsperror("panic: splittyconap: bad tycon application (no tycon)"); + } +} |