init
This commit is contained in:
commit
7088f2666b
15
.clang-format
Normal file
15
.clang-format
Normal 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
1
.gitignore
vendored
Normal file
@ -0,0 +1 @@
|
||||
calc
|
20
Makefile
Normal file
20
Makefile
Normal 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
62
calc.h
Normal 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
11
compile_flags.txt
Normal 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
97
expr.c
Normal 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
185
main.c
Normal 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;
|
||||
}
|
Loading…
Reference in New Issue
Block a user