@@ -0,0 +1,8 @@ | |||
cmake_minimum_required(VERSION 3.12) | |||
project(crisp C) | |||
set(CMAKE_C_STANDARD 99) | |||
add_executable(main main.c mpc.c eval.c eval.h) | |||
target_link_libraries(main edit) |
@@ -1,3 +1,5 @@ | |||
# crisp | |||
lisp but crispier | |||
lisp but crispier | |||
follows the book http://www.buildyourownlisp.com |
@@ -0,0 +1,77 @@ | |||
// | |||
// Created by red on 05/12/19. | |||
// | |||
#include "eval.h" | |||
// create number type cval | |||
cval cval_num(long x) { | |||
cval v; | |||
v.type = CVAL_NUM; | |||
v.num = x; | |||
return v; | |||
} | |||
// create error type cval | |||
cval cval_err(int x){ | |||
cval v; | |||
v.type = CVAL_ERR; | |||
v.err = x; | |||
return v; | |||
} | |||
void cval_print(cval v) { | |||
switch (v.type) { | |||
case CVAL_NUM: printf("%li", v.num); break; | |||
case CVAL_ERR: { | |||
switch (v.err) { | |||
case CERR_DIV_ZERO: printf("Error: Division by zero"); break; | |||
case CERR_BAD_OP: printf("Error: Invalid operator"); break; | |||
case CERR_BAD_NUM: printf("Error: Invalid number"); break; | |||
default: break; | |||
} | |||
} | |||
default: break; | |||
} | |||
} | |||
void cval_println(cval v) { | |||
cval_print(v); | |||
putchar('\n'); | |||
} | |||
cval eval_op(cval x, char* op, cval y) { | |||
if (x.type == CVAL_ERR) {return x;} | |||
if (y.type == CVAL_ERR) {return y;} | |||
switch (*op) { | |||
case '+': return cval_num(x.num + y.num); | |||
case '-': return cval_num(x.num - y.num); | |||
case '*': return cval_num(x.num * y.num); | |||
//return error | |||
case '/': return y.num == 0 ? cval_err(CERR_DIV_ZERO) : cval_num(x.num / y.num); | |||
default: return cval_err(CERR_BAD_OP); | |||
} | |||
} | |||
cval eval(mpc_ast_t* t) { | |||
if (strstr(t->tag, "number")) { | |||
errno = 0; | |||
long x = strtol(t->contents, NULL, 10); | |||
return errno != ERANGE ? cval_num(x) : cval_err(CERR_BAD_NUM); | |||
} | |||
// the operator is always the second child | |||
char* op = t->children[1]->contents; | |||
cval x = eval(t->children[2]); | |||
// iterate over the remaining children and combine | |||
int i = 3; | |||
while (strstr(t->children[i]->tag, "expr")) { | |||
x = eval_op(x, op, eval(t->children[i])); | |||
i++; | |||
} | |||
return x; | |||
} |
@@ -0,0 +1,21 @@ | |||
// | |||
// Created by red on 05/12/19. | |||
// | |||
#include "mpc.h" | |||
#ifndef CRISP_EVAL_H | |||
#define CRISP_EVAL_H | |||
typedef struct { | |||
int type; | |||
long num; | |||
int err; | |||
} cval; | |||
enum { CVAL_NUM, CVAL_ERR }; | |||
enum { CERR_DIV_ZERO, CERR_BAD_OP, CERR_BAD_NUM }; | |||
cval eval(mpc_ast_t* t); | |||
void cval_print(cval v); | |||
void cval_println(cval v); | |||
#endif //CRISP_EVAL_H |
@@ -0,0 +1,54 @@ | |||
// | |||
// Created by red on 03/12/19. | |||
// | |||
#include <stdio.h> | |||
#include <editline/readline.h> | |||
#include <editline/history.h> | |||
#include <stdlib.h> | |||
#include "mpc.h" | |||
#include "eval.h" | |||
#pragma clang diagnostic ignored "-Wmissing-noreturn" | |||
int main(int argc, char** argv) { | |||
mpc_parser_t* Number = mpc_new("number"); | |||
mpc_parser_t* Operator = mpc_new("operator"); | |||
mpc_parser_t* Expr = mpc_new("expr"); | |||
mpc_parser_t* Crisp = mpc_new("crisp"); | |||
mpca_lang(MPCA_LANG_DEFAULT, | |||
" number : /-?[0-9]+/ ;" | |||
" operator : '+' | '-' | '*' | '/' ;" | |||
" expr : <number> | '(' <operator> <expr>+ ')' ;" | |||
" crisp : /^/ <operator> <expr>+ /$/ ;", | |||
Number, Operator, Expr, Crisp); | |||
puts("Crisp v.0.0.1"); | |||
puts("Press Ctrl-C to exit\n"); | |||
for (;;) { | |||
char* input = readline("crisp> "); | |||
add_history(input); | |||
// attempt to parse input | |||
mpc_result_t r; | |||
if (mpc_parse("<stdin>", input, Crisp, &r)) { | |||
cval result = eval(r.output); | |||
cval_println(result); | |||
mpc_ast_delete(r.output); | |||
} else { | |||
mpc_err_print(r.error); | |||
mpc_err_delete(r.error); | |||
} | |||
free(input); | |||
} | |||
mpc_cleanup(4, Number, Operator, Expr, Crisp); | |||
return EXIT_SUCCESS; | |||
} |