jehanne/sys/src/cmd/disk/prep/calc.y

211 lines
2.9 KiB
Plaintext

%{
#include <u.h>
#define YYSIZE_T size_t
typedef struct Exp Exp;
enum {
NUM,
DOT,
DOLLAR,
ADD,
SUB,
MUL,
DIV,
FRAC,
NEG,
};
struct Exp {
int ty;
int64_t n;
Exp *e1;
Exp *e2;
};
typedef Exp* Expptr;
#define YYSTYPE Expptr
Exp *yyexp;
static Exp* mkOP(int ty, Exp *e1, Exp *e2);
static Exp* mkNUM(int64_t x);
static int yylex(void);
static void yyerror(char *s);
%}
%token NUMBER
%left '+' '-'
%left '*' '/'
%left UNARYMINUS '%'
%%
top: expr { yyexp = $1; return 0; }
expr: NUMBER
| '.' { $$ = mkOP(DOT, nil, nil); }
| '$' { $$ = mkOP(DOLLAR, nil, nil); }
| '(' expr ')' { $$ = $2; }
| expr '+' expr { $$ = mkOP(ADD, $1, $3); }
| expr '-' expr { $$ = mkOP(SUB, $1, $3); }
| expr '*' expr { $$ = mkOP(MUL, $1, $3); }
| expr '/' expr { $$ = mkOP(DIV, $1, $3); }
| expr '%' { $$ = mkOP(FRAC, $1, nil); }
| '-' expr %prec UNARYMINUS { $$ = mkOP(NEG, $2, nil); }
;
%%
#include <lib9.h>
#include <jehanne/ctype.h>
#include "disk.h"
#include "edit.h"
static Exp*
mkNUM(int64_t x)
{
Exp *n;
n = emalloc(sizeof *n);
n->ty = NUM;
n->n = x;
return n;
}
static Exp*
mkOP(int ty, Exp *e1, Exp *e2)
{
Exp *n;
n = emalloc(sizeof *n);
n->ty = ty;
n->e1 = e1;
n->e2 = e2;
return n;
}
static char *inp;
static jmp_buf jmp;
static int64_t dot, size, dollar, unit;
static char** errp;
static int
yylex(void)
{
int c;
uint64_t n;
while(isspace(*inp))
inp++;
if(*inp == 0)
return 0;
if(isdigit(*inp)) {
n = strtoull(inp, &inp, 0); /* default unit is sectors */
c = *inp++;
if(isascii(c) && isupper(c))
c = tolower(c);
switch(c) {
case 't':
n *= 1024;
/* fall through */
case 'g':
n *= 1024;
/* fall through */
case 'm':
n *= 1024;
/* fall through */
case 'k':
n *= 1024;
n /= unit; /* convert to sectors */
break;
default:
--inp;
break;
}
yylval = mkNUM(n);
return NUMBER;
}
return *inp++;
}
static void
yyerror(char *s)
{
*errp = s;
longjmp(jmp, 1);
}
static int64_t
eval(Exp *e)
{
int64_t i;
switch(e->ty) {
case NUM:
return e->n;
case DOT:
return dot;
case DOLLAR:
return dollar;
case ADD:
return eval(e->e1)+eval(e->e2);
case SUB:
return eval(e->e1)-eval(e->e2);
case MUL:
return eval(e->e1)*eval(e->e2);
case DIV:
i = eval(e->e2);
if(i == 0)
yyerror("division by zero");
return eval(e->e1)/i;
case FRAC:
return (size*eval(e->e1))/100;
case NEG:
return -eval(e->e1);
}
assert(0);
return 0;
}
int yyparse(void);
char*
parseexpr(char *s, int64_t xdot, int64_t xdollar, int64_t xsize, int64_t xunit, int64_t *result)
{
char *err;
errp = &err;
if(setjmp(jmp))
return err;
inp = s;
dot = xdot;
size = xsize;
dollar = xdollar;
unit = xunit;
yyparse();
if(yyexp == nil)
return "nil yylval?";
*result = eval(yyexp);
return nil;
}
#ifdef TEST
void
main(int argc, char **argv)
{
int i;
int64_t r;
char *e;
for(i=1; i<argc; i++)
if(e = parseexpr(argv[i], 1000, 1000000, 1000000, &r))
print("%s\n", e);
else
print("%lld\n", r);
}
#endif