From 8581abcc0c80de312a0b62f33e8faffe75b64e6b Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Fri, 5 May 2023 14:03:32 +0400 Subject: [PATCH] Add booleans --- src/builtins.c | 2 +- src/ctype.c | 13 +- src/ctype.h | 1 + src/lexer.c | 63 ++++++++++ src/main.c | 320 +++++++++++++++++++------------------------------ src/object.c | 2 +- src/parser.c | 16 +++ src/tokens.c | 1 + src/tokens.h | 1 + 9 files changed, 219 insertions(+), 200 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index 3583564..87d873c 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -293,7 +293,7 @@ struct Object *func_display(struct Object *const args) printf(")"); break; case TYPE_BOOLEAN: - printf("%s", object->boolean ? "#t" : "#f"); + printf("%s", object->boolean ? "#true" : "#false"); break; case TYPE_CHAR: printf("#\\TODO"); // TODO diff --git a/src/ctype.c b/src/ctype.c index c4bb557..b9012e4 100644 --- a/src/ctype.c +++ b/src/ctype.c @@ -2,7 +2,7 @@ #include -bool is_space(char chr) +bool is_space(const char chr) { // ' ' (space) // '\f' (form-feed) @@ -13,7 +13,12 @@ bool is_space(char chr) return isspace(chr); } -bool is_ident_head(char chr) +bool is_tag(const char chr) +{ + return isalnum(chr); +} + +bool is_ident_head(const char chr) { return isalpha(chr) || chr == '!' || @@ -36,12 +41,12 @@ bool is_ident_head(char chr) chr == '~'; } -bool is_ident_tail(char chr) +bool is_ident_tail(const char chr) { return is_ident_head(chr) || isdigit(chr); } -bool is_number(char chr) +bool is_number(const char chr) { return isdigit(chr); } diff --git a/src/ctype.h b/src/ctype.h index 2d293ac..5d30daa 100644 --- a/src/ctype.h +++ b/src/ctype.h @@ -4,6 +4,7 @@ #include bool is_space(char chr); +bool is_tag(char chr); bool is_ident_head(char chr); bool is_ident_tail(char chr); bool is_number(char chr); diff --git a/src/lexer.c b/src/lexer.c index b2259f6..b0dc7e4 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -17,6 +17,8 @@ enum State { STATE_CURLY_OPEN, STATE_CURLY_CLOSE, STATE_QUOTE, + STATE_SHARP, + STATE_TAG, STATE_IDENT, STATE_NUM, }; @@ -58,6 +60,7 @@ State_to_token_type(const enum State state, enum TokenType *const token_type) case STATE_CURLY_CLOSE: *token_type = TOKEN_CURLY_CLOSE; break; case STATE_QUOTE: *token_type = TOKEN_QUOTE; break; case STATE_IDENT: *token_type = TOKEN_IDENT; break; + case STATE_TAG: *token_type = TOKEN_TAG; break; case STATE_NUM: *token_type = TOKEN_NUM; break; default: return false; } @@ -112,6 +115,8 @@ void Lexer_lex(const Lexer self, const char chr) } else if (chr == '\'') { state = STATE_QUOTE; buffer_add(chr); + } else if (chr == '#') { + state = STATE_SHARP; } else if (is_space(chr)) { state = STATE_WHITESPACE; buffer_add(chr); @@ -161,6 +166,10 @@ void Lexer_lex(const Lexer self, const char chr) buffer_clean(); state = STATE_QUOTE; buffer_add(chr); + } else if (chr == '#') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_SHARP; } else if (is_space(chr)) { buffer_add(chr); } else if (is_ident_head(chr)) { @@ -219,6 +228,10 @@ void Lexer_lex(const Lexer self, const char chr) buffer_clean(); state = STATE_QUOTE; buffer_add(chr); + } else if (chr == '#') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_SHARP; } else if (is_space(chr)) { token_add(self->tokens, state, buffer); buffer_clean(); @@ -238,6 +251,56 @@ void Lexer_lex(const Lexer self, const char chr) abort(); } break; + case STATE_SHARP: + if (is_tag(chr)) { + state = STATE_TAG; + buffer_add(chr); + } else { + abort(); + } + break; + case STATE_TAG: + if (chr == '(') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_ROUND_OPEN; + buffer_add(chr); + } else if (chr == ')') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_ROUND_CLOSE; + buffer_add(chr); + } else if (chr == '[') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_SQUARE_OPEN; + buffer_add(chr); + } else if (chr == ']') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_SQUARE_CLOSE; + buffer_add(chr); + } else if (chr == '{') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_CURLY_OPEN; + buffer_add(chr); + } else if (chr == '}') { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_CURLY_CLOSE; + buffer_add(chr); + } else if (is_space(chr)) { + token_add(self->tokens, state, buffer); + buffer_clean(); + state = STATE_WHITESPACE; + buffer_add(chr); + } else if (is_tag(chr)) { + buffer_add(chr); + } else { + abort(); + } + break; case STATE_IDENT: if (chr == '(') { token_add(self->tokens, state, buffer); diff --git a/src/main.c b/src/main.c index 4951f09..22333a2 100644 --- a/src/main.c +++ b/src/main.c @@ -260,8 +260,8 @@ void test() struct Object *const sharp_true = Object_new_boolean(true); struct Object *const num_123 = Object_new_number(123); - // #t - // #t + // #true + // #true assert(eval(sharp_true) == sharp_true); // 123 @@ -276,8 +276,8 @@ void test() ) == sym_foo ); - // (quote #t) - // #t + // (quote #true) + // #true assert( eval( Object_build_list(2, Object_new_symbol("quote"), sharp_true) @@ -306,7 +306,7 @@ void test_if() struct Object *const num_123 = Object_new_number(123); struct Object *const num_456 = Object_new_number(456); - // (if #t 123 456) + // (if #true 123 456) assert(eval(Object_build_list( 4, Object_new_symbol("if"), @@ -324,7 +324,7 @@ void test_if() num_456 )) == num_123); - // (if #f 123 456) + // (if #false 123 456) assert(eval(Object_build_list( 4, Object_new_symbol("if"), @@ -419,31 +419,23 @@ void test_list() void test_booleanQN() { // (boolean? +) - // #f + // #false assert(Object_is_false(eval_str("(boolean? +)"))); // (boolean? '()) - // #f + // #false assert(Object_is_false(eval_str("(boolean? '())"))); - // (boolean? #t) - // #t - assert(Object_is_true(eval(Object_build_list( - 2, - Object_new_symbol("boolean?"), - Object_new_boolean(true) - )))); + // (boolean? #true) + // #true + assert(Object_is_true(eval_str("(boolean? #true)"))); - // (boolean? #f) - // #t - assert(Object_is_true(eval(Object_build_list( - 2, - Object_new_symbol("boolean?"), - Object_new_boolean(false) - )))); + // (boolean? #false) + // #true + assert(Object_is_true(eval_str("(boolean? #false)"))); // (boolean? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("boolean?"), @@ -451,11 +443,11 @@ void test_booleanQN() )))); // (boolean? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(boolean? 'foo)"))); // (boolean? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("boolean?"), @@ -463,42 +455,34 @@ void test_booleanQN() )))); // (boolean? 123) - // #f + // #false assert(Object_is_false(eval_str("(boolean? 123)"))); // (boolean? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(boolean? (cons 123 456))"))); } void test_charQN() { // (char? +) - // #f + // #false assert(Object_is_false(eval_str("(char? +)"))); // (char? '()) - // #f + // #false assert(Object_is_false(eval_str("(char? '())"))); - // (char? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("char?"), - Object_new_boolean(true) - )))); + // (char? #true) + // #false + assert(Object_is_false(eval_str("(char? #true)"))); - // (char? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("char?"), - Object_new_boolean(false) - )))); + // (char? #false) + // #false + assert(Object_is_false(eval_str("(char? #false)"))); // (char? #\n) - // #t + // #true assert(Object_is_true(eval(Object_build_list( 2, Object_new_symbol("char?"), @@ -506,11 +490,11 @@ void test_charQN() )))); // (char? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(char? 'foo)"))); // (char? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("char?"), @@ -518,42 +502,34 @@ void test_charQN() )))); // (char? 123) - // #f + // #false assert(Object_is_false(eval_str("(char? 123)"))); // (char? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(char? (cons 123 456))"))); } void test_nullQN() { // (null? +) - // #f + // #false assert(Object_is_false(eval_str("(null? +)"))); // (null? '()) - // #t + // #true assert(Object_is_true(eval_str("(null? '())"))); - // (null? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("null?"), - Object_new_boolean(true) - )))); + // (null? #true) + // #false + assert(Object_is_false(eval_str("(null? #true)"))); - // (null? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("null?"), - Object_new_boolean(false) - )))); + // (null? #false) + // #false + assert(Object_is_false(eval_str("(null? #false)"))); // (null? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("null?"), @@ -561,11 +537,11 @@ void test_nullQN() )))); // (null? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(null? 'foo)"))); // (null? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("null?"), @@ -573,42 +549,34 @@ void test_nullQN() )))); // (null? 123) - // #f + // #false assert(Object_is_false(eval_str("(null? 123)"))); // (null? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(null? (cons 123 456))"))); } void test_numberQN() { // (number? +) - // #f + // #false assert(Object_is_false(eval_str("(number? +)"))); // (number? '()) - // #f + // #false assert(Object_is_false(eval_str("(number? '())"))); - // (number? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("number?"), - Object_new_boolean(true) - )))); + // (number? #true) + // #false + assert(Object_is_false(eval_str("(number? #true)"))); - // (number? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("number?"), - Object_new_boolean(false) - )))); + // (number? #false) + // #false + assert(Object_is_false(eval_str("(number? #false)"))); // (number? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("number?"), @@ -616,11 +584,11 @@ void test_numberQN() )))); // (number? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(number? 'foo)"))); // (number? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("number?"), @@ -628,42 +596,34 @@ void test_numberQN() )))); // (number? 123) - // #t + // #true assert(Object_is_true(eval_str("(number? 123)"))); // (number? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(number? (cons 123 456))"))); } void test_pairQN() { // (pair? +) - // #f + // #false assert(Object_is_false(eval_str("(pair? +)"))); // (pair? '()) - // #f + // #false assert(Object_is_false(eval_str("(pair? '())"))); - // (pair? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("pair?"), - Object_new_boolean(true) - )))); + // (pair? #true) + // #false + assert(Object_is_false(eval_str("(pair? #true)"))); - // (pair? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("pair?"), - Object_new_boolean(false) - )))); + // (pair? #false) + // #false + assert(Object_is_false(eval_str("(pair? #false)"))); // (pair? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("pair?"), @@ -671,11 +631,11 @@ void test_pairQN() )))); // (pair? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(pair? 'foo)"))); // (pair? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("pair?"), @@ -683,42 +643,34 @@ void test_pairQN() )))); // (pair? 123) - // #f + // #false assert(Object_is_false(eval_str("(pair? 123)"))); // (pair? (cons 123 456)) - // #t + // #true assert(Object_is_true(eval_str("(pair? (cons 123 456))"))); } void test_procedureQN() { // (procedure? +) - // #t + // #true assert(Object_is_true(eval_str("(procedure? +)"))); // (procedure? '()) - // #f + // #false assert(Object_is_false(eval_str("(procedure? '())"))); - // (procedure? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("procedure?"), - Object_new_boolean(true) - )))); + // (procedure? #true) + // #false + assert(Object_is_false(eval_str("(procedure? #true)"))); - // (procedure? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("procedure?"), - Object_new_boolean(false) - )))); + // (procedure? #false) + // #false + assert(Object_is_false(eval_str("(procedure? #false)"))); // (procedure? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("procedure?"), @@ -726,11 +678,11 @@ void test_procedureQN() )))); // (procedure? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(procedure? 'foo)"))); // (procedure? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("procedure?"), @@ -738,42 +690,34 @@ void test_procedureQN() )))); // (procedure? 123) - // #f + // #false assert(Object_is_false(eval_str("(procedure? 123)"))); // (procedure? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(procedure? (cons 123 456))"))); } void test_stringQN() { // (string? +) - // #f + // #false assert(Object_is_false(eval_str("(string? +)"))); // (string? '()) - // #f + // #false assert(Object_is_false(eval_str("(string? '())"))); - // (string? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("string?"), - Object_new_boolean(true) - )))); + // (string? #true) + // #false + assert(Object_is_false(eval_str("(string? #true)"))); - // (string? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("string?"), - Object_new_boolean(false) - )))); + // (string? #false) + // #false + assert(Object_is_false(eval_str("(string? #false)"))); // (string? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("string?"), @@ -781,11 +725,11 @@ void test_stringQN() )))); // (string? 'foo) - // #f + // #false assert(Object_is_false(eval_str("(string? 'foo)"))); // (string? "foo") - // #t + // #true assert(Object_is_true(eval(Object_build_list( 2, Object_new_symbol("string?"), @@ -793,42 +737,34 @@ void test_stringQN() )))); // (string? 123) - // #f + // #false assert(Object_is_false(eval_str("(string? 123)"))); // (string? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(string? (cons 123 456))"))); } void test_symbolQN() { // (symbol? +) - // #f + // #false assert(Object_is_false(eval_str("(symbol? +)"))); // (symbol? '()) - // #f + // #false assert(Object_is_false(eval_str("(symbol? '())"))); - // (symbol? #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("symbol?"), - Object_new_boolean(true) - )))); + // (symbol? #true) + // #false + assert(Object_is_false(eval_str("(symbol? #true)"))); - // (symbol? #f) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("symbol?"), - Object_new_boolean(false) - )))); + // (symbol? #false) + // #false + assert(Object_is_false(eval_str("(symbol? #false)"))); // (symbol? #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("symbol?"), @@ -836,11 +772,11 @@ void test_symbolQN() )))); // (symbol? 'foo) - // #t + // #true assert(Object_is_true(eval_str("(symbol? 'foo)"))); // (symbol? "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("symbol?"), @@ -848,11 +784,11 @@ void test_symbolQN() )))); // (symbol? 123) - // #f + // #false assert(Object_is_false(eval_str("(symbol? 123)"))); // (symbol? (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(symbol? (cons 123 456))"))); } @@ -887,30 +823,26 @@ void test_numberTOstring() assert(strcmp(result->s, "1e240") == 0); } +/********************* + * Logical operators * + *********************/ + void test_not() { // (not '()) - // #f + // #false assert(Object_is_false(eval_str("(not '())"))); - // (not #t) - // #f - assert(Object_is_false(eval(Object_build_list( - 2, - Object_new_symbol("not"), - Object_new_boolean(true) - )))); + // (not #true) + // #false + assert(Object_is_false(eval_str("(not #true)"))); - // (not #f) - // #t - assert(Object_is_true(eval(Object_build_list( - 2, - Object_new_symbol("not"), - Object_new_boolean(false) - )))); + // (not #false) + // #true + assert(Object_is_true(eval_str("(not #false)"))); // (not #\n) - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("not"), @@ -918,11 +850,11 @@ void test_not() )))); // (not 'foo) - // #f + // #false assert(Object_is_false(eval_str("(not 'foo)"))); // (not "foo") - // #f + // #false assert(Object_is_false(eval(Object_build_list( 2, Object_new_symbol("not"), @@ -930,10 +862,10 @@ void test_not() )))); // (not 123) - // #f + // #false assert(Object_is_false(eval_str("(not 123)"))); // (not (cons 123 456)) - // #f + // #false assert(Object_is_false(eval_str("(not (cons 123 456))"))); } diff --git a/src/object.c b/src/object.c index 16bcc20..ecd5530 100644 --- a/src/object.c +++ b/src/object.c @@ -190,7 +190,7 @@ void Object_print(struct Object *const self, const unsigned indent) Object_print(self->pair.b, indent + 1); break; case TYPE_BOOLEAN: - printf("%s\n", self->boolean ? "#t" : "#f"); + printf("%s\n", self->boolean ? "#true" : "#false"); break; case TYPE_CHAR: printf("%c\n", self->chr); diff --git a/src/parser.c b/src/parser.c index f677b48..4daaa36 100644 --- a/src/parser.c +++ b/src/parser.c @@ -6,6 +6,7 @@ #include #include #include +#include static void expect(Tokens tokens, enum TokenType token_type); @@ -35,6 +36,21 @@ struct Object *expr(const Tokens tokens) case TOKEN_QUOTE: Tokens_pop(tokens); return Object_build_list(2, Object_new_symbol("quote"), expr(tokens)); + case TOKEN_TAG: + { + struct Object *object = NULL; + const char *const val = Tokens_top(tokens)->val; + assert(val); + if (strcmp(val, "f") == 0 || strcmp(val, "false") == 0) { + object = Object_new_boolean(false); + } else if (strcmp(val, "t") == 0 || strcmp(val, "true") == 0) { + object = Object_new_boolean(true); + } else { + assert(0); + } + Tokens_pop(tokens); + return object; + } case TOKEN_IDENT: { struct Object *const object = Object_new_symbol(Tokens_top(tokens)->val); diff --git a/src/tokens.c b/src/tokens.c index 196c8bc..0ea2dc9 100644 --- a/src/tokens.c +++ b/src/tokens.c @@ -16,6 +16,7 @@ const char *TokenType_to_str(const enum TokenType token_type) case TOKEN_CURLY_OPEN: return "TOKEN_CURLY_OPEN"; case TOKEN_CURLY_CLOSE: return "TOKEN_CURLY_CLOSE"; case TOKEN_QUOTE: return "TOKEN_QUOTE"; + case TOKEN_TAG: return "TOKEN_TAG"; case TOKEN_IDENT: return "TOKEN_IDENT"; case TOKEN_NUM: return "TOKEN_NUM"; } diff --git a/src/tokens.h b/src/tokens.h index 3ed8255..01d66e3 100644 --- a/src/tokens.h +++ b/src/tokens.h @@ -17,6 +17,7 @@ enum TokenType { TOKEN_CURLY_OPEN, TOKEN_CURLY_CLOSE, TOKEN_QUOTE, + TOKEN_TAG, TOKEN_IDENT, TOKEN_NUM, };