495 lines
12 KiB
C
495 lines
12 KiB
C
#include "scirpt/parser.h"
|
|
#include "common/string.h"
|
|
#include "parser.h"
|
|
#include "scirpt/ast.h"
|
|
#include "scirpt/lexer.h"
|
|
#include "scirpt/position.h"
|
|
#include "scirpt/token.h"
|
|
#include <stdint.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
|
|
#define TT(type) ScirptTokenType##type
|
|
#define AET(type) ScirptAstExprType##type
|
|
|
|
static inline ScirptAstExpr* alloc_expr(ScirptAstExpr data)
|
|
{
|
|
ScirptAstExpr* expr = malloc(sizeof(ScirptAstExpr));
|
|
*expr = data;
|
|
return expr;
|
|
}
|
|
static inline void
|
|
error(ScirptParser* parser, HeapString message, ScirptPosition pos)
|
|
{
|
|
scirpt_parser_error(parser, message, pos);
|
|
}
|
|
static inline void step(ScirptParser* parser) { scirpt_parser_step(parser); }
|
|
static inline ScirptAstExpr*
|
|
step_alloc_expr(ScirptParser* parser, ScirptAstExpr data)
|
|
{
|
|
step(parser);
|
|
return alloc_expr(data);
|
|
}
|
|
static inline bool current_is(const ScirptParser* parser, ScirptTokenType type)
|
|
{
|
|
return scirpt_parser_current_is(parser, type);
|
|
}
|
|
static inline bool done(const ScirptParser* parser)
|
|
{
|
|
return scirpt_parser_done(parser);
|
|
}
|
|
|
|
ScirptParser*
|
|
scirpt_parser_new(const char* text, size_t text_length, ScirptLexer* lexer)
|
|
{
|
|
ScirptParser* parser = malloc(sizeof(ScirptParser));
|
|
scirpt_parser_construct(parser, text, text_length, lexer);
|
|
return parser;
|
|
}
|
|
|
|
void scirpt_parser_delete(ScirptParser* parser) { free(parser); }
|
|
|
|
ScirptAstExpr* scirpt_parser_next(ScirptParser* parser)
|
|
{
|
|
if (done(parser)) {
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Eof),
|
|
});
|
|
} else {
|
|
return scirpt_parser_parse_statement(parser);
|
|
}
|
|
}
|
|
|
|
bool scirpt_parser_ok(const ScirptParser* parser) { return parser->ok; }
|
|
|
|
const ScirptParserErrorArray* scirpt_parser_errors(const ScirptParser* parser)
|
|
{
|
|
return &parser->errors;
|
|
}
|
|
|
|
void scirpt_parser_construct(
|
|
ScirptParser* parser,
|
|
const char* text,
|
|
size_t text_length,
|
|
ScirptLexer* lexer
|
|
)
|
|
{
|
|
ScirptParserErrorArray errors;
|
|
scirpt_parser_error_array_construct(&errors);
|
|
|
|
*parser = (ScirptParser) {
|
|
.text = text,
|
|
.text_length = text_length,
|
|
.lexer = lexer,
|
|
.current = scirpt_lexer_next(lexer),
|
|
.errors = errors,
|
|
.ok = true,
|
|
};
|
|
}
|
|
|
|
void scirpt_parser_destroy(ScirptParser* parser)
|
|
{
|
|
scirpt_parser_error_array_construct(&parser->errors);
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_statement(ScirptParser* parser)
|
|
{
|
|
return scirpt_parser_parse_expr(parser);
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_expr(ScirptParser* parser)
|
|
{
|
|
return scirpt_parser_parse_member_call_index_expr(parser);
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_member_call_index_expr(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
ScirptAstExpr* subject = scirpt_parser_parse_operand(parser);
|
|
while (true) {
|
|
if (current_is(parser, TT(Dot))) {
|
|
step(parser);
|
|
if (!current_is(parser, TT(Id))) {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected id"),
|
|
parser->current.pos
|
|
);
|
|
scirpt_ast_expr_delete(subject);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
HeapString value = heapstring_from(
|
|
&parser->text[parser->current.pos.index], parser->current.length
|
|
);
|
|
subject = alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Member),
|
|
.pos = pos,
|
|
.member = (ScirptAstExprMember) {
|
|
.subject = subject,
|
|
.value = value,
|
|
},
|
|
});
|
|
} else if (current_is(parser, TT(LParen))) {
|
|
step(parser);
|
|
ScirptAstExprArray args;
|
|
scirpt_ast_expr_array_construct(&args);
|
|
if (!done(parser) && parser->current.type != TT(RParen)) {
|
|
ScirptAstExpr* value = scirpt_parser_parse_operand(parser);
|
|
scirpt_ast_expr_array_append(&args, value);
|
|
while (current_is(parser, TT(Comma))) {
|
|
step(parser);
|
|
if (done(parser) || parser->current.type == TT(RParen))
|
|
break;
|
|
ScirptAstExpr* value = scirpt_parser_parse_operand(parser);
|
|
scirpt_ast_expr_array_append(&args, value);
|
|
}
|
|
}
|
|
if (!current_is(parser, TT(RParen))) {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected ')'"),
|
|
parser->current.pos
|
|
);
|
|
scirpt_ast_expr_delete(subject);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
step(parser);
|
|
subject = alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Call),
|
|
.pos = pos,
|
|
.call = (ScirptAstExprCall) {
|
|
.subject = subject,
|
|
.args = args,
|
|
},
|
|
});
|
|
} else if (current_is(parser, TT(LBracket))) {
|
|
step(parser);
|
|
ScirptAstExpr* value = scirpt_parser_parse_operand(parser);
|
|
if (!current_is(parser, TT(RBracket))) {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected ']'"),
|
|
parser->current.pos
|
|
);
|
|
scirpt_ast_expr_delete(subject);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
step(parser);
|
|
subject = alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Index),
|
|
.pos = pos,
|
|
.index = (ScirptAstExprIndex) {
|
|
.subject = subject,
|
|
.value = value,
|
|
},
|
|
});
|
|
} else {
|
|
break;
|
|
}
|
|
}
|
|
return subject;
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_operand(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
switch (parser->current.type) {
|
|
case TT(Id): {
|
|
HeapString value = heapstring_from(
|
|
&parser->text[parser->current.pos.index], parser->current.length
|
|
);
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Id),
|
|
.pos = pos,
|
|
.id_value = value,
|
|
});
|
|
}
|
|
case TT(Int):
|
|
return scirpt_parser_parse_int_or_float(parser);
|
|
case TT(String):
|
|
return scirpt_parser_parse_string(parser);
|
|
case TT(Null):
|
|
return step_alloc_expr(
|
|
parser,
|
|
(ScirptAstExpr) {
|
|
.type = AET(Null),
|
|
.pos = pos,
|
|
}
|
|
);
|
|
case TT(False):
|
|
return step_alloc_expr(
|
|
parser,
|
|
(ScirptAstExpr) {
|
|
.type = AET(Bool),
|
|
.pos = pos,
|
|
.bool_value = false,
|
|
}
|
|
);
|
|
case TT(True):
|
|
return step_alloc_expr(
|
|
parser,
|
|
(ScirptAstExpr) {
|
|
.type = AET(Bool),
|
|
.pos = pos,
|
|
.bool_value = true,
|
|
}
|
|
);
|
|
case TT(LBrace):
|
|
return scirpt_parser_parse_block(parser);
|
|
case TT(If):
|
|
return scirpt_parser_parse_if(parser);
|
|
case TT(Eof): {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected value, got Eof"),
|
|
parser->current.pos
|
|
);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
case TT(InvalidChar): {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("invalid char"),
|
|
parser->current.pos
|
|
);
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
case TT(MalformedComment): {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("malformed comment"),
|
|
parser->current.pos
|
|
);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
case TT(MalformedString): {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("malformed string"),
|
|
parser->current.pos
|
|
);
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
default: {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected value"),
|
|
parser->current.pos
|
|
);
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_int_or_float(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
HeapString int_string = heapstring_from(
|
|
&parser->text[parser->current.pos.index], parser->current.length
|
|
);
|
|
step(parser);
|
|
if (current_is(parser, TT(Decimals))) {
|
|
HeapString float_string = heapstring_from(
|
|
&parser->text[parser->current.pos.index], parser->current.length
|
|
);
|
|
step(parser);
|
|
StringBuilder builder;
|
|
stringbuilder_construct(&builder);
|
|
for (size_t i = 0; i < int_string.length; ++i)
|
|
stringbuilder_append(&builder, int_string.data[i]);
|
|
for (size_t i = 0; i < float_string.length; ++i)
|
|
stringbuilder_append(&builder, float_string.data[i]);
|
|
heapstring_destroy(&int_string);
|
|
heapstring_destroy(&float_string);
|
|
HeapString value_string = stringbuilder_build(&builder);
|
|
stringbuilder_destroy(&builder);
|
|
double value = atof(value_string.data);
|
|
heapstring_destroy(&value_string);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Float),
|
|
.pos = pos,
|
|
.float_value = value,
|
|
});
|
|
} else {
|
|
int64_t value = atoll(int_string.data);
|
|
heapstring_destroy(&int_string);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Int),
|
|
.pos = pos,
|
|
.int_value = value,
|
|
});
|
|
}
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_string(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
UnescapeStringResult result = common_unescape_string((StringView
|
|
) { .data = &parser->text[parser->current.pos.index],
|
|
.length = parser->current.length - 2 });
|
|
if (!result.ok) {
|
|
error(parser, result.error, parser->current.pos);
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
});
|
|
}
|
|
HeapString value = result.value;
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(String),
|
|
.pos = pos,
|
|
.string_value = value,
|
|
});
|
|
}
|
|
|
|
static inline bool requires_semicolon(ScirptAstExprType type)
|
|
{
|
|
switch (type) {
|
|
default:
|
|
return false;
|
|
}
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_block(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
step(parser);
|
|
ScirptAstExprArray statements;
|
|
scirpt_ast_expr_array_construct(&statements);
|
|
while (!done(parser) && parser->current.type != TT(RBrace)) {
|
|
ScirptAstExpr* statement = scirpt_parser_parse_statement(parser);
|
|
scirpt_ast_expr_array_append(&statements, statement);
|
|
if (current_is(parser, TT(Semicolon))) {
|
|
step(parser);
|
|
while (current_is(parser, TT(Semicolon)))
|
|
step(parser);
|
|
} else {
|
|
if (requires_semicolon(statement->type)) {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("';' required"),
|
|
parser->current.pos
|
|
);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (!current_is(parser, TT(RBrace))) {
|
|
error(
|
|
parser, heapstring_from_cstring("expected '}'"), parser->current.pos
|
|
);
|
|
// TODO clean up statements
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
.pos = pos,
|
|
});
|
|
}
|
|
step(parser);
|
|
return alloc_expr((ScirptAstExpr){
|
|
.type = AET(Block),
|
|
.pos = pos,
|
|
.block = (ScirptAstExprBlock) {
|
|
.statements = statements,
|
|
},
|
|
});
|
|
}
|
|
|
|
ScirptAstExpr* scirpt_parser_parse_if(ScirptParser* parser)
|
|
{
|
|
ScirptPosition pos = parser->current.pos;
|
|
step(parser);
|
|
ScirptAstExpr* condition = scirpt_parser_parse_expr(parser);
|
|
if (!current_is(parser, TT(LBrace))) {
|
|
error(
|
|
parser, heapstring_from_cstring("expected '{'"), parser->current.pos
|
|
);
|
|
scirpt_ast_expr_delete(condition);
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(Error),
|
|
});
|
|
}
|
|
ScirptAstExpr* truthy = scirpt_parser_parse_expr(parser);
|
|
ScirptAstExpr* falsy = NULL;
|
|
if (current_is(parser, TT(Else))) {
|
|
step(parser);
|
|
if (!current_is(parser, TT(LBrace))) {
|
|
error(
|
|
parser,
|
|
heapstring_from_cstring("expected '{'"),
|
|
parser->current.pos
|
|
);
|
|
scirpt_ast_expr_delete(condition);
|
|
scirpt_ast_expr_delete(truthy);
|
|
}
|
|
falsy = scirpt_parser_parse_expr(parser);
|
|
}
|
|
return alloc_expr((ScirptAstExpr) {
|
|
.type = AET(If),
|
|
.pos = pos,
|
|
.if_expr = (ScirptAstExprIf) {
|
|
.condition = condition,
|
|
.truthy = truthy,
|
|
.falsy = falsy,
|
|
},
|
|
});
|
|
}
|
|
|
|
void scirpt_parser_error(
|
|
ScirptParser* parser, HeapString message, ScirptPosition pos
|
|
)
|
|
{
|
|
if (parser->ok)
|
|
parser->ok = false;
|
|
scirpt_parser_error_array_append(
|
|
&parser->errors,
|
|
(ScirptParserError) {
|
|
.message = message,
|
|
.pos = pos,
|
|
}
|
|
);
|
|
}
|
|
|
|
void scirpt_parser_step(ScirptParser* parser)
|
|
{
|
|
parser->current = scirpt_lexer_next(parser->lexer);
|
|
}
|
|
|
|
bool scirpt_parser_current_is(const ScirptParser* parser, ScirptTokenType type)
|
|
{
|
|
return parser->current.type == type;
|
|
}
|
|
|
|
bool scirpt_parser_done(const ScirptParser* parser)
|
|
{
|
|
return parser->current.type == TT(Eof);
|
|
}
|