forsyth20121205-174063a80cc13 years agoHistory
911 lines · 16.9 KB · Brainfuck
RawDownload
implement Sed; ## partial sed implementation borrowed from plan9 sed.# include "sys.m";	sys: Sys;include "draw.m";include "arg.m";	arg: Arg;include "bufio.m";	bufio: Bufio;	Iobuf: import bufio;include "string.m";	str: String;include "regex.m";	regex: Regex;	Re: import regex; Sed : module {	init: fn(ctxt: ref Draw->Context, argv: list of string);};  false, true: con iota;bool: type int; Addr: adt {	pick {		None =>		Dollar =>		Line =>			line: int;		Regex =>			re: Re;	}}; Sedcom: adt {	command: fn(c: self ref Sedcom);	executable: fn(c: self ref Sedcom) : int; 	ad1, ad2: ref Addr;	negfl: bool;	active: int; 	pick {	S =>		gfl, pfl: int;		re: Re;		b: ref Iobuf;		rhs: string;	D or CD or P or Q or EQ or G or CG or H or CH or N or CN or X or CP or L=>	A or C or I =>		text: string;	R =>		filename: string;	W =>		b: ref Iobuf;	Y =>		map: list of (int, int);	B or T or Lab =>		lab: string;	}}; dflag := false;nflag := false;gflag := false;sflag := 0; delflag := 0;dolflag := 0;fhead := 0;files: list of string;fout: ref Iobuf;infile: ref Iobuf;jflag := 0;lastregex:  Re;linebuf: string;filename := "";lnum := 0;peekc := 0; holdsp := "";patsp := ""; cmds: list of ref Sedcom;appendlist: list of ref Sedcom;bufioflush: list of ref Iobuf; init(nil: ref Draw->Context, args: list of string){	sys = load Sys Sys->PATH; 	if ((arg = load Arg Arg->PATH) == nil)		fatal(sys->sprint("could not load %s: %r", Arg->PATH)); 	if ((bufio = load Bufio Bufio->PATH) == nil)		fatal(sys->sprint("could not load %s: %r", Bufio->PATH)); 	if ((str = load String String->PATH) == nil)		fatal(sys->sprint("could not load %s: %r", String->PATH)); 	if ((regex = load Regex Regex->PATH) == nil)		fatal(sys->sprint("could not load %s: %r", Regex->PATH)); 	arg->init(args); 	compfl := 0;	while ((c := arg->opt()) != 0)		case c {		'n' =>			nflag = true;		'g' =>			gflag = true;		'e' =>			if ((s := arg->arg()) == nil)				usage();			filename = "";			cmds = compile(bufio->sopen(s + "\n"), cmds);			compfl = 1;		'f' => if ((filename = arg->arg()) == nil)				usage();			b := bufio->open(filename, bufio->OREAD);			if (b == nil)				fatal(sys->sprint("couldn't open '%s': %r", filename));			cmds = compile(b, cmds);			compfl = 1;		'd' =>			dflag = true;		* =>			usage();		}	args = arg->argv();	if (compfl == 0) {		if (len args == 0)			fatal("missing pattern");		filename = "";		cmds = compile(bufio->sopen(hd args + "\n"), cmds);		args = tl args;	} 	# reverse command list, we could compile addresses here if required	l: list of ref Sedcom;	for (p := cmds; p != nil; p = tl p) {		l = hd p :: l;	}	cmds = l; 	# add files to file list (and reverse to get in right order)	f: list of string;	if (len args == 0)		f = "" :: f;	else for (; len args != 0; args = tl args)		f = hd args :: f;	for (;f != nil; f = tl f)		files = hd f :: files; 	if ((fout = bufio->fopen(sys->fildes(1), bufio->OWRITE)) == nil)		fatal(sys->sprint("couldn't buffer stdout: %r"));	bufioflush = fout :: bufioflush;	lnum = 0;	execute(cmds);	exits(nil);} depth := 0;maxdepth: con 20;cmdend := array [maxdepth] of string;cmdcnt := array [maxdepth] of int; compile(b: ref Iobuf, l: list of ref Sedcom) : list of ref Sedcom{	lnum = 1; nextline:	for (;;) {		err: int;		(err, linebuf) = getline(b);		if (err < 0)			break;				s := linebuf; 		do {			rep: ref Sedcom;			ad1, ad2: ref Addr;			negfl := 0; 			if (s != "")				s = str->drop(s, " \t;"); 			if (s == "" || s[0] == '#')				continue nextline; 			# read addresses			(s, ad1) = address(s);			pick a := ad1 {			None =>				ad2 = ref Addr.None();			* =>				if (s != "" && (s[0] == ',' || s[0] == ';')) {					(s, ad2) = address(s[1:]);				}				else {					ad2 = ref Addr.None();				}			} 			s = str->drop(s, " \t"); 			if (s != "" && str->in(s[0], "!")) {				negfl = true;				s = str->drop(s, "!");			}			s = str->drop(s, " \t");			if (s == "")				break;			c := s[0]; s = s[1:]; 			# mop up commands that got two addresses but only want one.			case c {				'a' or 'c' or 'q' or '=' or 'i' =>					if (tagof ad2 != tagof Addr.None)						fatal(sys->sprint("only one address allowed:  '%s'",						      linebuf));			} 			case c {			* =>				fatal(sys->sprint("unrecognised command: '%s' (%c)",					linebuf, c));			'a' =>				if (s != "" && s[0] == '\\')					s = s[1:];				if (s == "" || s[0] != '\n')					fatal("unexpected characters in a command: " + s);				rep = ref Sedcom.A (ad1, ad2, negfl, 0, s[1:]);				s = "";			'c' =>				if (s != "" && s[0] == '\\')					s = s[1:];				if (s == "" || s[0] != '\n')					fatal("unexpected characters in c command: " + s);				rep = ref Sedcom.C (ad1, ad2, negfl, 0, s[1:]);				s = "";			'i' =>				if (s != "" && s[0] == '\\')					s = s[1:];				if (s == "" || s[0] != '\n')					fatal("unexpected characters in i command: " + s);				rep = ref Sedcom.I (ad1, ad2, negfl, 0, s[1:]);				s = "";			'r' =>				s = str->drop(s, " \t");				rep = ref Sedcom.R (ad1, ad2, negfl, 0, s);				s = "";			'w' =>				if (s != "")					s = str->drop(s, " \t");				if (s == "")					fatal("no filename in w command: " + linebuf);				bo := bufio->open(s, bufio->OWRITE);				if (bo == nil)					bo = bufio->create(s, bufio->OWRITE, 8r666);				if (bo == nil)					fatal(sys->sprint("can't create output file: '%s'", s));				bufioflush = bo :: bufioflush;				rep = ref Sedcom.W (ad1, ad2, negfl, 0, bo);				s = "";							'd' =>				rep = ref Sedcom.D (ad1, ad2, negfl, 0);			'D' =>				rep = ref Sedcom.CD (ad1, ad2, negfl, 0);			'p' =>				rep = ref Sedcom.P (ad1, ad2, negfl, 0);			'P' =>				rep = ref Sedcom.CP (ad1, ad2, negfl, 0);			'q' =>				rep = ref Sedcom.Q (ad1, ad2, negfl, 0);			'=' =>				rep = ref Sedcom.EQ (ad1, ad2, negfl, 0);			'g' =>				rep = ref Sedcom.G (ad1, ad2, negfl, 0);			'G' =>				rep = ref Sedcom.CG (ad1, ad2, negfl, 0);			'h' =>				rep = ref Sedcom.H (ad1, ad2, negfl, 0);			'H' =>				rep = ref Sedcom.CH (ad1, ad2, negfl, 0);			'n' =>				rep = ref Sedcom.N (ad1, ad2, negfl, 0);			'N' =>				rep = ref Sedcom.CN (ad1, ad2, negfl, 0);			'x' =>				rep = ref Sedcom.X (ad1, ad2, negfl, 0);			'l' =>				rep = ref Sedcom.L (ad1, ad2, negfl, 0); 			'y' =>				if (s == "")					fatal("expected args: " + linebuf);				seof := s[0:1];				s = s[1:];				if (s == "")					fatal("no lhs: " + linebuf);				(lhs, s2) := str->splitl(s, seof);				if (s2 == "")					fatal("no lhs terminator: " + linebuf);				s2 = s2[1:];				(rhs, s4) := str->splitl(s2, seof);				if (s4 == "")					fatal("no rhs: " + linebuf);				s = s4[1:];				if (len lhs != len rhs)					fatal("y command needs same length sets: " + linebuf);				map: list of (int, int);				for (i := 0; i < len lhs; i++)					map = (lhs[i], rhs[i]) :: map;				rep = ref Sedcom.Y (ad1, ad2, negfl, 0, map);			's' =>				seof := s[0:1];				re: Re;				(re, s) = recomp(s);				rhs: string;				(s, rhs) = compsub(seof + s); 				gfl := gflag;				pfl := 0; 				if (s != "" && s[0] == 'g') {					gfl = 1;					s = s[1:];				}				if (s != "" && s[0] == 'p') {					pfl = 1;					s = s[1:];				}				if (s != "" && s[0] == 'P') {					pfl = 2;					s = s[1:];				} 				b: ref Iobuf = nil;				if (s != "" && s[0] == 'w') {					s = s[1:];					if (s != "")						s = str->drop(s, " \t");					if (s == "")						fatal("no filename in s with w: " + linebuf);					b = bufio->open(s, bufio->OWRITE);					if (b == nil)						b = bufio->create(s, bufio->OWRITE, 8r666);					if (b == nil)						fatal(sys->sprint("can't create output file: '%s'", s));					bufioflush = b :: bufioflush;					s = "";				}				rep = ref Sedcom.S (ad1, ad2, negfl, 0, gfl, pfl, re, b, rhs);			':' =>				if (s != "")					s = str->drop(s, " \t");				(lab, s1) := str->splitl(s, " \t;#");				s = s1;				if (lab == "")					fatal(sys->sprint("null label: '%s'", linebuf));				if (findlabel(lab))					fatal(sys->sprint("duplicate label: '%s'", lab));				rep = ref Sedcom.Lab (ad1, ad2, negfl, 0, lab);			'b' or 't' =>				if (s != "")					s = str->drop(s, " \t");				(lab, s1) := str->splitl(s, " \t;#");				s = s1;				if (c == 'b')					rep = ref Sedcom.B (ad1, ad2, negfl, 0, lab);				else					rep = ref Sedcom.T (ad1, ad2, negfl, 0, lab);			'{' =>				# replace { with branch to }.				lab := mklab(depth);				depth++;				rep = ref Sedcom.B (ad1, ad2, !negfl, 0, lab);				s = ";" + s;			'}' =>				if (tagof ad1 != tagof Addr.None)					fatal("did not expect address:" + linebuf);				if (--depth < 0)					fatal("too many }'s: " + linebuf);				lab := mklab(depth);				cmdcnt[depth]++;				rep = ref Sedcom.Lab ( ad1, ad2, negfl, 0, lab);				s = ";" + s;			} 			l = rep :: l;		} while (s != nil && str->in(s[0], ";{}")); 		if (s != nil)			fatal("leftover junk: " + s);	}	return l;} findlabel(lab: string) : bool{	for (l := cmds; l != nil; l = tl l)		pick x := hd l {		Lab =>			if (x.lab == lab)				return true;		}	return false;} mklab(depth: int): string{	return "_" + string cmdcnt[depth] + "_" + string depth;} Sedcom.command(c: self ref Sedcom){	pick x := c {	S =>		m: bool;		(m, patsp) = substitute(x, patsp);		if (m) {			case x.pfl {			0 =>				;			1 =>				fout.puts(patsp + "\n");			* =>				l: string;				(l, patsp) = str->splitl(patsp, "\n");				fout.puts(l + "\n");				break;			}			if (x.b != nil)				x.b.puts(patsp + "\n");		}	P =>		fout.puts(patsp + "\n");	CP =>		(s, nil) := str->splitl(patsp, "\n");		fout.puts(s + "\n");	A =>		appendlist = c :: appendlist;	R =>		appendlist = c :: appendlist;	C =>		delflag++;		if (c.active == 1)			fout.puts(x.text + "\n");	I =>		fout.puts(x.text + "\n");	W =>		x.b.puts(patsp + "\n");	G =>		patsp = holdsp;	CG =>		patsp += holdsp;	H =>		holdsp = patsp;	CH =>		holdsp += patsp;	X =>		(holdsp, patsp) = (patsp, holdsp);	Y =>		# yes this is O(N²).		for (i := 0; i < len patsp; i++)			for (h := x.map; h != nil; h = tl h) {				(s, d) := hd h;				if (patsp[i] == s)					patsp[i] = d;			}	D =>		delflag++;	CD =>		# loose upto \n.		(s1, s2) := str->splitl(patsp, "\n");		if (s2 == nil)			patsp = s1;		else if (len s2 > 1)			patsp = s2[1:];		else			patsp = "";		jflag++;	Q =>		if (!nflag)			fout.puts(patsp + "\n");		arout();		exits(nil);	N =>		if (!nflag)			fout.puts(patsp + "\n");		arout();		n: int;

Lines 1–500 of 911.