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) |
# crisp | # crisp | ||||
lisp but crispier | |||||
lisp but crispier | |||||
follows the book http://www.buildyourownlisp.com |
// | |||||
// 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; | |||||
} |
// | |||||
// 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 |
// | |||||
// 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; | |||||
} |