calc-tb/main.c
2025-03-13 12:32:17 +01:00

186 lines
4.2 KiB
C

#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;
}