Skip to content

Instantly share code, notes, and snippets.

@telescreen
Last active July 16, 2020 15:11
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save telescreen/39a61f01f7b71ddc0a13cb73fd698b3c to your computer and use it in GitHub Desktop.
Save telescreen/39a61f01f7b71ddc0a13cb73fd698b3c to your computer and use it in GitHub Desktop.
calc.c
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include <stdbool.h>
/* clang -Wall -W -g -std=c11 -Wno-missing-field-initializers -pedantic calc.c -o calc */
typedef enum {
PLUS,
MINUS,
TIMES,
SLASH,
LPAREN,
RPAREN,
NUMBER,
SEMI,
EOI,
} TokenType;
typedef void (*ParseFn)();
void unary();
void binary();
void grouping();
void number();
typedef enum {
PREC_NONE,
PREC_TERM,
PREC_FACTOR,
PREC_UNARY,
PREC_PRIMARY
} Precedence;
typedef struct {
ParseFn prefix;
ParseFn infix;
Precedence precedence;
} ParseRule;
ParseRule rules[] = {
[PLUS] = { unary, binary, PREC_TERM },
[MINUS] = { unary, binary, PREC_TERM },
[TIMES] = { NULL, binary, PREC_FACTOR },
[SLASH] = { NULL, binary, PREC_FACTOR },
[LPAREN] = { grouping, NULL, PREC_NONE },
[RPAREN] = { NULL, NULL, PREC_NONE },
[NUMBER] = { number, NULL, PREC_NONE },
[SEMI] = { NULL, NULL, PREC_NONE },
[EOI] = { NULL, NULL, PREC_NONE },
};
typedef struct {
char *start;
char *current;
int line;
} Scanner;
typedef struct {
TokenType type;
char* start;
int length;
int line;
} Token;
typedef struct {
Token current;
Token next;
} Parser;
Scanner scanner;
Parser parser;
Token makeToken(TokenType type)
{
Token token;
token.type = type;
token.start = scanner.start;
token.length = (int)(scanner.current - scanner.start);
token.line = scanner.line;
return token;
}
void initScanner(char* buffer)
{
scanner.start = buffer;
scanner.current = buffer;
scanner.line = 1;
}
void skipWhitespaceNewline()
{
/* Skip white space */
while(isblank(*scanner.current)) ++scanner.current;
while (*scanner.current == '\n') {
++scanner.current;
scanner.line++;
}
}
Token nextToken()
{
skipWhitespaceNewline();
if (*scanner.current == '\0') return makeToken(EOI);
scanner.start = scanner.current;
char* c = scanner.current++;
if (isdigit(*c)) {
while (isdigit(*scanner.current) || *scanner.current == '.') ++scanner.current;
return makeToken(NUMBER);
}
switch(*c) {
case '+': return makeToken(PLUS);
case '-': return makeToken(MINUS);
case '*': return makeToken(TIMES);
case '/': return makeToken(SLASH);
case '(': return makeToken(LPAREN);
case ')': return makeToken(RPAREN);
case ';': return makeToken(SEMI);
default:
fprintf(stderr, "Ignoring illegal input <%c>\n", *c);
return makeToken(EOI);
}
}
void errorAt(const Token *token, const char* message)
{
fprintf(stderr, "[line %d] Error at %.*s: %s\n",
token->line, token->length, token->start, message);
}
void error(const char* message)
{
errorAt(&parser.current, message);
}
void advance()
{
parser.current = parser.next;
parser.next = nextToken();
}
void consume(TokenType type, const char* message)
{
if (parser.next.type == type) {
advance();
return;
}
errorAt(&parser.next, message);
}
static ParseRule* getRule(TokenType type)
{
return &rules[type];
}
void parse(Precedence precedence)
{
advance();
ParseFn prefixFn = getRule(parser.current.type)->prefix;
if (prefixFn == NULL) {
error("Expect expression");
return;
}
prefixFn();
while (precedence <= getRule(parser.next.type)->precedence) {
advance();
ParseFn infixFn = getRule(parser.current.type)->infix;
infixFn();
}
}
void expression()
{
parse(PREC_TERM);
}
void unary()
{
TokenType oprand = parser.current.type;
parse(PREC_UNARY);
switch(oprand) {
case MINUS:
case PLUS:
printf("Binary operator: %d\n", oprand); break;
default:
return;
}
}
void binary()
{
TokenType operand = parser.current.type;
// Compile the right operand
parse(getRule(operand)->precedence + 1);
switch (operand) {
case PLUS:
case MINUS:
case TIMES:
case SLASH: printf("Binary operator: %d\n", operand); break;
default:
return;
}
}
void grouping()
{
expression();
consume(RPAREN, "Expect ')' after an expression.");
}
void number()
{
double value = strtod(parser.current.start, NULL);
printf("%.4f\n", value);
}
int main()
{
char buffer[1024];
if (fgets(buffer, 1024, stdin)) {
initScanner(buffer);
advance();
expression();
consume(EOI, "Expect end of expression");
}
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment