diff options
Diffstat (limited to 'appl/cmd/limbo/lex.b')
| -rw-r--r-- | appl/cmd/limbo/lex.b | 1146 |
1 files changed, 1146 insertions, 0 deletions
diff --git a/appl/cmd/limbo/lex.b b/appl/cmd/limbo/lex.b new file mode 100644 index 00000000..ae87b4a9 --- /dev/null +++ b/appl/cmd/limbo/lex.b @@ -0,0 +1,1146 @@ +Leof: con -1; +Linestart: con 0; + +Mlower, +Mupper, +Munder, +Mdigit, +Msign, +Mexp, +Mhex, +Mradix: con byte 1 << iota; +Malpha: con Mupper|Mlower|Munder; + +HashSize: con 1024; + +Keywd: adt +{ + name: string; + token: int; +}; + +# +# internals +# +savec: int; +files: array of ref File; # files making up the module, sorted by absolute line +nfiles: int; +lastfile := 0; # index of last file looked up +incpath := array[MaxIncPath] of string; +symbols := array[HashSize] of ref Sym; +strings := array[HashSize] of ref Sym; +map := array[256] of byte; +bins := array [MaxInclude] of ref Iobuf; +bin: ref Iobuf; +linestack := array[MaxInclude] of (int, int); +lineno: int; +linepos: int; +bstack: int; +lasttok: int; +lastyylval: YYSTYPE; +dowarn: int; +maxerr: int; +dosym: int; +toterrors: int; +fabort: int; +srcdir: string; +outfile: string; +stderr: ref Sys->FD; +dontinline: int; + +escmap := array[256] of +{ + '\'' => '\'', + '"' => '"', + '\\' => '\\', + 'a' => '\a', + 'b' => '\b', + 'f' => '\f', + 'n' => '\n', + 'r' => '\r', + 't' => '\t', + 'v' => '\v', + '0' => '\u0000', + + * => -1 +}; +unescmap := array[256] of +{ + '\'' => '\'', + '"' => '"', + '\\' => '\\', + '\a' => 'a', + '\b' => 'b', + '\f' => 'f', + '\n' => 'n', + '\r' => 'r', + '\t' => 't', + '\v' => 'v', + '\u0000' => '0', + + * => 0 +}; + +keywords := array [] of +{ + Keywd("adt", Ladt), + Keywd("alt", Lalt), + Keywd("array", Larray), + Keywd("big", Ltid), + Keywd("break", Lbreak), + Keywd("byte", Ltid), + Keywd("case", Lcase), + Keywd("chan", Lchan), + Keywd("con", Lcon), + Keywd("continue", Lcont), + Keywd("cyclic", Lcyclic), + Keywd("do", Ldo), + Keywd("else", Lelse), + Keywd("exception", Lexcept), + Keywd("exit", Lexit), + Keywd("fixed", Lfix), + Keywd("fn", Lfn), + Keywd("for", Lfor), + Keywd("hd", Lhd), + Keywd("if", Lif), + Keywd("implement", Limplement), + Keywd("import", Limport), + Keywd("include", Linclude), + Keywd("int", Ltid), + Keywd("len", Llen), + Keywd("list", Llist), + Keywd("load", Lload), + Keywd("module", Lmodule), + Keywd("nil", Lnil), + Keywd("of", Lof), + Keywd("or", Lor), + Keywd("pick", Lpick), + Keywd("raise", Lraise), + Keywd("raises", Lraises), + Keywd("real", Ltid), + Keywd("ref", Lref), + Keywd("return", Lreturn), + Keywd("self", Lself), + Keywd("spawn", Lspawn), + Keywd("string", Ltid), + Keywd("tagof", Ltagof), + Keywd("tl", Ltl), + Keywd("to", Lto), + Keywd("type", Ltype), + Keywd("while", Lwhile), +}; + +tokwords := array[] of +{ + Keywd("&=", Landeq), + Keywd("|=", Loreq), + Keywd("^=", Lxoreq), + Keywd("<<=", Llsheq), + Keywd(">>=", Lrsheq), + Keywd("+=", Laddeq), + Keywd("-=", Lsubeq), + Keywd("*=", Lmuleq), + Keywd("/=", Ldiveq), + Keywd("%=", Lmodeq), + Keywd("**=", Lexpeq), + Keywd(":=", Ldeclas), + Keywd("||", Loror), + Keywd("&&", Landand), + Keywd("::", Lcons), + Keywd("==", Leq), + Keywd("!=", Lneq), + Keywd("<=", Lleq), + Keywd(">=", Lgeq), + Keywd("<<", Llsh), + Keywd(">>", Lrsh), + Keywd("<-", Lcomm), + Keywd("++", Linc), + Keywd("--", Ldec), + Keywd("->", Lmdot), + Keywd("=>", Llabs), + Keywd("**", Lexp), + Keywd("EOF", Leof), +}; + +lexinit() +{ + for(i := 0; i < 256; i++){ + map[i] = byte 0; + if(i == '_' || i > 16ra0) + map[i] |= Munder; + if(i >= 'A' && i <= 'Z') + map[i] |= Mupper; + if(i >= 'a' && i <= 'z') + map[i] |= Mlower; + if(i >= 'A' && i <= 'F' || i >= 'a' && i <= 'f') + map[i] |= Mhex; + if(i == 'e' || i == 'E') + map[i] |= Mexp; + if(i == 'r' || i == 'R') + map[i] |= Mradix; + if(i == '-' || i == '+') + map[i] |= Msign; + if(i >= '0' && i <= '9') + map[i] |= Mdigit; + } + + for(i = 0; i < len keywords; i++) + enter(keywords[i].name, keywords[i].token); +} + +cmap(c: int): byte +{ + if(c<0) + return byte 0; + if(c<256) + return map[c]; + return Mlower; +} + +lexstart(in: string) +{ + savec = 0; + bstack = 0; + nfiles = 0; + addfile(ref File(in, 1, 0, -1, nil, 0, -1)); + bin = bins[bstack]; + lineno = 1; + linepos = Linestart; + + (srcdir, nil) = str->splitr(in, "/"); +} + +getc(): int +{ + if(c := savec){ + if(savec >= 0){ + linepos++; + savec = 0; + } + return c; + } + c = bin.getc(); + if(c < 0){ + savec = -1; + return savec; + } + linepos++; + return c; +} + +# +# dumps '\u0000' chararcters +# +ungetc(c: int) +{ + if(c > 0) + linepos--; + savec = c; +} + +addinclude(s: string) +{ + for(i := 0; i < MaxIncPath; i++){ + if(incpath[i] == nil){ + incpath[i] = s; + return; + } + } + fatal("out of include path space"); +} + +addfile(f: ref File): int +{ + if(lastfile >= nfiles) + lastfile = 0; + if(nfiles >= len files){ + nf := array[nfiles+32] of ref File; + nf[0:] = files; + files = nf; + } + files[nfiles] = f; + return nfiles++; +} + +# +# include a new file +# +includef(file: ref Sym) +{ + linestack[bstack] = (lineno, linepos); + bstack++; + if(bstack >= MaxInclude) + fatal(lineconv(lineno<<PosBits)+": include file depth too great"); + buf := file.name; + if(buf[0] != '/') + buf = srcdir+buf; + b := bufio->open(buf, Bufio->OREAD); + for(i := 0; b == nil && i < MaxIncPath && incpath[i] != nil && file.name[0] != '/'; i++){ + buf = incpath[i] + "/" + file.name; + b = bufio->open(buf, Bufio->OREAD); + } + bins[bstack] = b; + if(bins[bstack] == nil){ + yyerror("can't include "+file.name+": "+sprint("%r")); + bstack--; + }else{ + addfile(ref File(buf, lineno+1, -lineno, lineno, nil, 0, -1)); + lineno++; + linepos = Linestart; + } + bin = bins[bstack]; +} + +# +# we hit eof in the current file +# revert to the file which included it. +# +popinclude() +{ + savec = 0; + bstack--; + bin = bins[bstack]; + (oline, opos) := linestack[bstack]; + (f, ln) := fline(oline); + lineno++; + linepos = opos; + addfile(ref File(f.name, lineno, ln-lineno, f.in, f.act, f.actoff, -1)); +} + +# +# convert an absolute Line into a file and line within the file +# +fline(absline: int): (ref File, int) +{ + if(absline < files[lastfile].abs + || lastfile+1 < nfiles && absline >= files[lastfile+1].abs){ + lastfile = 0; + l := 0; + r := nfiles - 1; + while(l <= r){ + m := (r + l) / 2; + s := files[m].abs; + if(s <= absline){ + l = m + 1; + lastfile = m; + }else + r = m - 1; + } + } + return (files[lastfile], absline + files[lastfile].off); +} + +# +# read a comment; process #line file renamings +# +lexcom(): int +{ + i := 0; + buf := ""; + while((c := getc()) != '\n'){ + if(c == Bufio->EOF) + return -1; + buf[i++] = c; + } + + lineno++; + linepos = Linestart; + + if(len buf < 6 + || buf[len buf - 1] != '"' + || buf[:5] != "line " && buf[:5] != "line\t") + return 0; + for(s := 5; buf[s] == ' ' || buf[s] == '\t'; s++) + ; + if((cmap(buf[s]) & Mdigit) == byte 0) + return 0; + n := 0; + for(; (cmap(c = buf[s]) & Mdigit) != byte 0; s++) + n = n * 10 + c - '0'; + for(; buf[s] == ' ' || buf[s] == '\t'; s++) + ; + if(buf[s++] != '"') + return 0; + buf = buf[s:len buf - 1]; + f := files[nfiles - 1]; + if(n == f.off+lineno && buf == f.name) + return 1; + act := f.name; + actline := lineno + f.off; + if(f.act != nil){ + actline += f.actoff; + act = f.act; + } + addfile(ref File(buf, lineno, n-lineno, f.in, act, actline - n, -1)); + + return 1; +} + +curline(): Line +{ + return (lineno << PosBits) | (linepos & PosMask); +} + +lineconv(line: Line): string +{ + line >>= PosBits; + if(line < 0) + return "<noline>"; + (f, ln) := fline(line); + s := ""; + if(f.in >= 0){ + s = ": " + lineconv(f.in << PosBits); + } + if(f.act != nil) + s = " [ " + f.act + ":" + string(f.actoff+ln) + " ]" + s; + return f.name + ":" + string ln + s; +} + +posconv(s: Line): string +{ + if(s < 0) + return "nopos"; + spos := s & PosMask; + s >>= PosBits; + (f, ln) := fline(s); + return f.name + ":" + string ln + "." + string spos; +} + +srcconv(src: Src): string +{ + s := posconv(src.start); + s[len s] = ','; + s += posconv(src.stop); + return s; +} + +lexid(c: int): int +{ + id := ""; + i := 0; + for(;;){ + if(i < StrSize) + id[i++] = c; + c = getc(); + if(c == Bufio->EOF + || (cmap(c) & (Malpha|Mdigit)) == byte 0){ + ungetc(c); + break; + } + } + sym := enter(id, Lid); + t := sym.token; + if(t == Lid || t == Ltid) + yyctxt.lval.tok.v.idval = sym; + return t; +} + +maxfast := array[37] of +{ + 2 => 31, + 4 => 15, + 8 => 10, + 10 => 9, + 16 => 7, + 32 => 6, + * => 0, +}; + +strtoi(t: string, bbase: big): big +{ + # + # do the first part in ints + # + v := 0; + bv: big; + base := int bbase; + n := maxfast[base]; + + neg := 0; + i := 0; + if(i < len t && t[i] == '-'){ + neg = 1; + i++; + }else if(i < len t && t[i] == '+') + i++; + + for(; i < len t; i++){ + c := t[i]; + if(c >= '0' && c <= '9') + c -= '0'; + else if(c >= 'a' && c <= 'z') + c -= 'a' - 10; + else + c -= 'A' - 10; + if(c >= base){ + yyerror("digit '"+t[i:i+1]+"' is not radix "+string base); + return big -1; + } + if(i < n) + v = v * base + c; + else{ + if(i == n) + bv = big v; + bv = bv * bbase + big c; + } + } + if(i <= n) + bv = big v; + if(neg) + return -bv; + return bv; +} + +digit(c: int, base: int): int +{ + ck: byte; + cc: int; + + cc = c; + ck = cmap(c); + if((ck & Mdigit) != byte 0) + c -= '0'; + else if((ck & Mlower) != byte 0) + c = c - 'a' + 10; + else if((ck & Mupper) != byte 0) + c = c - 'A' + 10; + else if((ck & Munder) != byte 0) + ; + else + return -1; + if(c >= base){ + s := "z"; + s[0] = cc; + yyerror("digit '" + s + "' not radix " + string base); + } + return c; +} + +strtodb(t: string, base: int): real +{ + num, dem, rbase: real; + neg, eneg, dig, exp, c, d: int; + + t[len t] = 0; + + num = 0.0; + rbase = real base; + neg = 0; + dig = 0; + exp = 0; + eneg = 0; + + i := 0; + c = t[i++]; + if(c == '-' || c == '+'){ + if(c == '-') + neg = 1; + c = t[i++]; + } + while((d = digit(c, base)) >= 0){ + num = num*rbase + real d; + c = t[i++]; + } + if(c == '.') + c = t[i++]; + while((d = digit(c, base)) >= 0){ + num = num*rbase + real d; + dig++; + c = t[i++]; + } + if(c == 'e' || c == 'E'){ + c = t[i++]; + if(c == '-' || c == '+'){ + if(c == '-'){ + dig = -dig; + eneg = 1; + } + c = t[i++]; + } + while((d = digit(c, base)) >= 0){ + exp = exp*base + d; + c = t[i++]; + } + } + exp -= dig; + if(exp < 0){ + exp = -exp; + eneg = !eneg; + } + dem = rpow(rbase, exp); + if(eneg) + num /= dem; + else + num *= dem; + if(neg) + return -num; + return num; +} + +# +# parse a numeric identifier +# format [0-9]+(r[0-9A-Za-z]+)? +# or ([0-9]+(\.[0-9]*)?|\.[0-9]+)([eE][+-]?[0-9]+)? +# +lexnum(c: int): int +{ + Int, Radix, RadixSeen, Frac, ExpSeen, ExpSignSeen, Exp, FracB: con iota; + + i := 0; + buf := ""; + buf[i++] = c; + state := Int; + if(c == '.') + state = Frac; + radix := ""; + +done: for(;;){ + c = getc(); + if(c == Bufio->EOF){ + yyerror("end of file in numeric constant"); + return Leof; + } + + ck := cmap(c); + case state{ + Int => + if((ck & Mdigit) != byte 0) + break; + if((ck & Mexp) != byte 0){ + state = ExpSeen; + break; + } + if((ck & Mradix) != byte 0){ + radix = buf; + buf = ""; + i = 0; + state = RadixSeen; + break; + } + if(c == '.'){ + state = Frac; + break; + } + break done; + RadixSeen or + Radix => + if((ck & (Mdigit|Malpha)) != byte 0){ + state = Radix; + break; + } + if(c == '.'){ + state = FracB; + break; + } + break done; + Frac => + if((ck & Mdigit) != byte 0) + break; + if((ck & Mexp) != byte 0) + state = ExpSeen; + else + break done; + FracB => + if((ck & (Mdigit|Malpha)) != byte 0) + break; + break done; + ExpSeen => + if((ck & Msign) != byte 0){ + state = ExpSignSeen; + break; + } + if((ck & Mdigit) != byte 0){ + state = Exp; + break; + } + break done; + ExpSignSeen or + Exp => + if((ck & Mdigit) != byte 0){ + state = Exp; + break; + } + break done; + } + buf[i++] = c; + } + + ungetc(c); + v: big; + case state{ + * => + yyerror("malformed numerical constant '"+radix+buf+"'"); + yyctxt.lval.tok.v.ival = big 0; + return Lconst; + Radix => + v = strtoi(radix, big 10); + if(v < big 2 || v > big 36){ + yyerror("radix '"+radix+"' is not between 2 and 36"); + break; + } + v = strtoi(buf[1:], v); + Int => + v = strtoi(buf, big 10); + Frac or + Exp => + yyctxt.lval.tok.v.rval = real buf; + return Lrconst; + FracB => + v = strtoi(radix, big 10); + if(v < big 2 || v > big 36){ + yyerror("radix '"+radix+"' is not between 2 and 36"); + break; + } + yyctxt.lval.tok.v.rval = strtodb(buf[1:], int v); + return Lrconst; + } + yyctxt.lval.tok.v.ival = v; + return Lconst; +} + +escchar(): int +{ + c := getc(); + if(c == Bufio->EOF) + return Bufio->EOF; + if(c == 'u'){ + v := 0; + for(i := 0; i < 4; i++){ + c = getc(); + ck := cmap(c); + if(c == Bufio->EOF || (ck & (Mdigit|Mhex)) == byte 0){ + yyerror("malformed \\u escape sequence"); + ungetc(c); + break; + } + if((ck & Mdigit) != byte 0) + c -= '0'; + else if((ck & Mlower) != byte 0) + c = c - 'a' + 10; + else if((ck & Mupper) != byte 0) + c = c - 'A' + 10; + v = v * 16 + c; + } + return v; + } + if(c < len escmap && (v := escmap[c]) >= 0) + return v; + s := ""; + s[0] = c; + yyerror("unrecognized escape \\"+s); + return c; +} + +lexstring() +{ + s := ""; + i := 0; +loop: for(;;){ + case c := getc(){ + '\\' => + c = escchar(); + if(c != Bufio->EOF) + s[i++] = c; + Bufio->EOF => + yyerror("end of file in string constant"); + break loop; + '\n' => + yyerror("newline in string constant"); + lineno++; + linepos = Linestart; + break loop; + '"' => + break loop; + * => + s[i++] = c; + } + } + yyctxt.lval.tok.v.idval = enterstring(s); +} + +lex(): int +{ + for(;;){ + yyctxt.lval.tok.src.start = (lineno << PosBits) | (linepos & PosMask); + case c := getc(){ + Bufio->EOF => + bin.close(); + if(bstack == 0) + return Leof; + popinclude(); + '#' => + if(lexcom() < 0){ + bin.close(); + if(bstack == 0) + return Leof; + popinclude(); + } + '\n' => + lineno++; + linepos = Linestart; + ' ' or + '\t' or + '\r' or + '\v' => + ; + '"' => + lexstring(); + return Lsconst; + '\'' => + c = getc(); + if(c == '\\') + c = escchar(); + if(c == Bufio->EOF){ + yyerror("end of file in character constant"); + return Bufio->EOF; + }else + yyctxt.lval.tok.v.ival = big c; + c = getc(); + if(c != '\''){ + yyerror("missing closing '"); + ungetc(c); + } + return Lconst; + '(' or + ')' or + '[' or + ']' or + '{' or + '}' or + ',' or + ';' or + '~' => + return c; + ':' => + c = getc(); + if(c == ':') + return Lcons; + if(c == '=') + return Ldeclas; + ungetc(c); + return ':'; + '.' => + c = getc(); + ungetc(c); + if(c != Bufio->EOF && (cmap(c) & Mdigit) != byte 0) + return lexnum('.'); + return '.'; + '|' => + c = getc(); + if(c == '=') + return Loreq; + if(c == '|') + return Loror; + ungetc(c); + return '|'; + '&' => + c = getc(); + if(c == '=') + return Landeq; + if(c == '&') + return Landand; + ungetc(c); + return '&'; + '^' => + c = getc(); + if(c == '=') + return Lxoreq; + ungetc(c); + return '^'; + '*' => + c = getc(); + if(c == '=') + return Lmuleq; + if(c == '*'){ + c = getc(); + if(c == '=') + return Lexpeq; + ungetc(c); + return Lexp; + } + ungetc(c); + return '*'; + '/' => + c = getc(); + if(c == '=') + return Ldiveq; + ungetc(c); + return '/'; + '%' => + c = getc(); + if(c == '=') + return Lmodeq; + ungetc(c); + return '%'; + '=' => + c = getc(); + if(c == '=') + return Leq; + if(c == '>') + return Llabs; + ungetc(c); + return '='; + '!' => + c = getc(); + if(c == '=') + return Lneq; + ungetc(c); + return '!'; + '>' => + c = getc(); + if(c == '=') + return Lgeq; + if(c == '>'){ + c = getc(); + if(c == '=') + return Lrsheq; + ungetc(c); + return Lrsh; + } + ungetc(c); + return '>'; + '<' => + c = getc(); + if(c == '=') + return Lleq; + if(c == '-') + return Lcomm; + if(c == '<'){ + c = getc(); + if(c == '=') + return Llsheq; + ungetc(c); + return Llsh; + } + ungetc(c); + return '<'; + '+' => + c = getc(); + if(c == '=') + return Laddeq; + if(c == '+') + return Linc; + ungetc(c); + return '+'; + '-' => + c = getc(); + if(c == '=') + return Lsubeq; + if(c == '-') + return Ldec; + if(c == '>') + return Lmdot; + ungetc(c); + return '-'; + '0' to '9' => + return lexnum(c); + * => + if((cmap(c) & Malpha) != byte 0) + return lexid(c); + s := ""; + s[0] = c; + yyerror("unknown character '"+s+"'"); + } + } +} + +YYLEX.lex(nil: self ref YYLEX): int +{ + t := lex(); + yyctxt.lval.tok.src.stop = (lineno << PosBits) | (linepos & PosMask); + lasttok = t; + lastyylval = yyctxt.lval; + return t; +} + +toksp(t: int): string +{ + case(t){ + Lconst => + return sprint("%bd", lastyylval.tok.v.ival); + Lrconst => + return sprint("%f", lastyylval.tok.v.rval); + Lsconst => + return sprint("\"%s\"", lastyylval.tok.v.idval.name); + Ltid or Lid => + return lastyylval.tok.v.idval.name; + } + for(i := 0; i < len keywords; i++) + if(t == keywords[i].token) + return keywords[i].name; + for(i = 0; i < len tokwords; i++) + if(t == tokwords[i].token) + return tokwords[i].name; + if(t < 0 || t > 255) + fatal(sprint("bad token %d in toksp()", t)); + buf := "Z"; + buf[0] = t; + return buf; +} + +enterstring(name: string): ref Sym +{ + h := 0; + n := len name; + for(i := 0; i < n; i++){ + c := d := name[i]; + c ^= c << 6; + h += (c << 11) ^ (c >> 1); + h ^= (d << 14) + (d << 7) + (d << 4) + d; + } + + h &= HashSize-1; + for(s := strings[h]; s != nil; s = s.next){ + sn := s.name; + if(len sn == n && sn == name) + return s; + } + + + s = ref Sym; + s.token = -1; + s.name = name; + s.hash = h; + s.next = strings[h]; + strings[h] = s; + return s; +} + +stringcat(s, t: ref Sym): ref Sym +{ + return enterstring(s.name+t.name); +} + +enter(name: string, token: int): ref Sym +{ + h := 0; + n := len name; + for(i := 0; i < n; i++){ + c := d := name[i]; + c ^= c << 6; + h += (c << 11) ^ (c >> 1); + h ^= (d << 14) + (d << 7) + (d << 4) + d; + } + + h &= HashSize-1; + for(s := symbols[h]; s != nil; s = s.next){ + sn := s.name; + if(len sn == n && sn == name) + return s; + } + + if(token == 0) + token = Lid; + s = ref Sym; + s.token = token; + s.name = name; + s.hash = h; + s.next = symbols[h]; + symbols[h] = s; + return s; +} + +stringpr(sym: ref Sym): string +{ + s := sym.name; + n := len s; + if(n > 10) + n = 10; + sb := "\""; + for(i := 0; i < n; i++){ + case c := s[i]{ + '\\' or + '"' or + '\n' or + '\r' or + '\t' or + '\b' or + '\a' or + '\v' or + '\u0000' => + sb[len sb] = '\\'; + sb[len sb] = unescmap[c]; + * => + sb[len sb] = c; + } + } + if(n != len s) + sb += "..."; + sb[len sb] = '"'; + return sb; +} + +warn(line: Line, msg: string) +{ + if(errors || !dowarn) + return; + fprint(stderr, "%s: warning: %s\n", lineconv(line), msg); +} + +nwarn(n: ref Node, msg: string) +{ + if(errors || !dowarn) + return; + fprint(stderr, "%s: warning: %s\n", lineconv(n.src.start), msg); +} + +error(line: Line, msg: string) +{ + errors++; + if(errors > maxerr) + return; + fprint(stderr, "%s: %s\n", lineconv(line), msg); + if(errors == maxerr) + fprint(stderr, "too many errors, stopping\n"); +} + +nerror(n: ref Node, msg: string) +{ + errors++; + if(errors > maxerr) + return; + fprint(stderr, "%s: %s\n", lineconv(n.src.start), msg); + if(errors == maxerr) + fprint(stderr, "too many errors, stopping\n"); +} + +YYLEX.error(nil: self ref YYLEX, msg: string) +{ + errors++; + if(errors > maxerr) + return; + if(lasttok != 0) + fprint(stderr, "%s: near ` %s ` : %s\n", lineconv(lineno<<PosBits), toksp(lasttok), msg); + else + fprint(stderr, "%s: %s\n", lineconv(lineno<<PosBits), msg); + if(errors == maxerr) + fprint(stderr, "too many errors, stopping\n"); +} + +yyerror(msg: string) +{ + yyctxt.error(msg); +} + +fatal(msg: string) +{ + if(errors == 0 || fabort) + fprint(stderr, "fatal limbo compiler error: %s\n", msg); + if(bout != nil) + sys->remove(outfile); + if(fabort){ + n: ref Node; + if(n.ty == nil); # abort + } + raise "fail:error"; +} + +hex(v, n: int): string +{ + return sprint("%.*ux", n, v); +} + +bhex(v: big, n: int): string +{ + return sprint("%.*bux", n, v); +} |
