From 14477d73b01d3153506c44f32babcb3225910e16 Mon Sep 17 00:00:00 2001 From: zbyv Date: Thu, 28 Nov 2024 17:30:29 +0100 Subject: [PATCH] funkce vice promennych --- CMakeLists.txt | 1 + lex.c | 122 +++++++++++++++++++++++++---------------------- lex.h | 32 +++++-------- main.c | 2 +- math_functions.c | 64 +++++++++++++++++++++++++ math_functions.h | 18 +++++++ parser.c | 114 ++++++++++++++++++++++++++++--------------- parser.h | 3 +- tree.c | 89 ++++++++++++++++++---------------- tree.h | 7 +-- 10 files changed, 289 insertions(+), 163 deletions(-) create mode 100644 math_functions.c create mode 100644 math_functions.h diff --git a/CMakeLists.txt b/CMakeLists.txt index a42558a..88c44f2 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -16,6 +16,7 @@ add_executable(Graph "tree.c" "ps_graph.c" "error_buffer.c" + "math_functions.c" ) # Optionally, you can set compiler warnings diff --git a/lex.c b/lex.c index 13d0e34..69b27bd 100644 --- a/lex.c +++ b/lex.c @@ -4,12 +4,15 @@ #include #include -void lex_init(struct lexer *lex, const char *str) { +#include "math_functions.h" + +void lex_init(struct lexer *lex, const char *str, const char *variable_name) { error_buffer_init(&lex->eb); lex->start = str; lex->prev_p = str; lex->p = str; + lex->variable_name = variable_name; lex_next(lex); } @@ -39,6 +42,24 @@ static int try_advance(struct lexer *lex, const char *str) { } } +static int is_identifier_char(char c) { + return (c >= '0' && c <= '9') || (c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z'); +} + +static int try_advance_identifier(struct lexer *lex, const char *str) { + const char *temp_p = lex->p; + if (!try_advance(lex, str)) + return 0; + + /* overit konec identifikatoru */ + if (is_identifier_char(*lex->p)) { + lex->p = temp_p; + return 0; + } + + return 1; +} + static int try_token(struct lexer *lex, const char *tok_str, enum token_type type) { if (try_advance(lex, tok_str)) { lex->tok.type = type; @@ -47,12 +68,18 @@ static int try_token(struct lexer *lex, const char *tok_str, enum token_type typ return 0; } -static int try_fn(struct lexer *lex, const char *tok_str, enum math_fn fn) { - if (try_advance(lex, tok_str)) { - lex->tok.type = TOK_FUNCTION; - lex->tok.val.fn = fn; - return 1; +static int try_fns(struct lexer *lex) { + const struct math_function *const fns = fns_get(); + size_t i; + + for (i = 0; fns[i].name; ++i) { + if (try_advance_identifier(lex, fns[i].name)) { + lex->tok.type = TOK_FUNCTION; + lex->tok.val.fn_idx = i; + return 1; + } } + return 0; } @@ -82,24 +109,16 @@ void lex_next(struct lexer *lex) { if (try_token(lex, "/", TOK_DIVIDE)) return; if (try_token(lex, "^", TOK_POWER)) return; - if (try_token(lex, "x", TOK_X)) return; - if (try_token(lex, "(", TOK_LEFT_PAREN)) return; if (try_token(lex, ")", TOK_RIGHT_PAREN)) return; + if (try_token(lex, ",", TOK_COMMA)) return; - if (try_fn(lex, "abs", FN_ABS)) return; - if (try_fn(lex, "exp", FN_EXP)) return; - if (try_fn(lex, "ln", FN_LN)) return; - if (try_fn(lex, "log", FN_LOG)) return; - if (try_fn(lex, "sinh", FN_SINH)) return; - if (try_fn(lex, "sin", FN_SIN)) return; - if (try_fn(lex, "cosh", FN_COSH)) return; - if (try_fn(lex, "cos", FN_COS)) return; - if (try_fn(lex, "tanh", FN_TANH)) return; - if (try_fn(lex, "tan", FN_TAN)) return; - if (try_fn(lex, "asin", FN_ASIN)) return; - if (try_fn(lex, "acos", FN_ACOS)) return; - if (try_fn(lex, "atan", FN_ATAN)) return; + if (try_advance_identifier(lex, lex->variable_name)) { + lex->tok.type = TOK_VARIABLE; + return; + } + + if (try_fns(lex)) return; if (try_number(lex)) return; @@ -125,7 +144,7 @@ void lex_print_position(const struct lexer *lex, struct error_buffer *eb) { error_printf(eb, " "); for (i = 0; i < input_len; ++i) { - char c = '_'; + char c = '~'; if (i == pos) c = '^'; @@ -144,50 +163,39 @@ const char *lex_get_error_text(const struct lexer *lex) { return error_get_text(&lex->eb); } +const char *lex_token_str(enum token_type token) { + switch (token) + { + case TOK_EOF: return ""; + case TOK_ERROR: return ""; + case TOK_NUMBER: return ""; + case TOK_PLUS: return "'+'"; + case TOK_MINUS: return "'-'"; + case TOK_MULTIPLY: return "'*'"; + case TOK_DIVIDE: return "'/'"; + case TOK_POWER: return "'^'"; + case TOK_VARIABLE: return ""; + case TOK_FUNCTION: return ""; + case TOK_LEFT_PAREN: return "'('"; + case TOK_RIGHT_PAREN: return "')'"; + case TOK_COMMA: return "','"; + + default: return ""; + } +} + #ifdef LEX_DEBUG void lex_debug_print_token(const struct token *tok) { - static const char *token_str[] = { - "TOK_EOF", - "TOK_ERROR", - "TOK_NUMBER", - "TOK_PLUS", - "TOK_MINUS", - "TOK_MULTIPLY", - "TOK_DIVIDE", - "TOK_POWER", - "TOK_X", - "TOK_FUNCTION", - "TOK_LEFT_PAREN", - "TOK_RIGHT_PAREN" - }; - - - printf("%-20s ", token_str[tok->type]); + printf("%-20s ", lex_token_str(tok->type)); if (tok->type == TOK_NUMBER) printf("%.2f\n", tok->val.num); else if (tok->type == TOK_FUNCTION) { - static const char *fn_str[] = { - "FN_ABS", - "FN_EXP", - "FN_LN", - "FN_LOG", - "FN_SIN", - "FN_COS", - "FN_TAN", - "FN_ASIN", - "FN_ACOS", - "FN_ATAN", - "FN_SINH", - "FN_COSH", - "FN_TANH" - }; - - printf("%s\n", fn_str[tok->val.fn]); + printf("%s\n", fns_get()[tok->val.fn_idx].name); } else printf("\n"); } -#endif /* LEX_DEBUG */ \ No newline at end of file +#endif /* LEX_DEBUG */ diff --git a/lex.h b/lex.h index 948fec1..76b82c9 100644 --- a/lex.h +++ b/lex.h @@ -15,33 +15,20 @@ enum token_type { TOK_MULTIPLY, TOK_DIVIDE, TOK_POWER, - TOK_X, - TOK_FUNCTION, - TOK_LEFT_PAREN, - TOK_RIGHT_PAREN -}; -enum math_fn { - FN_ABS, - FN_EXP, - FN_LN, - FN_LOG, - FN_SIN, - FN_COS, - FN_TAN, - FN_ASIN, - FN_ACOS, - FN_ATAN, - FN_SINH, - FN_COSH, - FN_TANH + TOK_VARIABLE, + TOK_FUNCTION, + + TOK_LEFT_PAREN, + TOK_RIGHT_PAREN, + TOK_COMMA }; struct token { enum token_type type; union token_val { double num; - enum math_fn fn; + size_t fn_idx; } val; }; @@ -51,9 +38,10 @@ struct lexer { const char *p; struct token tok; struct error_buffer eb; + const char *variable_name; }; -void lex_init(struct lexer *lex, const char *str); +void lex_init(struct lexer *lex, const char *str, const char *variable_name); void lex_next(struct lexer *lex); struct token *lex_token(struct lexer *lex); @@ -63,6 +51,8 @@ void lex_print_position(const struct lexer *lex, struct error_buffer *eb); enum error_code lex_get_error(const struct lexer *lex); const char *lex_get_error_text(const struct lexer *lex); +const char *lex_token_str(enum token_type token); + #ifdef LEX_DEBUG void lex_debug_print_token(const struct token *tok); #endif /* LEX_DEBUG */ diff --git a/main.c b/main.c index bf4b747..1033fbe 100644 --- a/main.c +++ b/main.c @@ -17,7 +17,7 @@ int main(int argc, char *argv[]) { } parser_init(&parser); - node = parser_parse(&parser, argv[1]); + node = parser_parse(&parser, argv[1], "x"); if (!node) { fprintf(stderr, "%s", parser_get_error_text(&parser)); diff --git a/math_functions.c b/math_functions.c new file mode 100644 index 0000000..1892000 --- /dev/null +++ b/math_functions.c @@ -0,0 +1,64 @@ +#include "math_functions.h" + +#include + +#define MAKE_FUNCTION_1_ARG(name) \ + static double mf_##name(const double *args) { \ + return name(args[0]); \ + } + +MAKE_FUNCTION_1_ARG(fabs) +MAKE_FUNCTION_1_ARG(exp) +MAKE_FUNCTION_1_ARG(log) +MAKE_FUNCTION_1_ARG(log10) +MAKE_FUNCTION_1_ARG(sin) +MAKE_FUNCTION_1_ARG(cos) +MAKE_FUNCTION_1_ARG(tan) +MAKE_FUNCTION_1_ARG(asin) +MAKE_FUNCTION_1_ARG(acos) +MAKE_FUNCTION_1_ARG(atan) +MAKE_FUNCTION_1_ARG(sinh) +MAKE_FUNCTION_1_ARG(cosh) +MAKE_FUNCTION_1_ARG(tanh) + +static double mf_min(const double *args) { + if (args[0] < args[1]) + return args[0]; + + return args[1]; +} + +static double mf_max(const double *args) { + if (args[0] > args[1]) + return args[0]; + + return args[1]; +} + +static double mf_mod(const double *args) { + return fmod(args[0], args[1]); +} + +const struct math_function *fns_get(void) { + static const struct math_function fns[] = { + { "abs", 1, mf_fabs }, + { "exp", 1, mf_exp }, + { "ln", 1, mf_log }, + { "log", 1, mf_log10 }, + { "sin", 1, mf_sin }, + { "cos", 1, mf_cos }, + { "tan", 1, mf_tan }, + { "asin", 1, mf_asin }, + { "acos", 1, mf_acos }, + { "atan", 1, mf_atan }, + { "sinh", 1, mf_sinh }, + { "cosh", 1, mf_cosh }, + { "tanh", 1, mf_tanh }, + { "min", 2, mf_min }, + { "max", 2, mf_max }, + { "mod", 2, mf_mod }, + { NULL } + }; + + return fns; +} diff --git a/math_functions.h b/math_functions.h new file mode 100644 index 0000000..13d21b5 --- /dev/null +++ b/math_functions.h @@ -0,0 +1,18 @@ +#ifndef MATH_FUNCTIONS_H +#define MATH_FUNCTIONS_H + +#include + +#define MAX_MATH_FUNCTION_ARGS 2 + +typedef double (*math_function_ptr)(const double*); + +struct math_function { + const char *name; + size_t num_args; + math_function_ptr ptr; +}; + +const struct math_function *fns_get(void); + +#endif /* MATH_FUNCTIONS_H */ \ No newline at end of file diff --git a/parser.c b/parser.c index 9019ed0..1caa627 100644 --- a/parser.c +++ b/parser.c @@ -14,8 +14,8 @@ static double token_num(struct parser *parser) { return get_token(parser)->val.num; } -static enum math_fn token_fn(struct parser *parser) { - return get_token(parser)->val.fn; +static size_t token_fn_idx(struct parser *parser) { + return get_token(parser)->val.fn_idx; } static void next_token(struct parser *parser) { @@ -36,21 +36,6 @@ static void error_bad_alloc(struct parser *parser) { } static void error_expected_one_of(struct parser *parser, const int expected[]) { - static const char* token_name[] = { - "end of expression", - "lexer error", - "constant", - "+", - "-", - "*", - "/", - "^", - "x", - "function name", - "(", - ")" - }; - size_t i; enum token_type got = get_token(parser)->type; @@ -68,10 +53,10 @@ static void error_expected_one_of(struct parser *parser, const int expected[]) { if (i > 0) error_printf(&parser->eb, ", "); - error_printf(&parser->eb, "'%s'", token_name[expected[i]]); + error_printf(&parser->eb, "%s", lex_token_str(expected[i])); } - error_printf(&parser->eb, " - but got '%s'\n", token_name[got]); + error_printf(&parser->eb, " - but got %s\n", lex_token_str(got)); lex_print_position(&parser->lexer, &parser->eb); } @@ -83,6 +68,7 @@ static void error_expected_single(struct parser *parser, enum token_type expecte } static struct expr_node* parse_subexpression(struct parser *parser); +int parse_n_expressions(struct parser *parser, struct expr_node **out_nodes, size_t n); static struct expr_node* parse_bracketed(struct parser *parser) { struct expr_node* node; @@ -105,7 +91,7 @@ static struct expr_node* parse_bracketed(struct parser *parser) { } static struct expr_node *parse_base(struct parser *parser) { - struct expr_node *node, *inner; + struct expr_node *node; if (token_is(parser, TOK_NUMBER)) { double val = token_num(parser); @@ -118,7 +104,7 @@ static struct expr_node *parse_base(struct parser *parser) { return node; } - if (accept_token(parser, TOK_X)) { + if (accept_token(parser, TOK_VARIABLE)) { if (!(node = node_create_x())) { error_bad_alloc(parser); return NULL; @@ -127,15 +113,33 @@ static struct expr_node *parse_base(struct parser *parser) { } if (token_is(parser, TOK_FUNCTION)) { - enum math_fn fn = token_fn(parser); + struct expr_node *arg_nodes[MAX_MATH_FUNCTION_ARGS]; + size_t i; + size_t fn_idx = token_fn_idx(parser); + const struct math_function *fn = &fns_get()[fn_idx]; + next_token(parser); - if (!(inner = parse_bracketed(parser))) + if (!accept_token(parser, TOK_LEFT_PAREN)) { + error_expected_single(parser, TOK_LEFT_PAREN); + return NULL; + } + + if (!parse_n_expressions(parser, arg_nodes, fn->num_args)) return NULL; - if (!(node = node_create_fn(fn, inner))) { - node_free(inner); + if (!(node = node_create_fn(fn_idx, arg_nodes))) { error_bad_alloc(parser); + + for (i = 0; i < fn->num_args; ++i) + node_free(arg_nodes[i]); + + return NULL; + } + + if (!accept_token(parser, TOK_RIGHT_PAREN)) { + error_expected_single(parser, TOK_RIGHT_PAREN); + node_free(node); return NULL; } @@ -147,7 +151,7 @@ static struct expr_node *parse_base(struct parser *parser) { } { - static const int expected[] = { TOK_NUMBER, TOK_X, TOK_FUNCTION, TOK_LEFT_PAREN, -1 }; + static const int expected[] = { TOK_NUMBER, TOK_VARIABLE, TOK_FUNCTION, TOK_LEFT_PAREN, -1 }; error_expected_one_of(parser, expected); } @@ -269,29 +273,63 @@ static struct expr_node *parse_subexpression(struct parser *parser) { return node; } -static struct expr_node *parse_expression(struct parser *parser) { - struct expr_node *node; +int parse_n_expressions(struct parser *parser, struct expr_node **out_nodes, size_t n) { + size_t i; - if (!(node = parse_subexpression(parser))) - return NULL; + for (i = 0; i < n; ++i) { + struct expr_node *node; + if (!(node = parse_subexpression(parser))) + break; - if (!token_is(parser, TOK_EOF)) { - error_expected_single(parser, TOK_EOF); - node_free(node); - return NULL; + out_nodes[i] = node; + + if (i < n - 1) + accept_token(parser, TOK_COMMA); } - return node; + if (i != n) { + while (i) { + --i; + node_free(out_nodes[i]); + out_nodes[i] = NULL; + } + + return 0; + } + + return 1; } void parser_init(struct parser *parser) { error_buffer_init(&parser->eb); } -struct expr_node *parser_parse(struct parser *parser, const char *str) { - lex_init(&parser->lexer, str); - return parse_expression(parser); +int parser_parse_n(struct parser *parser, const char *str, const char *variable_name, struct expr_node **out_nodes, size_t n) { + lex_init(&parser->lexer, str, variable_name); + if (!parse_n_expressions(parser, out_nodes, n)) { + return 0; + } + + if (!token_is(parser, TOK_EOF)) { + size_t i; + error_expected_single(parser, TOK_EOF); + + for (i = 0; i < n; ++i) { + node_free(out_nodes[i]); + out_nodes[i] = NULL; + } + + return 0; + } + + return 1; +} + +struct expr_node *parser_parse(struct parser *parser, const char *str, const char *variable_name) { + struct expr_node *out_node = NULL; + parser_parse_n(parser, str, variable_name, &out_node, 1); + return out_node; } enum error_code parser_get_error(const struct parser *parser) { diff --git a/parser.h b/parser.h index e7fdb4c..0d1d881 100644 --- a/parser.h +++ b/parser.h @@ -10,7 +10,8 @@ struct parser { }; void parser_init(struct parser *parser); -struct expr_node *parser_parse(struct parser *parser, const char *str); +int parser_parse_n(struct parser *parser, const char *str, const char *variable_name, struct expr_node **out_nodes, size_t n); +struct expr_node *parser_parse(struct parser *parser, const char *str, const char *variable_name); enum error_code parser_get_error(const struct parser *parser); const char *parser_get_error_text(const struct parser *parser); diff --git a/tree.c b/tree.c index 9356611..d97d7d3 100644 --- a/tree.c +++ b/tree.c @@ -59,12 +59,18 @@ struct expr_node *node_create_x(void) { return node; } -struct expr_node *node_create_fn(enum math_fn fn, struct expr_node *arg) { +struct expr_node *node_create_fn(size_t fn_idx, struct expr_node **args) { + size_t i, num_args; struct expr_node *node = alloc_node(); if (!node) return NULL; node->type = EXPR_FN; - node->vals.fn.fn = fn; - node->vals.fn.arg = arg; + node->vals.fn.fn_idx = fn_idx; + + num_args = fns_get()[fn_idx].num_args; + for (i = 0; i < num_args; ++i) { + node->vals.fn.args[i] = args[i]; + } + return node; } @@ -86,7 +92,12 @@ void node_free(struct expr_node *node) { break; case EXPR_FN: - node_free(node->vals.fn.arg); + { + size_t i, num_args = fns_get()[node->vals.fn.fn_idx].num_args; + for (i = 0; i < num_args; ++i) { + node_free(node->vals.fn.args[i]); + } + } break; default: @@ -112,22 +123,6 @@ static void debug_print_binop(struct expr_node *node, const char* name, int inde debug_print(node->vals.binop.right, indent + 1); } -static const char* fn_str[] = { - "FN_ABS", - "FN_EXP", - "FN_LN", - "FN_LOG", - "FN_SIN", - "FN_COS", - "FN_TAN", - "FN_ASIN", - "FN_ACOS", - "FN_ATAN", - "FN_SINH", - "FN_COSH", - "FN_TANH" -}; - static void debug_print(struct expr_node *node, int indent) { switch (node->type) { @@ -166,11 +161,18 @@ static void debug_print(struct expr_node *node, int indent) { break; - case EXPR_FN: - debug_indent(indent); printf("[FN] %s\n", fn_str[node->vals.fn.fn]); - /*debug_indent(indent); printf("arg:\n");*/ - debug_print(node->vals.fn.arg, indent + 1); + case EXPR_FN: + { + size_t i; + const struct math_function *fn = &fns_get()[node->vals.fn.fn_idx]; + debug_indent(indent); printf("[FN] %s\n", fn->name); + + for (i = 0; i < fn->num_args; ++i) { + debug_print(node->vals.fn.args[i], indent + 1); + } + break; + } default: break; @@ -230,10 +232,20 @@ static void debug_print_gv(const struct expr_node *node, FILE *output) { break; case EXPR_FN: - fprintf(output, "node%p [label=\"FN: %s\"]\n", (void*)node, fn_str[node->vals.fn.fn]); - debug_print_gv(node->vals.fn.arg, output); - fprintf(output, "node%p -> node%p [label=arg]\n", (void*)node, (void*)node->vals.fn.arg); + { + size_t i; + const struct math_function *fn = &fns_get()[node->vals.fn.fn_idx]; + + fprintf(output, "node%p [label=\"FN: %s\"]\n", (void*)node, fn->name); + + for (i = 0; i < fn->num_args; ++i) { + struct expr_node *arg = node->vals.fn.args[i]; + debug_print_gv(arg, output); + fprintf(output, "node%p -> node%p [label=arg%d]\n", (void*)node, (void*)arg, (int)i + 1); + } + break; + } default: break; @@ -275,22 +287,15 @@ double node_eval(const struct expr_node *node, double x) { case EXPR_FN: { - double inner = node_eval(node->vals.fn.arg, x); - switch (node->vals.fn.fn) { - case FN_ABS: return fabs(inner); - case FN_EXP: return exp(inner); - case FN_LN: return log(inner); - case FN_LOG: return log10(inner); - case FN_SIN: return sin(inner); - case FN_COS: return cos(inner); - case FN_TAN: return tan(inner); - case FN_ASIN: return asin(inner); - case FN_ACOS: return acos(inner); - case FN_ATAN: return atan(inner); - case FN_SINH: return sinh(inner); - case FN_COSH: return cosh(inner); - case FN_TANH: return tanh(inner); + double inner_results[MAX_MATH_FUNCTION_ARGS]; + size_t i; + const struct math_function *fn = &fns_get()[node->vals.fn.fn_idx]; + + for (i = 0; i < fn->num_args; ++i) { + inner_results[i] = node_eval(node->vals.fn.args[i], x); } + + return fn->ptr(inner_results); } } diff --git a/tree.h b/tree.h index ff99970..bc08d20 100644 --- a/tree.h +++ b/tree.h @@ -2,6 +2,7 @@ #define TREE_H #include "lex.h" +#include "math_functions.h" enum expr_type { EXPR_CONST, @@ -23,8 +24,8 @@ struct expr_node { struct expr_node *right; } binop; struct expr_fn_vals { - enum math_fn fn; - struct expr_node *arg; + size_t fn_idx; + struct expr_node *args[MAX_MATH_FUNCTION_ARGS]; } fn; struct expr_node *unop; double num; @@ -39,7 +40,7 @@ extern struct expr_node *node_create_mult(struct expr_node *left, struct expr_no extern struct expr_node *node_create_div(struct expr_node *left, struct expr_node *right); extern struct expr_node *node_create_pow(struct expr_node *base, struct expr_node *power); extern struct expr_node *node_create_x(void); -extern struct expr_node *node_create_fn(enum math_fn fn, struct expr_node *arg); +extern struct expr_node *node_create_fn(size_t fn_idx, struct expr_node **args); extern void node_debug_print(struct expr_node *node); extern void node_debug_print_gv(const struct expr_node *node, FILE *output);