From 704979f9bbc8252f26796b1befdd0f86f2e1858e Mon Sep 17 00:00:00 2001 From: tovjemam Date: Sun, 13 Oct 2024 19:32:55 +0200 Subject: [PATCH] init --- CMakeLists.txt | 29 ++++++ lex.c | 191 +++++++++++++++++++++++++++++++++++ lex.h | 61 ++++++++++++ main.c | 38 +++++++ parser.c | 263 +++++++++++++++++++++++++++++++++++++++++++++++++ parser.h | 8 ++ tree.c | 232 +++++++++++++++++++++++++++++++++++++++++++ tree.h | 51 ++++++++++ 8 files changed, 873 insertions(+) create mode 100644 CMakeLists.txt create mode 100644 lex.c create mode 100644 lex.h create mode 100644 main.c create mode 100644 parser.c create mode 100644 parser.h create mode 100644 tree.c create mode 100644 tree.h 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