403 lines
9.1 KiB
C
403 lines
9.1 KiB
C
#include "enums.h"
|
|
#include "object.h"
|
|
#include "tokens.h"
|
|
|
|
#include <assert.h>
|
|
#include <ctype.h>
|
|
#include <stddef.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
static char buffer[1024];
|
|
static size_t buffer_index = 0;
|
|
|
|
static enum State state = STATE_INIT;
|
|
|
|
static void error(const char *msg);
|
|
|
|
/*********
|
|
* Lexer *
|
|
*********/
|
|
|
|
static void buffer_add(char chr);
|
|
static void buffer_clean();
|
|
static void token_add(enum State state, char *val);
|
|
static void lex(char chr);
|
|
|
|
/**********
|
|
* Parser *
|
|
**********/
|
|
|
|
static void expect(enum TokenType token_type);
|
|
|
|
static struct Object *parse();
|
|
static struct Object *expr();
|
|
static struct Object *parens();
|
|
static struct Object *parens_part();
|
|
|
|
/********
|
|
* Eval *
|
|
********/
|
|
|
|
static struct Object *eval(struct Object *program);
|
|
|
|
static struct Object *func_sum(struct Object *numbers);
|
|
|
|
/*******************
|
|
* Implementations *
|
|
*******************/
|
|
|
|
int main()
|
|
{
|
|
char chr;
|
|
while ((chr = getchar()) != EOF) {
|
|
lex(chr);
|
|
}
|
|
|
|
printf("Tokens:\n");
|
|
|
|
for (
|
|
const struct Tokens *token = tokens_top();
|
|
token;
|
|
token = token->next
|
|
) {
|
|
printf("%s:%s;\n", TokenType_to_str(token->type), token->val);
|
|
}
|
|
|
|
struct Object *const program = parse();
|
|
|
|
printf("\nProgram:\n");
|
|
Object_print(program, 0);
|
|
|
|
struct Object *const result = eval(program);
|
|
|
|
printf("\nResult:\n");
|
|
Object_print(result, 0);
|
|
|
|
exit(EXIT_SUCCESS);
|
|
}
|
|
|
|
void error(const char *msg)
|
|
{
|
|
printf("ERROR: %s\n", msg);
|
|
exit(EXIT_FAILURE);
|
|
}
|
|
|
|
/*********
|
|
* Lexer *
|
|
*********/
|
|
|
|
void buffer_add(char chr)
|
|
{
|
|
if (buffer_index >= 1000) error("token too long");
|
|
buffer[buffer_index++] = chr;
|
|
buffer[buffer_index] = 0;
|
|
}
|
|
|
|
void buffer_clean()
|
|
{
|
|
buffer_index = 0;
|
|
}
|
|
|
|
void token_add(enum State state, char *val)
|
|
{
|
|
if (state == STATE_WHITESPACE) return;
|
|
|
|
enum TokenType token_type;
|
|
if (!State_to_token_type(state, &token_type)) error("invalid state");
|
|
|
|
tokens_push(token_type, val);
|
|
}
|
|
|
|
void lex(char chr)
|
|
{
|
|
switch (state) {
|
|
case STATE_INIT:
|
|
if (chr == '(') {
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
state = STATE_WHITESPACE;
|
|
buffer_add(chr);
|
|
} else if (isalpha(chr)) {
|
|
state = STATE_ATOM;
|
|
buffer_add(chr);
|
|
} else if (isdigit(chr)) {
|
|
state = STATE_NUM;
|
|
buffer_add(chr);
|
|
} else {
|
|
error("invalid char (STATE_INIT)");
|
|
}
|
|
break;
|
|
case STATE_WHITESPACE:
|
|
if (chr == '(') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
buffer_add(chr);
|
|
} else if (isalpha(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_ATOM;
|
|
buffer[buffer_index++] = chr;
|
|
} else if (isdigit(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_NUM;
|
|
buffer[buffer_index++] = chr;
|
|
} else {
|
|
error("invalid char (STATE_WHITESPACE)");
|
|
}
|
|
break;
|
|
case STATE_OPEN:
|
|
if (chr == '(') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_WHITESPACE;
|
|
buffer_add(chr);
|
|
} else if (isalpha(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_ATOM;
|
|
buffer_add(chr);
|
|
} else if (isdigit(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_NUM;
|
|
buffer_add(chr);
|
|
} else {
|
|
error("invalid char (STATE_OPEN)");
|
|
}
|
|
break;
|
|
case STATE_CLOSE:
|
|
if (chr == '(') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_WHITESPACE;
|
|
buffer_add(chr);
|
|
} else if (isalpha(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_ATOM;
|
|
buffer_add(chr);
|
|
} else if (isdigit(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_NUM;
|
|
buffer_add(chr);
|
|
} else {
|
|
error("invalid char (STATE_CLOSE)");
|
|
}
|
|
break;
|
|
case STATE_ATOM:
|
|
if (chr == '(') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_WHITESPACE;
|
|
buffer_add(chr);
|
|
} else if (isalnum(chr)) {
|
|
buffer_add(chr);
|
|
} else {
|
|
error("invalid char (STATE_ATOM)");
|
|
}
|
|
break;
|
|
case STATE_NUM:
|
|
if (chr == '(') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_OPEN;
|
|
buffer_add(chr);
|
|
} else if (chr == ')') {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_CLOSE;
|
|
buffer_add(chr);
|
|
} else if (isspace(chr)) {
|
|
token_add(state, buffer);
|
|
buffer_clean();
|
|
state = STATE_WHITESPACE;
|
|
buffer_add(chr);
|
|
} else if (isdigit(chr)) {
|
|
buffer_add(chr);
|
|
} else {
|
|
error("invalid char (STATE_NUM)");
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/**********
|
|
* Parser *
|
|
**********/
|
|
|
|
void expect(const enum TokenType token_type)
|
|
{
|
|
if (!tokens_expect(token_type)) error("expect");
|
|
}
|
|
|
|
struct Object *parse()
|
|
{
|
|
return expr();
|
|
}
|
|
|
|
struct Object *expr()
|
|
{
|
|
assert(tokens_top());
|
|
|
|
switch (tokens_top()->type) {
|
|
case TOKEN_OPEN: return parens();
|
|
case TOKEN_ATOM:
|
|
{
|
|
struct Object *const object = Object_new_atom(tokens_top()->val);
|
|
tokens_pop();
|
|
return object;
|
|
}
|
|
case TOKEN_NUM:
|
|
{
|
|
struct Object *const object =
|
|
Object_new_number(atoll(tokens_top()->val));
|
|
tokens_pop();
|
|
return object;
|
|
}
|
|
default:
|
|
error("invalid expr");
|
|
return NULL;
|
|
}
|
|
}
|
|
|
|
struct Object *parens()
|
|
{
|
|
expect(STATE_OPEN);
|
|
struct Object *const object = parens_part();
|
|
expect(STATE_CLOSE);
|
|
return object;
|
|
}
|
|
|
|
struct Object *parens_part()
|
|
{
|
|
assert(tokens_top());
|
|
if (tokens_top()->type == TOKEN_CLOSE) return NULL;
|
|
|
|
struct Object *const a = expr();
|
|
assert(tokens_top());
|
|
|
|
struct Object *b = NULL;
|
|
if (tokens_top()->type != TOKEN_CLOSE) {
|
|
b = parens_part();
|
|
}
|
|
|
|
struct Object *object = Object_new_pair(a, b);
|
|
return object;
|
|
}
|
|
|
|
/********
|
|
* Eval *
|
|
********/
|
|
|
|
struct Object *eval(struct Object *const program)
|
|
{
|
|
// NULL is an empty list, can't eval
|
|
if (!program) {
|
|
error("can't eval null");
|
|
return NULL;
|
|
}
|
|
|
|
// Almost everything evaluates to itself
|
|
if (program->type != TYPE_PAIR && program->type != TYPE_ATOM) {
|
|
return program;
|
|
}
|
|
|
|
// Atoms are variable names, but we can't lookup
|
|
if (program->type == TYPE_ATOM) {
|
|
error("can't eval atoms");
|
|
return NULL;
|
|
}
|
|
|
|
// The first item of pair should be an atom - a function name
|
|
if (!program->pair.a || program->pair.a->type != TYPE_ATOM) {
|
|
error("eval expects atom");
|
|
return NULL;
|
|
}
|
|
|
|
// The func "sum"
|
|
if (strcmp(program->pair.a->s, "sum") == 0) {
|
|
return func_sum(program->pair.b);
|
|
}
|
|
|
|
error("unknown func");
|
|
return NULL;
|
|
}
|
|
|
|
struct Object *func_sum(struct Object *const numbers)
|
|
{
|
|
struct Object *const object = Object_new_number(0);
|
|
|
|
if (!numbers) return object;
|
|
|
|
if (numbers->type != TYPE_PAIR) {
|
|
error("sum expects pair");
|
|
return NULL;
|
|
}
|
|
|
|
if (!numbers->pair.a) {
|
|
error("type error");
|
|
return NULL;
|
|
}
|
|
|
|
if (numbers->pair.a->type != TYPE_NUMBER) {
|
|
error("type error");
|
|
return NULL;
|
|
}
|
|
|
|
object->i = numbers->pair.a->i;
|
|
|
|
if (!numbers->pair.b) return object;
|
|
|
|
struct Object *const b_sum = func_sum(numbers->pair.b);
|
|
|
|
object->i += b_sum->i;
|
|
|
|
return object;
|
|
}
|