shithub: purgatorio

ref: db1eb844461b07a25ca49117851fa874fd88e065
dir: /appl/cmd/disk/prep/calc.tab.b/

View raw version
implement Calc;

#line	2	"calc.y"
#
# from Plan 9.  subject to the Lucent Public License 1.02
#

include "sys.m";
	sys: Sys;

include "draw.m";

	NUM,
	DOT,
	DOLLAR,
	ADD,
	SUB,
	MUL,
	DIV,
	FRAC,
	NEG: con iota;

Exp: adt {
	ty:	int;
	n:	big;
	e1, e2:	cyclic ref Exp;
};

YYSTYPE: adt {
	e:	ref Exp;
};
yyexp: ref Exp;

YYLEX: adt {
	s:	string;
	n:	int;
	lval: YYSTYPE;
	lex: fn(l: self ref YYLEX): int;
	error: fn(l: self ref YYLEX, msg: string);
};
Calc: module {

	parseexpr: fn(s: string, a, b, c: big): (big, string);
	init:	fn(nil: ref Draw->Context, nil: list of string);
NUMBER: con	57346;
UNARYMINUS: con	57347;

};
YYEOFCODE: con 1;
YYERRCODE: con 2;
YYMAXDEPTH: con 200;

#line	68	"calc.y"


mkNUM(x: big): ref Exp
{
	return ref Exp(NUM, x, nil, nil);
}

mkOP(ty: int, e1: ref Exp, e2: ref Exp): ref Exp
{
	return ref Exp(ty, big 0, e1, e2);
}

dot, size, dollar: big;

YYLEX.lex(l: self ref YYLEX): int
{
	while(l.n < len l.s && isspace(l.s[l.n]))
		l.n++;

	if(l.n == len l.s)
		return -1;

	if(isdigit(l.s[l.n])){
		for(o := l.n; o < len l.s && isdigit(l.s[o]); o++)
			;
		l.lval.e = mkNUM(big l.s[l.n:o]);
		l.n = o;
		return NUMBER;
	}

	return l.s[l.n++];
}

isdigit(c: int): int
{
	return c >= '0' && c <= '9';
}

isspace(c: int): int
{
	return c == ' ' || c == '\t' || c == '\n' || c == '\r' || c == '\v' || c == '\f';
}

YYLEX.error(nil: self ref YYLEX, s: string)
{
	raise s;
}

eval(e: ref Exp): big
{
	case e.ty {
	NUM =>
		return e.n;
	DOT =>
		return dot;
	DOLLAR =>
		return dollar;
	ADD =>
		return eval(e.e1)+eval(e.e2);
	SUB =>
		return eval(e.e1)-eval(e.e2);
	MUL =>
		return eval(e.e1)*eval(e.e2);
	DIV =>
		i := eval(e.e2);
		if(i == big 0)
			raise "division by zero";
		return eval(e.e1)/i;
	FRAC =>
		return (size*eval(e.e1))/big 100;
	NEG =>
		return -eval(e.e1);
	* =>
		raise "invalid operator";
	}
}

parseexpr(s: string, xdot: big, xdollar: big, xsize: big): (big, string)
{
	dot = xdot;
	size = xsize;
	dollar = xdollar;
	l := ref YYLEX(s, 0, YYSTYPE(nil));
	{
		yyparse(l);
		if(yyexp == nil)
			return (big 0, "nil yylval?");
		return (eval(yyexp), nil);
	}exception e{
	"*" =>
		return (big 0, e);
	}
}

init(nil: ref Draw->Context, args: list of string)
{
	sys = load Sys Sys->PATH;

	while((args = tl args) != nil){
		(r, e) := parseexpr(hd args, big 1000, big 1000000, big 1000000);
		if(e != nil)
			sys->print("%s\n", e);
		else
			sys->print("%bd\n", r);
	}
}

yyexca := array[] of {-1, 1,
	1, -1,
	-2, 0,
};
YYNPROD: con 12;
YYPRIVATE: con 57344;
yytoknames: array of string;
yystates: array of string;
yydebug: con 0;
YYLAST:	con 30;
yyact := array[] of {
   8,   9,  10,  11,   3,  12,   7,   2,  12,  19,
   1,   4,   5,   6,  13,  14,  15,  16,  17,  18,
   8,   9,  10,  11,   0,  12,  10,  11,   0,  12,
};
yypact := array[] of {
   0,-1000,  15,-1000,-1000,-1000,   0,   0,   0,   0,
   0,   0,-1000,  -5,-1000,  19,  19,  -2,  -2,-1000,
};
yypgo := array[] of {
   0,   7,  10,
};
yyr1 := array[] of {
   0,   2,   1,   1,   1,   1,   1,   1,   1,   1,
   1,   1,
};
yyr2 := array[] of {
   0,   1,   1,   1,   1,   3,   3,   3,   3,   3,
   2,   2,
};
yychk := array[] of {
-1000,  -2,  -1,   4,  11,  12,  13,   6,   5,   6,
   7,   8,  10,  -1,  -1,  -1,  -1,  -1,  -1,  14,
};
yydef := array[] of {
   0,  -2,   1,   2,   3,   4,   0,   0,   0,   0,
   0,   0,  10,   0,  11,   6,   7,   8,   9,   5,
};
yytok1 := array[] of {
   1,   3,   3,   3,   3,   3,   3,   3,   3,   3,
   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
   3,   3,   3,   3,   3,   3,   3,   3,   3,   3,
   3,   3,   3,   3,   3,   3,  12,  10,   3,   3,
  13,  14,   7,   5,   3,   6,  11,   8,
};
yytok2 := array[] of {
   2,   3,   4,   9,
};
yytok3 := array[] of {
   0
};

YYSys: module
{
	FD: adt
	{
		fd:	int;
	};
	fildes:		fn(fd: int): ref FD;
	fprint:		fn(fd: ref FD, s: string, *): int;
};

yysys: YYSys;
yystderr: ref YYSys->FD;

YYFLAG: con -1000;

# parser for yacc output

yytokname(yyc: int): string
{
	if(yyc > 0 && yyc <= len yytoknames && yytoknames[yyc-1] != nil)
		return yytoknames[yyc-1];
	return "<"+string yyc+">";
}

yystatname(yys: int): string
{
	if(yys >= 0 && yys < len yystates && yystates[yys] != nil)
		return yystates[yys];
	return "<"+string yys+">\n";
}

yylex1(yylex: ref YYLEX): int
{
	c : int;
	yychar := yylex.lex();
	if(yychar <= 0)
		c = yytok1[0];
	else if(yychar < len yytok1)
		c = yytok1[yychar];
	else if(yychar >= YYPRIVATE && yychar < YYPRIVATE+len yytok2)
		c = yytok2[yychar-YYPRIVATE];
	else{
		n := len yytok3;
		c = 0;
		for(i := 0; i < n; i+=2) {
			if(yytok3[i+0] == yychar) {
				c = yytok3[i+1];
				break;
			}
		}
		if(c == 0)
			c = yytok2[1];	# unknown char
	}
	if(yydebug >= 3)
		yysys->fprint(yystderr, "lex %.4ux %s\n", yychar, yytokname(c));
	return c;
}

YYS: adt
{
	yyv: YYSTYPE;
	yys: int;
};

yyparse(yylex: ref YYLEX): int
{
	if(yydebug >= 1 && yysys == nil) {
		yysys = load YYSys "$Sys";
		yystderr = yysys->fildes(2);
	}

	yys := array[YYMAXDEPTH] of YYS;

	yyval: YYSTYPE;
	yystate := 0;
	yychar := -1;
	yynerrs := 0;		# number of errors
	yyerrflag := 0;		# error recovery flag
	yyp := -1;
	yyn := 0;

yystack:
	for(;;){
		# put a state and value onto the stack
		if(yydebug >= 4)
			yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));

		yyp++;
		if(yyp >= len yys)
			yys = (array[len yys * 2] of YYS)[0:] = yys;
		yys[yyp].yys = yystate;
		yys[yyp].yyv = yyval;

		for(;;){
			yyn = yypact[yystate];
			if(yyn > YYFLAG) {	# simple state
				if(yychar < 0)
					yychar = yylex1(yylex);
				yyn += yychar;
				if(yyn >= 0 && yyn < YYLAST) {
					yyn = yyact[yyn];
					if(yychk[yyn] == yychar) { # valid shift
						yychar = -1;
						yyp++;
						if(yyp >= len yys)
							yys = (array[len yys * 2] of YYS)[0:] = yys;
						yystate = yyn;
						yys[yyp].yys = yystate;
						yys[yyp].yyv = yylex.lval;
						if(yyerrflag > 0)
							yyerrflag--;
						if(yydebug >= 4)
							yysys->fprint(yystderr, "char %s in %s", yytokname(yychar), yystatname(yystate));
						continue;
					}
				}
			}
		
			# default state action
			yyn = yydef[yystate];
			if(yyn == -2) {
				if(yychar < 0)
					yychar = yylex1(yylex);
		
				# look through exception table
				for(yyxi:=0;; yyxi+=2)
					if(yyexca[yyxi] == -1 && yyexca[yyxi+1] == yystate)
						break;
				for(yyxi += 2;; yyxi += 2) {
					yyn = yyexca[yyxi];
					if(yyn < 0 || yyn == yychar)
						break;
				}
				yyn = yyexca[yyxi+1];
				if(yyn < 0){
					yyn = 0;
					break yystack;
				}
			}

			if(yyn != 0)
				break;

			# error ... attempt to resume parsing
			if(yyerrflag == 0) { # brand new error
				yylex.error("syntax error");
				yynerrs++;
				if(yydebug >= 1) {
					yysys->fprint(yystderr, "%s", yystatname(yystate));
					yysys->fprint(yystderr, "saw %s\n", yytokname(yychar));
				}
			}

			if(yyerrflag != 3) { # incompletely recovered error ... try again
				yyerrflag = 3;
	
				# find a state where "error" is a legal shift action
				while(yyp >= 0) {
					yyn = yypact[yys[yyp].yys] + YYERRCODE;
					if(yyn >= 0 && yyn < YYLAST) {
						yystate = yyact[yyn];  # simulate a shift of "error"
						if(yychk[yystate] == YYERRCODE)
							continue yystack;
					}
	
					# the current yyp has no shift onn "error", pop stack
					if(yydebug >= 2)
						yysys->fprint(yystderr, "error recovery pops state %d, uncovers %d\n",
							yys[yyp].yys, yys[yyp-1].yys );
					yyp--;
				}
				# there is no state on the stack with an error shift ... abort
				yyn = 1;
				break yystack;
			}

			# no shift yet; clobber input char
			if(yydebug >= 2)
				yysys->fprint(yystderr, "error recovery discards %s\n", yytokname(yychar));
			if(yychar == YYEOFCODE) {
				yyn = 1;
				break yystack;
			}
			yychar = -1;
			# try again in the same state
		}
	
		# reduction by production yyn
		if(yydebug >= 2)
			yysys->fprint(yystderr, "reduce %d in:\n\t%s", yyn, yystatname(yystate));
	
		yypt := yyp;
		yyp -= yyr2[yyn];
#		yyval = yys[yyp+1].yyv;
		yym := yyn;
	
		# consult goto table to find next state
		yyn = yyr1[yyn];
		yyg := yypgo[yyn];
		yyj := yyg + yys[yyp].yys + 1;
	
		if(yyj >= YYLAST || yychk[yystate=yyact[yyj]] != -yyn)
			yystate = yyact[yyg];
		case yym {
			
1=>
#line	54	"calc.y"
{ yyexp = yys[yypt-0].yyv.e; return 0; }
2=>
yyval.e = yys[yyp+1].yyv.e;
3=>
#line	57	"calc.y"
{ yyval.e = mkOP(DOT, nil, nil); }
4=>
#line	58	"calc.y"
{ yyval.e = mkOP(DOLLAR, nil, nil); }
5=>
#line	59	"calc.y"
{ yyval.e = yys[yypt-1].yyv.e; }
6=>
#line	60	"calc.y"
{ yyval.e = mkOP(ADD, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
7=>
#line	61	"calc.y"
{ yyval.e = mkOP(SUB, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
8=>
#line	62	"calc.y"
{ yyval.e = mkOP(MUL, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
9=>
#line	63	"calc.y"
{ yyval.e = mkOP(DIV, yys[yypt-2].yyv.e, yys[yypt-0].yyv.e); }
10=>
#line	64	"calc.y"
{ yyval.e = mkOP(FRAC, yys[yypt-1].yyv.e, nil); }
11=>
#line	65	"calc.y"
{ yyval.e = mkOP(NEG, yys[yypt-0].yyv.e, nil); }
		}
	}

	return yyn;
}