forsyth20121205-174063a80cc13 years agoHistory
281 lines · 5.7 KB · Brainfuck
RawDownload
implement Shellbuiltin; include "sys.m";	sys: Sys;include "draw.m";include "sh.m";	sh: Sh;	Listnode, Context: import sh;	myself: Shellbuiltin; initbuiltin(ctxt: ref Context, shmod: Sh): string{	sys = load Sys Sys->PATH;	sh = shmod;	myself = load Shellbuiltin "$self";	if (myself == nil)		ctxt.fail("bad module", sys->sprint("expr: cannot load self: %r")); 	ctxt.addsbuiltin("expr", myself);	ctxt.addbuiltin("ntest", myself);	return nil;} whatis(nil: ref Sh->Context, nil: Sh, nil: string, nil: int): string{	return nil;} getself(): Shellbuiltin{	return myself;} EQ, GT, LT, GE, LE, PLUS, MINUS, DIVIDE, AND, TIMES, MOD,OR, XOR, UMINUS, SHL, SHR, NOT, BNOT, NEQ, REP, SEQ: con iota; runbuiltin(ctxt: ref Sh->Context, nil: Sh,			cmd: list of ref Sh->Listnode, nil: int): string{	case (hd cmd).word {	"ntest" =>		if (len cmd != 2)			ctxt.fail("usage", "usage: ntest n");		if (big (hd tl cmd).word == big 0)			return "false";	}	return nil;} runsbuiltin(ctxt: ref Sh->Context, nil: Sh,			cmd: list of ref Sh->Listnode): list of ref Listnode{	# only one sbuiltin: expr.	stk: list of big;	lastop := -1;	lastn := -1;	lastname := "";	radix: int;	(cmd, radix) = opts(ctxt, tl cmd);	for (; cmd != nil; cmd = tl cmd) {		w := (hd cmd).word;		op := -1;		nops := 2;		case w {		"+" =>			op = PLUS; 		"-" =>			op = MINUS;		"x" or "*" or "×" =>			op = TIMES;		"/" =>			op = DIVIDE;		"%" =>			op = MOD;		"and" =>			op = AND;		"or" =>			op = OR;		"xor" =>			op = XOR;		"_"=>			(op, nops) = (UMINUS, 1);		"<<" or "shl" =>			op = SHL;		">>" or "shr" =>			op = SHR;		"=" or "==" or "eq" =>			op = EQ;		"!=" or "neq" =>			op = NEQ;		">" or "gt" =>			op = GT;		"<" or "lt" =>			op = LT;		">=" or "ge" =>			op = GE;		"<=" or "le" =>			op = LE;		"!" or "not" =>			(op, nops) = (NOT, 1);		"~" =>			(op, nops) = (BNOT, 1);		"rep" =>			(op, nops) = (REP, 0);		"seq" =>			(op, nops) = (SEQ, 2);		}		if (op == -1)			stk = makenum(ctxt, w) :: stk;		else 			stk = operator(ctxt, stk, op, nops, lastop, lastn, w, lastname);		lastop = op;		lastn = nops;		lastname = w;	}	r: list of ref Listnode;	for (; stk != nil; stk = tl stk)		r = ref Listnode(nil, big2string(hd stk, radix)) :: r;	return r;} opts(ctxt: ref Context, cmd: list of ref Listnode): (list of ref Listnode, int){	radix := 10;	if (cmd == nil)		return (nil, 10);	w := (hd cmd).word;	if (len w < 2)		return (cmd, 10);	if (w[0] != '-' || (w[1] >= '0' && w[1] <= '9'))		return (cmd, 10);	if (w[1] != 'r')		ctxt.fail("usage", "usage: expr [-r radix] [arg...]");	if (len w > 2)		w = w[2:];	else {		if (tl cmd == nil)			ctxt.fail("usage", "usage: expr [-r radix] [arg...]");		cmd = tl cmd;		w = (hd cmd).word;	}	r := int w;	if (r <= 0 || r > 36)		ctxt.fail("usage", "expr: invalid radix " + string r);	return (tl cmd, int w);} operator(ctxt: ref Context, stk: list of big, op, nops, lastop, lastn: int,		opname, lastopname: string): list of big{	al: list of big;	for (i := 0; i < nops; i++) {		if (stk == nil)			ctxt.fail("empty stack",				sys->sprint("expr: empty stack on op '%s'", opname));		al = hd stk :: al;		stk = tl stk;	}	return oper(ctxt, al, op, lastop, lastn, lastopname, stk);} # args are in reverse orderoper(ctxt: ref Context, args: list of big, op, lastop, lastn: int,		lastopname: string, stk: list of big): list of big{	if (op == REP) {		if (lastop == -1 || lastop == SEQ || lastn != 2)			ctxt.fail("usage", "expr: bad operator for rep");		if (stk == nil || tl stk == nil)			return stk;		while (tl stk != nil)			stk = operator(ctxt, stk, lastop, 2, -1, -1, lastopname, nil);		return stk;	}	n2 := big 0;	n1 := hd args;	if (tl args != nil)		n2 = hd tl args;	r := big 0;	case op {	EQ =>	r = big(n1 == n2);	NEQ =>	r = big(n1 != n2);	GT =>	r = big(n1 > n2);	LT =>	r = big(n1 < n2);	GE =>	r = big(n1 >= n2);	LE =>	r = big(n1 <= n2);	PLUS =>	r = big(n1 + n2);	MINUS =>	r = big(n1 - n2);	NOT	 =>	r = big(n1 != big 0);	DIVIDE =>			if (n2 == big 0)				ctxt.fail("divide by zero", "expr: division by zero");			r = n1 / n2;	MOD =>			if (n2 == big 0)				ctxt.fail("divide by zero", "expr: division by zero");			r = n1 % n2;	TIMES =>	r = n1 * n2;	AND =>	r = n1 & n2;	OR =>	r = n1 | n2;	XOR =>	r = n1 ^ n2;	UMINUS => r = -n1;	BNOT =>	r = ~n1;	SHL =>	r = n1 << int n2;	SHR =>	r = n1 >> int n2;	SEQ =>	return seq(n1, n2, stk);	}	return r :: stk;} seq(n1, n2: big, stk: list of big): list of big{	incr := big 1;	if (n2 < n1)		incr = big -1;	for (; n1 != n2; n1 += incr)		stk = n1 :: stk;	return n1 :: stk;} makenum(ctxt: ref Context, s: string): big{	if (s == nil || (s[0] != '-' && (s[0] < '0' || s[0] > '9')))		ctxt.fail("usage", sys->sprint("expr: unknown operator '%s'", s)); 	t := s;		if (neg := s[0] == '-')		s = s[1:];	radix := 10;	for (i := 0; i < len s && i < 3; i++) {		if (s[i] == 'r') {			radix = int s;			s = s[i+1:];			break;		}	}	if (radix == 10)		return big t;	if (radix == 0 || radix > 36)		ctxt.fail("usage", "expr: bad number " + t);	n := big 0;	for (i = 0; i < len s; i++) {		if ('0' <= s[i] && s[i] <= '9')			n = (n * big radix) + big(s[i] - '0');		else if ('a' <= s[i] && s[i] < 'a' + radix - 10)			n = (n * big radix) + big(s[i] - 'a' + 10);		else if ('A' <= s[i] && s[i]  < 'A' + radix - 10)			n = (n * big radix) + big(s[i] - 'A' + 10);		else			break;	}	if (neg)		return -n;	return n;} big2string(n: big, radix: int): string{	if (neg := n < big 0) {		n = -n;	}	s := "";	do {		c: int;		d := int (n % big radix);		if (d < 10)			c = '0' + d;		else			c = 'a' + d - 10;		s[len s] = c;		n /= big radix;	} while (n > big 0);	t := s;	for (i := len s - 1; i >= 0; i--)		t[len s - 1 - i] = s[i];	if (radix != 10)		t = string radix + "r" + t;	if (neg)		return "-" + t;	return t;}