#include "scirpt/parser.h" #include "common/result.h" #include "common/string.h" #include "common/string_array.h" #include "parser.h" #include "scirpt/ast.h" #include "scirpt/lexer.h" #include "scirpt/position.h" #include "scirpt/token.h" #include #include #include #include #define TT(type) ScirptTokenType##type #define AET(type) ScirptExprType##type void scirpt_parser_error_destroy(ScirptParserError* error) { heapstring_destroy(&error->message); } static inline ScirptExpr* alloc_expr(ScirptExpr data) { ScirptExpr* expr = malloc(sizeof(ScirptExpr)); *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 ScirptExpr* step_alloc_expr(ScirptParser* parser, ScirptExpr 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) { scirpt_parser_destroy(parser); free(parser); } ScirptExpr* scirpt_parser_next(ScirptParser* parser) { if (done(parser)) { return alloc_expr((ScirptExpr) { .type = AET(Eof), }); } else if (parser->current.type == TT(Semicolon)) { step(parser); while (current_is(parser, TT(Semicolon))) step(parser); return scirpt_parser_next(parser); } 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) { for (size_t i = 0; i < parser->errors.length; ++i) scirpt_parser_error_destroy(&parser->errors.data[i]); scirpt_parser_error_array_construct(&parser->errors); } ScirptExpr* scirpt_parser_parse_statement(ScirptParser* parser) { switch (parser->current.type) { case TT(If): return scirpt_parser_parse_if(parser); case TT(Loop): return scirpt_parser_parse_loop(parser); case TT(While): return scirpt_parser_parse_while(parser); case TT(For): return scirpt_parser_parse_for(parser); case TT(Let): return scirpt_parser_parse_let(parser); case TT(Fn): return scirpt_parser_parse_function(parser); case TT(Return): return scirpt_parser_parse_return(parser); case TT(Break): return scirpt_parser_parse_break(parser); default: return scirpt_parser_parse_assign(parser); } } static inline ScirptExpr* assign( ScirptExprAssignType type, ScirptExpr* subject, ScirptExpr* value, ScirptPosition pos ) { return alloc_expr((ScirptExpr) { .type = AET(Assign), .pos = pos, .assign = (ScirptExprAssign) { .type = type, .subject = subject, .value = value, }, }); } ScirptExpr* scirpt_parser_parse_assign(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* subject = scirpt_parser_parse_expr(parser); if (current_is(parser, TT(Equal))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeEqual, subject, value, pos); } else if (current_is(parser, TT(PlusEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeAdd, subject, value, pos); } else if (current_is(parser, TT(MinusEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeSubtract, subject, value, pos); } else if (current_is(parser, TT(AsteriskEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeMultiply, subject, value, pos); } else if (current_is(parser, TT(SlashEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeDivide, subject, value, pos); } else if (current_is(parser, TT(PercentEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeModulo, subject, value, pos); } else if (current_is(parser, TT(AsteriskAsteriskEqual))) { step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return assign(ScirptExprAssignTypeExponent, subject, value, pos); } else { return subject; } } ScirptExpr* scirpt_parser_parse_expr(ScirptParser* parser) { return scirpt_parser_parse_or(parser); } static inline ScirptExpr* binary( ScirptExprBinaryType type, ScirptExpr* left, ScirptExpr* right, ScirptPosition pos ) { return alloc_expr((ScirptExpr) { .type = AET(Binary), .pos = pos, .binary = (ScirptExprBinary) { .type = type, .left = left, .right = right, }, }); } ScirptExpr* scirpt_parser_parse_or(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_and(parser); while (!done(parser)) { if (current_is(parser, TT(Or))) { step(parser); ScirptExpr* right = scirpt_parser_parse_and(parser); left = binary(ScirptExprBinaryTypeOr, left, right, pos); } else { break; } } return left; } ScirptExpr* scirpt_parser_parse_and(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_equality(parser); while (!done(parser)) { if (current_is(parser, TT(And))) { step(parser); ScirptExpr* right = scirpt_parser_parse_equality(parser); left = binary(ScirptExprBinaryTypeAnd, left, right, pos); } else { break; } } return left; } ScirptExpr* scirpt_parser_parse_equality(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_comparison(parser); while (!done(parser)) { if (current_is(parser, TT(EqualEqual))) { step(parser); ScirptExpr* right = scirpt_parser_parse_comparison(parser); left = binary(ScirptExprBinaryTypeEqual, left, right, pos); } else if (current_is(parser, TT(ExclamationEqual))) { step(parser); ScirptExpr* right = scirpt_parser_parse_comparison(parser); left = binary(ScirptExprBinaryTypeInequal, left, right, pos); } else { break; } } return left; } ScirptExpr* scirpt_parser_parse_comparison(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_add_subtract(parser); if (current_is(parser, TT(Lt))) { step(parser); ScirptExpr* right = scirpt_parser_parse_add_subtract(parser); return binary(ScirptExprBinaryTypeLt, left, right, pos); } else if (current_is(parser, TT(Gt))) { step(parser); ScirptExpr* right = scirpt_parser_parse_add_subtract(parser); return binary(ScirptExprBinaryTypeGt, left, right, pos); } else if (current_is(parser, TT(LtEqual))) { step(parser); ScirptExpr* right = scirpt_parser_parse_add_subtract(parser); return binary(ScirptExprBinaryTypeLtEqual, left, right, pos); } else if (current_is(parser, TT(GtEqual))) { step(parser); ScirptExpr* right = scirpt_parser_parse_add_subtract(parser); return binary(ScirptExprBinaryTypeGtEqual, left, right, pos); } else if (current_is(parser, TT(In))) { step(parser); ScirptExpr* right = scirpt_parser_parse_add_subtract(parser); return binary(ScirptExprBinaryTypeIn, left, right, pos); } else { return left; } } ScirptExpr* scirpt_parser_parse_add_subtract(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_multiply_divide_modulo(parser); while (!done(parser)) { if (current_is(parser, TT(Plus))) { step(parser); ScirptExpr* right = scirpt_parser_parse_multiply_divide_modulo(parser); left = binary(ScirptExprBinaryTypeAdd, left, right, pos); } else if (current_is(parser, TT(Minus))) { step(parser); ScirptExpr* right = scirpt_parser_parse_multiply_divide_modulo(parser); left = binary(ScirptExprBinaryTypeSubtract, left, right, pos); } else { break; } } return left; } ScirptExpr* scirpt_parser_parse_multiply_divide_modulo(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_negate(parser); while (!done(parser)) { if (current_is(parser, TT(Asterisk))) { step(parser); ScirptExpr* right = scirpt_parser_parse_negate(parser); left = binary(ScirptExprBinaryTypeMultiply, left, right, pos); } else if (current_is(parser, TT(Slash))) { step(parser); ScirptExpr* right = scirpt_parser_parse_negate(parser); left = binary(ScirptExprBinaryTypeDivide, left, right, pos); } else if (current_is(parser, TT(Percent))) { step(parser); ScirptExpr* right = scirpt_parser_parse_negate(parser); left = binary(ScirptExprBinaryTypeModulo, left, right, pos); } else { break; } } return left; } static inline ScirptExpr* unary(ScirptExprUnaryType type, ScirptExpr* subject, ScirptPosition pos) { return alloc_expr((ScirptExpr) { .type = AET(Unary), .pos = pos, .unary = (ScirptExprUnary) { .type = type, .subject = subject, }, }); } ScirptExpr* scirpt_parser_parse_negate(ScirptParser* parser) { if (current_is(parser, TT(Minus))) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExpr* subject = scirpt_parser_parse_negate(parser); return unary(ScirptExprUnaryTypeNegate, subject, pos); } else { return scirpt_parser_parse_exponent(parser); } } ScirptExpr* scirpt_parser_parse_exponent(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* left = scirpt_parser_parse_unary(parser); while (!done(parser)) { if (current_is(parser, TT(AsteriskAsterisk))) { step(parser); ScirptExpr* right = scirpt_parser_parse_unary(parser); left = binary(ScirptExprBinaryTypeExponent, left, right, pos); } else { break; } } return left; } ScirptExpr* scirpt_parser_parse_unary(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; if (current_is(parser, TT(Not))) { step(parser); ScirptExpr* subject = scirpt_parser_parse_unary(parser); return unary(ScirptExprUnaryTypeNot, subject, pos); } else { return scirpt_parser_parse_member_call_index_expr(parser); } } ScirptExpr* scirpt_parser_parse_member_call_index_expr(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; ScirptExpr* subject = scirpt_parser_parse_operand(parser); while (!done(parser)) { 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_expr_delete(subject); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } HeapString value = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); subject = alloc_expr((ScirptExpr) { .type = AET(Member), .pos = pos, .member = (ScirptExprMember) { .subject = subject, .value = value, }, }); } else if (current_is(parser, TT(LParen))) { step(parser); ScirptExprArray args; scirpt_expr_array_construct(&args); if (!done(parser) && parser->current.type != TT(RParen)) { ScirptExpr* value = scirpt_parser_parse_operand(parser); scirpt_expr_array_append(&args, value); while (current_is(parser, TT(Comma))) { step(parser); if (done(parser) || parser->current.type == TT(RParen)) break; ScirptExpr* value = scirpt_parser_parse_operand(parser); scirpt_expr_array_append(&args, value); } } if (!current_is(parser, TT(RParen))) { error( parser, heapstring_from_cstring("expected ')'"), parser->current.pos ); scirpt_expr_delete(subject); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } step(parser); subject = alloc_expr((ScirptExpr) { .type = AET(Call), .pos = pos, .call = (ScirptExprCall) { .subject = subject, .args = args, }, }); } else if (current_is(parser, TT(LBracket))) { step(parser); ScirptExpr* value = scirpt_parser_parse_operand(parser); if (!current_is(parser, TT(RBracket))) { error( parser, heapstring_from_cstring("expected ']'"), parser->current.pos ); scirpt_expr_delete(subject); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } step(parser); subject = alloc_expr((ScirptExpr) { .type = AET(Index), .pos = pos, .index = (ScirptExprIndex) { .subject = subject, .value = value, }, }); } else { break; } } return subject; } ScirptExpr* 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((ScirptExpr) { .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, (ScirptExpr) { .type = AET(Null), .pos = pos, } ); case TT(False): return step_alloc_expr( parser, (ScirptExpr) { .type = AET(Bool), .pos = pos, .bool_value = false, } ); case TT(True): return step_alloc_expr( parser, (ScirptExpr) { .type = AET(Bool), .pos = pos, .bool_value = true, } ); case TT(LParen): return scirpt_parser_parse_group(parser); case TT(Exclamation): return scirpt_parser_parse_object(parser); case TT(LBracket): return scirpt_parser_parse_array(parser); case TT(LBrace): return scirpt_parser_parse_block(parser); case TT(If): return scirpt_parser_parse_if(parser); case TT(Loop): return scirpt_parser_parse_loop(parser); case TT(While): return scirpt_parser_parse_while(parser); case TT(For): return scirpt_parser_parse_for(parser); case TT(Fn): return scirpt_parser_parse_lambda(parser); case TT(Eof): { error( parser, heapstring_from_cstring("expected value, got Eof"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } case TT(InvalidChar): { error( parser, heapstring_from_cstring("invalid char"), parser->current.pos ); step(parser); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } case TT(MalformedComment): { error( parser, heapstring_from_cstring("malformed comment"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } case TT(MalformedString): { error( parser, heapstring_from_cstring("malformed string"), parser->current.pos ); step(parser); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } default: { fprintf(stderr, "unhandled token %d\n", parser->current.type); error( parser, heapstring_from_cstring("expected value"), parser->current.pos ); step(parser); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } } } ScirptExpr* 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((ScirptExpr) { .type = AET(Float), .pos = pos, .float_value = value, }); } else { int64_t value = atoll(int_string.data); heapstring_destroy(&int_string); return alloc_expr((ScirptExpr) { .type = AET(Int), .pos = pos, .int_value = value, }); } } ScirptExpr* 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((ScirptExpr) { .type = AET(Error), }); } HeapString value = result.value; step(parser); return alloc_expr((ScirptExpr) { .type = AET(String), .pos = pos, .string_value = value, }); } ScirptExpr* scirpt_parser_parse_group(ScirptParser* parser) { step(parser); ScirptExpr* expr = scirpt_parser_parse_expr(parser); if (!current_is(parser, TT(RParen))) { error( parser, heapstring_from_cstring("expected ')'"), parser->current.pos ); scirpt_expr_delete(expr); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } step(parser); return expr; } ScirptExpr* scirpt_parser_parse_array(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExprArray values; scirpt_expr_array_construct(&values); if (!done(parser) && parser->current.type != TT(RBracket)) { ScirptExpr* value = scirpt_parser_parse_expr(parser); scirpt_expr_array_append(&values, value); while (current_is(parser, TT(Comma))) { step(parser); if (done(parser) || parser->current.type == TT(RBracket)) break; ScirptExpr* value = scirpt_parser_parse_expr(parser); scirpt_expr_array_append(&values, value); } } if (!current_is(parser, TT(RBracket))) { error( parser, heapstring_from_cstring("expected ']'"), parser->current.pos ); scirpt_expr_array_clean_and_destroy(&values); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } step(parser); return alloc_expr((ScirptExpr) { .type = AET(Array), .pos = pos, .array_value = values, }); } RESULT_WITH_CTORS( HeapString, HeapString, IdOrStringValueResult, id_or_string_value_result ) static inline IdOrStringValueResult id_or_string_value(const ScirptParser* parser) { if (parser->current.type == TT(Id)) { return id_or_string_value_result_ok(heapstring_from( &parser->text[parser->current.pos.index], parser->current.length )); } else if (parser->current.type == TT(String)) { UnescapeStringResult result = common_unescape_string((StringView) { .data = &parser->text[parser->current.pos.index], .length = parser->current.length - 2, }); if (result.ok) { return id_or_string_value_result_ok(result.value); } else { return id_or_string_value_result_error(result.error); } } else { return id_or_string_value_result_error( heapstring_from_cstring("expected Id or String") ); } } RESULT_WITH_CTORS( ScirptExprObjectEntry, HeapString, ParseObjectEntryResult, parse_object_entry_result ) static inline ParseObjectEntryResult parse_object_entry(ScirptParser* parser) { IdOrStringValueResult key_result = id_or_string_value(parser); if (!key_result.ok) { error(parser, heapstring_clone(&key_result.error), parser->current.pos); return parse_object_entry_result_error(key_result.error); } step(parser); if (!current_is(parser, TT(Colon))) { return parse_object_entry_result_error( heapstring_from_cstring("expected ':'") ); } step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return parse_object_entry_result_ok((ScirptExprObjectEntry) { .key = key_result.value, .value = value, }); } ScirptExpr* scirpt_parser_parse_object(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } step(parser); ScirptExprObjectEntryArray entries; scirpt_expr_object_entry_array_construct(&entries); if (!done(parser) && parser->current.type != TT(RBrace)) { ParseObjectEntryResult entry_result = parse_object_entry(parser); if (!entry_result.ok) { error(parser, entry_result.error, parser->current.pos); scirpt_expr_object_entry_array_destroy(&entries); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } scirpt_expr_object_entry_array_append(&entries, entry_result.value); while (current_is(parser, TT(Comma))) { step(parser); if (done(parser) || parser->current.type == TT(RBrace)) break; ParseObjectEntryResult entry_result = parse_object_entry(parser); if (!entry_result.ok) { error(parser, entry_result.error, parser->current.pos); for (size_t i = 0; i < entries.length; ++i) { heapstring_destroy(&entries.data[i].key); scirpt_expr_delete(entries.data[i].value); } scirpt_expr_object_entry_array_destroy(&entries); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } scirpt_expr_object_entry_array_append(&entries, entry_result.value); } } if (!current_is(parser, TT(RBrace))) { error( parser, heapstring_from_cstring("expected '}'"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error) }); } step(parser); return alloc_expr((ScirptExpr) { .type = AET(Object), .pos = pos, .object = (ScirptExprObject) { .entries = entries, }, }); } static inline bool requires_semicolon(ScirptExprType type) { switch (type) { case AET(Let): return true; default: return false; } } ScirptExpr* scirpt_parser_parse_block(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExprArray statements; scirpt_expr_array_construct(&statements); while (!done(parser) && parser->current.type != TT(RBrace)) { ScirptExpr* statement = scirpt_parser_parse_statement(parser); scirpt_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( "expected ';', required for statement" ), parser->current.pos ); } break; } } if (!current_is(parser, TT(RBrace))) { error( parser, heapstring_from_cstring("expected '}'"), parser->current.pos ); for (size_t i = 0; i < statements.length; ++i) scirpt_expr_delete(statements.data[i]); scirpt_expr_array_destroy(&statements); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } step(parser); return alloc_expr((ScirptExpr){ .type = AET(Block), .pos = pos, .block = (ScirptExprBlock) { .statements = statements, }, }); } ScirptExpr* scirpt_parser_parse_if(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExpr* condition = scirpt_parser_parse_expr(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); scirpt_expr_delete(condition); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* truthy = scirpt_parser_parse_expr(parser); ScirptExpr* 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_expr_delete(condition); scirpt_expr_delete(truthy); } falsy = scirpt_parser_parse_expr(parser); } return alloc_expr((ScirptExpr) { .type = AET(If), .pos = pos, .if_expr = (ScirptExprIf) { .condition = condition, .truthy = truthy, .falsy = falsy, }, }); } ScirptExpr* scirpt_parser_parse_loop(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* body = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(Loop), .pos = pos, .loop = (ScirptExprLoop) { .body = body, }, }); } ScirptExpr* scirpt_parser_parse_while(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExpr* condition = scirpt_parser_parse_expr(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); scirpt_expr_delete(condition); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* body = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(While), .pos = pos, .while_expr = (ScirptExprWhile) { .condition = condition, .body = body, }, }); } ScirptExpr* scirpt_parser_parse_for(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString subject = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); step(parser); if (!current_is(parser, TT(In))) { error( parser, heapstring_from_cstring("expected 'in'"), parser->current.pos ); heapstring_destroy(&subject); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); heapstring_destroy(&subject); scirpt_expr_delete(value); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* body = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(While), .pos = pos, .while_expr = (ScirptExprWhile) { .condition = value, .body = body, }, }); } ScirptExpr* scirpt_parser_parse_lambda(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(LParen))) { error( parser, heapstring_from_cstring("expected '('"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } step(parser); HeapStringArray params; heapstring_array_construct(¶ms); if (!done(parser) && parser->current.type == TT(RParen)) { step(parser); if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString param = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); heapstring_array_append(¶ms, param); step(parser); while (current_is(parser, TT(Comma))) { step(parser); if (done(parser) || parser->current.type == TT(RParen)) break; if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString param = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); heapstring_array_append(¶ms, param); step(parser); } } if (!current_is(parser, TT(RParen))) { error( parser, heapstring_from_cstring("expected ')'"), parser->current.pos ); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } step(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* body = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(Lambda), .pos = pos, .lambda = (ScirptExprLambda) { .params = params, .body = body, }, }); } ScirptExpr* scirpt_parser_parse_function(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString subject = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); step(parser); if (!current_is(parser, TT(LParen))) { error( parser, heapstring_from_cstring("expected '('"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } step(parser); HeapStringArray params; heapstring_array_construct(¶ms); if (!done(parser) && parser->current.type == TT(RParen)) { step(parser); if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); heapstring_destroy(&subject); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString param = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); heapstring_array_append(¶ms, param); step(parser); while (current_is(parser, TT(Comma))) { step(parser); if (done(parser) || parser->current.type == TT(RParen)) break; if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); heapstring_destroy(&subject); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString param = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); heapstring_array_append(¶ms, param); step(parser); } } if (!current_is(parser, TT(RParen))) { error( parser, heapstring_from_cstring("expected ')'"), parser->current.pos ); heapstring_destroy(&subject); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), .pos = pos, }); } step(parser); if (!current_is(parser, TT(LBrace))) { error( parser, heapstring_from_cstring("expected '{'"), parser->current.pos ); heapstring_destroy(&subject); for (size_t i = 0; i < params.length; ++i) heapstring_destroy(¶ms.data[i]); heapstring_array_destroy(¶ms); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } ScirptExpr* body = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(Function), .pos = pos, .function = (ScirptExprFunction) { .subject = subject, .params = params, .body = body, }, }); } ScirptExpr* scirpt_parser_parse_let(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); if (!current_is(parser, TT(Id))) { error( parser, heapstring_from_cstring("expected Id"), parser->current.pos ); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } HeapString subject = heapstring_from( &parser->text[parser->current.pos.index], parser->current.length ); step(parser); if (!current_is(parser, TT(Equal))) { error( parser, heapstring_from_cstring("expected '='"), parser->current.pos ); heapstring_destroy(&subject); return alloc_expr((ScirptExpr) { .type = AET(Error), }); } step(parser); ScirptExpr* value = scirpt_parser_parse_expr(parser); return alloc_expr((ScirptExpr) { .type = AET(Let), .pos = pos, .let = (ScirptExprLet) { .subject = subject, .value = value, }, }); } ScirptExpr* scirpt_parser_parse_return(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExpr* value = NULL; if (!done(parser) && parser->current.type != TT(Semicolon)) { value = scirpt_parser_parse_expr(parser); } return alloc_expr((ScirptExpr) { .type = AET(Return), .pos = pos, .return_statement = (ScirptExprReturn) { .value = value, }, }); } ScirptExpr* scirpt_parser_parse_break(ScirptParser* parser) { ScirptPosition pos = parser->current.pos; step(parser); ScirptExpr* value = NULL; if (!done(parser) && parser->current.type != TT(Semicolon)) { value = scirpt_parser_parse_expr(parser); } return alloc_expr((ScirptExpr) { .type = AET(Break), .pos = pos, .break_statement = (ScirptExprBreak) { .value = value, }, }); } 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); }