diff --git a/README b/README index d572093..0bace46 100644 --- a/README +++ b/README @@ -438,9 +438,18 @@ Expressions ----------- Expressions can contain numeric constants (in non-exponential notation), -variable names, the arithmetic operations +, -, *, /, and unary -. +variable names, the arithmetic operations +, -, *, /, unary -, and the +functions sin(), cos(), and sqrt(). + Parentheses can be used to change precedence. +The argument of sin and cos is a dimensionless number that specifies the +angle in degrees. E.g., sin(90) yields 1. + +The argument of sqrt() can be dimensionless or have a dimension with an +exponent that's a multiple of two. E.g., sqrt(2) and sqrt(2mm*3mm) are +valid expressions, sqrt(2mm) isn't. + GUI --- diff --git a/expr.c b/expr.c index f43f72b..abdd236 100644 --- a/expr.c +++ b/expr.c @@ -249,6 +249,56 @@ static struct num compatible_mult(struct num *a, struct num *b, } +static struct num sin_cos(const struct expr *self, + const struct frame *frame, double (*fn)(double arg)) +{ + struct num res; + + res = eval_num(self->u.op.a, frame); + if (is_undef(res)) + return undef; + if (!is_dimensionless(res)) { + fail("angle must be dimensionless"); + return undef; + } + res.n = fn(res.n/180.0*M_PI); + return res; +} + + +struct num op_sin(const struct expr *self, const struct frame *frame) +{ + return sin_cos(self, frame, sin); +} + + +struct num op_cos(const struct expr *self, const struct frame *frame) +{ + return sin_cos(self, frame, cos); +} + + +struct num op_sqrt(const struct expr *self, const struct frame *frame) +{ + struct num res; + + res = eval_num(self->u.op.a, frame); + if (is_undef(res)) + return undef; + if (res.exponent & 1) { + fail("exponent of sqrt argument must be a multiple of two"); + return undef; + } + if (res.n < 0) { + fail("argument of sqrt must be positive"); + return undef; + } + res.n = sqrt(res.n); + res.exponent >>= 1; + return res; +} + + struct num op_minus(const struct expr *self, const struct frame *frame) { struct num res; @@ -306,7 +356,7 @@ struct num op_div(const struct expr *self, const struct frame *frame) { BINARY; if (!b.n) { - fail("Division by zero"); + fail("division by zero"); return undef; } res = compatible_mult(&a, &b, a.exponent-b.exponent); @@ -471,7 +521,8 @@ static void vacate_op(struct expr *expr) free(expr->u.str); return; } - if (expr->op == op_minus) { + if (expr->op == op_minus || + expr->op == op_sin || expr->op == op_cos || expr->op == op_sqrt) { free_expr(expr->u.op.a); return; } diff --git a/expr.h b/expr.h index f40b95d..6e6c201 100644 --- a/expr.h +++ b/expr.h @@ -111,6 +111,10 @@ struct num op_num(const struct expr *self, const struct frame *frame); struct num op_var(const struct expr *self, const struct frame *frame); struct num op_string(const struct expr *self, const struct frame *frame); +struct num op_sin(const struct expr *self, const struct frame *frame); +struct num op_cos(const struct expr *self, const struct frame *frame); +struct num op_sqrt(const struct expr *self, const struct frame *frame); + struct num op_minus(const struct expr *self, const struct frame *frame); struct num op_add(const struct expr *self, const struct frame *frame); diff --git a/fpd.y b/fpd.y index 5130c37..25008f2 100644 --- a/fpd.y +++ b/fpd.y @@ -43,6 +43,8 @@ static struct obj **next_obj; static int n_vars, n_values; +static const char *id_sin, *id_cos, *id_sqrt; + static struct frame *find_frame(const char *name) { @@ -186,6 +188,9 @@ all: { root_frame = zalloc_type(struct frame); set_frame(root_frame); + id_sin = unique("sin"); + id_cos = unique("cos"); + id_sqrt = unique("sqrt"); } fpd { @@ -704,6 +709,19 @@ primary_expr: { $$ = $2; } + | ID '(' expr ')' + { + if ($1 == id_sin) + $$ = binary_op(op_sin, $3, NULL); + else if ($1 == id_cos) + $$ = binary_op(op_cos, $3, NULL); + else if ($1 == id_sqrt) + $$ = binary_op(op_sqrt, $3, NULL); + else { + yyerrorf("unknown function \"%s\"", $1); + YYABORT; + } + } ; /* special sub-grammar */ diff --git a/unparse.c b/unparse.c index 684b76b..582cdeb 100644 --- a/unparse.c +++ b/unparse.c @@ -38,7 +38,8 @@ static int precedence(op_type op) return prec_mult; if (op == op_minus) return prec_unary; - if (op == op_num || op == op_string || op == op_var) + if (op == op_num || op == op_string || op == op_var || + op == op_sin || op == op_cos || op == op_sqrt) return prec_primary; abort(); } @@ -67,6 +68,21 @@ static char *merge2(const char *op, char *a) } +static char *unparse_op(const struct expr *expr, enum prec prec); + + +static char *unparse_fn(const char *name, const struct expr *expr) +{ + char *buf, *tmp; + + tmp = unparse_op(expr->u.op.a, prec_add); + buf = alloc_size(strlen(name)+strlen(tmp)+3); + sprintf(buf, "%s(%s)", name, tmp); + free(tmp); + return buf; +} + + static char *unparse_op(const struct expr *expr, enum prec prec) { char tmp[100]; @@ -103,6 +119,12 @@ static char *unparse_op(const struct expr *expr, enum prec prec) if (expr->op == op_div) return merge3(unparse_op(expr->u.op.a, prec_mult), "/", unparse_op(expr->u.op.b, prec_primary)); + if (expr->op == op_sin) + return unparse_fn("sin", expr); + if (expr->op == op_cos) + return unparse_fn("cos", expr); + if (expr->op == op_sqrt) + return unparse_fn("sqrt", expr); abort(); }