xref: /plan9/sys/src/cmd/disk/prep/calc.y (revision bfb6eab9346d861b5f68a2b1af55a1768a8fe25b)
1 %{
2 typedef struct Exp Exp;
3 enum {
4 	NUM,
5 	DOT,
6 	DOLLAR,
7 	ADD,
8 	SUB,
9 	MUL,
10 	DIV,
11 	FRAC,
12 	NEG,
13 };
14 
15 struct Exp {
16 	int ty;
17 	long long n;
18 	Exp *e1;
19 	Exp *e2;
20 };
21 
22 typedef Exp* Expptr;
23 #define YYSTYPE Expptr
24 Exp *yyexp;
25 %}
26 
27 %token NUMBER
28 
29 %left '+' '-'
30 %left '*' '/'
31 %left UNARYMINUS '%'
32 %%
33 top:	expr	{ yyexp = $1; return 0; }
34 
35 expr:	NUMBER
36 	| '.'	{ $$ = mkOP(DOT, nil, nil); }
37 	| '$'	{ $$ = mkOP(DOLLAR, nil, nil); }
38 	| '(' expr ')'	{ $$ = $2; }
39 	| expr '+' expr	{ $$ = mkOP(ADD, $1, $3); }
40 	| expr '-' expr 	{ $$ = mkOP(SUB, $1, $3); }
41 	| expr '*' expr	{ $$ = mkOP(MUL, $1, $3); }
42 	| expr '/' expr	{ $$ = mkOP(DIV, $1, $3); }
43 	| expr '%'		{ $$ = mkOP(FRAC, $1, nil); }
44 	| '-' expr %prec UNARYMINUS	{ $$ = mkOP(NEG, $2, nil); }
45 	;
46 
47 %%
48 
49 #include <u.h>
50 #include <libc.h>
51 #include <ctype.h>
52 #include "disk.h"
53 #include "edit.h"
54 
55 static Exp*
56 mkNUM(vlong x)
57 {
58 	Exp *n;
59 
60 	n = emalloc(sizeof *n);
61 
62 	n->ty = NUM;
63 	n->n = x;
64 	return n;
65 }
66 
67 static Exp*
mkOP(int ty,Exp * e1,Exp * e2)68 mkOP(int ty, Exp *e1, Exp *e2)
69 {
70 	Exp *n;
71 
72 	n = emalloc(sizeof *n);
73 	n->ty = ty;
74 	n->e1 = e1;
75 	n->e2 = e2;
76 
77 	return n;
78 }
79 
80 static char *inp;
81 static jmp_buf jmp;
82 static vlong dot, size, dollar;
83 static char** errp;
84 
85 static int
yylex(void)86 yylex(void)
87 {
88 	int c;
89 	uvlong n;
90 
91 	while(isspace(*inp))
92 		inp++;
93 
94 	if(*inp == 0)
95 		return 0;
96 
97 	if(isdigit(*inp)) {
98 		n = strtoull(inp, &inp, 0);	/* default unit is sectors */
99 		c = *inp++;
100 		if(isascii(c) && isupper(c))
101 			c = tolower(c);
102 		switch(c) {
103 		case 't':
104 			n *= 1024;
105 			/* fall through */
106 		case 'g':
107 			n *= 1024;
108 			/* fall through */
109 		case 'm':
110 			n *= 1024;
111 			/* fall through */
112 		case 'k':
113 			n *= 2;
114 			break;
115 		default:
116 			--inp;
117 			break;
118 		}
119 		yylval = mkNUM(n);
120 		return NUMBER;
121 	}
122 	return *inp++;
123 }
124 
125 static void
yyerror(char * s)126 yyerror(char *s)
127 {
128 	*errp = s;
129 	longjmp(jmp, 1);
130 }
131 
132 static vlong
eval(Exp * e)133 eval(Exp *e)
134 {
135 	vlong i;
136 
137 	switch(e->ty) {
138 	case NUM:
139 		return e->n;
140 	case DOT:
141 		return dot;
142 	case DOLLAR:
143 		return dollar;
144 	case ADD:
145 		return eval(e->e1)+eval(e->e2);
146 	case SUB:
147 		return eval(e->e1)-eval(e->e2);
148 	case MUL:
149 		return eval(e->e1)*eval(e->e2);
150 	case DIV:
151 		i = eval(e->e2);
152 		if(i == 0)
153 			yyerror("division by zero");
154 		return eval(e->e1)/i;
155 	case FRAC:
156 		return (size*eval(e->e1))/100;
157 	case NEG:
158 		return -eval(e->e1);
159 	}
160 	assert(0);
161 	return 0;
162 }
163 
164 int yyparse(void);
165 
166 char*
parseexpr(char * s,vlong xdot,vlong xdollar,vlong xsize,vlong * result)167 parseexpr(char *s, vlong xdot, vlong xdollar, vlong xsize, vlong *result)
168 {
169 	char *err;
170 
171 	errp = &err;
172 	if(setjmp(jmp))
173 		return err;
174 
175 	inp = s;
176 	dot = xdot;
177 	size = xsize;
178 	dollar = xdollar;
179 	yyparse();
180 	if(yyexp == nil)
181 		return "nil yylval?";
182 	*result = eval(yyexp);
183 	return nil;
184 }
185 
186 #ifdef TEST
187 void
main(int argc,char ** argv)188 main(int argc, char **argv)
189 {
190 	int i;
191 	vlong r;
192 	char *e;
193 
194 	for(i=1; i<argc; i++)
195 		if(e = parseexpr(argv[i], 1000, 1000000, 1000000, &r))
196 			print("%s\n", e);
197 		else
198 			print("%lld\n", r);
199 }
200 #endif
201