From 4c7acd92f0b3666c726252f9fc93e37153193f69 Mon Sep 17 00:00:00 2001 From: Alex Kotov Date: Sun, 7 May 2023 15:01:02 +0400 Subject: [PATCH] Escape in strings --- src/builtins.c | 13 ++++++++++++- src/lexer.c | 10 ++++++++++ src/lexer.h | 1 + tests/arcana.scm | 36 ++++++++++++++++++++++-------------- 4 files changed, 45 insertions(+), 15 deletions(-) diff --git a/src/builtins.c b/src/builtins.c index c59019f..9a81d72 100644 --- a/src/builtins.c +++ b/src/builtins.c @@ -655,7 +655,18 @@ struct Object *func_display( printf("%li", object->number.i64); break; case TYPE_STRING: - printf("\"%s\"", object->s); + putchar('"'); + for (char *str = object->s; *str; ++str) { + const char chr = *str; + if (chr == '\\') { + printf("\\\\"); + } else if (chr == '\"') { + printf("\\\""); + } else { + putchar(chr); + } + } + putchar('"'); break; } diff --git a/src/lexer.c b/src/lexer.c index efb257c..642fdb4 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -354,6 +354,8 @@ void Lexer_lex(const Lexer self, const char chr) if (chr == '"') { token_finish(self); token_start(self, STATE_STRING_END); + } else if (chr == '\\') { + self->state = STATE_STRING_ESCAPE; } else { token_putc(self, chr); } @@ -389,5 +391,13 @@ void Lexer_lex(const Lexer self, const char chr) assert(0); } break; + case STATE_STRING_ESCAPE: + if (chr == '\\' || chr == '"') { + token_putc(self, chr); + self->state = STATE_STRING_INSIDE; + } else { + assert(0); + } + break; } } diff --git a/src/lexer.h b/src/lexer.h index 452981d..be0187a 100644 --- a/src/lexer.h +++ b/src/lexer.h @@ -31,6 +31,7 @@ enum Lexer_State { STATE_STRING_START, STATE_STRING_INSIDE, STATE_STRING_END, + STATE_STRING_ESCAPE, }; typedef struct Lexer { diff --git a/tests/arcana.scm b/tests/arcana.scm index 2565e46..5b3738f 100644 --- a/tests/arcana.scm +++ b/tests/arcana.scm @@ -20,22 +20,30 @@ (assert-equal (arcana/tokenize "#false") (list (cons 'TOKEN_TAG "false"))) - ;(assert-equal - ; (arcana/tokenize "\"\"") - ; (list (cons 'TOKEN_STRING "\"\""))) - ;(assert-equal - ; (arcana/tokenize "\"qwe\"") - ; (list (cons 'TOKEN_STRING "\"qwe\""))) + (assert-equal + (arcana/tokenize "\"\"") + (list (cons 'TOKEN_STRING ""))) + (assert-equal + (arcana/tokenize "\"qwe\"") + (list (cons 'TOKEN_STRING "qwe"))) + (assert-equal + (arcana/tokenize "(displayln \"qwe\")") + (list + (cons 'TOKEN_ROUND_OPEN "(") + (cons 'TOKEN_IDENT "displayln") + (cons 'TOKEN_STRING "qwe") + (cons 'TOKEN_ROUND_CLOSE ")"))) + (assert-equal (arcana/tokenize "(displayln (list 1))") - (list - (cons 'TOKEN_ROUND_OPEN "(") - (cons 'TOKEN_IDENT "displayln") - (cons 'TOKEN_ROUND_OPEN "(") - (cons 'TOKEN_IDENT "list") - (cons 'TOKEN_NUM "1") - (cons 'TOKEN_ROUND_CLOSE ")") - (cons 'TOKEN_ROUND_CLOSE ")"))) + (list + (cons 'TOKEN_ROUND_OPEN "(") + (cons 'TOKEN_IDENT "displayln") + (cons 'TOKEN_ROUND_OPEN "(") + (cons 'TOKEN_IDENT "list") + (cons 'TOKEN_NUM "1") + (cons 'TOKEN_ROUND_CLOSE ")") + (cons 'TOKEN_ROUND_CLOSE ")"))) ;;; arcana/typeof ;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;; (assert-equal 'null (arcana/typeof '()))