Unscape all strings when parsing

This commit is contained in:
FelixBrendel
2018-10-27 18:16:43 +02:00
parent 5aa92062c3
commit 49f18d7771
6 changed files with 121 additions and 24 deletions
+24 -10
View File
@@ -82,26 +82,40 @@ the (rest) of the last element of the sequence."
(mutate e (pair (first e) elem))) (mutate e (pair (first e) elem)))
seq) seq)
(defun incr (val)
(+ val 1))
(defun decr (val)
(- val 1))
(defun append (seq elem) (defun append (seq elem)
(extend seq (pair elem nil))) (extend seq (pair elem nil)))
(defun length (seq)
(if (nil? seq)
0
(incr (length (rest seq)))))
(defmacro n-times (@times @action) (defmacro n-times (@times @action)
(unless (<= (eval @times) 0) (unless (<= (eval @times) 0)
(eval @action) (eval @action)
(macro-define @args (pair (pair - (pair (eval @times) (pair 1 nil))) (pair @action nil))) (eval (list n-times (list - @times 1) @action))))
;; [o|o] --------------------------> [o|o] -> nil
;; | | (defmacro for (@symbol @from @to :rest @for-body)
;; V V (if (< (eval @from) (eval @to))
;; [o|o] -> [o|o] -> [o|o]-> nil action (macro-define @op incr)
;; | | | (if (> (eval @from) (eval @to))
;; V V V (macro-define @op decr)
;; - times 1 (macro-define @op nil)))
(eval (pair n-times @args)))) (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) (defun range (:keys from :defaults-to 0 to)
"Returns a sequence of numbers starting with the number defined "Returns a sequence of numbers starting with the number defined
by the key 'from' and ends with the number defined in 'to'." 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)) (pair from (range :from (+ 1 from) :to to))
nil)) nil))
-1
View File
@@ -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 // also searching for thesymbol from the back, so we will find the
// latest defined one first, but a bit messy. Later we should use // latest defined one first, but a bit messy. Later we should use
// a hashmap here. @refactor // a hashmap here. @refactor
if (env->next_index == env->capacity) { if (env->next_index == env->capacity) {
env->capacity *= 2; env->capacity *= 2;
env->keys = (char**)realloc(env->keys, env->capacity * sizeof(char*)); env->keys = (char**)realloc(env->keys, env->capacity * sizeof(char*));
+61
View File
@@ -79,6 +79,67 @@ int asprintf(char *strp[], const char *fmt, ...) {
} }
// asprintf implementation end // 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* read_entire_file (char* filename) {
char *fileContent = nullptr; char *fileContent = nullptr;
FILE *fp = fopen(filename, "r"); FILE *fp = fopen(filename, "r");
+1
View File
@@ -3,6 +3,7 @@
#include <string.h> #include <string.h>
#include <stdlib.h> #include <stdlib.h>
#include <stdarg.h> /* needed for va_list */ #include <stdarg.h> /* needed for va_list */
#include <ctype.h>
#include <math.h> #include <math.h>
#include "./helpers.c" #include "./helpers.c"
+23 -4
View File
@@ -128,7 +128,18 @@ Ast_Node* parse_string(char* text, int* index_in_text) {
text[*index_in_text+string_length] = '\0'; text[*index_in_text+string_length] = '\0';
char* string = (char*)malloc(string_length*sizeof(char)+1); // plus null char 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)); 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] = '"'; text[*index_in_text+string_length] = '"';
@@ -201,9 +212,13 @@ Ast_Node* parse_expression(char* text, int* index_in_text) {
while (true) { while (true) {
if (text[(*index_in_text)] == '(' || text[(*index_in_text)] == '\'' ) { 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 { } 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); eat_until_code(text, index_in_text);
@@ -245,9 +260,13 @@ Ast_Node* parse_single_expression(char* text) {
Ast_Node* result; Ast_Node* result;
eat_until_code(text, &index_in_text); eat_until_code(text, &index_in_text);
if (text[(index_in_text)] == '(' || 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 else
result = parse_atom(text, &index_in_text); try {
result = parse_atom(text, &index_in_text);
}
eat_until_code(text, &index_in_text); eat_until_code(text, &index_in_text);
if (text[(index_in_text)] == '\0') if (text[(index_in_text)] == '\0')
return result; return result;
+12 -9
View File
@@ -330,15 +330,18 @@ set to see if we are in an errornious state.
* DONE use an enum for builtin identifiers * DONE use an enum for builtin identifiers
CLOSED: [2018-10-11 Do 17:15] CLOSED: [2018-10-11 Do 17:15]
* TODO =assert_equal_type= macro in testing * DONE Print escaped chracters correctly
* TODO =t= ast node type, universal source of truth CLOSED: [2018-10-27 Sa 18:16]
* TODO backquoting * TODO [#A] =t= ast node type, universal source of truth
* TODO dont create new nils or builtins, but store one of each globally * TODO [#A] source code locations for errors
* TODO make keywords unique (binary tree) * TODO [#A] String error messages
* TODO store all ast nodes in a huge arena * TODO [#A] Rename macro to =special= or something
* TODO source code locations for errors * TODO [#B] =assert_equal_type= macro in testing
* TODO String error messages * TODO [#B] dont create new nils or builtins, but store one of each globally
* TODO Rename macro to =special= or something * 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] * Build-in forms [29/30]
** TODO info ** TODO info