This commit is contained in:
Simon 2025-03-13 12:32:17 +01:00
commit 7088f2666b
7 changed files with 391 additions and 0 deletions

15
.clang-format Normal file
View File

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

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
calc

20
Makefile Normal file
View File

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

62
calc.h Normal file
View File

@ -0,0 +1,62 @@
#pragma once
#include <stdbool.h>
#include <stddef.h>
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);

11
compile_flags.txt Normal file
View File

@ -0,0 +1,11 @@
-xc
-std=c17
-Wall
-Wextra
-Wpedantic
-Wconversion
-pedantic
-pedantic-errors
-Wno-unused-parameter
-Wno-empty-translation-unit

97
expr.c Normal file
View File

@ -0,0 +1,97 @@
#include "calc.h"
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
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';
}

185
main.c Normal file
View File

@ -0,0 +1,185 @@
#include "calc.h"
#include <stdbool.h>
#include <stddef.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
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;
}