186 lines
4.2 KiB
C
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;
|
|
}
|