summaryrefslogtreecommitdiff
path: root/sys/src/cmd/forp/parse.c
diff options
context:
space:
mode:
authoraiju <devnull@localhost>2018-03-28 17:08:30 +0000
committeraiju <devnull@localhost>2018-03-28 17:08:30 +0000
commit382d37dbf0ee8bf5af9594e922db6094e30ace2a (patch)
tree26d20b8c336da4017376c931fc8f0e507f16c613 /sys/src/cmd/forp/parse.c
parent80474f7f59ee755cd1967c5703e3be724582f001 (diff)
add forp
Diffstat (limited to 'sys/src/cmd/forp/parse.c')
-rw-r--r--sys/src/cmd/forp/parse.c454
1 files changed, 454 insertions, 0 deletions
diff --git a/sys/src/cmd/forp/parse.c b/sys/src/cmd/forp/parse.c
new file mode 100644
index 000000000..b813e696a
--- /dev/null
+++ b/sys/src/cmd/forp/parse.c
@@ -0,0 +1,454 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <ctype.h>
+#include <mp.h>
+#include "dat.h"
+#include "fns.h"
+
+Biobuf *bin;
+Line line;
+char lexbuf[512];
+int peektok;
+
+enum {
+ TEOF = -1,
+ TSYM = -2,
+ TNUM = -3,
+ TBIT = -4,
+ TOBVIOUSLY = -5,
+ TEQ = -6,
+ TNEQ = -7,
+ TLSH = -8,
+ TRSH = -9,
+ TLE = -10,
+ TGE = -11,
+ TLAND = -12,
+ TLOR = -13,
+ TASSUME = -14,
+ TIMP = -15,
+ TEQV = -16,
+ TSIGNED = -17,
+};
+
+typedef struct Keyword Keyword;
+typedef struct Oper Oper;
+struct Keyword {
+ char *name;
+ int tok;
+};
+/* both tables must be sorted */
+static Keyword kwtab[] = {
+ "assume", TASSUME,
+ "bit", TBIT,
+ "obviously", TOBVIOUSLY,
+ "signed", TSIGNED,
+};
+/* <=> is implemented through a hack below */
+static Keyword koptab[] = {
+ "!=", TNEQ,
+ "&&", TLAND,
+ "<<", TLSH,
+ "<=", TLE,
+ "==", TEQ,
+ "=>", TIMP,
+ ">=", TGE,
+ ">>", TRSH,
+ "||", TLOR,
+};
+static Keyword *kwjmp[128];
+static Keyword *kopjmp[128];
+struct Oper {
+ int tok;
+ int type;
+ int pred;
+ char *str;
+};
+#define MAXPREC 15
+static Oper optab[] = {
+ '*', OPMUL, 14, "*",
+ '/', OPDIV, 14, "/",
+ '%', OPMOD, 14, "%",
+ '+', OPADD, 13, "+",
+ '-', OPSUB, 13, "-",
+ TLSH, OPLSH, 12, "<<",
+ TRSH, OPRSH, 12, ">>",
+ '<', OPLT, 11, "<",
+ TLE, OPLE, 11, "<=",
+ '>', OPGT, 11, ">",
+ TGE, OPGE, 11, ">=",
+ TEQ, OPEQ, 10, "==",
+ TNEQ, OPNEQ, 10, "!=",
+ '&', OPAND, 9, "&",
+ '^', OPXOR, 8, "^",
+ '|', OPOR, 7, "|",
+ TLAND, OPLAND, 6, "&&",
+ TLOR, OPLOR, 5, "||",
+ TEQV, OPEQV, 4, "<=>",
+ TIMP, OPIMP, 4, "=>",
+ /* ?: */
+ '=', OPASS, 2, "=",
+ ',', OPCOMMA, 1, ",",
+ -1, OPNOT, MAXPREC, "!",
+ -1, OPCOM, MAXPREC, "~",
+ -1, OPNEG, MAXPREC, "-",
+};
+
+void
+error(Line *l, char *msg, ...)
+{
+ char buf[256];
+ Fmt f;
+ va_list va;
+
+ if(l == nil) l = &line;
+ fmtfdinit(&f, 2, buf, sizeof(buf));
+ fmtprint(&f, "%s:%d: ", l->filen, l->lineno);
+ va_start(va, msg);
+ fmtvprint(&f, msg, va);
+ va_end(va);
+ fmtrune(&f, '\n');
+ fmtfdflush(&f);
+ exits("error");
+}
+
+static int
+tokfmt(Fmt *f)
+{
+ int t;
+ Keyword *k;
+
+ t = va_arg(f->args, int);
+ if(t >= ' ' && t < 0x7f) return fmtprint(f, "%c", t);
+ for(k = kwtab; k < kwtab + nelem(kwtab); k++)
+ if(k->tok == t)
+ return fmtprint(f, "%s", k->name);
+ for(k = koptab; k < koptab + nelem(koptab); k++)
+ if(k->tok == t)
+ return fmtprint(f, "%s", k->name);
+ switch(t){
+ case TSYM: return fmtprint(f, "TSYM"); break;
+ case TNUM: return fmtprint(f, "TNUM"); break;
+ case TEOF: return fmtprint(f, "eof"); break;
+ default: return fmtprint(f, "%d", t); break;
+ }
+}
+
+static int
+exprfmt(Fmt *f)
+{
+ Node *n;
+ Oper *o;
+ int w;
+
+ n = va_arg(f->args, Node *);
+ if(n == nil) return fmtprint(f, "nil");
+ switch(n->type){
+ case ASTSYM: return fmtprint(f, "%s", n->sym->name);
+ case ASTBIN:
+ for(o = optab; o < optab + nelem(optab); o++)
+ if(o->type == n->op)
+ break;
+ if(o == optab + nelem(optab)) return fmtprint(f, "[unknown operation %O]", n->op);
+ w = f->width;
+ if(w > o->pred) fmtrune(f, '(');
+ fmtprint(f, "%*ε %s %*ε", o->pred, n->n1, o->str, o->pred + 1, n->n2);
+ if(w > o->pred) fmtrune(f, ')');
+ return 0;
+ case ASTNUM: return fmtprint(f, "0x%B", n->num);
+ default: return fmtprint(f, "???(%α)", n->type);
+ }
+}
+
+static int
+issymchar(int c)
+{
+ return c >= 0 && (isalnum(c) || c == '_' || c >= 0x80);
+}
+
+static int
+lex(void)
+{
+ int c, d;
+ char *p;
+ Keyword *kw;
+
+ if(peektok != 0){
+ c = peektok;
+ peektok = 0;
+ return c;
+ }
+loop:
+ do{
+ c = Bgetc(bin);
+ if(c == '\n') line.lineno++;
+ }while(c >= 0 && isspace(c));
+ if(c < 0) return TEOF;
+ if(c == '/'){
+ c = Bgetc(bin);
+ if(c == '/'){
+ do
+ c = Bgetc(bin);
+ while(c >= 0 && c != '\n');
+ if(c < 0) return TEOF;
+ line.lineno++;
+ goto loop;
+ }else if(c == '*'){
+ s0:
+ c = Bgetc(bin);
+ if(c != '*') goto s0;
+ s1:
+ c = Bgetc(bin);
+ if(c == '*') goto s1;
+ if(c != '/') goto s0;
+ goto loop;
+ }else{
+ Bungetc(bin);
+ return '/';
+ }
+ }
+ if(isdigit(c)){
+ p = lexbuf;
+ *p++ = c;
+ while(c = Bgetc(bin), issymchar(c))
+ if(p < lexbuf + sizeof(lexbuf) - 1)
+ *p++ = c;
+ Bungetc(bin);
+ *p = 0;
+ strtol(lexbuf, &p, 0);
+ if(p == lexbuf || *p != 0)
+ error(nil, "invalid number %q", lexbuf);
+ return TNUM;
+ }
+ if(issymchar(c)){
+ p = lexbuf;
+ *p++ = c;
+ while(c = Bgetc(bin), issymchar(c))
+ if(p < lexbuf + sizeof(lexbuf) - 1)
+ *p++ = c;
+ Bungetc(bin);
+ *p = 0;
+ c = lexbuf[0];
+ if((signed char)c>= 0 && (kw = kwjmp[c], kw != nil))
+ for(; kw < kwtab + nelem(kwtab) && kw->name[0] == c; kw++)
+ if(strcmp(lexbuf, kw->name) == 0)
+ return kw->tok;
+ return TSYM;
+ }
+ if(kw = kopjmp[c], kw != nil){
+ d = Bgetc(bin);
+ for(; kw < koptab + nelem(koptab) && kw->name[0] == c; kw++)
+ if(kw->name[1] == d){
+ if(kw->tok == TLE){
+ c = Bgetc(bin);
+ if(c == '>')
+ return TEQV;
+ Bungetc(bin);
+ }
+ return kw->tok;
+ }
+ Bungetc(bin);
+ }
+ return c;
+}
+
+static void
+superman(int t)
+{
+ assert(peektok == 0);
+ peektok = t;
+}
+
+static int
+peek(void)
+{
+ if(peektok != 0) return peektok;
+ return peektok = lex();
+}
+
+static void
+expect(int t)
+{
+ int s;
+
+ s = lex();
+ if(t != s)
+ error(nil, "expected %t, got %t", t, s);
+}
+
+static int
+got(int t)
+{
+ return peek() == t && (lex(), 1);
+}
+
+static Node *
+expr(int level)
+{
+ Node *a, *b, *c;
+ Oper *op;
+ Symbol *s;
+ mpint *num;
+ int t;
+
+ if(level == MAXPREC+1){
+ switch(t = lex()){
+ case '~': return node(ASTUN, OPCOM, expr(level));
+ case '!': return node(ASTUN, OPNOT, expr(level));
+ case '+': return expr(level);
+ case '-': return node(ASTUN, OPNEG, expr(level));
+ case '(':
+ a = expr(0);
+ expect(')');
+ return a;
+ case TSYM:
+ s = symget(lexbuf);
+ switch(s->type){
+ case SYMNONE:
+ error(nil, "%#q undefined", s->name);
+ default:
+ error(nil, "%#q symbol type error", s->name);
+ case SYMBITS:
+ break;
+ }
+ return node(ASTSYM, s);
+ case TNUM:
+ num = strtomp(lexbuf, nil, 0, nil);
+ return node(ASTNUM, num);
+ default:
+ error(nil, "unexpected %t", t);
+ }
+ }else if(level == MAXPREC){
+ a = expr(level + 1);
+ if(got('[')){
+ b = expr(0);
+ if(got(':'))
+ c = expr(0);
+ else
+ c = nil;
+ expect(']');
+ a = node(ASTIDX, a, b, c);
+ }
+ return a;
+ }else if(level == 3){
+ a = expr(level + 1);
+ if(got('?')){
+ b = expr(level);
+ expect(':');
+ c = expr(level);
+ a = node(ASTTERN, a, b, c);
+ }
+ return a;
+ }
+ a = expr(level+1);
+ for(;;){
+ t = peek();
+ for(op = optab; op < optab + nelem(optab); op++)
+ if(op->tok == t && op->pred >= level)
+ break;
+ if(op == optab+nelem(optab)) return a;
+ lex();
+ a = node(ASTBIN, op->type, a, expr(level+1));
+ }
+}
+
+static void
+vardecl(void)
+{
+ Symbol *s;
+ int l, flags;
+
+ flags = 0;
+ for(;;)
+ switch(l = lex()){
+ case TBIT: if((flags & 1) != 0) goto err; flags |= 1; break;
+ case TSIGNED: if((flags & 2) != 0) goto err; flags |= 2; break;
+ default: superman(l); goto out;
+ }
+out:
+ do{
+ expect(TSYM);
+ s = symget(lexbuf);
+ if(s->type != SYMNONE) error(nil, "%#q redefined", s->name);
+ s->type = SYMBITS;
+ if((flags & 2) != 0)
+ s->flags |= SYMFSIGNED;
+ s->size = 1;
+ if(got('[')){
+ expect(TNUM);
+ s->type = SYMBITS;
+ s->size = strtol(lexbuf, nil, 0);
+ expect(']');
+ }
+ s->vars = emalloc(sizeof(int) * s->size);
+ }while(got(','));
+ expect(';');
+ return;
+err: error(nil, "syntax error");
+}
+
+static int
+statement(void)
+{
+ Node *n;
+ int t;
+
+ switch(t=peek()){
+ case TEOF:
+ return 0;
+ case TBIT:
+ case TSIGNED:
+ vardecl();
+ break;
+ case TASSUME:
+ case TOBVIOUSLY:
+ lex();
+ n = expr(0);
+ expect(';');
+ convert(n, -1);
+ if(t == TASSUME)
+ assume(n);
+ else
+ obviously(n);
+ break;
+ case ';':
+ lex();
+ break;
+ default:
+ n = expr(0);
+ convert(n, -1);
+ expect(';');
+ }
+ return 1;
+}
+
+void
+parsinit(void)
+{
+ Keyword *k;
+
+ fmtinstall('t', tokfmt);
+ fmtinstall(L'ε', exprfmt);
+ for(k = kwtab; k < kwtab + nelem(kwtab); k++)
+ if(kwjmp[k->name[0]] == nil)
+ kwjmp[k->name[0]] = k;
+ for(k = koptab; k < koptab + nelem(koptab); k++)
+ if(kopjmp[k->name[0]] == nil)
+ kopjmp[k->name[0]] = k;
+}
+
+void
+parse(char *fn)
+{
+ if(fn == nil){
+ bin = Bfdopen(0, OREAD);
+ line.filen = "<stdin>";
+ }else{
+ bin = Bopen(fn, OREAD);
+ line.filen = strdup(fn);
+ }
+ if(bin == nil) sysfatal("open: %r");
+ line.lineno = 1;
+ while(statement())
+ ;
+}