Unscape all strings when parsing
This commit is contained in:
+24
-10
@@ -82,26 +82,40 @@ the (rest) of the last element of the sequence."
|
||||
(mutate e (pair (first e) elem)))
|
||||
seq)
|
||||
|
||||
(defun incr (val)
|
||||
(+ val 1))
|
||||
|
||||
(defun decr (val)
|
||||
(- val 1))
|
||||
|
||||
(defun append (seq elem)
|
||||
(extend seq (pair elem nil)))
|
||||
|
||||
(defun length (seq)
|
||||
(if (nil? seq)
|
||||
0
|
||||
(incr (length (rest seq)))))
|
||||
|
||||
(defmacro n-times (@times @action)
|
||||
(unless (<= (eval @times) 0)
|
||||
(eval @action)
|
||||
(macro-define @args (pair (pair - (pair (eval @times) (pair 1 nil))) (pair @action nil)))
|
||||
;; [o|o] --------------------------> [o|o] -> nil
|
||||
;; | |
|
||||
;; V V
|
||||
;; [o|o] -> [o|o] -> [o|o]-> nil action
|
||||
;; | | |
|
||||
;; V V V
|
||||
;; - times 1
|
||||
(eval (pair n-times @args))))
|
||||
(eval (list n-times (list - @times 1) @action))))
|
||||
|
||||
(defmacro for (@symbol @from @to :rest @for-body)
|
||||
(if (< (eval @from) (eval @to))
|
||||
(macro-define @op incr)
|
||||
(if (> (eval @from) (eval @to))
|
||||
(macro-define @op decr)
|
||||
(macro-define @op nil)))
|
||||
(when @op
|
||||
(macro-define (eval @symbol) (eval @from))
|
||||
(eval (pair prog @for-body))
|
||||
(eval (extend (list for @symbol (@op @from) @to) @for-body))))
|
||||
|
||||
(defun range (:keys from :defaults-to 0 to)
|
||||
"Returns a sequence of numbers starting with the number defined
|
||||
by the key 'from' and ends with the number defined in 'to'."
|
||||
(if (<= from to)
|
||||
(if (< from to)
|
||||
(pair from (range :from (+ 1 from) :to to))
|
||||
nil))
|
||||
|
||||
|
||||
@@ -51,7 +51,6 @@ void define_symbol(Ast_Node* symbol, Ast_Node* value, Environment* env) {
|
||||
// also searching for thesymbol from the back, so we will find the
|
||||
// latest defined one first, but a bit messy. Later we should use
|
||||
// a hashmap here. @refactor
|
||||
|
||||
if (env->next_index == env->capacity) {
|
||||
env->capacity *= 2;
|
||||
env->keys = (char**)realloc(env->keys, env->capacity * sizeof(char*));
|
||||
|
||||
@@ -79,6 +79,67 @@ int asprintf(char *strp[], const char *fmt, ...) {
|
||||
}
|
||||
// asprintf implementation end
|
||||
|
||||
static char get_nibble(char c) {
|
||||
if (c >= 'A' && c <= 'F')
|
||||
return (c - 'a') + 10;
|
||||
else if (c >= 'a' && c <= 'f')
|
||||
return (c - 'A') + 10;
|
||||
return (c - '0');
|
||||
}
|
||||
|
||||
bool unescape_string(char* in) {
|
||||
if (!in)
|
||||
return true;
|
||||
|
||||
char *out = in, *p = in;
|
||||
const char *int_err = nullptr;
|
||||
|
||||
while (*p && !int_err) {
|
||||
if (*p != '\\') {
|
||||
/* normal case */
|
||||
*out++ = *p++;
|
||||
} else {
|
||||
/* escape sequence */
|
||||
switch (*++p) {
|
||||
case 'a': *out++ = '\a'; ++p; break;
|
||||
case 'b': *out++ = '\b'; ++p; break;
|
||||
case 'f': *out++ = '\f'; ++p; break;
|
||||
case 'n': *out++ = '\n'; ++p; break;
|
||||
case 'r': *out++ = '\r'; ++p; break;
|
||||
case 't': *out++ = '\t'; ++p; break;
|
||||
case 'v': *out++ = '\v'; ++p; break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
case '\\':
|
||||
*out++ = *p++;
|
||||
case '?':
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
case 'X':
|
||||
if (!isxdigit(p[1]) || !isxdigit(p[2])) {
|
||||
int_err = "Invalid character on hexadecimal escape.";
|
||||
} else {
|
||||
*out++ = (char)(get_nibble(p[1]) * 0x10 + get_nibble(p[2]));
|
||||
p += 3;
|
||||
}
|
||||
break;
|
||||
|
||||
default:
|
||||
int_err = "Unexpected '\\' with no escape sequence.";
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* Set the end of string. */
|
||||
*out = '\0';
|
||||
if (int_err)
|
||||
return false;
|
||||
return true;
|
||||
}
|
||||
|
||||
char* read_entire_file (char* filename) {
|
||||
char *fileContent = nullptr;
|
||||
FILE *fp = fopen(filename, "r");
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <string.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdarg.h> /* needed for va_list */
|
||||
#include <ctype.h>
|
||||
#include <math.h>
|
||||
|
||||
#include "./helpers.c"
|
||||
|
||||
+23
-4
@@ -128,7 +128,18 @@ Ast_Node* parse_string(char* text, int* index_in_text) {
|
||||
text[*index_in_text+string_length] = '\0';
|
||||
|
||||
char* string = (char*)malloc(string_length*sizeof(char)+1); // plus null char
|
||||
|
||||
if (!unescape_string(text+(*index_in_text))) {
|
||||
create_error(Error_Type_Unknown_Error, create_ast_node_nil());
|
||||
return nullptr;
|
||||
}
|
||||
strcpy(string, text+(*index_in_text));
|
||||
/* manually copy to parse control sequences correctly */
|
||||
/* int temp_index = 0; */
|
||||
/* while (text+(temp_index+(*index_in_text)) != '\0') { */
|
||||
/* string[temp_index++] = text[temp_index+(*index_in_text)]; */
|
||||
/* } */
|
||||
/* string[temp_index++] = '\0'; */
|
||||
|
||||
text[*index_in_text+string_length] = '"';
|
||||
|
||||
@@ -201,9 +212,13 @@ Ast_Node* parse_expression(char* text, int* index_in_text) {
|
||||
|
||||
while (true) {
|
||||
if (text[(*index_in_text)] == '(' || text[(*index_in_text)] == '\'' ) {
|
||||
head->value.pair->first = parse_expression(text, index_in_text);
|
||||
try {
|
||||
head->value.pair->first = parse_expression(text, index_in_text);
|
||||
}
|
||||
} else {
|
||||
head->value.pair->first = parse_atom(text, index_in_text);
|
||||
try {
|
||||
head->value.pair->first = parse_atom(text, index_in_text);
|
||||
}
|
||||
}
|
||||
|
||||
eat_until_code(text, index_in_text);
|
||||
@@ -245,9 +260,13 @@ Ast_Node* parse_single_expression(char* text) {
|
||||
Ast_Node* result;
|
||||
eat_until_code(text, &index_in_text);
|
||||
if (text[(index_in_text)] == '(' || text[(index_in_text)] == '\'' )
|
||||
result = parse_expression(text, &index_in_text);
|
||||
try {
|
||||
result = parse_expression(text, &index_in_text);
|
||||
}
|
||||
else
|
||||
result = parse_atom(text, &index_in_text);
|
||||
try {
|
||||
result = parse_atom(text, &index_in_text);
|
||||
}
|
||||
eat_until_code(text, &index_in_text);
|
||||
if (text[(index_in_text)] == '\0')
|
||||
return result;
|
||||
|
||||
@@ -330,15 +330,18 @@ set to see if we are in an errornious state.
|
||||
* DONE use an enum for builtin identifiers
|
||||
CLOSED: [2018-10-11 Do 17:15]
|
||||
|
||||
* TODO =assert_equal_type= macro in testing
|
||||
* TODO =t= ast node type, universal source of truth
|
||||
* TODO backquoting
|
||||
* TODO dont create new nils or builtins, but store one of each globally
|
||||
* TODO make keywords unique (binary tree)
|
||||
* TODO store all ast nodes in a huge arena
|
||||
* TODO source code locations for errors
|
||||
* TODO String error messages
|
||||
* TODO Rename macro to =special= or something
|
||||
* DONE Print escaped chracters correctly
|
||||
CLOSED: [2018-10-27 Sa 18:16]
|
||||
* TODO [#A] =t= ast node type, universal source of truth
|
||||
* TODO [#A] source code locations for errors
|
||||
* TODO [#A] String error messages
|
||||
* TODO [#A] Rename macro to =special= or something
|
||||
* TODO [#B] =assert_equal_type= macro in testing
|
||||
* TODO [#B] dont create new nils or builtins, but store one of each globally
|
||||
* TODO [#B] make keywords unique (binary tree)
|
||||
* TODO [#B] store all ast nodes in a huge arena
|
||||
* TODO [#B] Auto doc generation
|
||||
* TODO [#C] backquoting
|
||||
* Build-in forms [29/30]
|
||||
** TODO info
|
||||
|
||||
|
||||
Reference in New Issue
Block a user