| @@ -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 | +} |