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)))
|
(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))
|
||||||
|
|
||||||
|
|||||||
@@ -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*));
|
||||||
|
|||||||
@@ -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");
|
||||||
|
|||||||
@@ -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
@@ -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;
|
||||||
|
|||||||
@@ -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
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user