implement M4; include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "sh.m"; include "arg.m"; M4: module{ init: fn(nil: ref Draw->Context, nil: list of string);}; NHASH: con 131; Name: adt { name: string; repl: string; impl: ref fn(nil: array of string); dol: int; # repl contains $[0-9] asis: int; # replacement text not rescanned text: fn(n: self ref Name): string;}; names := array[NHASH] of list of ref Name; File: adt { name: string; line: int; fp: ref Iobuf;}; Param: adt { s: string;}; pushedback: string;pushedp := 0; # next available index in pushedbackdiverted := array[10] of string;curdiv := 0;curarg: ref Param; # non-nil if collecting argument stringinstack: list of ref File;lquote := '`';rquote := '\'';initcom := "#";endcom := "\n";bout: ref Iobuf;sh: Sh; init(nil: ref Draw->Context, args: list of string){ sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); define("inferno", "inferno", 0); builtin("changecom", dochangecom); builtin("changequote", dochangequote); builtin("copydef", docopydef); builtin("define", dodefine); builtin("divert", dodivert); builtin("divnum", dodivnum); builtin("dnl", dodnl); builtin("dumpdef", dodumpdef); builtin("errprint", doerrprint); builtin("eval", doeval); builtin("ifdef", doifdef); builtin("ifelse", doifelse); builtin("include", doinclude); builtin("incr", doincr); builtin("index", doindex); builtin("len", dolen); builtin("maketemp", domaketemp); builtin("sinclude", dosinclude); builtin("substr", dosubstr); builtin("syscmd", dosyscmd); builtin("translit", dotranslit); builtin("undefine", doundefine); builtin("undivert", doundivert); arg := load Arg Arg->PATH; arg->setusage("m4 [-Dname[=value]] [-Qname[=value]] [-Uname] [file ...]"); arg->init(args); while((o := arg->opt()) != 0){ case o { 'D' => argdefine(arg->earg(), 0); 'Q' => argdefine(arg->earg(), 1); 'U' => undefine(arg->earg()); * => arg->usage(); } } args = arg->argv(); arg = nil; if(args != nil){ for(; args != nil; args = tl args){ f := bufio->open(hd args, Sys->OREAD); if(f == nil) error(sys->sprint("can't open %s: %r", hd args)); pushfile(hd args, f); scan(); } }else{ pushfile("standard input", bufio->fopen(sys->fildes(0), Sys->OREAD)); scan(); } bout.flush();} argdefine(s: string, asis: int){ text := ""; for(i := 0; i < len s; i++) if(s[i] == '='){ text = s[i+1:]; break; } n := lookup(s[0: i]); if(n != nil && n.impl != nil) error(sys->sprint("can't redefine built-in %s", s[0: i])); define(s[0: i], text, asis);} scan(){ while((c := getc()) >= 0){ if(isalpha(c)) called(c); else if(c == lquote) quoted(); else if(initcom != nil && initcom[0] == c) comment(); else putc(c); }} error(s: string){ where := ""; if(instack != nil){ ios := hd instack; where = sys->sprint(" %s:%d:", ios.name, ios.line); } sys->fprint(sys->fildes(2), "m4:%s %s\n", where, s); raise "fail:error";} pushfile(name: string, fp: ref Iobuf){ instack = ref File(name, 1, fp) :: instack;} called(c: int){ tok: string; do{ tok[len tok] = c; c = getc(); }while(isalpha(c) || c >= '0' && c <= '9'); def := lookup(tok); if(def == nil){ pushc(c); puts(tok); return; } if(c != '(' || def.asis){ # no parameters pushc(c); expand(def, array[] of {tok}); return; } # collect arguments, allowing for nested parentheses; # on ')' expand definition, further expanding $n references therein argstack := def.name :: nil; # $0 savearg := curarg; # save parameter (if any) for outer call curarg = ref Param(""); nesting := 0; # () depth skipws(); for(;;){ if((c = getc()) < 0) error("EOF in parameters"); if(isalpha(c)) called(c); else if(c == lquote) quoted(); else{ if(c == '(') nesting++; if(nesting > 0){ if(c == ')') nesting--; putc(c); }else if(c == ','){ argstack = curarg.s :: argstack; curarg = ref Param(""); skipws(); }else if(c == ')') break; else putc(c); } } argstack = curarg.s :: argstack; curarg = savearg; # restore outer parameter (if any) # build arguments narg := len argstack; args := array[narg] of string; for(; argstack != nil; argstack = tl argstack) args[--narg] = hd argstack; expand(def, args);} quoted(){ nesting :=0; while((c := getc()) != rquote || nesting > 0){ if(c < 0) error("EOF in string"); if(c == rquote) nesting--; else if(c == lquote) nesting++; putc(c); }} comment(){ for(i := 1; i < len initcom; i++){ if((c := getc()) != initcom[i]){ if(c < 0) error("EOF in comment"); pushc(c); pushs(initcom[1: i]); putc(initcom[0]); return; } } puts(initcom); for(i = 0; i < len endcom;){ c := getc(); if(c < 0) error("EOF in comment"); putc(c); if(c == endcom[i]) i++; else i = c == endcom[0]; }} skipws(){ while(isspace(c := getc())) {} pushc(c);} isspace(c: int): int{ return c == ' ' || c == '\t' || c == '\n' || c == '\r';} isname(s: string): int{ if(s == nil || !isalpha(s[0])) return 0; for(i := 1; i < len s; i++) if(!(isalpha(s[i]) || s[i]>='0' && s[i]<='9')) return 0; return 1;} isalpha(c: int): int{ return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || c > 16rA0;} hash(name: string): int{ h := 0; for(i := 0; i < len name; i++) h = h*65599 + name[i]; return (h & ~(1<<31)) % NHASH;} builtin(name: string, impl: ref fn(nil: array of string)){ h := hash(name); n := ref Name(name, nil, impl, 0, 0); names[h] = n :: names[h];} define(name: string, repl: string, asis: int){ h := hash(name); dol := hasdol(repl); for(l := names[h]; l != nil; l = tl l){ n := hd l; if(n.name == name){ *n = Name(name, repl, nil, dol, asis); return; } } n := ref Name(name, repl, nil, dol, asis); names[h] = n :: names[h];} lookup(name: string): ref Name{ h := hash(name); for(l := names[h]; l != nil; l = tl l) if((hd l).name == name) return hd l; return nil;} undefine(name: string){ h := hash(name); rl: list of ref Name; for(l := names[h]; l != nil; l = tl l){ if((hd l).name == name){ l = tl l; for(; rl != nil; rl = tl rl) l = hd rl :: l; names[h] = l; return; }else rl = hd l :: rl; }} Name.text(n: self ref Name): string{ if(n.impl != nil) return sys->sprint("builtin %q", n.name); return sys->sprint("%c%s%c", lquote, n.repl, rquote);} dodumpdef(args: array of string){ if(len args > 1){ for(i := 1; i < len args; i++) if((n := lookup(args[i])) != nil) sys->fprint(sys->fildes(2), "%q %s\n", n.name, n.text()); }else{ for(i := 0; i < len names; i++) for(l := names[i]; l != nil; l = tl l) sys->fprint(sys->fildes(2), "%q %s\n", (hd l).name, (hd l).text()); }} pushs(s: string){ for(i := len s; --i >= 0;) pushedback[pushedp++] = s[i];} pushc(c: int){ if(c >= 0) pushedback[pushedp++] = c;} getc(): int{ if(pushedp > 0) return pushedback[--pushedp]; for(; instack != nil; instack = tl instack){ ios := hd instack; c := ios.fp.getc(); if(c >= 0){ if(c == '\n') ios.line++; return c; } } return -1;} puts(s: string){ if(curarg != nil) curarg.s += s; else if(curdiv > 0) diverted[curdiv] += s; else if(curdiv == 0) bout.puts(s);} putc(c: int){ if(curarg != nil){ # stow in argument collection buffer curarg.s[len curarg.s] = c; }else if(curdiv > 0){ l := len diverted[curdiv]; diverted[curdiv][l] = c; }else if(curdiv == 0) bout.putc(c);} expand(def: ref Name, args: array of string){ if(def.impl != nil){ def.impl(args); return; } if(def.repl == def.name || def.repl == "$0"){ puts(def.name); return; } if(!def.dol || def.repl == nil){ pushs(def.repl); return; } # expand $n s := def.repl; for(i := len s; --i >= 1;){ if(s[i-1] == '$' && (c := s[i]-'0') >= 0 && c <= 9){ if(c < len args) pushs(args[c]); i--; }else pushc(s[i]); } if(i >= 0) pushc(s[0]);} hasdol(s: string): int{ for(i := 0; i < len s; i++) if(s[i] == '$') return 1; return 0;} dodefine(args: array of string){ if(len args > 2) define(args[1], args[2], 0); else if(len args > 1) define(args[1], "", 0);} doundefine(args: array of string){ for(i := 1; i < len args; i++) undefine(args[i]);} docopydef(args: array of string){ if(len args > 2 && args[1] != args[2]){ undefine(args[2]); if((n := lookup(args[1])) != nil){ if(n.impl == nil) define(args[2], n.repl, n.asis); else builtin(args[2], n.impl); }else define(args[2], "", 0); }} doeval(args: array of string){ if(len args > 1) pushs(string eval(args[1]));} dodivert(args: array of string){ if(len args > 1){ n := int args[1]; if(n < 0 || n >= len diverted) n = -1; curdiv = n; }else curdiv = 0;} dodivnum(nil: array of string)Lines 1–500 of 946. Load more
implement M4; include "sys.m"; sys: Sys; include "draw.m"; include "bufio.m"; bufio: Bufio; Iobuf: import bufio; include "sh.m"; include "arg.m"; M4: module { init: fn(nil: ref Draw->Context, nil: list of string); }; NHASH: con 131; Name: adt { name: string; repl: string; impl: ref fn(nil: array of string); dol: int; # repl contains $[0-9] asis: int; # replacement text not rescanned text: fn(n: self ref Name): string; }; names := array[NHASH] of list of ref Name; File: adt { name: string; line: int; fp: ref Iobuf; }; Param: adt { s: string; }; pushedback: string; pushedp := 0; # next available index in pushedback diverted := array[10] of string; curdiv := 0; curarg: ref Param; # non-nil if collecting argument string instack: list of ref File; lquote := '`'; rquote := '\''; initcom := "#"; endcom := "\n"; bout: ref Iobuf; sh: Sh; init(nil: ref Draw->Context, args: list of string) { sys = load Sys Sys->PATH; bufio = load Bufio Bufio->PATH; bout = bufio->fopen(sys->fildes(1), Sys->OWRITE); define("inferno", "inferno", 0); builtin("changecom", dochangecom); builtin("changequote", dochangequote); builtin("copydef", docopydef); builtin("define", dodefine); builtin("divert", dodivert); builtin("divnum", dodivnum); builtin("dnl", dodnl); builtin("dumpdef", dodumpdef); builtin("errprint", doerrprint); builtin("eval", doeval); builtin("ifdef", doifdef); builtin("ifelse", doifelse); builtin("include", doinclude); builtin("incr", doincr); builtin("index", doindex); builtin("len", dolen); builtin("maketemp", domaketemp); builtin("sinclude", dosinclude); builtin("substr", dosubstr); builtin("syscmd", dosyscmd); builtin("translit", dotranslit); builtin("undefine", doundefine); builtin("undivert", doundivert); arg := load Arg Arg->PATH; arg->setusage("m4 [-Dname[=value]] [-Qname[=value]] [-Uname] [file ...]"); arg->init(args); while((o := arg->opt()) != 0){ case o { 'D' => argdefine(arg->earg(), 0); 'Q' => argdefine(arg->earg(), 1); 'U' => undefine(arg->earg()); * => arg->usage(); } } args = arg->argv(); arg = nil; if(args != nil){ for(; args != nil; args = tl args){ f := bufio->open(hd args, Sys->OREAD); if(f == nil) error(sys->sprint("can't open %s: %r", hd args)); pushfile(hd args, f); scan(); } }else{ pushfile("standard input", bufio->fopen(sys->fildes(0), Sys->OREAD)); scan(); } bout.flush(); } argdefine(s: string, asis: int) { text := ""; for(i := 0; i < len s; i++) if(s[i] == '='){ text = s[i+1:]; break; } n := lookup(s[0: i]); if(n != nil && n.impl != nil) error(sys->sprint("can't redefine built-in %s", s[0: i])); define(s[0: i], text, asis); } scan() { while((c := getc()) >= 0){ if(isalpha(c)) called(c); else if(c == lquote) quoted(); else if(initcom != nil && initcom[0] == c) comment(); else putc(c); } } error(s: string) { where := ""; if(instack != nil){ ios := hd instack; where = sys->sprint(" %s:%d:", ios.name, ios.line); } sys->fprint(sys->fildes(2), "m4:%s %s\n", where, s); raise "fail:error"; } pushfile(name: string, fp: ref Iobuf) { instack = ref File(name, 1, fp) :: instack; } called(c: int) { tok: string; do{ tok[len tok] = c; c = getc(); }while(isalpha(c) || c >= '0' && c <= '9'); def := lookup(tok); if(def == nil){ pushc(c); puts(tok); return; } if(c != '(' || def.asis){ # no parameters pushc(c); expand(def, array[] of {tok}); return; } # collect arguments, allowing for nested parentheses; # on ')' expand definition, further expanding $n references therein argstack := def.name :: nil; # $0 savearg := curarg; # save parameter (if any) for outer call curarg = ref Param(""); nesting := 0; # () depth skipws(); for(;;){ if((c = getc()) < 0) error("EOF in parameters"); if(isalpha(c)) called(c); else if(c == lquote) quoted(); else{ if(c == '(') nesting++; if(nesting > 0){ if(c == ')') nesting--; putc(c); }else if(c == ','){ argstack = curarg.s :: argstack; curarg = ref Param(""); skipws(); }else if(c == ')') break; else putc(c); } } argstack = curarg.s :: argstack; curarg = savearg; # restore outer parameter (if any) # build arguments narg := len argstack; args := array[narg] of string; for(; argstack != nil; argstack = tl argstack) args[--narg] = hd argstack; expand(def, args); } quoted() { nesting :=0; while((c := getc()) != rquote || nesting > 0){ if(c < 0) error("EOF in string"); if(c == rquote) nesting--; else if(c == lquote) nesting++; putc(c); } } comment() { for(i := 1; i < len initcom; i++){ if((c := getc()) != initcom[i]){ if(c < 0) error("EOF in comment"); pushc(c); pushs(initcom[1: i]); putc(initcom[0]); return; } } puts(initcom); for(i = 0; i < len endcom;){ c := getc(); if(c < 0) error("EOF in comment"); putc(c); if(c == endcom[i]) i++; else i = c == endcom[0]; } } skipws() { while(isspace(c := getc())) {} pushc(c); } isspace(c: int): int { return c == ' ' || c == '\t' || c == '\n' || c == '\r'; } isname(s: string): int { if(s == nil || !isalpha(s[0])) return 0; for(i := 1; i < len s; i++) if(!(isalpha(s[i]) || s[i]>='0' && s[i]<='9')) return 0; return 1; } isalpha(c: int): int { return c >= 'a' && c <= 'z' || c >= 'A' && c <= 'Z' || c == '_' || c > 16rA0; } hash(name: string): int { h := 0; for(i := 0; i < len name; i++) h = h*65599 + name[i]; return (h & ~(1<<31)) % NHASH; } builtin(name: string, impl: ref fn(nil: array of string)) { h := hash(name); n := ref Name(name, nil, impl, 0, 0); names[h] = n :: names[h]; } define(name: string, repl: string, asis: int) { h := hash(name); dol := hasdol(repl); for(l := names[h]; l != nil; l = tl l){ n := hd l; if(n.name == name){ *n = Name(name, repl, nil, dol, asis); return; } } n := ref Name(name, repl, nil, dol, asis); names[h] = n :: names[h]; } lookup(name: string): ref Name { h := hash(name); for(l := names[h]; l != nil; l = tl l) if((hd l).name == name) return hd l; return nil; } undefine(name: string) { h := hash(name); rl: list of ref Name; for(l := names[h]; l != nil; l = tl l){ if((hd l).name == name){ l = tl l; for(; rl != nil; rl = tl rl) l = hd rl :: l; names[h] = l; return; }else rl = hd l :: rl; } } Name.text(n: self ref Name): string { if(n.impl != nil) return sys->sprint("builtin %q", n.name); return sys->sprint("%c%s%c", lquote, n.repl, rquote); } dodumpdef(args: array of string) { if(len args > 1){ for(i := 1; i < len args; i++) if((n := lookup(args[i])) != nil) sys->fprint(sys->fildes(2), "%q %s\n", n.name, n.text()); }else{ for(i := 0; i < len names; i++) for(l := names[i]; l != nil; l = tl l) sys->fprint(sys->fildes(2), "%q %s\n", (hd l).name, (hd l).text()); } } pushs(s: string) { for(i := len s; --i >= 0;) pushedback[pushedp++] = s[i]; } pushc(c: int) { if(c >= 0) pushedback[pushedp++] = c; } getc(): int { if(pushedp > 0) return pushedback[--pushedp]; for(; instack != nil; instack = tl instack){ ios := hd instack; c := ios.fp.getc(); if(c >= 0){ if(c == '\n') ios.line++; return c; } } return -1; } puts(s: string) { if(curarg != nil) curarg.s += s; else if(curdiv > 0) diverted[curdiv] += s; else if(curdiv == 0) bout.puts(s); } putc(c: int) { if(curarg != nil){ # stow in argument collection buffer curarg.s[len curarg.s] = c; }else if(curdiv > 0){ l := len diverted[curdiv]; diverted[curdiv][l] = c; }else if(curdiv == 0) bout.putc(c); } expand(def: ref Name, args: array of string) { if(def.impl != nil){ def.impl(args); return; } if(def.repl == def.name || def.repl == "$0"){ puts(def.name); return; } if(!def.dol || def.repl == nil){ pushs(def.repl); return; } # expand $n s := def.repl; for(i := len s; --i >= 1;){ if(s[i-1] == '$' && (c := s[i]-'0') >= 0 && c <= 9){ if(c < len args) pushs(args[c]); i--; }else pushc(s[i]); } if(i >= 0) pushc(s[0]); } hasdol(s: string): int { for(i := 0; i < len s; i++) if(s[i] == '$') return 1; return 0; } dodefine(args: array of string) { if(len args > 2) define(args[1], args[2], 0); else if(len args > 1) define(args[1], "", 0); } doundefine(args: array of string) { for(i := 1; i < len args; i++) undefine(args[i]); } docopydef(args: array of string) { if(len args > 2 && args[1] != args[2]){ undefine(args[2]); if((n := lookup(args[1])) != nil){ if(n.impl == nil) define(args[2], n.repl, n.asis); else builtin(args[2], n.impl); }else define(args[2], "", 0); } } doeval(args: array of string) { if(len args > 1) pushs(string eval(args[1])); } dodivert(args: array of string) { if(len args > 1){ n := int args[1]; if(n < 0 || n >= len diverted) n = -1; curdiv = n; }else curdiv = 0; } dodivnum(nil: array of string)