commit 704979f9bbc8252f26796b1befdd0f86f2e1858e Author: tovjemam Date: Sun Oct 13 19:32:55 2024 +0200 init diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..43a2f8e --- /dev/null +++ b/CMakeLists.txt @@ -0,0 +1,29 @@ +# Minimum required CMake version +cmake_minimum_required(VERSION 3.10) + +# Project name and version +project(Graph VERSION 1.0) + +# Specify the C standard +set(CMAKE_C_STANDARD 90) +set(CMAKE_C_STANDARD_REQUIRED True) + +# Add the executable target +add_executable(Graph + "main.c" + "lex.c" + "parser.c" + "tree.c" +) + +# Optionally, you can set compiler warnings +if (MSVC) + target_compile_options(Graph PRIVATE /W4) +else() + target_compile_options(Graph PRIVATE -Wall -Wextra -pedantic) +endif() + +# Optionally, set the output directory for the executable +# set_target_properties(Graph PROPERTIES +# RUNTIME_OUTPUT_DIRECTORY "${CMAKE_BINARY_DIR}/bin" +# ) \ No newline at end of file diff --git a/lex.c b/lex.c new file mode 100644 index 0000000..37067d5 --- /dev/null +++ b/lex.c @@ -0,0 +1,191 @@ +#include +#include + +#include "lex.h" + +struct lexer { + const char* start; + const char* prev_p; + const char* p; + struct token tok; +}; + +struct lexer *lex_create(const char *str) { + struct lexer *lex = malloc(sizeof(struct lexer)); + + if (!lex) + return NULL; + + lex->start = str; + lex->prev_p = str; + lex->p = str; + + lex_next(lex); + + return lex; +} + +void lex_free(struct lexer *lex) { + free(lex); +} + +static int is_whitespace(char p) { + return p == ' ' || p == '\t' || p == '\n' || p == '\r'; +} + +static void skip_whitespace(struct lexer *lex) { + while (is_whitespace(*lex->p)) + ++lex->p; +} + +static int try_advance(struct lexer *lex, const char *str) { + const char *temp_p = lex->p; + while (1) { + if (!*str) { + lex->p = temp_p; + return 1; + } + + if (*str != *temp_p) { + return 0; + } + + ++temp_p; + ++str; + } +} + +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; + return 1; + } + 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; + } + return 0; +} + +static int try_number(struct lexer* lex) { + char *end; + double val; + val = strtod(lex->p, &end); + if (lex->p == end) return 0; + lex->tok.type = TOK_NUMBER; + lex->tok.val.num = val; + lex->p = end; + return 1; +} + +void lex_next(struct lexer *lex) { + skip_whitespace(lex); + lex->prev_p = lex->p; + + if (!*lex->p) { + lex->tok.type = TOK_EOF; + return; + } + + if (try_token(lex, "+", TOK_PLUS)) return; + if (try_token(lex, "-", TOK_MINUS)) return; + if (try_token(lex, "*", TOK_MULTIPLY)) return; + 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_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_number(lex)) return; + + fprintf(stderr, "Lexer error - unrecognized sequence \"%s\"\n", lex->p); + lex_print_position(lex); + + lex->tok.type = TOK_ERROR; + return; +} + +struct token *lex_token(struct lexer *lex) { + return &lex->tok; +} + +void lex_print_position(struct lexer *lex) { + int i; + int pos = lex->prev_p - lex->start; + + fprintf(stderr, "At character %d\n", pos); + fprintf(stderr, " %s\n", lex->start); + fprintf(stderr, " "); + for (i = 0; i < pos; ++i) + fprintf(stderr, " "); + + fprintf(stderr, "^\n----------------------------------------------------\n"); +} + + +#ifdef LEX_DEBUG + +void lex_debug_print_token(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" + }; + + 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("%-20s ", token_str[tok->type]); + + if (tok->type == TOK_NUMBER) + printf("%.2f\n", tok->val.num); + else if (tok->type == TOK_FUNCTION) + printf("%s\n", fn_str[tok->val.fn]); + else + printf("\n"); +} + +#endif /* LEX_DEBUG */ \ No newline at end of file diff --git a/lex.h b/lex.h new file mode 100644 index 0000000..cc5dca6 --- /dev/null +++ b/lex.h @@ -0,0 +1,61 @@ +#ifndef LEX_H +#define LEX_H + +#define LEX_DEBUG + +enum token_type { + 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 +}; + +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 +}; + +struct token { + enum token_type type; + union token_val { + double num; + enum math_fn fn; + } val; +}; + +struct lexer; + +extern struct lexer *lex_create(const char *str); +extern void lex_free(struct lexer *lex); + +extern void lex_next(struct lexer *lex); +extern struct token *lex_token(struct lexer *lex); + +extern void lex_print_position(struct lexer *lex); + + +#ifdef LEX_DEBUG +extern void lex_debug_print_token(struct token *tok); +#endif /* LEX_DEBUG */ + +#endif /* LEX_H */ \ No newline at end of file diff --git a/main.c b/main.c new file mode 100644 index 0000000..1b46599 --- /dev/null +++ b/main.c @@ -0,0 +1,38 @@ +#include +#include +#include "lex.h" +#include "parser.h" + +int main(int argc, char *argv[]) { + + struct lexer *lex; + struct expr_node *node; + + if (argc < 2) { + printf("Usage: %s \n", argv[0]); + return 1; + } + + /* + //struct lexer *lex = lex_create("x* sin (x ^ 2) * 5.5e+2* cosh (x )"); + //struct lexer *lex = lex_create("-1 + -2 - 3 + 4"); + */ + lex = lex_create(argv[1]); + + node = parse_expression(lex); + lex_free(lex); + + if (node) { + double x; + node_debug_print(node); + printf("polygon("); + for (x = -10.0; x < 10.0; x += 0.1) { + printf("(%.2f,%.2f),", x, node_eval(node, x)); + } + printf("(10, -1000),(-10,-1000))\n"); + } + + node_free(node); + + return 0; +} \ No newline at end of file diff --git a/parser.c b/parser.c new file mode 100644 index 0000000..399b700 --- /dev/null +++ b/parser.c @@ -0,0 +1,263 @@ +#include +#include +#include "parser.h" + +static int token_is(struct lexer *lex, enum token_type type) { + return lex_token(lex)->type == type; +} + +static int accept(struct lexer *lex, enum token_type type) { + if (token_is(lex, type)) { + lex_next(lex); + return 1; + } + return 0; +} + +static double token_num(struct lexer *lex) { + return lex_token(lex)->val.num; +} + +static enum math_fn token_fn(struct lexer *lex) { + return lex_token(lex)->val.fn; +} + +static void error_expected_one_of(struct lexer *lex, const int expected[], enum token_type got) { + static const char* token_name[] = { + "end of expression", + "lexer error", + "constant", + "+", + "-", + "*", + "/", + "^", + "x", + "function name", + "(", + ")" + }; + + int separate = 0; + + fprintf(stderr, "Syntax error - expected "); + + for (; *expected >= 0; ++expected) { + if (separate) + fprintf(stderr, ", "); + else + separate = 1; + + fprintf(stderr, "'%s'", token_name[*expected]); + } + + fprintf(stderr, " - but got '%s'\n", token_name[got]); + + lex_print_position(lex); +} + +static void error_expected_single(struct lexer *lex, enum token_type expected, enum token_type got) { + static int expected_arr[] = { -1, -1 }; + expected_arr[0] = expected; + error_expected_one_of(lex, expected_arr, got); +} + +struct expr_node *parse_subexpression(struct lexer *lex); + +static struct expr_node *parse_bracketed(struct lexer* lex) { + struct expr_node *node; + + if (!accept(lex, TOK_LEFT_PAREN)) { + error_expected_single(lex, TOK_LEFT_PAREN, lex_token(lex)->type); + return NULL; + } + + if (!(node = parse_subexpression(lex))) + return NULL; + + if (!accept(lex, TOK_RIGHT_PAREN)) { + error_expected_single(lex, TOK_RIGHT_PAREN, lex_token(lex)->type); + node_free(node); + return NULL; + } + + return node; +} + +static struct expr_node *parse_base(struct lexer *lex) { + struct expr_node *inner; + + if (token_is(lex, TOK_NUMBER)) { + double val = token_num(lex); + lex_next(lex); + return node_create_const(val); + } + + if (accept(lex, TOK_X)) { + return node_create_x(); + } + + if (token_is(lex, TOK_FUNCTION)) { + enum math_fn fn = token_fn(lex); + lex_next(lex); + + if (!(inner = parse_bracketed(lex))) + return NULL; + + return node_create_fn(fn, inner); + } + + if (token_is(lex, TOK_LEFT_PAREN)) { + return parse_bracketed(lex); + } + + { + static const int expected[] = { TOK_NUMBER, TOK_X, TOK_FUNCTION, TOK_LEFT_PAREN, -1 }; + error_expected_one_of(lex, expected, lex_token(lex)->type); + } + + return NULL; +} + +static struct expr_node* parse_unary(struct lexer *lex) { + if (accept(lex, TOK_MINUS)) { + struct expr_node *node, *inner; + + if (!(inner = parse_base(lex))) + return NULL; + + if (!(node = node_create_neg(inner))) { + node_free(inner); + return NULL; + } + + return node; + } + + accept(lex, TOK_PLUS); + return parse_base(lex); +} + +static struct expr_node *parse_factor(struct lexer *lex) { + struct expr_node *node, *new_node, *inner; + + if (!(node = parse_unary(lex))) + return NULL; + + while (accept(lex, TOK_POWER)) { + if (!(inner = parse_unary(lex))) { + node_free(node); + return NULL; + } + + if (!(new_node = node_create_pow(node, inner))) { + node_free(node); + node_free(inner); + return NULL; + } + + node = new_node; + } + + return node; +} + +static struct expr_node *parse_term(struct lexer *lex) { + struct expr_node *node, *new_node, *inner; + + if (!(node = parse_factor(lex))) + return NULL; + + while (1) { + struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right); + + if (accept(lex, TOK_MULTIPLY)) + create_node = node_create_mult; + else if (accept(lex, TOK_DIVIDE)) + create_node = node_create_div; + else + break; + + if (!(inner = parse_factor(lex))) { + node_free(node); + return NULL; + } + + if (!(new_node = create_node(node, inner))) { + node_free(node); + node_free(inner); + return NULL; + } + + node = new_node; + } + + return node; +} + +struct expr_node *parse_subexpression(struct lexer *lex) { + struct expr_node *node, *new_node, *inner; + + /* + if (accept(lex, TOK_MINUS)) { + if (!(term = parse_term(lex))) + return NULL; + + if (!(node = node_create_neg(term))) { + node_free(term); + return NULL; + } + } + else { + accept(lex, TOK_PLUS); + + if (!(node = parse_term(lex))) + return NULL; + } + + */ + + if (!(node = parse_term(lex))) + return NULL; + + while (1) { + struct expr_node *(*create_node)(struct expr_node *left, struct expr_node *right); + + if (accept(lex, TOK_PLUS)) + create_node = node_create_add; + else if (accept(lex, TOK_MINUS)) + create_node = node_create_sub; + else + break; + + if (!(inner = parse_term(lex))) { + node_free(node); + return NULL; + } + + if (!(new_node = create_node(node, inner))) { + node_free(node); + node_free(inner); + return NULL; + } + + node = new_node; + } + + return node; +} + +struct expr_node *parse_expression(struct lexer *lex) { + struct expr_node *node; + + if (!(node = parse_subexpression(lex))) + return NULL; + + if (!token_is(lex, TOK_EOF)) { + error_expected_single(lex, TOK_EOF, lex_token(lex)->type); + node_free(node); + return NULL; + } + + return node; +} \ No newline at end of file diff --git a/parser.h b/parser.h new file mode 100644 index 0000000..f070592 --- /dev/null +++ b/parser.h @@ -0,0 +1,8 @@ +#ifndef PARSER_H +#define PARSER_H + +#include "tree.h" + +extern struct expr_node *parse_expression(struct lexer *lex); + +#endif /* PARSER_H */ \ No newline at end of file diff --git a/tree.c b/tree.c new file mode 100644 index 0000000..6989833 --- /dev/null +++ b/tree.c @@ -0,0 +1,232 @@ +#include +#include +#include +#include "tree.h" + +static struct expr_node *alloc_node(void) { + return malloc(sizeof(struct expr_node)); +} + +struct expr_node *node_create_const(double val) { + struct expr_node *node = alloc_node(); + if (!node) return NULL; + node->type = EXPR_CONST; + node->vals.num = val; + return node; +} + +struct expr_node *node_create_neg(struct expr_node *unop) { + struct expr_node *node = alloc_node(); + if (!node) return NULL; + node->type = EXPR_NEG; + node->vals.unop = unop; + return node; +} + +static struct expr_node *create_binary_node(enum expr_type type, struct expr_node *left, struct expr_node *right) { + struct expr_node *node = alloc_node(); + if (!node) return NULL; + node->type = type; + node->vals.binop.left = left; + node->vals.binop.right = right; + return node; +} + +struct expr_node *node_create_add(struct expr_node *left, struct expr_node *right) { + return create_binary_node(EXPR_ADD, left, right); +} + +struct expr_node *node_create_sub(struct expr_node *left, struct expr_node *right) { + return create_binary_node(EXPR_SUB, left, right); +} + +struct expr_node *node_create_mult(struct expr_node *left, struct expr_node *right) { + return create_binary_node(EXPR_MULT, left, right); +} + +struct expr_node *node_create_div(struct expr_node *left, struct expr_node *right) { + return create_binary_node(EXPR_DIV, left, right); +} + +struct expr_node *node_create_pow(struct expr_node *base, struct expr_node *power) { + return create_binary_node(EXPR_POW, base, power); +} + +struct expr_node *node_create_x(void) { + struct expr_node *node = alloc_node(); + if (!node) return NULL; + node->type = EXPR_X; + return node; +} + +struct expr_node *node_create_fn(enum math_fn fn, struct expr_node *arg) { + struct expr_node *node = alloc_node(); + if (!node) return NULL; + node->type = EXPR_FN; + node->vals.fn.fn = fn; + node->vals.fn.arg = arg; + return node; +} + +void node_free(struct expr_node *node) { + if (!node) return; + + switch (node->type) { + case EXPR_ADD: + case EXPR_SUB: + case EXPR_MULT: + case EXPR_DIV: + case EXPR_POW: + node_free(node->vals.binop.left); + node_free(node->vals.binop.right); + break; + + case EXPR_NEG: + node_free(node->vals.unop); + break; + + case EXPR_FN: + node_free(node->vals.fn.arg); + break; + + default: + break; + } + + free(node); +} + +static void debug_indent(int indent) { + int i; + for (i = 0; i < indent; ++i) + printf(" "); +} + +static void debug_print(struct expr_node *node, int indent); + +static void debug_print_binop(struct expr_node *node, const char* name, int indent) { + debug_indent(indent); printf("[%s]\n", name); + /*debug_indent(indent); printf("left:\n");*/ + debug_print(node->vals.binop.left, indent + 1); + /*debug_indent(indent); printf("right:\n");*/ + debug_print(node->vals.binop.right, indent + 1); +} + +static void debug_print(struct expr_node *node, int indent) { + 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" + }; + + switch (node->type) { + case EXPR_ADD: + debug_print_binop(node, "ADD", indent); + break; + + case EXPR_SUB: + debug_print_binop(node, "SUB", indent); + break; + + case EXPR_MULT: + debug_print_binop(node, "MULT", indent); + break; + + case EXPR_DIV: + debug_print_binop(node, "DIV", indent); + break; + + case EXPR_POW: + debug_print_binop(node, "POW", indent); + break; + + case EXPR_NEG: + debug_indent(indent); printf("[NEG]\n"); + /*debug_indent(indent); printf("unop:\n");*/ + debug_print(node->vals.unop, indent + 1); + break; + + case EXPR_CONST: + debug_indent(indent); printf("[CONST] %.2f\n", node->vals.num); + break; + + case EXPR_X: + debug_indent(indent); printf("[X]\n"); + + 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); + break; + + default: + break; + } +} + + +void node_debug_print(struct expr_node *node) { + debug_print(node, 0); +} + +double node_eval(struct expr_node *node, double x) { + switch (node->type) { + case EXPR_CONST: + return node->vals.num; + + case EXPR_X: + return x; + + case EXPR_NEG: + return -node_eval(node->vals.unop, x); + + case EXPR_ADD: + return node_eval(node->vals.binop.left, x) + node_eval(node->vals.binop.right, x); + + case EXPR_SUB: + return node_eval(node->vals.binop.left, x) - node_eval(node->vals.binop.right, x); + + case EXPR_MULT: + return node_eval(node->vals.binop.left, x) * node_eval(node->vals.binop.right, x); + + case EXPR_DIV: + return node_eval(node->vals.binop.left, x) / node_eval(node->vals.binop.right, x); + + case EXPR_POW: + return pow(node_eval(node->vals.binop.left, x), node_eval(node->vals.binop.right, 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); + } + } + } + + return 0.0; +} diff --git a/tree.h b/tree.h new file mode 100644 index 0000000..cd9be8f --- /dev/null +++ b/tree.h @@ -0,0 +1,51 @@ +#ifndef TREE_H +#define TREE_H + +#include "lex.h" + +enum expr_type { + EXPR_CONST, + EXPR_NEG, + EXPR_ADD, + EXPR_SUB, + EXPR_MULT, + EXPR_DIV, + EXPR_POW, + EXPR_X, + EXPR_FN +}; + +struct expr_node { + enum expr_type type; + union expr_vals { + struct expr_binop_vals { + struct expr_node *left; + struct expr_node *right; + } binop; + struct expr_fn_vals { + enum math_fn fn; + struct expr_node *arg; + } fn; + struct expr_node *unop; + double num; + } vals; +}; + +extern struct expr_node *node_create_const(double val); +extern struct expr_node *node_create_neg(struct expr_node *unop); +extern struct expr_node *node_create_add(struct expr_node *left, struct expr_node *right); +extern struct expr_node *node_create_sub(struct expr_node *left, struct expr_node *right); +extern struct expr_node *node_create_mult(struct expr_node *left, struct expr_node *right); +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 void node_debug_print(struct expr_node *node); + +extern double node_eval(struct expr_node *node, double x); + +extern void node_free(struct expr_node *node); + + +#endif /* TREE_H */ \ No newline at end of file