#include <assert.h>
-#include <stdlib.h>
#include <ctype.h>
+#include <errno.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <inttypes.h>
+#include "script/builtins.h"
#include "script/parser.h"
+#include "system/lt.h"
+#include "system/lt/lt_adapters.h"
+
+#define MAX_BUFFER_LENGTH (5 * 1000 * 1000)
-static bool is_symbol_char(char x)
+static struct ParseResult parse_expr(Gc *gc, struct Token current_token);
+
+static struct ParseResult parse_cdr(Gc *gc, struct Token current_token)
{
- static const char forbidden_symbol_chars[] = {
- '(', ')', '"', '\'', ';'
- };
- static const size_t n = sizeof(forbidden_symbol_chars) / sizeof(char);
+ if (*current_token.begin != '.') {
+ return parse_failure("Expected .", current_token.begin);
+ }
- for (size_t i = 0; i < n; ++i) {
- if (x == forbidden_symbol_chars[i] || isspace(x)) {
- return false;
- }
+ struct ParseResult cdr = read_expr_from_string(gc, current_token.end);
+ if (cdr.is_error) {
+ return cdr;
+ }
+
+ current_token = next_token(cdr.end);
+
+ if (*current_token.begin != ')') {
+ return parse_failure("Expected )", current_token.begin);
}
- return true;
+ return parse_success(cdr.expr, current_token.end);
}
-static void skip_whitespaces(const char *str, size_t *cursor, size_t n)
+static struct ParseResult parse_list_end(Gc *gc, struct Token current_token)
{
- assert(str);
- assert(cursor);
-
- while (*cursor < n && isspace(str[*cursor])) {
- (*cursor)++;
+ if (*current_token.begin != ')') {
+ return parse_failure("Expected )", current_token.begin);
}
+
+ return parse_success(atom_as_expr(create_symbol_atom(gc, "nil", NULL)),
+ current_token.end);
}
-struct ParseResult create_expr_from_str(const char *str,
- size_t *cursor,
- size_t n)
+static struct ParseResult parse_list(Gc *gc, struct Token current_token)
{
- assert(str);
- assert(cursor);
+ if (*current_token.begin != '(') {
+ return parse_failure("Expected (", current_token.begin);
+ }
+
+ current_token = next_token(current_token.end);
- /* TODO(#297): create_expr_from_str doesn't parse lists */
- /* TODO(#291): create_expr_from_str doesn't no support comments */
+ if (*current_token.begin == ')') {
+ return parse_list_end(gc, current_token);
+ }
- skip_whitespaces(str, cursor, n);
- if (*cursor >= n) {
- return parse_failure("EOF", *cursor);
+ struct ParseResult car = parse_expr(gc, current_token);
+ if (car.is_error) {
+ return car;
}
- switch (str[*cursor]) {
- case '(': {
- (*cursor)++;
- struct ParseResult car = create_expr_from_str(str, cursor, n);
+ struct Cons *list = create_cons(gc, car.expr, void_expr());
+ struct Cons *cons = list;
+ current_token = next_token(car.end);
+
+ while (*current_token.begin != '.' &&
+ *current_token.begin != ')' &&
+ *current_token.begin != 0) {
+ car = parse_expr(gc, current_token);
if (car.is_error) {
return car;
}
- skip_whitespaces(str, cursor, n);
- if (*cursor >= n) {
- return parse_failure("EOF", *cursor);
- }
+ cons->cdr = cons_as_expr(create_cons(gc, car.expr, void_expr()));
+ cons = cons->cdr.cons;
- if (str[*cursor] != '.') {
- return parse_failure("Expected .", *cursor);
- }
- (*cursor)++;
+ current_token = next_token(car.end);
+ }
- skip_whitespaces(str, cursor, n);
- if (*cursor >= n) {
- return parse_failure("EOF", *cursor);
- }
+ struct ParseResult cdr = *current_token.begin == '.'
+ ? parse_cdr(gc, current_token)
+ : parse_list_end(gc, current_token);
- struct ParseResult cdr = create_expr_from_str(str, cursor, n);
- if (cdr.is_error) {
- return cdr;
- }
+ if (cdr.is_error) {
+ return cdr;
+ }
- skip_whitespaces(str, cursor, n);
- if (*cursor >= n) {
- return parse_failure("EOF", *cursor);
- }
+ cons->cdr = cdr.expr;
- if (str[*cursor] != ')') {
- return parse_failure("Expected )", *cursor);
- }
+ return parse_success(cons_as_expr(list), cdr.end);
+}
+
+static struct ParseResult parse_string(Gc *gc, struct Token current_token)
+{
+ if (*current_token.begin != '"') {
+ return parse_failure("Expected \"", current_token.begin);
+ }
+
+ if (*(current_token.end - 1) != '"') {
+ return parse_failure("Unclosed string", current_token.begin);
+ }
+
+ if (current_token.begin + 1 == current_token.end) {
+ return parse_success(atom_as_expr(create_string_atom(gc, "", NULL)),
+ current_token.end);
+ }
+
+ return parse_success(
+ atom_as_expr(
+ create_string_atom(gc, current_token.begin + 1, current_token.end - 1)),
+ current_token.end);
+}
- (*cursor)++;
+static struct ParseResult parse_number(Gc *gc, struct Token current_token)
+{
+ char *endptr = 0;
+ const long int x = strtoimax(current_token.begin, &endptr, 10);
+
+ if (current_token.begin == endptr || current_token.end != endptr) {
+ return parse_failure("Expected number", current_token.begin);
+ }
+
+ return parse_success(
+ atom_as_expr(create_number_atom(gc, x)),
+ current_token.end);
+}
+
+static struct ParseResult parse_symbol(Gc *gc, struct Token current_token)
+{
+ if (*current_token.begin == 0) {
+ return parse_failure("EOF", current_token.begin);
+ }
+
+ return parse_success(
+ atom_as_expr(create_symbol_atom(gc, current_token.begin, current_token.end)),
+ current_token.end);
+}
- return parse_success(cons_as_expr(create_cons(car.expr, cdr.expr)));
+static struct ParseResult parse_expr(Gc *gc, struct Token current_token)
+{
+ if (*current_token.begin == 0) {
+ return parse_failure("EOF", current_token.begin);
}
- case '"': {
- /* TODO(#292): parser does not support escaped string characters */
- const size_t str_begin = *cursor + 1;
- size_t str_end = str_begin;
+ switch (*current_token.begin) {
+ case '(': return parse_list(gc, current_token);
+ /* TODO(#292): parser does not support escaped string characters */
+ case '"': return parse_string(gc, current_token);
+ case '\'': {
+ struct ParseResult result = parse_expr(gc, next_token(current_token.end));
- while(str_end < n && str[str_end] != '"') {
- str_end++;
+ if (result.is_error) {
+ return result;
}
- if (str_end >= n) {
- return parse_failure("Unclosed string", str_begin);
+ result.expr = list(gc, 2, SYMBOL(gc, "quote"), result.expr);
+
+ return result;
+ } break;
+ default: {}
+ }
+
+ if (*current_token.begin == '-' || isdigit(*current_token.begin)) {
+ struct ParseResult result = parse_number(gc, current_token);
+ if (!result.is_error) {
+ return result;
}
+ }
+
+ return parse_symbol(gc, current_token);
+}
+
+struct ParseResult read_expr_from_string(Gc *gc, const char *str)
+{
+ assert(str);
+ return parse_expr(gc, next_token(str));
+}
- *cursor = str_end + 1;
+struct ParseResult read_expr_from_file(Gc *gc, const char *filename)
+{
+ assert(filename);
- return parse_success(
- atom_as_expr(
- create_string_atom(str + str_begin, str + str_end)));
+ Lt *lt = create_lt();
+ if (lt == NULL) {
+ return parse_failure("Could not create Lt object", NULL);
}
- default: {
- if (isdigit(str[*cursor])) {
- const char *nptr = str + *cursor;
- char *endptr = 0;
- const float x = strtof(nptr, &endptr);
+ FILE *stream = PUSH_LT(lt, fopen(filename, "rb"), fclose_lt);
+ if (!stream) {
+ /* TODO(#307): ParseResult should not be used for reporting IO failures */
+ RETURN_LT(lt, parse_failure(strerror(errno), NULL));
+ }
- if (nptr == endptr) {
- return parse_failure("Number expected", *cursor);
- }
+ if (fseek(stream, 0, SEEK_END) != 0) {
+ RETURN_LT(lt, parse_failure("Could not find the end of the file", NULL));
+ }
+
+ const long int buffer_length = ftell(stream);
- *cursor += (size_t) (endptr - nptr);
+ if (buffer_length < 0) {
+ RETURN_LT(lt, parse_failure("Couldn't get the size of file", NULL));
+ }
- return parse_success(atom_as_expr(create_number_atom(x)));
- } else if (is_symbol_char(str[*cursor])) {
- const size_t sym_begin = *cursor;
- size_t sym_end = sym_begin;
+ if (buffer_length == 0) {
+ RETURN_LT(lt, parse_failure("File is empty", NULL));
+ }
- while (sym_end < n && is_symbol_char(str[sym_end])) {
- sym_end++;
- }
+ if (buffer_length >= MAX_BUFFER_LENGTH) {
+ RETURN_LT(lt, parse_failure("File is too big", NULL));
+ }
- *cursor = sym_end;
+ if (fseek(stream, 0, SEEK_SET) != 0) {
+ RETURN_LT(lt, parse_failure("Could not find the beginning of the file", NULL));
+ }
- return parse_success(
- atom_as_expr(
- create_symbol_atom(str + sym_begin, str + sym_end)));
- }
+ char * const buffer = PUSH_LT(lt, malloc((size_t) buffer_length + 1), free);
+ if (buffer == NULL) {
+ RETURN_LT(lt, parse_failure(strerror(errno), NULL));
}
+
+ if (fread(buffer, 1, (size_t) buffer_length, stream) != (size_t) buffer_length) {
+ RETURN_LT(lt, parse_failure("Could not read the file", NULL));
}
- return parse_failure("Unexpected sequence of characters", *cursor);
+ struct ParseResult result = read_expr_from_string(gc, buffer);
+
+ RETURN_LT(lt, result);
}
-struct ParseResult parse_success(struct Expr expr)
+struct ParseResult parse_success(struct Expr expr,
+ const char *end)
{
struct ParseResult result = {
.is_error = false,
- .expr = expr
+ .expr = expr,
+ .end = end
};
return result;
}
struct ParseResult parse_failure(const char *error_message,
- size_t error_cursor)
+ const char *end)
{
struct ParseResult result = {
.is_error = true,
- .error = {
- .error_message = error_message,
- .error_cursor = error_cursor
- }
+ .error_message = error_message,
+ .end = end
};
return result;
void print_parse_error(FILE *stream,
const char *str,
- struct ParseError error)
+ struct ParseResult result)
{
- /* TODO(#293): print_parse_error doesn't support colors */
/* TODO(#294): print_parse_error doesn't support multiple lines */
+ if (!result.is_error) {
+ return;
+ }
- fprintf(stream, "%s\n", str);
- for (size_t i = 0; i < error.error_cursor; ++i) {
- fprintf(stream, " ");
+ if (result.end) {
+ fprintf(stream, "%s\n", str);
+ for (size_t i = 0; i < (size_t) (result.end - str); ++i) {
+ fprintf(stream, " ");
+ }
+ fprintf(stream, "^\n");
}
- fprintf(stream, "^\n");
- fprintf(stream, "%s\n", error.error_message);
+
+ fprintf(stream, "%s\n", result.error_message);
}