From 7088f2666bf9b413e9bc42c53642742f596b4c39 Mon Sep 17 00:00:00 2001 From: sfja Date: Thu, 13 Mar 2025 12:32:17 +0100 Subject: [PATCH] init --- .clang-format | 15 ++++ .gitignore | 1 + Makefile | 20 +++++ calc.h | 62 ++++++++++++++++ compile_flags.txt | 11 +++ expr.c | 97 ++++++++++++++++++++++++ main.c | 185 ++++++++++++++++++++++++++++++++++++++++++++++ 7 files changed, 391 insertions(+) create mode 100644 .clang-format create mode 100644 .gitignore create mode 100644 Makefile create mode 100644 calc.h create mode 100644 compile_flags.txt create mode 100644 expr.c create mode 100644 main.c diff --git a/.clang-format b/.clang-format new file mode 100644 index 0000000..c4323f6 --- /dev/null +++ b/.clang-format @@ -0,0 +1,15 @@ +Language: Cpp +BasedOnStyle: WebKit +IndentWidth: 4 +ColumnLimit: 80 +IndentCaseLabels: true +InsertNewlineAtEOF: true +AllowShortFunctionsOnASingleLine: None + +AlignAfterOpenBracket: AlwaysBreak +BinPackArguments: false +AllowAllArgumentsOnNextLine: true + +BinPackParameters: false +AllowAllParametersOfDeclarationOnNextLine: true + diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ddbbca --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +calc diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..ad4f8eb --- /dev/null +++ b/Makefile @@ -0,0 +1,20 @@ + +COMPILE_FLAGS = \ + -std=c17 \ + -Wall -Wextra \ + -Wpedantic \ + -pedantic \ + -pedantic-errors \ + -Wno-unused-parameter \ + +FEATURE_FLAGS = \ + -fsanitize=address,undefined \ + +all: calc + +calc: main.c expr.c + gcc -o calc $^ $(COMPILE_FLAGS) $(FEATURE_FLAGS) + +clean: + rm -rf calc + diff --git a/calc.h b/calc.h new file mode 100644 index 0000000..e90b01c --- /dev/null +++ b/calc.h @@ -0,0 +1,62 @@ +#pragma once + +#include +#include +typedef enum { + ET_Add, + ET_Subtract, + ET_Multiply, + ET_Divide, + ET_Int, +} ExprType; + +typedef struct Expr Expr; + +struct Expr { + ExprType type; + int int_value; + struct { + Expr* left; + Expr* right; + }; +}; + +Expr* exprNew(Expr expr); +Expr* intExprNew(int int_value); +Expr* binaryExprNew(ExprType type, Expr* left, Expr* right); +void exprFree(Expr* expr); + +int exprEval(const Expr* expr); +void exprPrint(const Expr* expr); + +char binarySymbol(ExprType type); + + + +typedef enum { + TT_Plus = '+', + TT_Minus = '-', + TT_Asterisk = '*', + TT_Slash = '/', + TT_Int = '0', + +} TokenType; + +typedef struct { + TokenType type; + int value; +} Token; + +typedef struct { + Token* tokens; + size_t tokens_size; + size_t i; +} Parser; + +size_t lex(Token* tokens, size_t tokens_max_size, const char* text); +bool parserDone(const Parser* parser); +void parserStep(Parser* parser); +Token parserCurrent(const Parser* parser); +void parserConstruct(Parser* parser, Token* tokens, size_t tokens_size); +Expr* parseExpr(Parser* parser); + diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..826b0dc --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,11 @@ +-xc +-std=c17 +-Wall +-Wextra +-Wpedantic +-Wconversion +-pedantic +-pedantic-errors +-Wno-unused-parameter +-Wno-empty-translation-unit + diff --git a/expr.c b/expr.c new file mode 100644 index 0000000..f722823 --- /dev/null +++ b/expr.c @@ -0,0 +1,97 @@ +#include "calc.h" +#include +#include +#include + +Expr* exprNew(Expr expr) +{ + Expr* myNode = malloc(sizeof(Expr)); + *myNode = expr; + return myNode; +} + +Expr* intExprNew(int int_value) +{ + Expr* myNode = malloc(sizeof(Expr)); + *myNode = (Expr) { .type = ET_Int, .int_value = int_value }; + return myNode; +} + +Expr* binaryExprNew(ExprType type, Expr* left, Expr* right) +{ + + Expr* myNode = malloc(sizeof(Expr)); + *myNode = (Expr) { .type = type, .left = left, .right = right }; + return myNode; +} + +void exprFree(Expr* expr) +{ + switch (expr->type) { + case ET_Add: + case ET_Subtract: + case ET_Multiply: + case ET_Divide: + exprFree(expr->left); + exprFree(expr->right); + break; + case ET_Int: + break; + } + free(expr); +} + +int exprEval(const Expr* expr) +{ + switch (expr->type) { + + case ET_Add: + return exprEval(expr->left) + exprEval(expr->right); + case ET_Subtract: + return exprEval(expr->left) - exprEval(expr->right); + case ET_Multiply: + return exprEval(expr->left) * exprEval(expr->right); + case ET_Divide: + return exprEval(expr->left) / exprEval(expr->right); + case ET_Int: + return expr->int_value; + } + return 0; +} + +void exprPrint(const Expr* expr) +{ + switch (expr->type) { + + case ET_Add: + case ET_Subtract: + case ET_Multiply: + case ET_Divide: { + printf("%c ", binarySymbol(expr->type)); + exprPrint(expr->left); + printf(" "); + exprPrint(expr->right); + break; + } + case ET_Int: + printf("%d", expr->int_value); + break; + } +} + +char binarySymbol(ExprType type) +{ + switch (type) { + case ET_Add: + return '+'; + case ET_Subtract: + return '-'; + case ET_Multiply: + return '*'; + case ET_Divide: + return '/'; + default: + break; + } + return '\0'; +} diff --git a/main.c b/main.c new file mode 100644 index 0000000..e0c471c --- /dev/null +++ b/main.c @@ -0,0 +1,185 @@ +#include "calc.h" +#include +#include +#include +#include +#include +#include + +static inline bool isBinaryOp(TokenType tt); +static inline ExprType binaryOpExprType(TokenType tt); +static inline bool contains(char ch, const char* string); + +static inline int evalText(const char* text); + +int main(void) +{ + while (true) { + printf("> "); + fflush(stdout); + char line[128] = ""; + fgets(line, 127, stdin); + size_t line_length = strlen(line); + line[--line_length] = '\0'; + printf("line = \"%s\"\n", line); + int result = evalText(line); + printf("%d\n", result); + } + + Expr* expr = binaryExprNew(ET_Add, intExprNew(-200), intExprNew(23)); + exprPrint(expr); + fputc('\n', stdout); + int result = exprEval(expr); + printf("%d\n", result); + exprFree(expr); +} + +static inline int evalText(const char* text) +{ + Token tokens[128]; + size_t tokens_size = lex(tokens, 128, text); + + Parser parser; + parserConstruct(&parser, tokens, tokens_size); + + Expr* expr = parseExpr(&parser); + if (!expr) + return 0; + + int result = exprEval(expr); + exprFree(expr); + + return result; +} + +void parserConstruct(Parser* parser, Token* tokens, size_t tokens_size) +{ + *parser = (Parser) { + .tokens = tokens, + .tokens_size = tokens_size, + .i = 0, + }; +} + +void parserStep(Parser* parser) +{ + parser->i += 1; +} + +bool parserDone(const Parser* parser) +{ + return parser->i >= parser->tokens_size; +} + +Token parserCurrent(const Parser* parser) +{ + return parser->tokens[parser->i]; +} + +Expr* parseExpr(Parser* parser) +{ + if (parserDone(parser)) { + fprintf(stderr, "error: expected expr, got end-of-file\n"); + return NULL; + } + + Token tok = parserCurrent(parser); + if (isBinaryOp(tok.type)) { + parserStep(parser); + Expr* left = parseExpr(parser); + if (!left) + return NULL; + Expr* right = parseExpr(parser); + if (!right) + return NULL; + ExprType et = binaryOpExprType(tok.type); + return binaryExprNew(et, left, right); + } else if (tok.type == TT_Int) { + parserStep(parser); + return intExprNew(tok.value); + } else { + parserStep(parser); + fprintf(stderr, "error: expected expr\n"); + return NULL; + } +} + +size_t lex(Token* tokens, size_t tokens_max_size, const char* text) +{ + const size_t text_length = strlen(text); + + size_t tokens_i = 0; + size_t i = 0; + while (i < text_length && tokens_i < tokens_max_size) { + + while (contains(text[i], " \t\n")) { + i++; + } + if (contains(text[i], "1234567890")) { + char text_value[16] = ""; + size_t j = 0; + while (contains(text[i], "1234567890") && j < 15) { + text_value[j] = text[i]; + j++; + i++; + } + tokens[tokens_i++] + = (Token) { .type = TT_Int, .value = atoi(text_value) }; + } else if (contains(text[i], "+-*/")) { + char ch = text[i]; + i++; + tokens[tokens_i++] = (Token) { + .type = (TokenType)ch, + .value = 0, + }; + } else { + char ch = text[i]; + i++; + fprintf(stderr, "error: illegal character '%c'\n", ch); + } + } + + return tokens_i; +} + +static inline bool isBinaryOp(TokenType tt) +{ + switch (tt) { + case TT_Plus: + case TT_Minus: + case TT_Asterisk: + case TT_Slash: + return true; + case TT_Int: + default: + return false; + } +} + +static inline ExprType binaryOpExprType(TokenType tt) +{ + + switch (tt) { + case TT_Plus: + return ET_Add; + case TT_Minus: + return ET_Subtract; + case TT_Asterisk: + return ET_Multiply; + case TT_Slash: + return ET_Divide; + case TT_Int: + break; + } + return 0; +} + +static inline bool contains(char ch, const char* string) +{ + for (size_t i = 0; i < strlen(string); ++i) { + if (string[i] == ch) { + return true; + } + } + return false; +}