Compare commits
2 Commits
60a8aff329
...
c50551dca8
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c50551dca8 | ||
|
|
a5d9e88f77 |
2
lex.c
2
lex.c
@ -141,7 +141,7 @@ void lex_next(struct lexer *lex) {
|
|||||||
|
|
||||||
if (try_constant(lex, "pi", CONST_PI)) return;
|
if (try_constant(lex, "pi", CONST_PI)) return;
|
||||||
if (try_constant(lex, "e", CONST_E)) return;
|
if (try_constant(lex, "e", CONST_E)) return;
|
||||||
if (try_constant(lex, "skibidi", CONST_S)) return;
|
if (try_constant(lex, "skibidi", CONST_S)) return; /* easter egg */
|
||||||
|
|
||||||
if (try_advance_identifier(lex, lex->variable_name)) {
|
if (try_advance_identifier(lex, lex->variable_name)) {
|
||||||
lex->tok.type = TOK_VARIABLE;
|
lex->tok.type = TOK_VARIABLE;
|
||||||
|
|||||||
97
main.c
97
main.c
@ -3,17 +3,83 @@
|
|||||||
#include "lex.h"
|
#include "lex.h"
|
||||||
#include "parser.h"
|
#include "parser.h"
|
||||||
#include "ps_graph.h"
|
#include "ps_graph.h"
|
||||||
|
#include "error_buffer.h"
|
||||||
|
|
||||||
|
static void print_usage(FILE *f, const char *name) {
|
||||||
|
fprintf(f, "Usage: %s <function> <output file> [<range>]\n", name);
|
||||||
|
fprintf(f, " <function> ..... The function to plot the graph of.\n");
|
||||||
|
fprintf(f, " <output file> .. Name of the output PostScript file.\n");
|
||||||
|
fprintf(f, " <range> ........ Ranges of the graph in the format of \"xMin:xMax:yMin:yMax\". Optional.\n");
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
static int read_double(const char **str, double *out) {
|
||||||
|
char *end;
|
||||||
|
*out = strtod(*str, &end);
|
||||||
|
|
||||||
|
if (*str == end)
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
*str = end;
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define EXPECT_DOUBLE(str, out) \
|
||||||
|
if (!read_double(&str, out)) \
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
#define EXPECT_CHAR(str, c) \
|
||||||
|
if (*str++ != c) \
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
static int parse_range(const char *str, struct graph_range *range) {
|
||||||
|
EXPECT_DOUBLE(str, &range->xmin);
|
||||||
|
EXPECT_CHAR(str, ':');
|
||||||
|
EXPECT_DOUBLE(str, &range->xmax);
|
||||||
|
EXPECT_CHAR(str, ':');
|
||||||
|
EXPECT_DOUBLE(str, &range->ymin);
|
||||||
|
EXPECT_CHAR(str, ':');
|
||||||
|
EXPECT_DOUBLE(str, &range->ymax);
|
||||||
|
EXPECT_CHAR(str, '\0');
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum error_code export_to_file(const struct expr_node *node, const struct graph_range *range, const char *out_name) {
|
||||||
|
FILE *file;
|
||||||
|
|
||||||
|
if (!(file = fopen(out_name, "w"))) {
|
||||||
|
fprintf(stderr, "Cannot open \"%s\" for writing!\n", out_name);
|
||||||
|
return ERR_INVALID_FILENAME;
|
||||||
|
}
|
||||||
|
|
||||||
|
ps_export_graph(file, node, range);
|
||||||
|
|
||||||
|
fclose(file);
|
||||||
|
return ERR_NO_ERR;
|
||||||
|
}
|
||||||
|
|
||||||
int main(int argc, char *argv[]) {
|
int main(int argc, char *argv[]) {
|
||||||
|
enum error_code err;
|
||||||
struct parser parser;
|
struct parser parser;
|
||||||
struct expr_node *node;
|
struct expr_node *node;
|
||||||
FILE *file;
|
struct graph_range range;
|
||||||
struct graph_range graph;
|
|
||||||
|
|
||||||
if (argc < 2) {
|
if (argc < 3) {
|
||||||
printf("Usage: %s <expression>\n", argv[0]);
|
print_usage(stderr, argv[0]);
|
||||||
return 1;
|
return ERR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (argc > 3) {
|
||||||
|
if (!parse_range(argv[3], &range)) {
|
||||||
|
fprintf(stderr, "Invalid format of ranges! The correct format is \"xMin:xMax:yMin:yMax\".\n");
|
||||||
|
return ERR_INVALID_ARGS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
range.xmin = -10.0;
|
||||||
|
range.xmax = 10.0;
|
||||||
|
range.ymin = -10.0;
|
||||||
|
range.ymax = 10.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
node = parser_parse(&parser, argv[1], "x");
|
node = parser_parse(&parser, argv[1], "x");
|
||||||
@ -22,23 +88,14 @@ int main(int argc, char *argv[]) {
|
|||||||
fprintf(stderr, "%s", parser_get_error_text(&parser));
|
fprintf(stderr, "%s", parser_get_error_text(&parser));
|
||||||
return parser_get_error(&parser);
|
return parser_get_error(&parser);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
err = export_to_file(node, &range, argv[2]);
|
||||||
|
|
||||||
graph.xmin = 0.0;
|
/* file = fopen("graph.dot", "w");
|
||||||
graph.xmax = 6.28;
|
|
||||||
graph.ymin = 0.0;
|
|
||||||
graph.ymax = 2.0;
|
|
||||||
graph.step = 0.001;
|
|
||||||
|
|
||||||
file = fopen("out.ps", "w");
|
|
||||||
ps_export_graph(file, node, &graph);
|
|
||||||
fclose(file);
|
|
||||||
|
|
||||||
file = fopen("graph.dot", "w");
|
|
||||||
node_debug_print_gv(node, file);
|
node_debug_print_gv(node, file);
|
||||||
fclose(file);
|
fclose(file); */
|
||||||
|
|
||||||
node_free(node);
|
node_free(node);
|
||||||
|
|
||||||
|
return err;
|
||||||
return 0;
|
|
||||||
}
|
}
|
||||||
@ -1,10 +1,15 @@
|
|||||||
#include "math_functions.h"
|
#include "math_functions.h"
|
||||||
|
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <errno.h>
|
||||||
|
|
||||||
#define MAKE_FUNCTION_1_ARG(name) \
|
#define MAKE_FUNCTION_1_ARG(name) \
|
||||||
static double mf_##name(const double *args) { \
|
static enum eval_result mf_##name(double *y, const double *args) { \
|
||||||
return name(args[0]); \
|
errno = 0; \
|
||||||
|
*y = name(args[0]); \
|
||||||
|
if (errno) \
|
||||||
|
return EVAL_ERROR; \
|
||||||
|
return EVAL_OK; \
|
||||||
}
|
}
|
||||||
|
|
||||||
MAKE_FUNCTION_1_ARG(fabs)
|
MAKE_FUNCTION_1_ARG(fabs)
|
||||||
@ -23,35 +28,51 @@ MAKE_FUNCTION_1_ARG(tanh)
|
|||||||
MAKE_FUNCTION_1_ARG(floor)
|
MAKE_FUNCTION_1_ARG(floor)
|
||||||
MAKE_FUNCTION_1_ARG(ceil)
|
MAKE_FUNCTION_1_ARG(ceil)
|
||||||
|
|
||||||
static double mf_sgn(const double *args) {
|
static double mf_sgn(double *y, const double *args) {
|
||||||
if (args[0] < 0.0)
|
if (args[0] < 0.0)
|
||||||
return -1.0;
|
*y = -1.0;
|
||||||
else if (args[0] > 0.0)
|
else if (args[0] > 0.0)
|
||||||
return 1.0;
|
*y = 1.0;
|
||||||
else
|
else
|
||||||
return 0.0;
|
*y = 0.0;
|
||||||
|
|
||||||
|
return EVAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double mf_min(const double *args) {
|
static double mf_min(double *y, const double *args) {
|
||||||
if (args[0] < args[1])
|
if (args[0] < args[1])
|
||||||
return args[0];
|
*y = args[0];
|
||||||
|
else
|
||||||
|
*y = args[1];
|
||||||
|
|
||||||
return args[1];
|
return EVAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double mf_max(const double *args) {
|
static double mf_max(double *y, const double *args) {
|
||||||
if (args[0] > args[1])
|
if (args[0] > args[1])
|
||||||
return args[0];
|
*y = args[0];
|
||||||
|
else
|
||||||
|
*y = args[1];
|
||||||
|
|
||||||
return args[1];
|
return EVAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
static double mf_mod(const double *args) {
|
static double mf_mod(double *y, const double *args) {
|
||||||
return fmod(args[0], args[1]);
|
errno = 0;
|
||||||
|
*y = fmod(args[0], args[1]);
|
||||||
|
|
||||||
|
if (errno)
|
||||||
|
return EVAL_ERROR;
|
||||||
|
|
||||||
|
return EVAL_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
const struct math_function *fns_get(void) {
|
const struct math_function *fns_get(void) {
|
||||||
static const struct math_function fns[] = {
|
static const struct math_function fns[] = {
|
||||||
|
/* +--- název
|
||||||
|
| +--- počet argumentů
|
||||||
|
| | +--- vyhodnocovač
|
||||||
|
| | | */
|
||||||
{ "abs", 1, mf_fabs },
|
{ "abs", 1, mf_fabs },
|
||||||
{ "exp", 1, mf_exp },
|
{ "exp", 1, mf_exp },
|
||||||
{ "ln", 1, mf_log },
|
{ "ln", 1, mf_log },
|
||||||
|
|||||||
@ -5,10 +5,18 @@
|
|||||||
|
|
||||||
#define MAX_MATH_FUNCTION_ARGS 2
|
#define MAX_MATH_FUNCTION_ARGS 2
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Výsledek vyhodnocení výrazu
|
||||||
|
*/
|
||||||
|
enum eval_result {
|
||||||
|
EVAL_OK,
|
||||||
|
EVAL_ERROR
|
||||||
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Ukazatel na vyhodnocovač matematické funkce
|
* @brief Ukazatel na vyhodnocovač matematické funkce
|
||||||
*/
|
*/
|
||||||
typedef double (*math_function_ptr)(const double*);
|
typedef enum eval_result (*math_function_ptr)(double* y, const double* args);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Matematická funkce
|
* @brief Matematická funkce
|
||||||
|
|||||||
11
ps_graph.c
11
ps_graph.c
@ -1,8 +1,10 @@
|
|||||||
#include "ps_graph.h"
|
#include "ps_graph.h"
|
||||||
|
#include <math.h>
|
||||||
|
|
||||||
#define GRAPH_SIZE 400
|
#define GRAPH_SIZE 400
|
||||||
#define HALF_GRAPH_SIZE (GRAPH_SIZE / 2)
|
#define HALF_GRAPH_SIZE (GRAPH_SIZE / 2)
|
||||||
#define GRID_STEP (GRAPH_SIZE / 10)
|
#define GRID_STEP (GRAPH_SIZE / 10)
|
||||||
|
#define FUNCTION_SEGMENTS 1000
|
||||||
|
|
||||||
void ps_export_graph(FILE *file, const struct expr_node *node, const struct graph_range *range) {
|
void ps_export_graph(FILE *file, const struct expr_node *node, const struct graph_range *range) {
|
||||||
int i;
|
int i;
|
||||||
@ -87,12 +89,14 @@ void ps_export_graph(FILE *file, const struct expr_node *node, const struct grap
|
|||||||
"newpath\n"
|
"newpath\n"
|
||||||
);
|
);
|
||||||
|
|
||||||
for (x = range->xmin; x <= range->xmax; x += range->step) {
|
for (i = 0; i < FUNCTION_SEGMENTS; ++i) {
|
||||||
const char *cmd = "lineto";
|
const char *cmd = "lineto";
|
||||||
double xpos, ypos;
|
double xpos, ypos;
|
||||||
double y = node_eval(node, x);
|
double y;
|
||||||
|
|
||||||
if (y != y) {
|
x = range->xmin + (range->xmax - range->xmin) * (double)i / (double)FUNCTION_SEGMENTS;
|
||||||
|
|
||||||
|
if (node_eval(node, x, &y) != EVAL_OK) {
|
||||||
first = 1;
|
first = 1;
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@ -105,6 +109,7 @@ void ps_export_graph(FILE *file, const struct expr_node *node, const struct grap
|
|||||||
first = 0;
|
first = 0;
|
||||||
}
|
}
|
||||||
fprintf(file, "%.2f %.2f %s\n", xpos, ypos, cmd);
|
fprintf(file, "%.2f %.2f %s\n", xpos, ypos, cmd);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
fprintf(file,
|
fprintf(file,
|
||||||
|
|||||||
@ -7,7 +7,6 @@
|
|||||||
struct graph_range {
|
struct graph_range {
|
||||||
double xmin, xmax;
|
double xmin, xmax;
|
||||||
double ymin, ymax;
|
double ymin, ymax;
|
||||||
double step;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
extern void ps_export_graph(FILE *file, const struct expr_node *node, const struct graph_range *range);
|
extern void ps_export_graph(FILE *file, const struct expr_node *node, const struct graph_range *range);
|
||||||
|
|||||||
70
tree.c
70
tree.c
@ -1,6 +1,7 @@
|
|||||||
#include <stdlib.h>
|
#include <stdlib.h>
|
||||||
#include <stdio.h>
|
#include <stdio.h>
|
||||||
#include <math.h>
|
#include <math.h>
|
||||||
|
#include <errno.h>
|
||||||
#include "tree.h"
|
#include "tree.h"
|
||||||
|
|
||||||
static struct expr_node *alloc_node(void) {
|
static struct expr_node *alloc_node(void) {
|
||||||
@ -255,32 +256,79 @@ void node_debug_print_gv(const struct expr_node *node, FILE *output) {
|
|||||||
fprintf(output, "}\n");
|
fprintf(output, "}\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#define INF (1.0e256)
|
||||||
|
|
||||||
|
static int is_real(double x) {
|
||||||
|
return x == x && x < INF && x > -INF;
|
||||||
|
}
|
||||||
|
|
||||||
|
static enum eval_result check_real(double x) {
|
||||||
|
if (is_real(x))
|
||||||
|
return EVAL_OK;
|
||||||
|
else
|
||||||
|
return EVAL_ERROR;
|
||||||
|
}
|
||||||
|
|
||||||
|
#define RETURN_CHECK_REAL(x) if (is_real((x))) return EVAL_OK; else return EVAL_ERROR;
|
||||||
|
|
||||||
|
#define EVAL_CHECK(node, x, y) if (node_eval((node), (x), (y)) != EVAL_OK) return EVAL_ERROR;
|
||||||
|
|
||||||
|
enum eval_result node_eval(const struct expr_node *node, double x, double *y) {
|
||||||
|
double tmp1, tmp2;
|
||||||
|
|
||||||
double node_eval(const struct expr_node *node, double x) {
|
|
||||||
switch (node->type) {
|
switch (node->type) {
|
||||||
case EXPR_CONST:
|
case EXPR_CONST:
|
||||||
return node->vals.num;
|
*y = node->vals.num;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_X:
|
case EXPR_X:
|
||||||
return x;
|
*y = x;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_NEG:
|
case EXPR_NEG:
|
||||||
return -node_eval(node->vals.unop, x);
|
EVAL_CHECK(node->vals.unop, x, &tmp1);
|
||||||
|
*y = -tmp1;
|
||||||
|
return EVAL_OK;
|
||||||
|
|
||||||
case EXPR_ADD:
|
case EXPR_ADD:
|
||||||
return node_eval(node->vals.binop.left, x) + node_eval(node->vals.binop.right, x);
|
EVAL_CHECK(node->vals.binop.left, x, &tmp1);
|
||||||
|
EVAL_CHECK(node->vals.binop.right, x, &tmp2);
|
||||||
|
*y = tmp1 + tmp2;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_SUB:
|
case EXPR_SUB:
|
||||||
return node_eval(node->vals.binop.left, x) - node_eval(node->vals.binop.right, x);
|
EVAL_CHECK(node->vals.binop.left, x, &tmp1);
|
||||||
|
EVAL_CHECK(node->vals.binop.right, x, &tmp2);
|
||||||
|
*y = tmp1 - tmp2;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_MULT:
|
case EXPR_MULT:
|
||||||
return node_eval(node->vals.binop.left, x) * node_eval(node->vals.binop.right, x);
|
EVAL_CHECK(node->vals.binop.left, x, &tmp1);
|
||||||
|
EVAL_CHECK(node->vals.binop.right, x, &tmp2);
|
||||||
|
*y = tmp1 * tmp2;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_DIV:
|
case EXPR_DIV:
|
||||||
return node_eval(node->vals.binop.left, x) / node_eval(node->vals.binop.right, x);
|
EVAL_CHECK(node->vals.binop.left, x, &tmp1);
|
||||||
|
EVAL_CHECK(node->vals.binop.right, x, &tmp2);
|
||||||
|
|
||||||
|
if (tmp2 == 0.0)
|
||||||
|
return EVAL_ERROR;
|
||||||
|
|
||||||
|
*y = tmp1 / tmp2;
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_POW:
|
case EXPR_POW:
|
||||||
return pow(node_eval(node->vals.binop.left, x), node_eval(node->vals.binop.right, x));
|
EVAL_CHECK(node->vals.binop.left, x, &tmp1);
|
||||||
|
EVAL_CHECK(node->vals.binop.right, x, &tmp2);
|
||||||
|
|
||||||
|
errno = 0;
|
||||||
|
*y = pow(tmp1, tmp2);
|
||||||
|
|
||||||
|
if (errno)
|
||||||
|
return EVAL_ERROR;
|
||||||
|
|
||||||
|
return check_real(*y);
|
||||||
|
|
||||||
case EXPR_FN:
|
case EXPR_FN:
|
||||||
{
|
{
|
||||||
@ -289,10 +337,10 @@ double node_eval(const struct expr_node *node, double x) {
|
|||||||
const struct math_function *fn = &fns_get()[node->vals.fn.fn_idx];
|
const struct math_function *fn = &fns_get()[node->vals.fn.fn_idx];
|
||||||
|
|
||||||
for (i = 0; i < fn->num_args; ++i) {
|
for (i = 0; i < fn->num_args; ++i) {
|
||||||
inner_results[i] = node_eval(node->vals.fn.args[i], x);
|
EVAL_CHECK(node->vals.fn.args[i], x, &inner_results[i]);
|
||||||
}
|
}
|
||||||
|
|
||||||
return fn->ptr(inner_results);
|
return fn->ptr(y, inner_results);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
5
tree.h
5
tree.h
@ -123,9 +123,10 @@ void node_debug_print_gv(const struct expr_node *node, FILE *output);
|
|||||||
*
|
*
|
||||||
* @param node Uzel
|
* @param node Uzel
|
||||||
* @param x Proměnná
|
* @param x Proměnná
|
||||||
* @return Hodnota v bodě x
|
* @param y Výsledek vyhodnocení
|
||||||
|
* @return Stav vyhodnocení
|
||||||
*/
|
*/
|
||||||
double node_eval(const struct expr_node *node, double x);
|
enum eval_result node_eval(const struct expr_node *node, double x, double *y);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Uvolní uzel
|
* @brief Uvolní uzel
|
||||||
|
|||||||
Loading…
x
Reference in New Issue
Block a user