funkce vice promennych

This commit is contained in:
zbyv 2024-11-28 17:30:29 +01:00
parent a62531d893
commit 14477d73b0
10 changed files with 289 additions and 163 deletions

View File

@ -16,6 +16,7 @@ add_executable(Graph
"tree.c"
"ps_graph.c"
"error_buffer.c"
"math_functions.c"
)
# Optionally, you can set compiler warnings

122
lex.c
View File

@ -4,12 +4,15 @@
#include <stdio.h>
#include <string.h>
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 "<eof>";
case TOK_ERROR: return "<error>";
case TOK_NUMBER: return "<number>";
case TOK_PLUS: return "'+'";
case TOK_MINUS: return "'-'";
case TOK_MULTIPLY: return "'*'";
case TOK_DIVIDE: return "'/'";
case TOK_POWER: return "'^'";
case TOK_VARIABLE: return "<variable>";
case TOK_FUNCTION: return "<function>";
case TOK_LEFT_PAREN: return "'('";
case TOK_RIGHT_PAREN: return "')'";
case TOK_COMMA: return "','";
default: return "<unknown token>";
}
}
#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 */
#endif /* LEX_DEBUG */

32
lex.h
View File

@ -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 */

2
main.c
View File

@ -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));

64
math_functions.c Normal file
View File

@ -0,0 +1,64 @@
#include "math_functions.h"
#include <math.h>
#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;
}

18
math_functions.h Normal file
View File

@ -0,0 +1,18 @@
#ifndef MATH_FUNCTIONS_H
#define MATH_FUNCTIONS_H
#include <stdlib.h>
#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 */

114
parser.c
View File

@ -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) {

View File

@ -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);

89
tree.c
View File

@ -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);
}
}

7
tree.h
View File

@ -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);