| @@ -0,0 +1,899 @@ |
| | 1 | implement M4; |
| | 2 | |
| | 3 | include "sys.m"; |
| | 4 | sys: Sys; |
| | 5 | |
| | 6 | include "draw.m"; |
| | 7 | |
| | 8 | include "bufio.m"; |
| | 9 | bufio: Bufio; |
| | 10 | Iobuf: import bufio; |
| | 11 | |
| | 12 | include "arg.m"; |
| | 13 | |
| | 14 | M4: module |
| | 15 | { |
| | 16 | init: fn(nil: ref Draw->Context, nil: list of string); |
| | 17 | }; |
| | 18 | |
| | 19 | NHASH: con 131; |
| | 20 | |
| | 21 | Name: adt { |
| | 22 | name: string; |
| | 23 | repl: string; |
| | 24 | impl: ref fn(nil: array of string); |
| | 25 | dol: int; # repl contains $[0-9] |
| | 26 | |
| | 27 | text: fn(n: self ref Name): string; |
| | 28 | }; |
| | 29 | |
| | 30 | names := array[NHASH] of list of ref Name; |
| | 31 | |
| | 32 | File: adt { |
| | 33 | name: string; |
| | 34 | line: int; |
| | 35 | fp: ref Iobuf; |
| | 36 | }; |
| | 37 | |
| | 38 | Param: adt { |
| | 39 | s: string; |
| | 40 | }; |
| | 41 | |
| | 42 | pushedback: string; |
| | 43 | pushedp := 0; # next available index in pushedback |
| | 44 | diverted := array[10] of string; |
| | 45 | curdiv := 0; |
| | 46 | curarg: ref Param; # non-nil if collecting argument string |
| | 47 | instack: list of ref File; |
| | 48 | lquote := '`'; |
| | 49 | rquote := '\''; |
| | 50 | initcom := "#"; |
| | 51 | endcom := "\n"; |
| | 52 | bout: ref Iobuf; |
| | 53 | |
| | 54 | init(nil: ref Draw->Context, args: list of string) |
| | 55 | { |
| | 56 | sys = load Sys Sys->PATH; |
| | 57 | bufio = load Bufio Bufio->PATH; |
| | 58 | |
| | 59 | bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); |
| | 60 | |
| | 61 | define("inferno", "inferno"); |
| | 62 | |
| | 63 | builtin("changecom", dochangecom); |
| | 64 | builtin("changequote", dochangequote); |
| | 65 | builtin("define", dodefine); |
| | 66 | builtin("divert", dodivert); |
| | 67 | builtin("divnum", dodivnum); |
| | 68 | builtin("dnl", dodnl); |
| | 69 | builtin("dumpdef", dodumpdef); |
| | 70 | builtin("errprint", doerrprint); |
| | 71 | builtin("eval", doeval); |
| | 72 | builtin("ifdef", doifdef); |
| | 73 | builtin("ifelse", doifelse); |
| | 74 | builtin("include", doinclude); |
| | 75 | builtin("incr", doincr); |
| | 76 | builtin("index", doindex); |
| | 77 | builtin("len", dolen); |
| | 78 | builtin("maketemp", domaketemp); |
| | 79 | builtin("sinclude", dosinclude); |
| | 80 | builtin("substr", dosubstr); |
| | 81 | builtin("translit", dotranslit); |
| | 82 | builtin("undefine", doundefine); |
| | 83 | builtin("undivert", doundivert); |
| | 84 | |
| | 85 | arg := load Arg Arg->PATH; |
| | 86 | arg->setusage("m4 [-Dname[=value]] [-Uname] [file ...]"); |
| | 87 | arg->init(args); |
| | 88 | |
| | 89 | while((o := arg->opt()) != 0){ |
| | 90 | case o { |
| | 91 | 'D' => |
| | 92 | s := arg->earg(); |
| | 93 | for(i := 0; i < len s; i++) |
| | 94 | if(s[i] == '='){ |
| | 95 | define(s[0: i], s[i+1:]); |
| | 96 | break; |
| | 97 | } |
| | 98 | if(i == len s) |
| | 99 | define(s[0: i], ""); |
| | 100 | 'U' => |
| | 101 | undefine(arg->earg()); |
| | 102 | * => |
| | 103 | arg->usage(); |
| | 104 | } |
| | 105 | } |
| | 106 | args = arg->argv(); |
| | 107 | arg = nil; |
| | 108 | |
| | 109 | if(args != nil){ |
| | 110 | for(; args != nil; args = tl args){ |
| | 111 | f := bufio->open(hd args, Sys->OREAD); |
| | 112 | if(f == nil) |
| | 113 | error(sys->sprint("can't open %s: %r", hd args)); |
| | 114 | pushfile(hd args, f); |
| | 115 | scan(); |
| | 116 | } |
| | 117 | }else{ |
| | 118 | pushfile("standard input", bufio->fopen(sys->fildes(0), Sys->OREAD)); |
| | 119 | scan(); |
| | 120 | } |
| | 121 | bout.flush(); |
| | 122 | } |
| | 123 | |
| | 124 | scan() |
| | 125 | { |
| | 126 | while((c := getc()) >= 0){ |
| | 127 | if(isalpha(c)) |
| | 128 | called(c); |
| | 129 | else if(c == lquote) |
| | 130 | quoted(); |
| | 131 | else if(initcom != nil && initcom[0] == c) |
| | 132 | comment(); |
| | 133 | else |
| | 134 | putc(c); |
| | 135 | } |
| | 136 | } |
| | 137 | |
| | 138 | error(s: string) |
| | 139 | { |
| | 140 | where := ""; |
| | 141 | if(instack != nil){ |
| | 142 | ios := hd instack; |
| | 143 | where = sys->sprint(" %s:%d:", ios.name, ios.line); |
| | 144 | } |
| | 145 | sys->fprint(sys->fildes(2), "m4:%s %s\n", where, s); |
| | 146 | raise "fail:error"; |
| | 147 | } |
| | 148 | |
| | 149 | pushfile(name: string, fp: ref Iobuf) |
| | 150 | { |
| | 151 | instack = ref File(name, 1, fp) :: instack; |
| | 152 | } |
| | 153 | |
| | 154 | called(c: int) |
| | 155 | { |
| | 156 | tok: string; |
| | 157 | do{ |
| | 158 | tok[len tok] = c; |
| | 159 | c = getc(); |
| | 160 | }while(isalpha(c) || c >= '0' && c <= '9'); |
| | 161 | def := lookup(tok); |
| | 162 | if(def == nil){ |
| | 163 | pushc(c); |
| | 164 | puts(tok); |
| | 165 | return; |
| | 166 | } |
| | 167 | if(c != '('){ # no parameters |
| | 168 | pushc(c); |
| | 169 | expand(def, array[] of {tok}); |
| | 170 | return; |
| | 171 | } |
| | 172 | # collect arguments, allowing for nested parentheses; |
| | 173 | # on ')' expand definition, further expanding $n references therein |
| | 174 | argstack := def.name :: nil; # $0 |
| | 175 | savearg := curarg; # save parameter (if any) for outer call |
| | 176 | curarg = ref Param(""); |
| | 177 | nesting := 0; # () depth |
| | 178 | skipws(); |
| | 179 | for(;;){ |
| | 180 | if((c = getc()) < 0) |
| | 181 | error("EOF in parameters"); |
| | 182 | if(isalpha(c)) |
| | 183 | called(c); |
| | 184 | else if(c == lquote) |
| | 185 | quoted(); |
| | 186 | else{ |
| | 187 | if(c == '(') |
| | 188 | nesting++; |
| | 189 | if(nesting > 0){ |
| | 190 | if(c == ')') |
| | 191 | nesting--; |
| | 192 | putc(c); |
| | 193 | }else if(c == ','){ |
| | 194 | argstack = curarg.s :: argstack; |
| | 195 | curarg = ref Param(""); |
| | 196 | skipws(); |
| | 197 | }else if(c == ')') |
| | 198 | break; |
| | 199 | else |
| | 200 | putc(c); |
| | 201 | } |
| | 202 | } |
| | 203 | argstack = curarg.s :: argstack; |
| | 204 | curarg = savearg; # restore outer parameter (if any) |
| | 205 | # build arguments |
| | 206 | narg := len argstack; |
| | 207 | args := array[narg] of string; |
| | 208 | for(; argstack != nil; argstack = tl argstack) |
| | 209 | args[--narg] = hd argstack; |
| | 210 | expand(def, args); |
| | 211 | } |
| | 212 | |
| | 213 | quoted() |
| | 214 | { |
| | 215 | nesting :=0; |
| | 216 | while((c := getc()) != rquote || nesting > 0){ |
| | 217 | if(c < 0) |
| | 218 | error("EOF in string"); |
| | 219 | if(c == rquote) |
| | 220 | nesting--; |
| | 221 | else if(c == lquote) |
| | 222 | nesting++; |
| | 223 | putc(c); |
| | 224 | } |
| | 225 | } |
| | 226 | |
| | 227 | comment() |
| | 228 | { |
| | 229 | for(i := 1; i < len initcom; i++){ |
| | 230 | if((c := getc()) != initcom[i]){ |
| | 231 | if(c < 0) |
| | 232 | error("EOF in comment"); |
| | 233 | pushc(c); |
| | 234 | pushs(initcom[1: i]); |
| | 235 | putc(initcom[0]); |
| | 236 | return; |
| | 237 | } |
| | 238 | } |
| | 239 | puts(initcom); |
| | 240 | for(i = 0; i < len endcom;){ |
| | 241 | c := getc(); |
| | 242 | if(c < 0) |
| | 243 | error("EOF in comment"); |
| | 244 | putc(c); |
| | 245 | if(c == endcom[i]) |
| | 246 | i++; |
| | 247 | else |
| | 248 | i = c == endcom[0]; |
| | 249 | } |
| | 250 | } |
| | 251 | |
| | 252 | skipws() |
| | 253 | { |
| | 254 | while(isspace(c := getc())) |
| | 255 | {} |
| | 256 | pushc(c); |
| | 257 | } |
| | 258 | |
| | 259 | isspace(c: int): int |
| | 260 | { |
| | 261 | return c == ' ' || c == '\t' || c == '\n' || c == '\r'; |
| | 262 | } |
| | 263 | |
| | 264 | isname(s: string): int |
| | 265 | { |
| | 266 | if(s == nil || !isalpha(s[0])) |
| | 267 | return 0; |
| | 268 | for(i := 1; i < len s; i++) |
| | 269 | if(!(isalpha(s[i]) || s[i]>='0' && s[i]<='9')) |
| | 270 | return 0; |
| | 271 | return 1; |
| | 272 | } |
| | 273 | |
| | 274 | isalpha(c: int): int |
| | 275 | { |
| | 276 | return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || c > 16r80; |
| | 277 | } |
| | 278 | |
| | 279 | hash(name: string): int |
| | 280 | { |
| | 281 | h := 0; |
| | 282 | for(i := 0; i < len name; i++) |
| | 283 | h = h*65599 + name[i]; |
| | 284 | return (h & ~(1<<31)) % NHASH; |
| | 285 | } |
| | 286 | |
| | 287 | builtin(name: string, impl: ref fn(nil: array of string)) |
| | 288 | { |
| | 289 | h := hash(name); |
| | 290 | n := ref Name(name, nil, impl, 0); |
| | 291 | names[h] = n :: names[h]; |
| | 292 | } |
| | 293 | |
| | 294 | define(name: string, repl: string) |
| | 295 | { |
| | 296 | h := hash(name); |
| | 297 | dol := hasdol(repl); |
| | 298 | for(l := names[h]; l != nil; l = tl l){ |
| | 299 | n := hd l; |
| | 300 | if(n.name == name){ |
| | 301 | n.impl = nil; |
| | 302 | n.repl = repl; |
| | 303 | n.dol = dol; |
| | 304 | return; |
| | 305 | } |
| | 306 | } |
| | 307 | n := ref Name(name, repl, nil, dol); |
| | 308 | names[h] = n :: names[h]; |
| | 309 | } |
| | 310 | |
| | 311 | lookup(name: string): ref Name |
| | 312 | { |
| | 313 | h := hash(name); |
| | 314 | for(l := names[h]; l != nil; l = tl l) |
| | 315 | if((hd l).name == name) |
| | 316 | return hd l; |
| | 317 | return nil; |
| | 318 | } |
| | 319 | |
| | 320 | undefine(name: string) |
| | 321 | { |
| | 322 | h := hash(name); |
| | 323 | rl: list of ref Name; |
| | 324 | for(l := names[h]; l != nil; l = tl l){ |
| | 325 | if((hd l).name == name){ |
| | 326 | l = tl l; |
| | 327 | for(; rl != nil; rl = tl rl) |
| | 328 | l = hd rl :: l; |
| | 329 | names[h] = l; |
| | 330 | return; |
| | 331 | }else |
| | 332 | rl = hd l :: rl; |
| | 333 | } |
| | 334 | } |
| | 335 | |
| | 336 | Name.text(n: self ref Name): string |
| | 337 | { |
| | 338 | if(n.impl != nil) |
| | 339 | return sys->sprint("builtin %q", n.name); |
| | 340 | return sys->sprint("%c%s%c", lquote, n.repl, rquote); |
| | 341 | } |
| | 342 | |
| | 343 | dodumpdef(args: array of string) |
| | 344 | { |
| | 345 | if(len args > 1){ |
| | 346 | for(i := 1; i < len args; i++) |
| | 347 | if((n := lookup(args[i])) != nil) |
| | 348 | sys->fprint(sys->fildes(2), "%q %s\n", n.name, n.text()); |
| | 349 | }else{ |
| | 350 | for(i := 0; i < len names; i++) |
| | 351 | for(l := names[i]; l != nil; l = tl l) |
| | 352 | sys->fprint(sys->fildes(2), "%q %s\n", (hd l).name, (hd l).text()); |
| | 353 | } |
| | 354 | } |
| | 355 | |
| | 356 | pushs(s: string) |
| | 357 | { |
| | 358 | for(i := len s; --i >= 0;) |
| | 359 | pushedback[pushedp++] = s[i]; |
| | 360 | } |
| | 361 | |
| | 362 | pushc(c: int) |
| | 363 | { |
| | 364 | if(c >= 0) |
| | 365 | pushedback[pushedp++] = c; |
| | 366 | } |
| | 367 | |
| | 368 | getc(): int |
| | 369 | { |
| | 370 | if(pushedp > 0) |
| | 371 | return pushedback[--pushedp]; |
| | 372 | for(; instack != nil; instack = tl instack){ |
| | 373 | ios := hd instack; |
| | 374 | c := ios.fp.getc(); |
| | 375 | if(c >= 0){ |
| | 376 | if(c == '\n') |
| | 377 | ios.line++; |
| | 378 | return c; |
| | 379 | } |
| | 380 | } |
| | 381 | return -1; |
| | 382 | } |
| | 383 | |
| | 384 | puts(s: string) |
| | 385 | { |
| | 386 | if(curarg != nil) |
| | 387 | curarg.s += s; |
| | 388 | else if(curdiv > 0) |
| | 389 | diverted[curdiv] += s; |
| | 390 | else if(curdiv == 0) |
| | 391 | bout.puts(s); |
| | 392 | } |
| | 393 | |
| | 394 | putc(c: int) |
| | 395 | { |
| | 396 | if(curarg != nil){ |
| | 397 | # stow in argument collection buffer |
| | 398 | curarg.s[len curarg.s] = c; |
| | 399 | }else if(curdiv > 0){ |
| | 400 | l := len diverted[curdiv]; |
| | 401 | diverted[curdiv][l] = c; |
| | 402 | }else if(curdiv == 0) |
| | 403 | bout.putc(c); |
| | 404 | } |
| | 405 | |
| | 406 | expand(def: ref Name, args: array of string) |
| | 407 | { |
| | 408 | if(def.impl != nil){ |
| | 409 | def.impl(args); |
| | 410 | return; |
| | 411 | } |
| | 412 | if(def.repl == def.name || def.repl == "$0"){ |
| | 413 | puts(def.name); |
| | 414 | return; |
| | 415 | } |
| | 416 | if(!def.dol || def.repl == nil){ |
| | 417 | pushs(def.repl); |
| | 418 | return; |
| | 419 | } |
| | 420 | # expand $n |
| | 421 | s := def.repl; |
| | 422 | for(i := len s; --i >= 1;){ |
| | 423 | if(s[i-1] == '$' && (c := s[i]-'0') >= 0 && c <= 9){ |
| | 424 | if(c < len args) |
| | 425 | pushs(args[c]); |
| | 426 | i--; |
| | 427 | }else |
| | 428 | pushc(s[i]); |
| | 429 | } |
| | 430 | if(i >= 0) |
| | 431 | pushc(s[0]); |
| | 432 | } |
| | 433 | |
| | 434 | hasdol(s: string): int |
| | 435 | { |
| | 436 | for(i := 0; i < len s; i++) |
| | 437 | if(s[i] == '$') |
| | 438 | return 1; |
| | 439 | return 0; |
| | 440 | } |
| | 441 | |
| | 442 | dodefine(args: array of string) |
| | 443 | { |
| | 444 | if(len args > 2) |
| | 445 | define(args[1], args[2]); |
| | 446 | else if(len args > 1) |
| | 447 | define(args[1], ""); |
| | 448 | } |
| | 449 | |
| | 450 | doundefine(args: array of string) |
| | 451 | { |
| | 452 | for(i := 1; i < len args; i++) |
| | 453 | undefine(args[i]); |
| | 454 | } |
| | 455 | |
| | 456 | doeval(args: array of string) |
| | 457 | { |
| | 458 | if(len args > 1) |
| | 459 | pushs(string eval(args[1])); |
| | 460 | } |
| | 461 | |
| | 462 | dodivert(args: array of string) |
| | 463 | { |
| | 464 | if(len args > 1){ |
| | 465 | n := int args[1]; |
| | 466 | if(n < 0 || n >= len diverted) |
| | 467 | n = -1; |
| | 468 | curdiv = n; |
| | 469 | }else |
| | 470 | curdiv = 0; |
| | 471 | } |
| | 472 | |
| | 473 | dodivnum(nil: array of string) |
| | 474 | { |
| | 475 | pushs(string curdiv); |
| | 476 | } |
| | 477 | |
| | 478 | doundivert(args: array of string) |
| | 479 | { |
| | 480 | if(len args <= 1){ # do all but current, in order |
| | 481 | for(i := 1; i < len diverted; i++){ |
| | 482 | if(i != curdiv){ |
| | 483 | puts(diverted[i]); |
| | 484 | diverted[i] = nil; |
| | 485 | } |
| | 486 | } |
| | 487 | }else{ # do those specified |
| | 488 | for(i := 1; i < len args; i++){ |
| | 489 | n := int args[i]; |
| | 490 | if(n > 0 && n < len diverted && n != curdiv){ |
| | 491 | puts(diverted[n]); |
| | 492 | diverted[n] = nil; |
| | 493 | } |
| | 494 | } |
| | 495 | } |
| | 496 | } |
| | 497 | |
| | 498 | doifdef(args: array of string) |
| | 499 | { |
| | 500 | if(len args < 2) |
| | 501 | return; |
| | 502 | n := lookup(args[1]); |
| | 503 | if(n != nil) |
| | 504 | pushs(args[2]); |
| | 505 | else if(len args > 2) |
| | 506 | pushs(args[3]); |
| | 507 | } |
| | 508 | |
| | 509 | doifelse(args: array of string) |
| | 510 | { |
| | 511 | for(i := 1; i+2 < len args; i += 3){ |
| | 512 | if(args[i] == args[i+1]){ |
| | 513 | pushs(args[i+2]); |
| | 514 | return; |
| | 515 | } |
| | 516 | } |
| | 517 | if(i > 2 && i == len args-1) |
| | 518 | pushs(args[i]); |
| | 519 | } |
| | 520 | |
| | 521 | doincr(args: array of string) |
| | 522 | { |
| | 523 | if(len args > 1) |
| | 524 | pushs(string (int args[1] + 1)); |
| | 525 | } |
| | 526 | |
| | 527 | doindex(args: array of string) |
| | 528 | { |
| | 529 | if(len args > 2){ |
| | 530 | a := args[1]; |
| | 531 | b := args[2]; |
| | 532 | for(i := 0; i+len b <= len a; i++){ |
| | 533 | if(a[i: i+len b] == b){ |
| | 534 | pushs(string i); |
| | 535 | return; |
| | 536 | } |
| | 537 | } |
| | 538 | pushs("-1"); |
| | 539 | } |
| | 540 | } |
| | 541 | |
| | 542 | doinclude(args: array of string) |
| | 543 | { |
| | 544 | for(i := len args; --i >= 1;){ |
| | 545 | fp := bufio->open(args[i], Sys->OREAD); |
| | 546 | if(fp == nil) |
| | 547 | error(sys->sprint("can't open %s: %r", args[i])); |
| | 548 | pushfile(args[i], fp); |
| | 549 | } |
| | 550 | } |
| | 551 | |
| | 552 | dosinclude(args: array of string) |
| | 553 | { |
| | 554 | for(i := len args; --i >= 1;){ |
| | 555 | fp := bufio->open(args[i], Sys->OREAD); |
| | 556 | if(fp != nil) |
| | 557 | pushfile(args[i], fp); |
| | 558 | } |
| | 559 | } |
| | 560 | |
| | 561 | clip(v, l, u: int): int |
| | 562 | { |
| | 563 | if(v < l) |
| | 564 | return l; |
| | 565 | if(v > u) |
| | 566 | return u; |
| | 567 | return v; |
| | 568 | } |
| | 569 | |
| | 570 | dosubstr(args: array of string) |
| | 571 | { |
| | 572 | if(len args > 2){ |
| | 573 | l := len args[1]; |
| | 574 | o := clip(int args[2], 0, l); |
| | 575 | n := l; |
| | 576 | if(len args > 3) |
| | 577 | n = clip(int args[3], 0, l); |
| | 578 | if((n += o) > l) |
| | 579 | n = l; |
| | 580 | pushs(args[1][o: n]); |
| | 581 | } |
| | 582 | } |
| | 583 | |
| | 584 | cindex(s: string, c: int): int |
| | 585 | { |
| | 586 | for(i := 0; i < len s; i++) |
| | 587 | if(s[i] == c) |
| | 588 | return i; |
| | 589 | return -1; |
| | 590 | } |
| | 591 | |
| | 592 | dotranslit(args: array of string) |
| | 593 | { |
| | 594 | if(len args < 3) |
| | 595 | return; |
| | 596 | s := args[1]; |
| | 597 | f := args[2]; |
| | 598 | t := ""; |
| | 599 | if(len args > 3) |
| | 600 | t = args[3]; |
| | 601 | o := ""; |
| | 602 | for(i := 0; i < len s; i++){ |
| | 603 | if((j := cindex(f, s[i])) >= 0){ |
| | 604 | if(j < len t) |
| | 605 | o[len o] = t[j]; |
| | 606 | }else |
| | 607 | o[len o] = s[i]; |
| | 608 | } |
| | 609 | pushs(o); |
| | 610 | } |
| | 611 | |
| | 612 | doerrprint(args: array of string) |
| | 613 | { |
| | 614 | s := ""; |
| | 615 | for(i := 1; i < len args; i++) |
| | 616 | s += " "+args[i]; |
| | 617 | if(s != nil) |
| | 618 | sys->fprint(sys->fildes(2), "m4:%s\n", s); |
| | 619 | } |
| | 620 | |
| | 621 | dolen(args: array of string) |
| | 622 | { |
| | 623 | if(len args > 1) |
| | 624 | puts(string len args[1]); |
| | 625 | } |
| | 626 | |
| | 627 | dochangecom(args: array of string) |
| | 628 | { |
| | 629 | case len args { |
| | 630 | 1 => |
| | 631 | initcom = ""; |
| | 632 | endcom = ""; |
| | 633 | 2 => |
| | 634 | initcom = args[1]; |
| | 635 | endcom = "\n"; |
| | 636 | * => |
| | 637 | initcom = args[1]; |
| | 638 | endcom = args[2]; |
| | 639 | if(endcom == "") |
| | 640 | endcom = "\n"; |
| | 641 | } |
| | 642 | } |
| | 643 | |
| | 644 | dochangequote(args: array of string) |
| | 645 | { |
| | 646 | case len args { |
| | 647 | 1 => |
| | 648 | lquote = '`'; |
| | 649 | rquote = '\''; |
| | 650 | 2 => |
| | 651 | if(args[1] != nil) |
| | 652 | lquote = rquote = args[1][0]; |
| | 653 | * => |
| | 654 | if(args[1] != nil) |
| | 655 | lquote = args[1][0]; |
| | 656 | if(args[2] != nil) |
| | 657 | rquote = args[2][0]; |
| | 658 | } |
| | 659 | } |
| | 660 | |
| | 661 | dodnl(nil: array of string) |
| | 662 | { |
| | 663 | while((c := getc()) >= 0 && c != '\n') |
| | 664 | {} |
| | 665 | } |
| | 666 | |
| | 667 | domaketemp(args: array of string) |
| | 668 | { |
| | 669 | if(len args > 1) |
| | 670 | pushs(mktemp(args[1])); |
| | 671 | } |
| | 672 | |
| | 673 | sysname: string; |
| | 674 | |
| | 675 | mktemp(s: string): string |
| | 676 | { |
| | 677 | if(sysname == nil) |
| | 678 | sysname = readfile("/dev/sysname", "m4"); |
| | 679 | # trim trailing X's |
| | 680 | for (x := len s; --x >= 0;) |
| | 681 | if(s[x] == 'X'){ |
| | 682 | while(x > 0 && s[x-1] == 'X') |
| | 683 | x--; |
| | 684 | s = s[0: x]; |
| | 685 | break; |
| | 686 | } |
| | 687 | # add system name, process ID and 'a' |
| | 688 | s += sys->sprint(".%s.%.10uda", sysname, sys->pctl(0, nil)); |
| | 689 | while(sys->stat(s).t0 >= 0){ |
| | 690 | if(s[len s-1] == 'z') |
| | 691 | error("out of temp files: "+s); |
| | 692 | s[len s-1]++; |
| | 693 | } |
| | 694 | return s; |
| | 695 | } |
| | 696 | |
| | 697 | readfile(name: string, default: string): string |
| | 698 | { |
| | 699 | fd := sys->open(name, Sys->OREAD); |
| | 700 | if(fd == nil) |
| | 701 | return default; |
| | 702 | buf := array[Sys->NAMEMAX] of byte; |
| | 703 | n := sys->read(fd, buf, len buf); |
| | 704 | if(n <= 0) |
| | 705 | return default; |
| | 706 | return string buf[0: n]; |
| | 707 | } |
| | 708 | |
| | 709 | # |
| | 710 | # expressions provided use Limbo operators (C with signed shift and **), |
| | 711 | # instead of original m4 ones (where | and & were || and &&, and ^ was power), |
| | 712 | # but that's true of later unix m4 implementations too |
| | 713 | # |
| | 714 | |
| | 715 | Oeof, Ogok, Oge, Ole, One, Oeq, Opow, Oand, Oor, Orsh, Olsh, Odigits: con 'a'+iota; |
| | 716 | Syntax, Badeval: exception; |
| | 717 | evalin: string; |
| | 718 | evalp := 0; |
| | 719 | |
| | 720 | eval(s: string): int |
| | 721 | { |
| | 722 | evalin = s; |
| | 723 | evalp = 0; |
| | 724 | looked = -1; |
| | 725 | { |
| | 726 | v := expr(1); |
| | 727 | if(evalp < len evalin) |
| | 728 | raise Syntax; |
| | 729 | return v; |
| | 730 | }exception{ |
| | 731 | Syntax => |
| | 732 | error(sys->sprint("syntax error: %q %q", evalin[0: evalp], evalin[evalp:])); |
| | 733 | return 0; |
| | 734 | Badeval => |
| | 735 | error(sys->sprint("zero divide in %q", evalin)); |
| | 736 | return 0; |
| | 737 | } |
| | 738 | } |
| | 739 | |
| | 740 | eval1(op: int, v1, v2: int): int raises Badeval |
| | 741 | { |
| | 742 | case op{ |
| | 743 | '+' => return v1 + v2; |
| | 744 | '-' => return v1 - v2; |
| | 745 | '*' => return v1 * v2; |
| | 746 | '%' => |
| | 747 | if(v2 == 0) |
| | 748 | raise Badeval; # division by zero |
| | 749 | return v1 % v2; |
| | 750 | '/' => |
| | 751 | if(v2 == 0) |
| | 752 | raise Badeval; # division by zero |
| | 753 | return v1 / v2; |
| | 754 | Opow => |
| | 755 | if(v2 < 0) |
| | 756 | raise Badeval; |
| | 757 | return v1 ** v2; |
| | 758 | '&' => return v1 & v2; |
| | 759 | '|' => return v1 | v2; |
| | 760 | '^' => return v1 ^ v2; |
| | 761 | Olsh => return v1 << v2; |
| | 762 | Orsh => return v1 >> v2; |
| | 763 | Oand => return v1 && v2; |
| | 764 | Oor => return v1 || v2; |
| | 765 | '<' => return v1 < v2; |
| | 766 | '>' => return v1 > v2; |
| | 767 | Ole => return v1 <= v2; |
| | 768 | Oge => return v1 >= v2; |
| | 769 | One => return v1 != v2; |
| | 770 | Oeq => return v1 == v2; |
| | 771 | * => |
| | 772 | sys->print("unknown op: %c\n", op); # shouldn't happen |
| | 773 | raise Badeval; |
| | 774 | } |
| | 775 | } |
| | 776 | |
| | 777 | priority(c: int): int |
| | 778 | { |
| | 779 | case c { |
| | 780 | Oor => return 1; |
| | 781 | Oand => return 2; |
| | 782 | '|' => return 3; |
| | 783 | '^' => return 4; |
| | 784 | '&' => return 5; |
| | 785 | Oeq or One => return 6; |
| | 786 | '<' or '>' or Oge or Ole => return 7; |
| | 787 | Olsh or Orsh => return 8; |
| | 788 | '+' or '-' => return 9; |
| | 789 | '*' or '/' or '%' => return 10; |
| | 790 | Opow => return 11; |
| | 791 | * => return 0; |
| | 792 | } |
| | 793 | } |
| | 794 | |
| | 795 | rightassoc(c: int): int |
| | 796 | { |
| | 797 | return c == Opow; |
| | 798 | } |
| | 799 | |
| | 800 | expr(prec: int): int raises(Syntax, Badeval) |
| | 801 | { |
| | 802 | { |
| | 803 | v := primary(); |
| | 804 | while(priority(look()) >= prec){ |
| | 805 | op := lex(); |
| | 806 | r := priority(op) + !rightassoc(op); |
| | 807 | v = eval1(op, v, expr(r)); |
| | 808 | } |
| | 809 | return v; |
| | 810 | }exception{ |
| | 811 | Syntax or Badeval => |
| | 812 | raise; |
| | 813 | } |
| | 814 | } |
| | 815 | |
| | 816 | primary(): int raises Syntax |
| | 817 | { |
| | 818 | { |
| | 819 | case lex() { |
| | 820 | '(' => |
| | 821 | v := expr(1); |
| | 822 | if(lex() != ')') |
| | 823 | raise Syntax; |
| | 824 | return v; |
| | 825 | '+' => |
| | 826 | return primary(); |
| | 827 | '-' => |
| | 828 | return -primary(); |
| | 829 | '!' => |
| | 830 | return !primary(); |
| | 831 | '~' => |
| | 832 | return ~primary(); |
| | 833 | Odigits => |
| | 834 | return yylval; |
| | 835 | * => |
| | 836 | raise Syntax; |
| | 837 | } |
| | 838 | }exception{ |
| | 839 | Syntax => |
| | 840 | raise; |
| | 841 | } |
| | 842 | } |
| | 843 | |
| | 844 | yylval := 0; |
| | 845 | looked := -1; |
| | 846 | |
| | 847 | look(): int |
| | 848 | { |
| | 849 | looked = lex(); |
| | 850 | return looked; |
| | 851 | } |
| | 852 | |
| | 853 | lex(): int |
| | 854 | { |
| | 855 | if((c := looked) >= 0){ |
| | 856 | looked = -1; |
| | 857 | return c; # if Odigits, assumes yylval untouched |
| | 858 | } |
| | 859 | while(evalp < len evalin && isspace(evalin[evalp])) |
| | 860 | evalp++; |
| | 861 | if(evalp >= len evalin) |
| | 862 | return Oeof; |
| | 863 | case c = evalin[evalp++] { |
| | 864 | '*' => |
| | 865 | return ifnext('*', Opow, '*'); |
| | 866 | '>' => |
| | 867 | return ifnext('=', Oge, ifnext('>', Orsh, '>')); |
| | 868 | '<' => |
| | 869 | return ifnext('=', Ole, ifnext('<', Olsh, '<')); |
| | 870 | '=' => |
| | 871 | return ifnext('=', Oeq, Oeq); |
| | 872 | '!' => |
| | 873 | return ifnext('=', One, '!'); |
| | 874 | '|' => |
| | 875 | return ifnext('|', Oor, '|'); |
| | 876 | '&' => |
| | 877 | return ifnext('&', Oand, '&'); |
| | 878 | '0' to '9' => |
| | 879 | evalp--; |
| | 880 | n := 0; |
| | 881 | while(evalp < len evalin && (c = evalin[evalp]) >= '0' && c <= '9'){ |
| | 882 | n = n*10 + (c-'0'); |
| | 883 | evalp++; |
| | 884 | } |
| | 885 | yylval = n; |
| | 886 | return Odigits; |
| | 887 | * => |
| | 888 | return c; |
| | 889 | } |
| | 890 | } |
| | 891 | |
| | 892 | ifnext(a, t, f: int): int |
| | 893 | { |
| | 894 | if(evalp < len evalin && evalin[evalp] == a){ |
| | 895 | evalp++; |
| | 896 | return t; |
| | 897 | } |
| | 898 | return f; |
| | 899 | } |