diff --git a/include/allocator/MemoryAllocator.h b/include/allocator/MemoryAllocator.h index b4aa578..d2fdecc 100644 --- a/include/allocator/MemoryAllocator.h +++ b/include/allocator/MemoryAllocator.h @@ -9,6 +9,7 @@ enum AllocatorAction { MEMORY_ALLOC, MEMORY_CALLOC, MEMORY_REALLOC, + MEMORY_RECALLOC, MEMORY_FREE, MEMORY_CLEANUP }; @@ -16,6 +17,7 @@ enum AllocatorAction { void* mem_alloc(size_t size); void* mem_calloc(size_t nitems, size_t size); void* mem_realloc(void *ptr, size_t size); +void* mem_recalloc(void *ptr, size_t oldNitems, size_t nitems, size_t size); void mem_free(void *ptr); void* safe_malloc(size_t size); diff --git a/include/compiler/Result.h b/include/compiler/Result.h index cb35684..4de2427 100644 --- a/include/compiler/Result.h +++ b/include/compiler/Result.h @@ -2,8 +2,8 @@ #include "internal/String.h" #include "internal/Array.h" -#ifndef PARSER_H -#define PARSER_H +#ifndef RESULT_H +#define RESULT_H enum Severity { SEVERITY_NONE = 0, @@ -12,35 +12,20 @@ enum Severity { SEVERITY_INFO }; - -/* - - • 1 - chyba v programu v rámci lexikální analýzy (chybná struktura aktuálního lexému). - • 2 - chyba v programu v rámci syntaktické analýzy (chybná syntaxe programu, chybějící hlavička, atp.). - • 3 - sémantická chyba v programu – nedefinovaná funkce, pokus o redefinice funkce. - • 4 - sémantická/běhová chyba v programu – špatný počet/typ parametrů u volání funkce či typ návratové hodnoty z funkce. - • 5 - sémantická chyba v programu – použití nedefinované proměnné. - • 6 - sémantická/běhová chyba v programu – chybějící/přebývající výraz v příkazu návratu z funkce. - • 7 - sémantická/běhová chyba typové kompatibility v aritmetických, řetězcových a - relačních výrazech. - • 8 - ostatní sémantické chyby. - • 99 - interní chyba překladače tj. neovlivněná vstupním programem (např. chyba alokace paměti atd.). - - - - */ enum ResultType { RESULT_INVALID = -3, RESULT_NO_MATCH = -2, RESULT_ASSERTION = -1, RESULT_SUCCESS = 0, - RESULT_ERROR_STATIC_LEXICAL_ANALYSIS = 1, - RESULT_ERROR_STATIC_SYNTACTIC_ANALYSIS = 2, - // TODO: Add more error types - RESULT_ERROR_SEMANTIC_FUNCTION = 3, - RESULT_ERROR_RUNTIME_UNDEFINED_VARIABLE = 5, - RESULT_ERROR_SEMANTIC_OTHER = 8, - RESULT_ERROR_INTERNAL = 99 + RESULT_ERROR_LEXICAL_ANALYSIS = 1, // Chybná struktura aktuálního lexému + RESULT_ERROR_SYNTACTIC_ANALYSIS = 2, // Chybná syntaxe programu, chybějící hlavička, atp. + RESULT_ERROR_SEMANTIC_FUNCTION_DEFINITION = 3, // Nedefinovaná funkce, pokus o redefinice funkce + RESULT_ERROR_SEMANTIC_INVALID_FUNCTION_CALL = 4, // Špatný počet/typ parametrů u volání funkce či typ návratové hodnoty z funkce + RESULT_ERROR_SEMANTIC_UNDEFINED_VARIABLE = 5, // Použití nedefinované proměnné + RESULT_ERROR_SEMANTIC_INVALID_RETURN = 6, // Chybějící/přebývající výraz v příkazu návratu z funkce + RESULT_ERROR_SEMANTIC_INVALID_TYPE = 7, // Chyba typové kompatibility v aritmetických, řetězcových a relačních výrazech + RESULT_ERROR_SEMANTIC_OTHER = 8, // Ostatní sémantické chyby + RESULT_ERROR_INTERNAL = 99 // Interní chyba překladače tj. neovlivněná vstupním programem (např. chyba alokace paměti atd.) }; typedef struct Result { diff --git a/include/compiler/lexer/LexerResult.h b/include/compiler/lexer/LexerResult.h index 8a0a301..36539da 100644 --- a/include/compiler/lexer/LexerResult.h +++ b/include/compiler/lexer/LexerResult.h @@ -35,6 +35,6 @@ LexerResult LexerResult_construct( #define LexerSuccess() LexerResult_construct(RESULT_SUCCESS, SEVERITY_NONE, NULL, NULL, NULL) #define LexerNoMatch() LexerResult_construct(RESULT_NO_MATCH, SEVERITY_NONE, NULL, NULL, NULL) -#define LexerError(message, markers) LexerResult_construct(RESULT_ERROR_STATIC_LEXICAL_ANALYSIS, SEVERITY_ERROR, message, markers, NULL) +#define LexerError(message, markers) LexerResult_construct(RESULT_ERROR_LEXICAL_ANALYSIS, SEVERITY_ERROR, message, markers, NULL) #endif diff --git a/include/compiler/lexer/Token.h b/include/compiler/lexer/Token.h index 9f8667c..add7dfd 100644 --- a/include/compiler/lexer/Token.h +++ b/include/compiler/lexer/Token.h @@ -9,6 +9,7 @@ enum TokenType { TOKEN_EOF = 1, TOKEN_CONTROL, // Forgot what this is for :( TOKEN_MARKER, // Marker in the source code (for error messages) + TOKEN_STRING_INTERPOLATION_MARKER, TOKEN_LITERAL, TOKEN_IDENTIFIER, @@ -21,6 +22,9 @@ enum TokenKind { TOKEN_DEFAULT = 0, // Literals + // This has to be at this position in the enum + // because it's being converted into another + // enum with the same values. TOKEN_STRING, TOKEN_INTEGER, TOKEN_FLOATING, @@ -67,13 +71,13 @@ enum TokenKind { enum WhitespaceType { WHITESPACE_NONE = 0, // No whitespace - // WHITESPACE_LEFT_LIMIT = 1 << 0, // BOF or EOF - WHITESPACE_LEFT_SPACE = 1 << 1, // Space, tab, or vertical tab - WHITESPACE_LEFT_NEWLINE = 1 << 2, // Line feed or carriage return + // WHITESPACE_LEFT_LIMIT = 1 << 0, // BOF or EOF + WHITESPACE_LEFT_SPACE = 1 << 1, // Space, tab, vertical tab or multi-line comment on a single line + WHITESPACE_LEFT_NEWLINE = 1 << 2, // Line feed, carriage return, multi-line comment on multiple lines or single-line comment - // WHITESPACE_RIGHT_LIMIT = 1 << 4, // BOF or EOF - WHITESPACE_RIGHT_SPACE = 1 << 5, // Space, tab, or vertical tab - WHITESPACE_RIGHT_NEWLINE = 1 << 6, // Line feed or carriage return + // WHITESPACE_RIGHT_LIMIT = 1 << 4, // BOF or EOF + WHITESPACE_RIGHT_SPACE = 1 << 5, // Space, tab, vertical tab or multi-line comment on a single line + WHITESPACE_RIGHT_NEWLINE = 1 << 6, // Line feed, carriage return, multi-line comment on multiple lines or single-line comment WHITESPACE_LEFT = WHITESPACE_LEFT_SPACE | WHITESPACE_LEFT_NEWLINE, WHITESPACE_RIGHT = WHITESPACE_RIGHT_SPACE | WHITESPACE_RIGHT_NEWLINE, @@ -83,7 +87,11 @@ enum WhitespaceType { WHITESPACE_MASK_RIGHT = /*WHITESPACE_RIGHT_LIMIT |*/ WHITESPACE_RIGHT_SPACE | WHITESPACE_RIGHT_NEWLINE }; -#define whitespace_both(whitespace) (((whitespace) & WHITESPACE_LEFT) && ((whitespace) & WHITESPACE_RIGHT)) +#define whitespace_left(whitespace) ((whitespace) & WHITESPACE_LEFT) +#define whitespace_right(whitespace) ((whitespace) & WHITESPACE_RIGHT) +#define whitespace_both(whitespace) (whitespace_left(whitespace) && whitespace_right(whitespace)) +#define whitespace_none(whitespace) ((whitespace) == WHITESPACE_NONE) +#define whitespace_consistent(whitespace) (whitespace_both(whitespace) || whitespace_none(whitespace)) #define right_to_left_whitespace(whitespace) (((whitespace) & WHITESPACE_RIGHT) >> 4) #define left_to_right_whitespace(whitespace) (((whitespace) & WHITESPACE_LEFT) << 4) diff --git a/include/compiler/parser/ASTNodes.h b/include/compiler/parser/ASTNodes.h index 0d82368..ace7516 100644 --- a/include/compiler/parser/ASTNodes.h +++ b/include/compiler/parser/ASTNodes.h @@ -2,6 +2,7 @@ #include "internal/Array.h" #include "internal/String.h" +#include "compiler/lexer/Token.h" #ifndef ASTNode_H #define ASTNode_H @@ -13,15 +14,52 @@ enum ASTNodeType { NODE_IDENTIFIER, NODE_TYPE_REFERENCE, NODE_VARIABLE_DECLARATION, + NODE_VARIABLE_DECLARATION_LIST, + NODE_VARIABLE_DECLARATOR, NODE_EXPRESSION_STATEMENT, NODE_RETURN_STATEMENT, NODE_PARAMETER, NODE_PARAMETER_LIST, NODE_FUNCTION_DECLARATION, NODE_ARGUMENT, - NODE_FUNCTION_CALL + NODE_BINARY_EXPRESSION, + NODE_UNARY_EXPRESSION, + NODE_LITERAL_EXPRESSION, + NODE_ARGUMENT_LIST, + NODE_FUNCTION_CALL, + NODE_IF_STATEMENT, + NODE_PATTERN, + NODE_CONDITION, + NODE_OPTIONAL_BINDING_CONDITION, + NODE_WHILE_STATEMENT, + NODE_ASSIGNMENT_STATEMENT }; +typedef enum OperatorType { + OPERATOR_DEFAULT = 0, + OPERATOR_PLUS, + OPERATOR_MINUS, + OPERATOR_MUL, + OPERATOR_DIV, + OPERATOR_UNWRAP, + OPERATOR_NULL_COALESCING, + OPERATOR_EQUAL, + OPERATOR_NOT_EQUAL, + OPERATOR_LESS, + OPERATOR_GREATER, + OPERATOR_LESS_EQUAL, + OPERATOR_GREATER_EQUAL +} OperatorType; + +typedef enum LiteralType { + LITERAL_INVALID = 0, + LITERAL_STRING, + LITERAL_INTEGER, + LITERAL_FLOATING, + LITERAL_BOOLEAN, + LITERAL_NIL +} LiteralType; + /* Definition of AST nodes */ @@ -53,10 +91,21 @@ typedef struct TypeReferenceASTNode { bool isNullable; } TypeReferenceASTNode; +typedef struct VariableDeclaratorASTNode { + enum ASTNodeType _type; + struct PatternASTNode *pattern; + ExpressionASTNode *initializer; +} VariableDeclaratorASTNode; + +typedef struct VariableDeclarationListASTNode { + enum ASTNodeType _type; + Array /**/ *declarators; +} VariableDeclarationListASTNode; + typedef struct VariableDeclarationASTNode { enum ASTNodeType _type; - IdentifierASTNode *id; - TypeReferenceASTNode *type; + VariableDeclarationListASTNode *declaratorList; + bool isConstant; } VariableDeclarationASTNode; typedef struct ExpressionStatementASTNode { @@ -71,10 +120,10 @@ typedef struct ReturnStatementASTNode { typedef struct ParameterASTNode { enum ASTNodeType _type; - IdentifierASTNode *id; + IdentifierASTNode *internalId; TypeReferenceASTNode *type; ExpressionASTNode *initializer; - IdentifierASTNode *externalName; + IdentifierASTNode *externalId; bool isLabeless; } ParameterASTNode; @@ -83,12 +132,6 @@ typedef struct ParameterListASTNode { Array /**/ *parameters; } ParameterListASTNode; -typedef struct ArgumentASTNode { - enum ASTNodeType _type; - ExpressionASTNode *expression; - IdentifierASTNode *label; -} ArgumentASTNode; - typedef struct FunctionDeclarationASTNode { enum ASTNodeType _type; IdentifierASTNode *id; @@ -97,35 +140,124 @@ typedef struct FunctionDeclarationASTNode { BlockASTNode *body; } FunctionDeclarationASTNode; +typedef struct ArgumentASTNode { + enum ASTNodeType _type; + ExpressionASTNode *expression; + IdentifierASTNode *label; +} ArgumentASTNode; + +typedef struct ArgumentListASTNode { + enum ASTNodeType _type; + Array /**/ *arguments; +} ArgumentListASTNode; + typedef struct FunctionCallASTNode { enum ASTNodeType _type; IdentifierASTNode *id; - Array /**/ *arguments; + ArgumentListASTNode *argumentList; } FunctionCallASTNode; +// typedef struct ExpressionASTNode { +// enum ASTNodeType _type; +// BinaryExpressionASTNode *BExpression; +// UnaryExpressionASTNode *UExpression; +// LiteralExpressionASTNode *LExpression; +// IdentifierASTNode *IExpression; +// } ExpressionASTNode; + +typedef struct BinaryExpressionASTNode { + enum ASTNodeType _type; + ExpressionASTNode *left; + ExpressionASTNode *right; + OperatorType operator; +} BinaryExpressionASTNode; + +typedef struct UnaryExpressionASTNode { + enum ASTNodeType _type; + ExpressionASTNode *argument; + OperatorType operator; + // bool isPrefix; +} UnaryExpressionASTNode; + +typedef struct LiteralExpressionASTNode { + enum ASTNodeType _type; + LiteralType type; + union TokenValue value; +} LiteralExpressionASTNode; + +typedef struct PatternASTNode { + enum ASTNodeType _type; + IdentifierASTNode *id; + TypeReferenceASTNode *type; +} PatternASTNode; + +typedef struct OptionalBindingConditionASTNode { + enum ASTNodeType _type; + PatternASTNode *pattern; + ExpressionASTNode *initializer; + bool isConstant; +} OptionalBindingConditionASTNode; + +typedef struct ConditionASTNode { + enum ASTNodeType _type; + ExpressionASTNode *expression; + OptionalBindingConditionASTNode *optionalBindingCondition; +} ConditionASTNode; + +typedef struct IfStatementASTNode { + enum ASTNodeType _type; + ConditionASTNode *condition; + BlockASTNode *body; + ASTNode /* BlockASTNode | IfStatementASTNode | null */ *alternate; +} IfStatementASTNode; + +typedef struct WhileStatementASTNode { + enum ASTNodeType _type; + ConditionASTNode *condition; + BlockASTNode *body; +} WhileStatementASTNode; + +typedef struct AssignmentStatementASTNode { + enum ASTNodeType _type; + IdentifierASTNode *id; + ExpressionASTNode *assignment; +} AssignmentStatementASTNode; + // TODO: Add more AST nodes /* Constructors for AST nodes */ -ProgramASTNode * new_ProgramASTNode(BlockASTNode *block); -BlockASTNode * new_BlockASTNode(Array *statements); -IdentifierASTNode * new_IdentifierASTNode(String *name); -TypeReferenceASTNode * new_TypeReferenceASTNode(IdentifierASTNode *id, bool isNullable); -VariableDeclarationASTNode * new_VariableDeclarationASTNode(IdentifierASTNode *id, TypeReferenceASTNode *type); -ReturnStatementASTNode * new_ReturnStatementASTNode(ExpressionASTNode *expression); -ParameterASTNode * new_ParameterASTNode(IdentifierASTNode *id, TypeReferenceASTNode *type, ExpressionASTNode *initializer, IdentifierASTNode *externalName, bool isLabeless); -ParameterListASTNode * new_ParameterListASTNode(Array *parameters); -ArgumentASTNode * new_ArgumentASTNode(ExpressionASTNode *expression, IdentifierASTNode *label); -FunctionDeclarationASTNode * new_FunctionDeclarationASTNode(IdentifierASTNode *id, ParameterListASTNode *parameterList, TypeReferenceASTNode *returnType, BlockASTNode *body); -FunctionCallASTNode * new_FunctionCallASTNode(IdentifierASTNode *id, Array *arguments); +ProgramASTNode* new_ProgramASTNode(BlockASTNode *block); +BlockASTNode* new_BlockASTNode(Array *statements); +IdentifierASTNode* new_IdentifierASTNode(String *name); +TypeReferenceASTNode* new_TypeReferenceASTNode(IdentifierASTNode *id, bool isNullable); +VariableDeclarationASTNode* new_VariableDeclarationASTNode(VariableDeclarationListASTNode *declaratorList, bool isConstant); +VariableDeclaratorASTNode* new_VariableDeclaratorASTNode(PatternASTNode *pattern, ExpressionASTNode *initializer); +VariableDeclarationListASTNode* new_VariableDeclarationListASTNode(Array *declarators); +ReturnStatementASTNode* new_ReturnStatementASTNode(ExpressionASTNode *expression); +ParameterASTNode* new_ParameterASTNode(IdentifierASTNode *internalId, TypeReferenceASTNode *type, ExpressionASTNode *initializer, IdentifierASTNode *externalId, bool isLabeless); +ParameterListASTNode* new_ParameterListASTNode(Array *parameters); +FunctionDeclarationASTNode* new_FunctionDeclarationASTNode(IdentifierASTNode *id, ParameterListASTNode *parameterList, TypeReferenceASTNode *returnType, BlockASTNode *body); +BinaryExpressionASTNode* new_BinaryExpressionASTNode(ExpressionASTNode *left, ExpressionASTNode *right, OperatorType operator); +UnaryExpressionASTNode* new_UnaryExpressionASTNode(ExpressionASTNode *argument, OperatorType operator /*, bool isPrefix*/); +LiteralExpressionASTNode* new_LiteralExpressionASTNode(LiteralType type, union TokenValue value); +ArgumentASTNode* new_ArgumentASTNode(ExpressionASTNode *expression, IdentifierASTNode *label); +ArgumentListASTNode* new_ArgumentListASTNode(Array *arguments); +FunctionCallASTNode* new_FunctionCallASTNode(IdentifierASTNode *id, ArgumentListASTNode *argumentList); +PatternASTNode* new_PatternASTNode(IdentifierASTNode *id, TypeReferenceASTNode *type); +OptionalBindingConditionASTNode* new_OptionalBindingConditionASTNode(PatternASTNode *pattern, ExpressionASTNode *initializer, bool isConstant); +ConditionASTNode* new_ConditionASTNode(ExpressionASTNode *expression, OptionalBindingConditionASTNode *optionalBindingCondition); +IfStatementASTNode* new_IfStatementASTNode(ConditionASTNode *condition, BlockASTNode *body, ASTNode *alternate); +WhileStatementASTNode* new_WhileStatementASTNode(ConditionASTNode *condition, BlockASTNode *body); +AssignmentStatementASTNode* new_AssignmentStatementASTNode(IdentifierASTNode *id, ExpressionASTNode *assignment); // TODO: Add more AST node constructors /* Other public functions */ -ASTNode * ASTNode_alloc(size_t size, enum ASTNodeType type); +ASTNode* ASTNode_alloc(size_t size, enum ASTNodeType type); void ASTNode_free(ASTNode *node); void ASTNode_print(ASTNode *node); diff --git a/include/compiler/parser/ExpressionParser.h b/include/compiler/parser/ExpressionParser.h new file mode 100644 index 0000000..2021ff1 --- /dev/null +++ b/include/compiler/parser/ExpressionParser.h @@ -0,0 +1,43 @@ +#include "compiler/lexer/Token.h" +#include "compiler/parser/ASTNodes.h" + +#ifndef EXPRESSIONS_H +#define EXPRESSIONS_H + +enum PrecTableRelation { + S, // Shift + R, // Reduce + E, // Equal + X // Error +}; + +enum PrecTableIndex { + I_ADDITIVE, + I_MULTIPLICATIVE, + I_UNWRAP_OP, + I_NIL_COALES, + I_REL_OP, + I_ID, + I_LEFT_PAREN, + I_RIGHT_PAREN, + I_DOLLAR +}; + +typedef enum { + S_BOTTOM, + S_STOP, + S_TERMINAL, + S_NONTERMINAL +}StackItemType; + +typedef struct StackItem { + Token *token; + StackItemType Stype; + ExpressionASTNode *node; +} StackItem; + +StackItem* Expr_getTopTerminal(Array *stack); +void Expr_pushAfterTopTerminal(Array *stack); +StackItem* Expr_performReduction(Array *stack); + +#endif diff --git a/include/compiler/parser/Parser.h b/include/compiler/parser/Parser.h index cda1620..396aa6b 100644 --- a/include/compiler/parser/Parser.h +++ b/include/compiler/parser/Parser.h @@ -4,13 +4,18 @@ #include "compiler/lexer/Lexer.h" #include "compiler/lexer/Token.h" +#ifndef PARSER_H +#define PARSER_H + // TODO: Symbol table management typedef struct Parser { Lexer *lexer; // SymbolTable *symbolTable; } Parser; -void Parser_constructor(Parser *parser); +void Parser_constructor(Parser *parser, Lexer *lexer); void Parser_destructor(Parser *parser); void Parser_setLexer(Parser *parser, Lexer *lexer); ParserResult Parser_parse(Parser *parser); + +#endif diff --git a/include/compiler/parser/ParserResult.h b/include/compiler/parser/ParserResult.h index 845bcfc..4502d90 100644 --- a/include/compiler/parser/ParserResult.h +++ b/include/compiler/parser/ParserResult.h @@ -35,7 +35,7 @@ ParserResult ParserResult_construct( #define ParserSuccess(node) ParserResult_construct(RESULT_SUCCESS, SEVERITY_NONE, NULL, NULL, (ASTNode*)node) #define ParserNoMatch() ParserResult_construct(RESULT_NO_MATCH, SEVERITY_NONE, NULL, NULL, NULL) -#define ParserError(message, markers) ParserResult_construct(RESULT_ERROR_STATIC_SYNTACTIC_ANALYSIS, SEVERITY_ERROR, message, markers, NULL) -#define LexerToParserError(lexerResult) ParserResult_construct(RESULT_ERROR_STATIC_SYNTACTIC_ANALYSIS, SEVERITY_ERROR, lexerResult.message, lexerResult.markers, NULL) +#define ParserError(message, markers) ParserResult_construct(RESULT_ERROR_SYNTACTIC_ANALYSIS, SEVERITY_ERROR, message, markers, NULL) +#define LexerToParserError(lexerResult) ParserResult_construct(lexerResult.type, lexerResult.severity, lexerResult.message, lexerResult.markers, NULL) #endif diff --git a/src/allocator/MemoryAllocator.c b/src/allocator/MemoryAllocator.c index 88a4baa..c2ce89c 100644 --- a/src/allocator/MemoryAllocator.c +++ b/src/allocator/MemoryAllocator.c @@ -37,7 +37,7 @@ void safe_free(void *ptr) { // Private -void* Allocator_memoryAction(void *ptr, size_t nitems, size_t size, enum AllocatorAction action) { +void* Allocator_memoryAction(void *ptr, size_t oldNitems, size_t nitems, size_t size, enum AllocatorAction action) { // Use static variable to avoid globals (has the same effect as a global variable tho, but not mentioned in the instructions ;) ) static PointerSet *set = NULL; @@ -103,6 +103,38 @@ void* Allocator_memoryAction(void *ptr, size_t nitems, size_t size, enum Allocat return newPtr; } break; + case MEMORY_RECALLOC: { + // assert(ptr != NULL); // realloc with NULL pointer acts like a malloc + assertf(size > 0, PREFIX "recalloc: Cannot allocate 0 bytes"); + + #ifdef ALLOCATOR_USE_DEFAULT + assertf(size < 0, PREFIX "recalloc: There's no recalloc implementation in the default allocator"); + #endif + + ptr && assertf(PointerSet_has(set, ptr), PREFIX "recalloc: Provided pointer has not been allocated by this allocator (Maybe used 'malloc()' instead of 'mem_alloc()'?)"); + + // Remove the pointer from the set + PointerSet_remove(set, ptr); + + // Allocate new zero-initialized memory + void *newPtr = safe_calloc(nitems, size); + + // Copy the data when the old pointer is not NULL + if(ptr) { + // Copy the data from the old pointer to the new one + memcpy(newPtr, ptr, (oldNitems > nitems ? nitems : oldNitems) * size); + + // Free the old pointer + safe_free(ptr); + } + + // Add the pointer to the set + PointerSet_add(set, newPtr); + + // Return the new pointer + return newPtr; + } break; + case MEMORY_FREE: { assertf(ptr != NULL, PREFIX "free: Cannot free NULL pointer"); @@ -136,23 +168,27 @@ void* Allocator_memoryAction(void *ptr, size_t nitems, size_t size, enum Allocat void Allocator_cleanup() { - Allocator_memoryAction(NULL, 0, 0, MEMORY_CLEANUP); + Allocator_memoryAction(NULL, 0, 0, 0, MEMORY_CLEANUP); } void* mem_alloc(size_t size) { - return Allocator_memoryAction(NULL, 0, size, MEMORY_ALLOC); + return Allocator_memoryAction(NULL, 0, 0, size, MEMORY_ALLOC); } void* mem_calloc(size_t nitems, size_t size) { - return Allocator_memoryAction(NULL, nitems, size, MEMORY_CALLOC); + return Allocator_memoryAction(NULL, 0, nitems, size, MEMORY_CALLOC); } void* mem_realloc(void *ptr, size_t size) { - return Allocator_memoryAction(ptr, 0, size, MEMORY_REALLOC); + return Allocator_memoryAction(ptr, 0, 0, size, MEMORY_REALLOC); +} + +void* mem_recalloc(void *ptr, size_t oldNitems, size_t nitems, size_t size) { + return Allocator_memoryAction(ptr, oldNitems, nitems, size, MEMORY_RECALLOC); } void mem_free(void *ptr) { - Allocator_memoryAction(ptr, 0, 0, MEMORY_FREE); + Allocator_memoryAction(ptr, 0, 0, 0, MEMORY_FREE); } #undef PREFIX diff --git a/src/compiler/lexer/Lexer.c b/src/compiler/lexer/Lexer.c index a8016d0..094fde8 100644 --- a/src/compiler/lexer/Lexer.c +++ b/src/compiler/lexer/Lexer.c @@ -7,7 +7,7 @@ #include "inspector.h" #include "assertf.h" -char __Lexer_resolveEscapedChar(char ch); +bool __Lexer_resolveEscapedChar(char ch, char *out); LexerResult __Lexer_tokenizeWhitespace(Lexer *lexer); LexerResult __Lexer_tokenizeSpace(Lexer *lexer); @@ -357,6 +357,7 @@ LexerResult Lexer_tokenizeNextToken(Lexer *lexer) { return LexerSuccess(); } // Match string literals + // TODO: Allow only double-quoted string literals else if(ch == '"' || ch == '\'') { return __Lexer_tokenizeString(lexer); } @@ -496,24 +497,180 @@ LexerResult Lexer_tokenize(Lexer *lexer, char *source) { return LexerSuccess(); } -char __Lexer_resolveEscapedChar(char ch) { +LexerResult __Lexer_tokenizeUntilStringInterpolationTerminator(Lexer *lexer) { + if(!lexer) return LexerNoMatch(); + if(!lexer->currentChar) return LexerNoMatch(); + + // Keep track of the depth in case of nested parentheses + // 1 - the initial string interpolation parenthesis to match with the closing one + size_t depth = 1; + + // While there are tokens to process + LexerResult result = LexerSuccess(); + // while((result = Lexer_tokenizeNextToken(lexer)).token && result.token->type != TOKEN_EOF) { + // if(result.token->kind == TOKEN_LEFT_PAREN) { + // depth++; + // } else if(result.token->kind == TOKEN_RIGHT_PAREN) { + // depth--; + + // if(depth == 0) break; + // } + // } + + do { + result = Lexer_tokenizeNextToken(lexer); + if(!result.success) return result; + + // Get the token at the top of the token stream + Token *token = Array_get(lexer->tokens, -1); + + // If the token is an EOF, the interpolation is not terminated + if(token->type == TOKEN_EOF) return LexerError( + String_fromFormat("cannot find ')' to match opening '(' in string interpolation"), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + lexer->currentChar - 1, + lexer->currentChar, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); + + // Sort out the parentheses + // We must NOT consume the closing parentheses, because of potentionally consuming + // whitespace directly after the closing parenthesis which is part of the string, + // so instead we will just check for the current char + if(*lexer->currentChar == '(') { + depth++; + } else if(*lexer->currentChar == ')') { + depth--; + } + } while(depth != 0); + + // Consume the closing parenthesis + Lexer_advance(lexer); + + return LexerSuccess(); +} + +bool __Lexer_resolveEscapedChar(char ch, char *out) { switch(ch) { - case '0': return '\0'; - case 'a': return '\a'; - case 'b': return '\b'; - case 'f': return '\f'; - case 'n': return '\n'; - case 'r': return '\r'; - case 't': return '\t'; - case 'v': return '\v'; - case '\\': return '\\'; - case '\'': return '\''; - case '"': return '"'; - case 'd': return 'd'; - case 'x': return 'x'; - case 'u': return 'u'; - default: return '\0'; + case '0': *out = '\0'; break; + case 'n': *out = '\n'; break; + case 'r': *out = '\r'; break; + case 't': *out = '\t'; break; + case '\\': *out = '\\'; break; + case '\'': *out = '\''; break; + case '"': *out = '"'; break; + default: return false; } + + return true; +} + +LexerResult __Lexer_parseUnicodeEscapeSequence(Lexer *lexer, char *out) { + // Consume the opening brace + if(!Lexer_match(lexer, "{")) return LexerError( + String_fromFormat("expected hexadecimal code in braces after unicode escape"), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + lexer->currentChar, + lexer->currentChar + 1, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); + + // Parse the hexadecimal code + char *start = lexer->currentChar; + char *end = NULL; + long code = strtol(lexer->currentChar, &end, 16); + + // Check if the code is valid + if(!end || code < 0 || code > 0x10FFFF) return LexerError( + String_fromFormat("invalid unicode scalar '%d'", code), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + start, + end, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); + + // Check for length of the code + size_t length = end - lexer->currentChar; + if(length < 1 || length > 8) return LexerError( + String_fromFormat("\\u{...} escape sequence expects between 1 and 8 hex digits"), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + start, + end, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); + + // Consume the hexadecimal code + lexer->currentChar = end; + + // Check for the closing brace + if(!Lexer_match(lexer, "}")) return LexerError( + String_fromFormat("expected closing brace '}' after unicode escape"), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + lexer->currentChar, + lexer->currentChar + 1, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); + + // Write the code to the output + // NOTICE: This will only work for values in range 0-255 + *out = code; + + return LexerSuccess(); } // Swift multiline string literals @@ -563,7 +720,7 @@ LexerResult __Lexer_tokenizeString(Lexer *lexer) { // Match string while(ch != quote) { // Handle unterminated string literals - if(ch == '\0') return LexerError( + if(ch == '\0' || ch == '\n') return LexerError( String_fromFormat("unterminated string literal"), Array_fromArgs( 1, @@ -582,15 +739,119 @@ LexerResult __Lexer_tokenizeString(Lexer *lexer) { ) ); + // TODO: Handle multiline string literals + // This consumes two characters, so in case of `\`, both backslash // and the quote are consumed and therefore the loop will not terminate if(ch == '\\') { - char escaped = Lexer_advance(lexer); - // TODO: Add support for unicode and hex escapes according to the language specification - ch = __Lexer_resolveEscapedChar(escaped); - } + // Get the character to escape (consume the backslash) + char toEscape = Lexer_advance(lexer); + + // Pick the escaping strategy + if(toEscape == 'u') { + // TODO: Add support for unicode and hex escapes according to the language specification + // Consume the 'u' + Lexer_advance(lexer); + + char escaped = '\0'; + LexerResult res = __Lexer_parseUnicodeEscapeSequence(lexer, &escaped); + + if(!res.success) return res; + + String_appendChar(string, escaped); + lexer->currentChar--; // Go back one character + } else if(toEscape == '(') { + // TODO: Add escape sequences for interpolation + + // Finish the current string literal + { + TextRange range; + TextRange_constructor(&range, start, lexer->currentChar, lexer->line, lexer->column); + + fetch_next_whitespace(lexer); + + // Create a token + Token *token = Token_alloc(TOKEN_LITERAL, TOKEN_STRING, __wh_bit, range, (union TokenValue){.string = string}); + assertf(token != NULL); + + // Add the token to the array + Array_push(lexer->tokens, token); + } + + // Add string interpolation marker + { + TextRange range; + TextRange_constructor(&range, lexer->currentChar, lexer->currentChar, lexer->line, lexer->column); + + fetch_next_whitespace(lexer); + + // Create a token + Token *token = Token_alloc(TOKEN_STRING_INTERPOLATION_MARKER, TOKEN_DEFAULT, __wh_bit, range, (union TokenValue){0}); + assertf(token != NULL); + + // Add the token to the array + Array_push(lexer->tokens, token); + } + + // Tokenize the interpolated expression + { + // Consume the opening paren + Lexer_advance(lexer); + + LexerResult res = __Lexer_tokenizeUntilStringInterpolationTerminator(lexer); + if(!res.success) return res; + + lexer->currentChar--; // Go back one character + } + + // Add string interpolation marker + { + TextRange range; + TextRange_constructor(&range, lexer->currentChar, lexer->currentChar, lexer->line, lexer->column); + + fetch_next_whitespace(lexer); + + // Create a token + Token *token = Token_alloc(TOKEN_STRING_INTERPOLATION_MARKER, TOKEN_DEFAULT, __wh_bit, range, (union TokenValue){0}); + assertf(token != NULL); + + // Add the token to the array + Array_push(lexer->tokens, token); + } + + // Start a new string literal + { + start = lexer->currentChar + 1; // +1 to fix the text range + string = String_alloc(""); + } + } else { + char escaped = '\0'; + bool res = __Lexer_resolveEscapedChar(toEscape, &escaped); + + if(!res) return LexerError( + String_fromFormat("invalid escape sequence '\\%s' in literal", format_char(toEscape)), + Array_fromArgs( + 1, + Token_alloc( + TOKEN_MARKER, + TOKEN_CARET, + WHITESPACE_NONE, + TextRange_construct( + lexer->currentChar - 1, + lexer->currentChar, + lexer->line, + lexer->column + ), + (union TokenValue){0} + ) + ) + ); - String_appendChar(string, ch); + String_appendChar(string, escaped); + } + } else { + String_appendChar(string, ch); + } ch = Lexer_advance(lexer); } diff --git a/src/compiler/parser/ASTNodes.c b/src/compiler/parser/ASTNodes.c index c78d02e..02441d7 100644 --- a/src/compiler/parser/ASTNodes.c +++ b/src/compiler/parser/ASTNodes.c @@ -7,7 +7,7 @@ /* Definitions of ASTNodes */ -ProgramASTNode* new_ProgramASTNode( +ProgramASTNode * new_ProgramASTNode( BlockASTNode *block ) { prepare_node_of(ProgramASTNode, NODE_PROGRAM) @@ -15,7 +15,7 @@ ProgramASTNode* new_ProgramASTNode( return node; } -BlockASTNode* new_BlockASTNode( +BlockASTNode * new_BlockASTNode( Array *statements ) { prepare_node_of(BlockASTNode, NODE_BLOCK) @@ -23,7 +23,7 @@ BlockASTNode* new_BlockASTNode( return node; } -IdentifierASTNode* new_IdentifierASTNode( +IdentifierASTNode * new_IdentifierASTNode( String *name ) { prepare_node_of(IdentifierASTNode, NODE_IDENTIFIER) @@ -31,7 +31,7 @@ IdentifierASTNode* new_IdentifierASTNode( return node; } -TypeReferenceASTNode* new_TypeReferenceASTNode( +TypeReferenceASTNode * new_TypeReferenceASTNode( IdentifierASTNode *id, bool isNullable ) { @@ -41,17 +41,36 @@ TypeReferenceASTNode* new_TypeReferenceASTNode( return node; } -VariableDeclarationASTNode* new_VariableDeclarationASTNode( - IdentifierASTNode *id, - TypeReferenceASTNode *type +VariableDeclaratorASTNode * new_VariableDeclaratorASTNode( + PatternASTNode *pattern, + ExpressionASTNode *initializer +) { + prepare_node_of(VariableDeclaratorASTNode, NODE_VARIABLE_DECLARATOR) + node->pattern = pattern; + node->initializer = initializer; + return node; +} + +VariableDeclarationListASTNode * new_VariableDeclarationListASTNode( + Array *declarators +) { + prepare_node_of(VariableDeclarationListASTNode, NODE_VARIABLE_DECLARATION_LIST) + node->declarators = declarators; + return node; +} + +VariableDeclarationASTNode * new_VariableDeclarationASTNode( + VariableDeclarationListASTNode *declaratorList, + bool isConstant ) { prepare_node_of(VariableDeclarationASTNode, NODE_VARIABLE_DECLARATION) - node->id = id; - node->type = type; + node->declaratorList = declaratorList; + node->isConstant = isConstant; return node; } -ExpressionStatementASTNode* new_ExpressionStatementASTNode( + +ExpressionStatementASTNode * new_ExpressionStatementASTNode( ExpressionASTNode *expression ) { prepare_node_of(ExpressionStatementASTNode, NODE_EXPRESSION_STATEMENT) @@ -59,7 +78,7 @@ ExpressionStatementASTNode* new_ExpressionStatementASTNode( return node; } -ReturnStatementASTNode* new_ReturnStatementASTNode( +ReturnStatementASTNode * new_ReturnStatementASTNode( ExpressionASTNode *expression ) { prepare_node_of(ReturnStatementASTNode, NODE_RETURN_STATEMENT) @@ -67,23 +86,23 @@ ReturnStatementASTNode* new_ReturnStatementASTNode( return node; } -ParameterASTNode* new_ParameterASTNode( - IdentifierASTNode *id, +ParameterASTNode * new_ParameterASTNode( + IdentifierASTNode *internalId, TypeReferenceASTNode *type, ExpressionASTNode *initializer, - IdentifierASTNode *externalName, + IdentifierASTNode *externalId, bool isLabeless ) { prepare_node_of(ParameterASTNode, NODE_PARAMETER) - node->id = id; + node->internalId = internalId; node->type = type; node->initializer = initializer; - node->externalName = externalName; + node->externalId = externalId; node->isLabeless = isLabeless; return node; } -ParameterListASTNode* new_ParameterListASTNode( +ParameterListASTNode * new_ParameterListASTNode( Array *parameters ) { prepare_node_of(ParameterListASTNode, NODE_PARAMETER_LIST) @@ -91,7 +110,21 @@ ParameterListASTNode* new_ParameterListASTNode( return node; } -ArgumentASTNode* new_ArgumentASTNode( +FunctionDeclarationASTNode * new_FunctionDeclarationASTNode( + IdentifierASTNode *id, + ParameterListASTNode *parameterList, + TypeReferenceASTNode *returnType, + BlockASTNode *block +) { + prepare_node_of(FunctionDeclarationASTNode, NODE_FUNCTION_DECLARATION) + node->id = id; + node->parameterList = parameterList; + node->returnType = returnType; + node->body = block; + return node; +} + +ArgumentASTNode * new_ArgumentASTNode( ExpressionASTNode *expression, IdentifierASTNode *label ) { @@ -101,27 +134,119 @@ ArgumentASTNode* new_ArgumentASTNode( return node; } -FunctionDeclarationASTNode* new_FunctionDeclarationASTNode( +ArgumentListASTNode * new_ArgumentListASTNode( + Array *arguments +) { + prepare_node_of(ArgumentListASTNode, NODE_ARGUMENT_LIST) + node->arguments = arguments; + return node; +} + +FunctionCallASTNode * new_FunctionCallASTNode( IdentifierASTNode *id, - ParameterListASTNode *parameterList, - TypeReferenceASTNode *returnType, - BlockASTNode *block + ArgumentListASTNode *argumentList ) { - prepare_node_of(FunctionDeclarationASTNode, NODE_FUNCTION_DECLARATION) + prepare_node_of(FunctionCallASTNode, NODE_FUNCTION_CALL) node->id = id; - node->parameterList = parameterList; - node->returnType = returnType; - node->body = block; + node->argumentList = argumentList; return node; } -FunctionCallASTNode* new_FunctionCallASTNode( +PatternASTNode * new_PatternASTNode( IdentifierASTNode *id, - Array *arguments + TypeReferenceASTNode *type ) { - prepare_node_of(FunctionCallASTNode, NODE_FUNCTION_CALL) + prepare_node_of(PatternASTNode, NODE_PATTERN) node->id = id; - node->arguments = arguments; + node->type = type; + return node; +} + +OptionalBindingConditionASTNode * new_OptionalBindingConditionASTNode( + PatternASTNode *pattern, + ExpressionASTNode *initializer, + bool isConstant +) { + prepare_node_of(OptionalBindingConditionASTNode, NODE_OPTIONAL_BINDING_CONDITION) + node->pattern = pattern; + node->initializer = initializer; + node->isConstant = isConstant; + return node; +} + +ConditionASTNode * new_ConditionASTNode( + ExpressionASTNode *expression, + OptionalBindingConditionASTNode *optionalBindingCondition +) { + prepare_node_of(ConditionASTNode, NODE_CONDITION) + node->expression = expression; + node->optionalBindingCondition = optionalBindingCondition; + return node; +} + +IfStatementASTNode * new_IfStatementASTNode( + ConditionASTNode *condition, + BlockASTNode *body, + ASTNode *alternate +) { + prepare_node_of(IfStatementASTNode, NODE_IF_STATEMENT) + node->condition = condition; + node->body = body; + node->alternate = alternate; + return node; +} + +WhileStatementASTNode * new_WhileStatementASTNode( + ConditionASTNode *condition, + BlockASTNode *body +) { + prepare_node_of(WhileStatementASTNode, NODE_WHILE_STATEMENT) + node->condition = condition; + node->body = body; + return node; +} + +AssignmentStatementASTNode * new_AssignmentStatementASTNode( + IdentifierASTNode *id, + ExpressionASTNode *assignment +) { + prepare_node_of(AssignmentStatementASTNode, NODE_ASSIGNMENT_STATEMENT) + node->id = id; + node->assignment = assignment; + return node; +} + +BinaryExpressionASTNode* new_BinaryExpressionASTNode( + ExpressionASTNode *left, + ExpressionASTNode *right, + OperatorType operator +) { + prepare_node_of(BinaryExpressionASTNode, NODE_BINARY_EXPRESSION) + node->left = left; + node->right = right; + node->operator = operator; + return node; +} + +UnaryExpressionASTNode* new_UnaryExpressionASTNode( + ExpressionASTNode *argument, + OperatorType operator + // bool IsPrefix +) { + prepare_node_of(UnaryExpressionASTNode, NODE_UNARY_EXPRESSION) + node->argument = argument; + node->operator = operator; + // node->isPrefix = isPrefix; + return node; +} + +LiteralExpressionASTNode* new_LiteralExpressionASTNode( + LiteralType type, + union TokenValue value +) { + prepare_node_of(LiteralExpressionASTNode, NODE_LITERAL_EXPRESSION) + node->type = type; + node->value = value; return node; } @@ -130,7 +255,7 @@ FunctionCallASTNode* new_FunctionCallASTNode( /* General purpose methods */ -ASTNode* ASTNode_alloc(size_t size, enum ASTNodeType type) { +ASTNode * ASTNode_alloc(size_t size, enum ASTNodeType type) { ASTNode *node = mem_alloc(size); node->_type = type; return node; diff --git a/src/compiler/parser/ExpressionParser.c b/src/compiler/parser/ExpressionParser.c new file mode 100644 index 0000000..5ff9f5f --- /dev/null +++ b/src/compiler/parser/ExpressionParser.c @@ -0,0 +1,336 @@ +#include "compiler/parser/ExpressionParser.h" + +#include + +#include "assertf.h" +// #include "compiler/lexer/Lexer.h" +#include "allocator/MemoryAllocator.h" +#include "internal/Array.h" +#include "compiler/lexer/Token.h" +#include "compiler/parser/Parser.h" +#include "compiler/parser/ASTNodes.h" + +#define TABLE_SIZE 9 +#define STACK_SIZE 20 + +int precedence_table[TABLE_SIZE][TABLE_SIZE] = { // [stack top terminal][input token] + // +-|*/| ! |??|r |i |( |) |$ + {R, S, S, R, R, S, S, R, R}, // +- + {R, R, S, R, R, S, S, R, R}, // */ + {R, R, X, R, R, X, X, R, R}, // ! + {S, S, S, S, S, S, S, R, R}, // ?? + {S, S, S, R, X, S, S, R, R}, // r (==, !=, <, >, <=, >=) + {R, R, R, R, R, X, X, R, R}, // i + {S, S, S, S, S, S, S, E, X}, // ( + {R, R, R, R, R, X, X, R, R}, // ) + {S, S, S, S, S, S, S, X, X} // $ +}; + +int Expr_getPrecTbIndex(Token *token) { + if(!token){ + return I_DOLLAR; + } + switch(token->kind) { + case TOKEN_PLUS: + case TOKEN_MINUS: + return I_ADDITIVE; + + case TOKEN_STAR: + case TOKEN_SLASH: + return I_MULTIPLICATIVE; + + case TOKEN_EXCLAMATION: + return I_UNWRAP_OP; + + case TOKEN_NULL_COALESCING: + return I_NIL_COALES; + + case TOKEN_EQUALITY: + case TOKEN_NOT_EQUALITY: + case TOKEN_LESS: + case TOKEN_GREATER: + case TOKEN_LESS_EQUAL: + case TOKEN_GREATER_EQUAL: + return I_REL_OP; + + case TOKEN_LEFT_PAREN: + return I_LEFT_PAREN; + + case TOKEN_RIGHT_PAREN: + return I_RIGHT_PAREN; + + case TOKEN_DEFAULT: + if(token->type == TOKEN_IDENTIFIER) { + return I_ID; + } + return I_DOLLAR; + + case TOKEN_STRING: + case TOKEN_INTEGER: + case TOKEN_FLOATING: + case TOKEN_NIL: + case TOKEN_BOOLEAN: + return I_ID; + + default: + return I_DOLLAR; + } +} + +StackItem* Expr_getTopTerminal(Array *stack) { + StackItem *top = NULL; + + for(size_t i = 0; i < stack->size; i++) { + top = Array_get(stack, stack->size - i - 1); + + if(top->Stype == S_TERMINAL || top->Stype == S_BOTTOM) { + return top; + } + } + + return top; +} + +void Expr_pushAfterTopTerminal(Array *stack) { + StackItem *stopReduction = mem_alloc(sizeof(StackItem)); + stopReduction->token = NULL; + stopReduction->Stype = S_STOP; + stopReduction->node = NULL; + + for(size_t i = 0; i < stack->size; i++) { + StackItem *top = Array_get(stack, stack->size - i - 1); + + if(top->Stype == S_TERMINAL ||top->Stype == S_BOTTOM) { + Array_insert(stack, (int)stack->size - i, stopReduction); + return; + } + } +} + +StackItem* Expr_performReduction(Array *stack) { + + // E -> i + if(stack->size == 1) { + StackItem *id = Array_pop(stack); + + if(id->Stype == S_TERMINAL) { + if(id->token->type == TOKEN_LITERAL) { + LiteralExpressionASTNode *literalE = new_LiteralExpressionASTNode((LiteralType)id->token->kind, id->token->value); + id->node = (ExpressionASTNode*)literalE; + id->Stype = S_NONTERMINAL; + + return id; + } + + if(id->token->type == TOKEN_IDENTIFIER) { + IdentifierASTNode *identifierE = new_IdentifierASTNode(id->token->value.string); // string or identifier? + id->node = (ExpressionASTNode*)identifierE; + id->Stype = S_NONTERMINAL; + + return id; + } + } + // two operators consecutively + else { + return NULL; + } + } + + // E -> E! + if(stack->size == 2) { + StackItem *argument = Array_pop(stack); + StackItem *operator = Array_pop(stack); + + if(operator->token->kind == TOKEN_EXCLAMATION && argument->Stype == S_NONTERMINAL) { + UnaryExpressionASTNode *unaryE = new_UnaryExpressionASTNode(argument->node, OPERATOR_UNWRAP); + operator->node = (ExpressionASTNode*)unaryE; + operator->Stype = S_NONTERMINAL; + + mem_free(argument); + + return operator; + } else { + return NULL; + } + } + + // Binary operations and parentheses + if(stack->size == 3) { + StackItem *leftOperand = Array_pop(stack); + StackItem *operator = Array_pop(stack); + StackItem *rightOperand = Array_pop(stack); + + // E -> (E) + if(operator->Stype == S_NONTERMINAL && leftOperand->token->kind == TOKEN_LEFT_PAREN && rightOperand->token->kind == TOKEN_RIGHT_PAREN) { + mem_free(leftOperand); + mem_free(rightOperand); + + return operator; + } + + enum OperatorType operatorType = 0; + if(leftOperand->Stype == S_NONTERMINAL && rightOperand->Stype == S_NONTERMINAL) + switch(operator->token->kind) { + case TOKEN_PLUS: + operatorType = OPERATOR_PLUS; + break; + case TOKEN_MINUS: + operatorType = OPERATOR_MINUS; + break; + case TOKEN_STAR: + operatorType = OPERATOR_MUL; + break; + case TOKEN_SLASH: + operatorType = OPERATOR_DIV; + break; + case TOKEN_EQUALITY: + operatorType = OPERATOR_EQUAL; + break; + case TOKEN_NOT_EQUALITY: + operatorType = OPERATOR_NOT_EQUAL; + break; + case TOKEN_LESS: + operatorType = OPERATOR_LESS; + break; + case TOKEN_GREATER: + operatorType = OPERATOR_GREATER; + break; + case TOKEN_LESS_EQUAL: + operatorType = OPERATOR_LESS_EQUAL; + break; + case TOKEN_GREATER_EQUAL: + operatorType = OPERATOR_GREATER_EQUAL; + break; + case TOKEN_NULL_COALESCING: + operatorType = OPERATOR_NULL_COALESCING; + break; + default: + break; + } + if(operatorType) { + BinaryExpressionASTNode *binaryE = new_BinaryExpressionASTNode(leftOperand->node, rightOperand->node, operatorType); + operator->node = (ExpressionASTNode*)binaryE; + operator->Stype = S_NONTERMINAL; + + mem_free(leftOperand); + mem_free(rightOperand); + + return operator; + } + } + + return NULL; +} + +bool Expr_Reduce(Array *stack, StackItem *currentToken) { + Array *reduceStack = Array_alloc(STACK_SIZE); + + if(stack->size == 1){ + return false; + } + + while((currentToken = Array_pop(stack))->Stype != S_STOP) { + if(currentToken->Stype != S_STOP) { + Array_push(reduceStack, currentToken); + } + } + + // Perform reduction and push result on stack (nonterminal) + currentToken = Expr_performReduction(reduceStack); + Array_free(reduceStack); + + if(currentToken != NULL) { + Array_push(stack, currentToken); + return true; + } else { + return false; + } +} + +ParserResult __Parser_parseExpression(Parser *parser) { + assertf(parser != NULL); + + Array *stack = Array_alloc(STACK_SIZE); + // Array *reduceStack = Array_alloc(STACK_SIZE); + // Token *token = NULL; + StackItem *bottom = mem_alloc(sizeof(StackItem)); + + bottom->Stype = S_BOTTOM; + bottom->node = NULL; + bottom->token = NULL; + Array_push(stack, bottom); + + bool reductionSuccess; + int offset = 1; + LexerResult current = Lexer_peekToken(parser->lexer, offset); + LexerResult removeFromTokenStream; + + while(true) { + if(!current.success) return LexerToParserError(current); + + StackItem *topTerminal = Expr_getTopTerminal(stack); + // topTerminal returns S_BOTTOM, which has no token, + // this token is being dereferenced in Expr_getPrecTbIndex, thus causing a segfault + + int topTerminalIndex = Expr_getPrecTbIndex(topTerminal->token); + int currentTokenIndex = Expr_getPrecTbIndex(current.token); + + enum PrecTableRelation operation = precedence_table[topTerminalIndex][currentTokenIndex]; + + StackItem *isItFinal = Array_get(stack, stack->size - 1); + if(isItFinal->Stype == S_NONTERMINAL && stack->size == 2 && operation == X) { + StackItem *finalExpression = Array_pop(stack); + bottom = Array_pop(stack); + mem_free(bottom); + Array_free(stack); + // Array_free(reduceStack); + + return ParserSuccess(finalExpression->node); + } + + StackItem *currentToken = mem_alloc(sizeof(StackItem)); + + switch(operation) { + case S: { + currentToken->Stype = S_TERMINAL; + currentToken->token = current.token; + currentToken->node = NULL; + Expr_pushAfterTopTerminal(stack); + Array_push(stack, currentToken); + + removeFromTokenStream = Lexer_nextToken(parser->lexer); + if(!(removeFromTokenStream.success)) return LexerToParserError(current); + current = Lexer_peekToken(parser->lexer, offset); + } break; + + case R: { + reductionSuccess = Expr_Reduce(stack, currentToken); + if(!reductionSuccess) { + return ParserError(String_fromFormat("Syntax error in expression"), Array_fromArgs(1, current.token)); + } + } break; + + case E: { + currentToken->Stype = S_TERMINAL; + currentToken->token = current.token; + currentToken->node = NULL; + Array_push(stack, currentToken); + + removeFromTokenStream = Lexer_nextToken(parser->lexer); + if(!(removeFromTokenStream.success)) return LexerToParserError(current); + current = Lexer_peekToken(parser->lexer, offset); + } break; + + case X: { + reductionSuccess = Expr_Reduce(stack, currentToken); + if(!reductionSuccess) { + return ParserError(String_fromFormat("Syntax error in expression"), Array_fromArgs(1, current.token)); + } + } break; + + default: {} break; + } + + } + return ParserNoMatch(); +} diff --git a/src/compiler/parser/Parser.c b/src/compiler/parser/Parser.c index b2dcf94..2fbb433 100644 --- a/src/compiler/parser/Parser.c +++ b/src/compiler/parser/Parser.c @@ -14,16 +14,32 @@ ParserResult __Parser_parseProgram(Parser *parser); ParserResult __Parser_parseBlock(Parser *parser, bool requireBraces); ParserResult __Parser_parseStatement(Parser *parser); ParserResult __Parser_parseExpression(Parser *parser); -ParserResult __Parser_parseFuncStatement(Parser *parser); ParserResult __Parser_parseTypeReference(Parser *parser); ParserResult __Parser_parseParameter(Parser *parser); ParserResult __Parser_parseParameterList(Parser *parser); +ParserResult __Parser_parseFuncStatement(Parser *parser); +ParserResult __Parser_parsePattern(Parser *parser); +ParserResult __Parser_parseOptionalBindingCondition(Parser *parser); +ParserResult __Parser_parseCondition(Parser *parser); +ParserResult __Parser_parseIfStatement(Parser *parser); +ParserResult __Parser_parseWhileStatement(Parser *parser); +ParserResult __Parser_parseReturnStatement(Parser *parser); +ParserResult __Parser_parseVariableDeclarator(Parser *parser); +ParserResult __Parser_parseVariableDeclarationList(Parser *parser); +ParserResult __Parser_parseVariableDeclarationStatement(Parser *parser, bool isConstant); +ParserResult __Parser_parseArgument(Parser *parser); +ParserResult __Parser_parseArgumentList(Parser *parser); +ParserResult __Parser_parseFunctionCallExpression(Parser *parser); +ParserResult __Parser_parseAssignmentStatement(Parser *parser); /* Definitions of public functions */ -void Parser_constructor(Parser *parser) { +void Parser_constructor(Parser *parser, Lexer *lexer) { + assertf(parser != NULL); + assertf(lexer != NULL); + // TODO: Symbol table management - parser->lexer = NULL; + parser->lexer = lexer; } void Parser_destructor(Parser *parser) { @@ -80,11 +96,36 @@ ParserResult __Parser_parseBlock(Parser *parser, bool requireBraces) { // Parse statements Array *statements = Array_alloc(0); - while(!Parser_isAtEnd(parser)) { + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + while((!requireBraces && !Parser_isAtEnd(parser)) || (requireBraces && peek.token->kind != TOKEN_RIGHT_BRACE)) { ParserResult result = __Parser_parseStatement(parser); if(!result.success) return result; Array_push(statements, result.node); + + // Check for delimiter after statement + peek = Lexer_peekToken(parser->lexer, 0); + if(!peek.success) return LexerToParserError(peek); + + // They don't want us to have semicolons :( + if(peek.token->kind == TOKEN_SEMICOLON) { + return ParserError( + String_fromFormat("';' is not supported after statement, use new line instead"), + Array_fromArgs(1, peek.token) + ); + } else if(!(peek.token->whitespace & WHITESPACE_RIGHT_NEWLINE) && !Lexer_isAtEnd(parser->lexer)) { + return ParserError( + String_fromFormat("expected new line after statement"), + Array_fromArgs(1, peek.token) + ); + } + + if(requireBraces) { + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + } } // Check for right brace @@ -113,26 +154,60 @@ ParserResult __Parser_parseStatement(Parser *parser) { if(result.token->kind == TOKEN_FUNC) { ParserResult funcResult = __Parser_parseFuncStatement(parser); if(!funcResult.success) return funcResult; + return ParserSuccess(funcResult.node); } - return ParserNoMatch(); -} + if(result.token->kind == TOKEN_IF) { + ParserResult ifResult = __Parser_parseIfStatement(parser); + if(!ifResult.success) return ifResult; + return ParserSuccess(ifResult.node); + } -ParserResult __Parser_parseExpression(Parser *parser) { - assertf(parser != NULL); + if(result.token->kind == TOKEN_WHILE) { + ParserResult whileResult = __Parser_parseWhileStatement(parser); + if(!whileResult.success) return whileResult; + return ParserSuccess(whileResult.node); + } + + if(result.token->kind == TOKEN_RETURN) { + ParserResult returnResult = __Parser_parseReturnStatement(parser); + if(!returnResult.success) return returnResult; + return ParserSuccess(returnResult.node); + } + + if(result.token->kind == TOKEN_LET || result.token->kind == TOKEN_VAR) { + bool isConstant = result.token->kind == TOKEN_LET; + ParserResult variableDeclarationResult = __Parser_parseVariableDeclarationStatement(parser, isConstant); + if(!variableDeclarationResult.success) return variableDeclarationResult; + return ParserSuccess(variableDeclarationResult.node); + } + + if(result.token->type == TOKEN_IDENTIFIER) { + LexerResult tmp = Lexer_peekToken(parser->lexer, 1); + if(!tmp.success) return LexerToParserError(tmp); + if(tmp.token->kind == TOKEN_EQUAL) { + ParserResult assignmentStatementResult = __Parser_parseAssignmentStatement(parser); + if(!assignmentStatementResult.success) return assignmentStatementResult; + return ParserSuccess(assignmentStatementResult.node); + } + } - // TODO: Add logic for parsing expressions (using LL(1) parsing) return ParserNoMatch(); } +// ParserResult __Parser_parseExpression(Parser *parser) { +// assertf(parser != NULL); + +// TODO: Add logic for parsing expressions (using LL(1) parsing) +// return ParserNoMatch(); +// } + ParserResult __Parser_parseTypeReference(Parser *parser) { // TODO: Add logic to output correct error messages assertf(parser != NULL); - - LexerResult result = Lexer_nextToken(parser->lexer); - LexerResult peek; int nullable = false; + LexerResult result = Lexer_nextToken(parser->lexer); if(!result.success) return LexerToParserError(result); if(result.token->type != TOKEN_IDENTIFIER) { @@ -141,8 +216,9 @@ ParserResult __Parser_parseTypeReference(Parser *parser) { Array_fromArgs(1, result.token)); } - peek = Lexer_peekToken(parser->lexer, 1); - if(!peek.success) return LexerToParserError(result); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); // nullable type @@ -159,16 +235,14 @@ ParserResult __Parser_parseTypeReference(Parser *parser) { return ParserSuccess(paramType); } - ParserResult __Parser_parseParameter(Parser *parser) { - // TODO: Add logic to output correct error messages - // TODO: Add expression parsing assertf(parser != NULL); bool isLabeless = false; IdentifierASTNode *paramLocalId = NULL; IdentifierASTNode *paramExternalId = NULL; ExpressionASTNode *initializer = NULL; + LexerResult peek; LexerResult result = Lexer_nextToken(parser->lexer); @@ -211,7 +285,6 @@ ParserResult __Parser_parseParameter(Parser *parser) { } // check for Type - ParserResult typeResult = __Parser_parseTypeReference(parser); if(!typeResult.success) return typeResult; @@ -219,19 +292,20 @@ ParserResult __Parser_parseParameter(Parser *parser) { // check for initializer peek = Lexer_peekToken(parser->lexer, 1); if(!peek.success) return LexerToParserError(peek); - if(peek.token->kind == TOKEN_EQUAL) { - // TODO: Add expression parsing - // : Add constructor for ExpressionASTNode - // : Expression until , or ) - initializer = NULL; + // Skip the '=' token + LexerResult tmp = Lexer_nextToken(parser->lexer); + if(!tmp.success) return LexerToParserError(result); + + ParserResult initializerResult = __Parser_parseExpression(parser); + if(!initializerResult.success) return initializerResult; + initializer = (ExpressionASTNode*)initializerResult.node; } ParameterASTNode *paramNode = new_ParameterASTNode(paramLocalId, (TypeReferenceASTNode*)typeResult.node, initializer, paramExternalId, isLabeless); return ParserSuccess(paramNode); } - ParserResult __Parser_parseParameterList(Parser *parser) { assertf(parser != NULL); @@ -247,16 +321,18 @@ ParserResult __Parser_parseParameterList(Parser *parser) { // parser parameter-list Array *parameters = Array_alloc(0); - while(true) { + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + while(peek.token->kind != TOKEN_RIGHT_PAREN) { + ParserResult paramResult = __Parser_parseParameter(parser); if(!paramResult.success) return paramResult; Array_push(parameters, (ParameterASTNode*)paramResult.node); - LexerResult peek = Lexer_peekToken(parser->lexer, 1); - - - if(!peek.success) return LexerToParserError(result); + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); if(peek.token->kind == TOKEN_COMMA) { result = Lexer_nextToken(parser->lexer); @@ -264,13 +340,12 @@ ParserResult __Parser_parseParameterList(Parser *parser) { } - peek = Lexer_peekToken(parser->lexer, 1); - if(peek.token->kind == TOKEN_RIGHT_PAREN) { - result = Lexer_nextToken(parser->lexer); - if(!result.success) return LexerToParserError(result); - break; - } } + + // consume ')' + result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + ParameterListASTNode *parameterList = new_ParameterListASTNode(parameters); return ParserSuccess(parameterList); @@ -279,12 +354,14 @@ ParserResult __Parser_parseParameterList(Parser *parser) { ParserResult __Parser_parseFuncStatement(Parser *parser) { // TODO: Symbol table management - assertf(parser != NULL); + LexerResult result = Lexer_nextToken(parser->lexer); - LexerResult peek; if(!result.success) return LexerToParserError(result); + LexerResult peek; + + if(result.token->type != TOKEN_IDENTIFIER) { return ParserError( String_fromFormat("expected identifier in function declaration"), @@ -296,6 +373,7 @@ ParserResult __Parser_parseFuncStatement(Parser *parser) { ParserResult parameterListResult = __Parser_parseParameterList(parser); if(!parameterListResult.success) return parameterListResult; + peek = Lexer_peekToken(parser->lexer, 1); if(!peek.success) return LexerToParserError(peek); @@ -313,13 +391,421 @@ ParserResult __Parser_parseFuncStatement(Parser *parser) { returnType = NULL; } + ParserResult blockResult = __Parser_parseBlock(parser, true); - // if(!blockResult.success) return blockResult; + if(!blockResult.success) return blockResult; FunctionDeclarationASTNode *func = new_FunctionDeclarationASTNode(funcId, (ParameterListASTNode*)parameterListResult.node, returnType, (BlockASTNode*)blockResult.node); return ParserSuccess(func); } +ParserResult __Parser_parsePattern(Parser *parser) { + assertf(parser != NULL); + + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + if(result.token->type != TOKEN_IDENTIFIER) { + return ParserError( + String_fromFormat("expected pattern"), + Array_fromArgs(1, result.token)); + } + + IdentifierASTNode *patternName = new_IdentifierASTNode(result.token->value.string); + TypeReferenceASTNode *type = NULL; + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + if(peek.token->kind == TOKEN_COLON) { + // skip ':' + LexerResult tmp = Lexer_nextToken(parser->lexer); + if(!tmp.success) return LexerToParserError(tmp); + + ParserResult typeResult = __Parser_parseTypeReference(parser); + if(!typeResult.success) return typeResult; + type = (TypeReferenceASTNode*)typeResult.node; + } + + PatternASTNode *pattern = new_PatternASTNode(patternName, type); + + return ParserSuccess(pattern); +} + +ParserResult __Parser_parseOptionalBindingCondition(Parser *parser) { + assertf(parser != NULL); + + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + bool isConstant = result.token->kind == TOKEN_LET; + + ParserResult patternResult = __Parser_parsePattern(parser); + if(!patternResult.success) return patternResult; + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + ExpressionASTNode *initializer = NULL; + + if(peek.token->kind == TOKEN_EQUAL) { + + // Skip the '=' token + LexerResult tmp = Lexer_nextToken(parser->lexer); + if(!tmp.success) return LexerToParserError(result); + + ParserResult initializerResult = __Parser_parseExpression(parser); + if(!initializerResult.success) return initializerResult; + initializer = (ExpressionASTNode*)initializerResult.node; + } + + OptionalBindingConditionASTNode *bindingCondition = new_OptionalBindingConditionASTNode((PatternASTNode*)patternResult.node, (ExpressionASTNode*)initializer, isConstant); + + return ParserSuccess(bindingCondition); +} + +ParserResult __Parser_parseCondition(Parser *parser) { + assertf(parser != NULL); + + ExpressionASTNode *expression = NULL; + OptionalBindingConditionASTNode *bindingCondition = NULL; + + // consume '(' optionally + bool hasOptionalParen = false; + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + if(peek.token->kind == TOKEN_LEFT_PAREN) { + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + hasOptionalParen = true; + } + + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + if(peek.token->kind == TOKEN_LET || peek.token->kind == TOKEN_VAR) { + if(hasOptionalParen) { + return ParserError( + String_fromFormat("cannot use optional binding in condition with parentheses"), + Array_fromArgs(1, peek.token)); + } + + ParserResult bindingConditionResult = __Parser_parseOptionalBindingCondition(parser); + if(!bindingConditionResult.success) return bindingConditionResult; + bindingCondition = (OptionalBindingConditionASTNode*)bindingConditionResult.node; + } else { + ParserResult expressionResult = __Parser_parseExpression(parser); + if(!expressionResult.success) return expressionResult; + + expression = (ExpressionASTNode*)expressionResult.node; + } + + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + // consume ')' if we consumed '(' + if(hasOptionalParen) { + if(peek.token->kind == TOKEN_RIGHT_PAREN) { + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + } else { + return ParserError( + String_fromFormat("expected ')' in condition"), + Array_fromArgs(1, peek.token)); + } + } + + ConditionASTNode *condition = new_ConditionASTNode(expression, bindingCondition); + + return ParserSuccess(condition); +} + +ParserResult __Parser_parseIfStatement(Parser *parser) { + assertf(parser != NULL); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + // look more into this + if(peek.token->kind == TOKEN_LEFT_BRACE) { + return ParserError( + String_fromFormat("missing condition in 'if' statement"), + Array_fromArgs(1, peek.token)); + } + + + if(peek.token->kind == TOKEN_ELSE || peek.token->type == TOKEN_EOF) { + return ParserError( + String_fromFormat("expected expression, var, or let in 'if' condition"), + Array_fromArgs(1, peek.token)); + } + + ParserResult conditionResult = __Parser_parseCondition(parser); + if(!conditionResult.success) return conditionResult; + + ParserResult blockResult = __Parser_parseBlock(parser, true); + if(!blockResult.success) return blockResult; + + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + ASTNode *alternate = NULL; + + if(peek.token->kind == TOKEN_ELSE) { + // skip else keyword + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + if(peek.token->kind == TOKEN_IF) { + // consume if keyword + result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + ParserResult ifStatementResult = __Parser_parseIfStatement(parser); + if(!ifStatementResult.success) return ifStatementResult; + alternate = (ASTNode*)ifStatementResult.node; + + } else { + ParserResult blockResult = __Parser_parseBlock(parser, true); + if(!blockResult.success) return blockResult; + alternate = (ASTNode*)blockResult.node; + } + } + + IfStatementASTNode *ifStatement = new_IfStatementASTNode((ConditionASTNode*)conditionResult.node, (BlockASTNode*)blockResult.node, (ASTNode*)alternate); + + return ParserSuccess(ifStatement); +} + +ParserResult __Parser_parseWhileStatement(Parser *parser) { + assertf(parser != NULL); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + // look more into this + if(peek.token->kind == TOKEN_LEFT_BRACE) { + return ParserError( + String_fromFormat("missing condition in 'while' statement"), + Array_fromArgs(1, peek.token)); + } + + if(peek.token->type == TOKEN_EOF) { + return ParserError( + String_fromFormat("expected expression, var, or let in 'while' condition"), + Array_fromArgs(1, peek.token)); + } + + ParserResult conditionResult = __Parser_parseCondition(parser); + if(!conditionResult.success) return conditionResult; + + ParserResult blockResult = __Parser_parseBlock(parser, true); + if(!blockResult.success) return blockResult; + + WhileStatementASTNode *whileStatement = new_WhileStatementASTNode((ConditionASTNode*)conditionResult.node, (BlockASTNode*)blockResult.node); + + return ParserSuccess(whileStatement); +} + +ParserResult __Parser_parseReturnStatement(Parser *parser) { + assertf(parser != NULL); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + ExpressionASTNode *expression = NULL; + + if(peek.token->type != TOKEN_EOF) { + ParserResult expressionResult = __Parser_parseExpression(parser); + if(!expressionResult.success) return expressionResult; + expression = (ExpressionASTNode*)expressionResult.node; + } + + ReturnStatementASTNode *returnStatement = new_ReturnStatementASTNode((ExpressionASTNode*)expression); + + return ParserSuccess(returnStatement); +} + +ParserResult __Parser_parseVariableDeclarator(Parser *parser) { + assertf(parser != NULL); + + ParserResult patternResult = __Parser_parsePattern(parser); + if(!patternResult.success) return patternResult; + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + ExpressionASTNode *initializer = NULL; + + if(peek.token->kind == TOKEN_EQUAL) { + // Consume the `=` token + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + ParserResult initializerResult = __Parser_parseExpression(parser); + if(!initializerResult.success) return initializerResult; + initializer = initializerResult.node; + } + + VariableDeclaratorASTNode *variableDeclarator = new_VariableDeclaratorASTNode((PatternASTNode*)patternResult.node, (ExpressionASTNode*)initializer); + + return ParserSuccess(variableDeclarator); +} + +ParserResult __Parser_parseVariableDeclarationList(Parser *parser) { + assertf(parser != NULL); + LexerResult peek; + LexerResult result; + + Array *declarators = Array_alloc(0); + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + while(true) { + ParserResult declaratorResult = __Parser_parseVariableDeclarator(parser); + if(!declaratorResult.success) return declaratorResult; + + Array_push(declarators, (VariableDeclaratorASTNode*)declaratorResult.node); + + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + // Consume the `,` token + if(peek.token->kind == TOKEN_COMMA) { + result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + } else { + break; + } + } + + VariableDeclarationListASTNode *variableDeclarationList = new_VariableDeclarationListASTNode(declarators); + + return ParserSuccess(variableDeclarationList); +} + +ParserResult __Parser_parseVariableDeclarationStatement(Parser *parser, bool isConstant) { + assertf(parser != NULL); + + ParserResult declarationList = __Parser_parseVariableDeclarationList(parser); + if(!declarationList.success) return declarationList; + + VariableDeclarationASTNode *variableDeclaration = new_VariableDeclarationASTNode((VariableDeclarationListASTNode*)declarationList.node, isConstant); + return ParserSuccess(variableDeclaration); +} + +ParserResult __Parser_parseArgument(Parser *parser) { + assertf(parser != NULL); + + IdentifierASTNode *argumentLabel = NULL; + ExpressionASTNode *expression = NULL; + + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + // labeled argument + if(result.token->type == TOKEN_IDENTIFIER && peek.token->kind == TOKEN_COLON) { + argumentLabel = new_IdentifierASTNode(result.token->value.string); + // Skip the ':' token + LexerResult tmp = Lexer_nextToken(parser->lexer); + if(!tmp.success) return LexerToParserError(result); + } + + ParserResult expressionResult = __Parser_parseExpression(parser); + if(!expressionResult.success) return expressionResult; + expression = (ExpressionASTNode*)expressionResult.node; + + ArgumentASTNode *argument = new_ArgumentASTNode(expression, argumentLabel); + + return ParserSuccess(argument); +} + +ParserResult __Parser_parseArgumentList(Parser *parser) { + assertf(parser != NULL); + + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + // parse argument-list + Array *arguments = Array_alloc(0); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + + while(peek.token->kind != TOKEN_RIGHT_PAREN) { + ParserResult argumentResult = __Parser_parseArgument(parser); + if(!argumentResult.success) return argumentResult; + + Array_push(arguments, (ArgumentASTNode*)argumentResult.node); + + LexerResult peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + + if(peek.token->kind == TOKEN_COMMA) { + result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + } + + peek = Lexer_peekToken(parser->lexer, 1); + if(!peek.success) return LexerToParserError(peek); + } + + // skip ')' + result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + + ArgumentListASTNode *argumentList = new_ArgumentListASTNode(arguments); + + return ParserSuccess(argumentList); + +} + +ParserResult __Parser_parseFunctionCallExpression(Parser *parser) { + assertf(parser != NULL); + + // identifier + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + IdentifierASTNode *funcId = new_IdentifierASTNode(result.token->value.string); + + ParserResult argumentListResult = __Parser_parseArgumentList(parser); + if(!argumentListResult.success) return argumentListResult; + + FunctionCallASTNode *fuctionCallExpression = new_FunctionCallASTNode(funcId, (ArgumentListASTNode*)argumentListResult.node); + + return ParserSuccess(fuctionCallExpression); +} + +ParserResult __Parser_parseAssignmentStatement(Parser *parser) { + assertf(parser != NULL); + + // identifier + LexerResult peek = Lexer_peekToken(parser->lexer, 0); + if(!peek.success) return LexerToParserError(peek); + IdentifierASTNode *variableId = new_IdentifierASTNode(peek.token->value.string); + + // skip '=' + LexerResult result = Lexer_nextToken(parser->lexer); + if(!result.success) return LexerToParserError(result); + + ParserResult assignmentResult = __Parser_parseExpression(parser); + if(!assignmentResult.success) return assignmentResult; + + AssignmentStatementASTNode *assignmentStatement = new_AssignmentStatementASTNode(variableId, (ExpressionASTNode*)assignmentResult.node); + return ParserSuccess(assignmentStatement); +} /* How to walk/traverse parsed AST or decide what kind of node the ASTNode * pointer refers to in general? */ diff --git a/src/compiler/parser/grammar.md b/src/compiler/parser/grammar.md index e718258..866cc24 100644 --- a/src/compiler/parser/grammar.md +++ b/src/compiler/parser/grammar.md @@ -1,18 +1,125 @@ -# Grammar of a function declaration +# Grammar -function-declaration → function-head function-name function-signature function-body? +IFJ23 -function-head → func -function-name → identifier +## base -function-signature → parameter-clause function-result? -function-result → -> type -function-body → code-block +type-annotation → `:` type
+//type → `String` | `Int` | `Double`
+type → identifier // Built-in types will be resolved at semantic analysis
-parameter-clause → ( ) | ( parameter-list ) -parameter-list → parameter | parameter , parameter-list -parameter → external-parameter-name? local-parameter-name type-annotation default-argument-clause? -parameter → external-parameter-name? local-parameter-name type-annotation -external-parameter-name → identifier -local-parameter-name → identifier -default-argument-clause → = expression +code-block → `{` statements? `}`
+ +## literals + +literal → numeric-literal | string-literal | boolean-literal | nil-literal
+ +? numeric-literal → `-`? integer-literal | `-`? floating-point-literal // Should this be included? Number literals are defined by FSM diagram
+boolean-literal → `true` | `false`
+nil-literal → `nil`
+ +## expressions + +expression → TODO
+ +// This is probably wrong (swift parses `a = b` as a `BinaryExpression`, but we want to be more explic, so this would be parsed as `AssignmentExpression`), so it needs rework; it's just to show the `function-call-expression` rule (this way the function call can appear anywhere in the expression (`myFunc(otherFunc(10))`, `myFunc() + otherFunc(10) * 8`, `a = myFunc() * -1`, ...)) + +expression → prefix-expression infix-expressions?
+expression-list → expression | expression `,` expression-list
+ +prefix-expression → prefix-operator? postfix-expression
+ +infix-expression → infix-operator prefix-expression
+infix-expression → assignment-operator prefix-expression
+infix-expression → conditional-operator prefix-expression
+? infix-expression → type-casting-operator // Are we gonna support explicit type casting?
+infix-expressions → infix-expression infix-expressions?
+ +postfix-expression → primary-expression
+postfix-expression → postfix-expression postfix-operator
+postfix-expression → function-call-expression
+ +primary-expression → identifier
+primary-expression → literal-expression
+primary-expression → parenthesized-expression
+ +assignment-operator → `=`
+ +assignment-expression → `=` expression
+ +conditional-operator → `?` expression `:`
+ +parenthesized-expression → `(` expression `)`
+ +literal-expression → literal
+ +## statements + +statement → function-declaration
+statement → variable-declaration
+statement → if-statement
+statement → while-statement
+statement → return-statement
+statement → expression-statement
+ +expression-statement → expression
+ +## function declaration + +function-declaration → `func` function-name function-signature function-body?
+ +function-name → identifier
+ +function-signature → parameter-clause function-result?
+function-result → `->` type
+function-body → code-block
+ +parameter-clause → `(` `)` | `(` parameter-list `)`
+parameter-list → parameter | parameter `,` parameter-list
+parameter → external-parameter-name? local-parameter-name type-annotation initializer?
+external-parameter-name → identifier
+local-parameter-name → identifier
+ +initializer → `=` expression
+ +## function call + +function-call-expression → function-name argument-clause
+ +argument-clause → `(` `)` | `(` argument-list `)`
+argument-list → argument | argument `,` argument-list
+ +argument → argument-name `:` expression
+argument → expression
+ +argument-name → identifier
+ +## assigment statement + +assignment-statement → variable-name assignment-expression
+ +## variable declaration + +variable-declaration → variable-head variable-declaration-list?
+variable-head → `let` | `var`
+variable-name → identifier
+ +variable-declaration-list → variable-declarator | variable-declarator `,` variable-declaration-list
+variable-declarator → pattern initializer?
+pattern → variable-name type-annotation?
+ +## if statement + +condition → expression | optional-binding-condition
+optional-binding-condition → `let` pattern initializer? | `var` pattern initializer? + +if-statement → `if` condition code-block else-clause?
+else-clause → `else` code-block | `else` if-statement
+ +## while statement + +while-statement → `while` condition code-block
+ +## return statement + +return-statement → `return` expression?
diff --git a/src/internal/Array.c b/src/internal/Array.c index 7735697..76fdb73 100644 --- a/src/internal/Array.c +++ b/src/internal/Array.c @@ -25,7 +25,6 @@ void Array_destructor(Array *array) { void Array_push(Array *array, void *value) { if(!array) return; - // If size exceeds capacity, resize the array to fit more elements if(array->size >= array->capacity) { Array_resize(array, (array->capacity ? array->capacity : 1) << 1); @@ -68,10 +67,16 @@ void Array_set(Array *array, int index, void *value) { index = __Array_resolveIndex(array, index); - // If size is not enough to fit the index, resize the array - if((size_t)index >= array->size) { - Array_resize(array, array->capacity + (index - array->size) + 1); - array->size = index + 1; + size_t size = index + 1; + + // If there's not enough capacity to fit the index, resize the array + if(size > array->capacity) { + Array_resize(array, size); + } + + // If the index is past the end of the array, set the size to the index + if(size > array->size) { + array->size = size; } array->data[index] = value; @@ -109,16 +114,16 @@ void Array_clear(Array *array) { void Array_resize(Array *array, size_t capacity) { if(!array) return; - array->capacity = capacity; - if(capacity) { // Non-zero capacity => reallocate the array - array->data = mem_realloc(array->data, array->capacity * sizeof(void*)); + array->data = mem_recalloc(array->data, array->capacity, capacity, sizeof(void*)); } else { // Zero capacity => free the array and set it to NULL if(array->data) mem_free(array->data); array->data = NULL; } + + array->capacity = capacity; } void Array_reserve(Array *array, size_t capacity) { diff --git a/test/compiler/lexer/Lexer.test.c b/test/compiler/lexer/Lexer.test.c index b2d2032..be7ec60 100644 --- a/test/compiler/lexer/Lexer.test.c +++ b/test/compiler/lexer/Lexer.test.c @@ -2,6 +2,8 @@ #include "unit.h" #include +#define TEST_PRIORITY 90 + DESCRIBE(comment_stripping, "Comments stripping") { Lexer lexer; Lexer_constructor(&lexer); @@ -1299,8 +1301,576 @@ DESCRIBE(string_tokenization, "String literals tokenization") { LexerResult result; Token *token; - (void)token; - (void)result; + TEST("Empty string", { + result = Lexer_tokenize(&lexer, "\"\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "")); + EXPECT_EQUAL_INT(token->value.string->length, 0); + }) + + TEST("Single character string", { + result = Lexer_tokenize(&lexer, "\"a\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "a")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multicharacter string", { + result = Lexer_tokenize(&lexer, "\"abc\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "abc")); + EXPECT_EQUAL_INT(token->value.string->length, 3); + }) +} + +DESCRIBE(string_invalid_tokeniz, "Invalid string literals tokenization") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + + TEST("Unterminated string", { + result = Lexer_tokenize(&lexer, "\""); + EXPECT_FALSE(result.success); + }) + + TEST("Unescaped quote", { + result = Lexer_tokenize(&lexer, "\"Hello \" World\""); + EXPECT_FALSE(result.success); + }) +} + +DESCRIBE(string_quotes_escape, "String literals tokenization with escaped quotes") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Single escaped double quote", { + result = Lexer_tokenize(&lexer, "\"\\\"\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\"")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple escaped double quotes", { + result = Lexer_tokenize(&lexer, "\"\\\"\\\"\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\"\"")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) + + TEST("Multiple escaped double quotes with text", { + result = Lexer_tokenize(&lexer, "\"pre \\\"in\\\" post\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre \"in\" post")); + EXPECT_EQUAL_INT(token->value.string->length, 13); + }) +} + +DESCRIBE(string_backslash_escape, "String literals tokenization with escaped backslashes") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Single escaped backslash", { + result = Lexer_tokenize(&lexer, "\"\\\\\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\\")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple escaped backslashes", { + result = Lexer_tokenize(&lexer, "\"\\\\\\\\\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\\\\")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) + + TEST("Multiple escaped backslashes with text", { + result = Lexer_tokenize(&lexer, "\"pre \\\\in\\\\ post\""); + EXPECT_TRUE(result.success); + EXPECT_EQUAL_INT(lexer.tokens->size, 2); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre \\in\\ post")); + EXPECT_EQUAL_INT(token->value.string->length, 13); + }) +} + +DESCRIBE(string_whitespace_escape, "String literals tokenization with escaped whitespace") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Single line feed", { + result = Lexer_tokenize(&lexer, "\"\\n\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\n")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple line feeds", { + result = Lexer_tokenize(&lexer, "\"\\n\\n\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\n\n")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) + + + TEST("Single carriage return", { + result = Lexer_tokenize(&lexer, "\"\\r\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\r")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple carriage returns", { + result = Lexer_tokenize(&lexer, "\"\\r\\r\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\r\r")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) + + + TEST("Single tab", { + result = Lexer_tokenize(&lexer, "\"\\t\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\t")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple tabs", { + result = Lexer_tokenize(&lexer, "\"\\t\\t\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\t\t")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) +} + +DESCRIBE(string_unicode_escape, "String literals tokenization with escaped unicode sequences") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Single unicode character", { + result = Lexer_tokenize(&lexer, "\"\\u{61}\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "a")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Single prefixed unicode character", { + result = Lexer_tokenize(&lexer, "\"\\u{0061}\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "a")); + EXPECT_EQUAL_INT(token->value.string->length, 1); + }) + + TEST("Multiple unicode characters", { + result = Lexer_tokenize(&lexer, "\"\\u{61}\\u{62}\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "ab")); + EXPECT_EQUAL_INT(token->value.string->length, 2); + }) + + TEST("Multiple unicode characters in text", { + result = Lexer_tokenize(&lexer, "\"pre \\u{61} in \\u{62} post\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre a in b post")); + EXPECT_EQUAL_INT(token->value.string->length, 15); + }) +} + +DESCRIBE(str_invalid_escape, "Invalid escape sequences in string literals tokenization") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + + TEST("Invalid escape sequence", { + result = Lexer_tokenize(&lexer, "\"\\a\""); + EXPECT_FALSE(result.success); + }) + + TEST("Incomplete unicode sequence 1", { + result = Lexer_tokenize(&lexer, "\"\\u\""); + EXPECT_FALSE(result.success); + }) + + TEST("Incomplete unicode sequence 2", { + result = Lexer_tokenize(&lexer, "\"\\u{\""); + EXPECT_FALSE(result.success); + }) + + TEST("Incomplete unicode sequence 3", { + result = Lexer_tokenize(&lexer, "\"\\u{61\""); + EXPECT_FALSE(result.success); + }) + + TEST("Too short unicode sequence", { + result = Lexer_tokenize(&lexer, "\"\\u{}\""); + EXPECT_FALSE(result.success); + }) + + TEST("Too long unicode sequence", { + result = Lexer_tokenize(&lexer, "\"\\u{123456789}\""); + EXPECT_FALSE(result.success); + }) + + TEST("Out of the range unicode sequence", { + result = Lexer_tokenize(&lexer, "\"\\u{FFFFFFFF}\""); + EXPECT_FALSE(result.success); + }) + + TEST("Invalid characters in unicode sequence", { + result = Lexer_tokenize(&lexer, "\"\\u{FFXF}\""); + EXPECT_FALSE(result.success); + }) +} + +DESCRIBE(string_escape_sequences, "String literals tokenization with escaped sequences") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Combination of all supported escape sequences", { + result = Lexer_tokenize(&lexer, "\"\\\"\\\\\\n\\r\\t\\u{61}\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "\"\\\n\r\ta")); + EXPECT_EQUAL_INT(token->value.string->length, 6); + }) +} + +DESCRIBE(string_interpolation, "Interpolated string literal tokenization") { + Lexer lexer; + Lexer_constructor(&lexer); + + LexerResult result; + Token *token; + + TEST("Simple interpolated string", { + result = Lexer_tokenize(&lexer, "\"Hello \\(name)!\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "Hello ")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "name")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "!")); + }) + + TEST_BEGIN("Interpolated string starts with expression") { + result = Lexer_tokenize(&lexer, "\"\\(expr) post\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "expr")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, " post")); + } TEST_END() + + TEST("Interpolated string ends with expression", { + result = Lexer_tokenize(&lexer, "\"pre \\(expr)\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre ")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "expr")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "")); + }) + + TEST("Interpolated string starts & ends with expression", { + result = Lexer_tokenize(&lexer, "\"\\(expr)\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "expr")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "")); + }) + + TEST("Interpolated string contining parentheses in expression", { + result = Lexer_tokenize(&lexer, "\"pre \\(expr * (a + b)) post\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre ")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "expr")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_OPERATOR); + EXPECT_TRUE(token->kind == TOKEN_STAR); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->type == TOKEN_PUNCTUATOR); + EXPECT_TRUE(token->kind == TOKEN_LEFT_PAREN); + + token = (Token*)Array_get(lexer.tokens, 5); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "a")); + + token = (Token*)Array_get(lexer.tokens, 6); + EXPECT_TRUE(token->type == TOKEN_OPERATOR); + EXPECT_TRUE(token->kind == TOKEN_PLUS); + + token = (Token*)Array_get(lexer.tokens, 7); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "b")); + + token = (Token*)Array_get(lexer.tokens, 8); + EXPECT_TRUE(token->type == TOKEN_PUNCTUATOR); + EXPECT_TRUE(token->kind == TOKEN_RIGHT_PAREN); + + token = (Token*)Array_get(lexer.tokens, 9); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 10); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, " post")); + }) + + TEST("Interpolated string contining string as teh expression", { + result = Lexer_tokenize(&lexer, "\"pre \\(\"in\") post\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre ")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "in")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, " post")); + }) + + TEST("Nested interpolated strings", { + result = Lexer_tokenize(&lexer, "\"pre \\(\"in_pre \\(expr) in_post\") post\""); + EXPECT_TRUE(result.success); + + token = (Token*)Array_get(lexer.tokens, 0); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "pre ")); + + token = (Token*)Array_get(lexer.tokens, 1); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 2); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, "in_pre ")); + + token = (Token*)Array_get(lexer.tokens, 3); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 4); + EXPECT_TRUE(token->type == TOKEN_IDENTIFIER); + EXPECT_TRUE(String_equals(token->value.identifier, "expr")); + + token = (Token*)Array_get(lexer.tokens, 5); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 6); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, " in_post")); + + token = (Token*)Array_get(lexer.tokens, 7); + EXPECT_TRUE(token->type == TOKEN_STRING_INTERPOLATION_MARKER); + + token = (Token*)Array_get(lexer.tokens, 8); + EXPECT_TRUE(token->kind == TOKEN_STRING); + EXPECT_TRUE(String_equals(token->value.string, " post")); + }) + + TEST("Invalid use interpolated strings", { + result = Lexer_tokenize(&lexer, "\"\\(\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\( post\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"\\( post\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\"\"\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\"\" post\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\"in\" post\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\"in_pre \\(expr)\" post\""); + EXPECT_FALSE(result.success); + + result = Lexer_tokenize(&lexer, "\"pre \\(\"in_pre \\(expr\") post\""); + EXPECT_FALSE(result.success); + }) } DESCRIBE(nextToken, "Token stream (nextToken)") { diff --git a/test/compiler/parser/ExpressionParser.test.c b/test/compiler/parser/ExpressionParser.test.c new file mode 100644 index 0000000..8fad9ff --- /dev/null +++ b/test/compiler/parser/ExpressionParser.test.c @@ -0,0 +1,9 @@ +#include + +#include "unit.h" +#include "compiler/lexer/Lexer.h" +#include "compiler/parser/Parser.h" + +#define TEST_PRIORITY 80 + +// TODO: Tests for ExpressionParser internals here... diff --git a/test/compiler/parser/Parser.test.c b/test/compiler/parser/Parser.test.c new file mode 100644 index 0000000..8b57a2b --- /dev/null +++ b/test/compiler/parser/Parser.test.c @@ -0,0 +1,863 @@ +#include + +#include "unit.h" +#include "parser_assertions.h" + +#include "compiler/lexer/Lexer.h" +#include "compiler/parser/Parser.h" +#include "compiler/parser/ASTNodes.h" + +#define TEST_PRIORITY 80 + +DESCRIBE(variable_declaration, "Variable declaration parsing") { + Lexer lexer; + Lexer_constructor(&lexer); + + Parser parser; + Parser_constructor(&parser, &lexer); + + ParserResult result; + + TEST_BEGIN("Constant without type annotation") { + Lexer_setSource(&lexer, "let a = 7"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_VARIABLE_DECLARATION); + + VariableDeclarationASTNode *declaration = (VariableDeclarationASTNode*)statement; + EXPECT_TRUE(declaration->isConstant); + EXPECT_NOT_NULL(declaration->declaratorList); + + VariableDeclarationListASTNode *list = declaration->declaratorList; + EXPECT_NOT_NULL(list->declarators); + + Array *arr = list->declarators; + EXPECT_EQUAL_INT(arr->size, 1); + + VariableDeclaratorASTNode *declarator = Array_get(arr, 0); + EXPECT_NOT_NULL(declarator); + + PatternASTNode *pattern = declarator->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NULL(pattern->type); + + IdentifierASTNode *id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "a")); + + LiteralExpressionASTNode *initializer = (LiteralExpressionASTNode*)declarator->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(initializer->value.integer, 7); + } TEST_END(); + + TEST_BEGIN("Constant with type annotation") { + Lexer_setSource(&lexer, "let hello_string_variable: String = \"hello\""); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_VARIABLE_DECLARATION); + + VariableDeclarationASTNode *declaration = (VariableDeclarationASTNode*)statement; + EXPECT_TRUE(declaration->isConstant); + EXPECT_NOT_NULL(declaration->declaratorList); + + VariableDeclarationListASTNode *list = declaration->declaratorList; + EXPECT_NOT_NULL(list->declarators); + + Array *arr = list->declarators; + EXPECT_EQUAL_INT(arr->size, 1); + + VariableDeclaratorASTNode *declarator = Array_get(arr, 0); + EXPECT_NOT_NULL(declarator); + + PatternASTNode *pattern = declarator->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NOT_NULL(pattern->type); + EXPECT_TRUE(pattern->_type == NODE_PATTERN); + EXPECT_TRUE(pattern->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(pattern->type->id->name, "String")); + + IdentifierASTNode *id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "hello_string_variable")); + + LiteralExpressionASTNode *initializer = (LiteralExpressionASTNode*)declarator->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_STRING); + EXPECT_TRUE(String_equals(initializer->value.string, "hello")); + + } TEST_END(); + + TEST_BEGIN("More variables declaration") { + Lexer_setSource(&lexer, "var a = \"hello\", b: Int = 20, c = 10.12"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_VARIABLE_DECLARATION); + + VariableDeclarationASTNode *declaration = (VariableDeclarationASTNode*)statement; + EXPECT_FALSE(declaration->isConstant); + EXPECT_NOT_NULL(declaration->declaratorList); + + VariableDeclarationListASTNode *list = declaration->declaratorList; + EXPECT_NOT_NULL(list->declarators); + + Array *arr = list->declarators; + EXPECT_EQUAL_INT(arr->size, 3); + + // first declarator a = "hello" + VariableDeclaratorASTNode *declarator = Array_get(arr, 0); + EXPECT_NOT_NULL(declarator); + + PatternASTNode *pattern = declarator->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NULL(pattern->type); + + IdentifierASTNode *id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "a")); + + LiteralExpressionASTNode *initializer = (LiteralExpressionASTNode*)declarator->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_STRING); + EXPECT_TRUE(String_equals(initializer->value.string, "hello")); + + // second declarator b = 20 + declarator = Array_get(arr, 1); + EXPECT_NOT_NULL(declarator); + + pattern = declarator->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NOT_NULL(pattern->type); + EXPECT_TRUE(pattern->_type == NODE_PATTERN); + EXPECT_TRUE(pattern->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(pattern->type->id->name, "Int")); + + id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "b")); + + initializer = (LiteralExpressionASTNode*)declarator->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(initializer->value.integer, 20); + + // third declarator c = 10.12 + declarator = Array_get(arr, 2); + EXPECT_NOT_NULL(declarator); + + pattern = declarator->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NULL(pattern->type); + + id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "c")); + + initializer = (LiteralExpressionASTNode*)declarator->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_FLOATING); + EXPECT_EQUAL_FLOAT(initializer->value.floating, 10.12); + + } TEST_END(); + + TEST_BEGIN("Missing colon") { + Lexer_setSource(&lexer, "let hello_string_variable String = \"hello\""); + result = Parser_parse(&parser); + + EXPECT_FALSE(result.success); + EXPECT_NULL(result.node); + + EXPECT_TRUE(result.type == RESULT_ERROR_SYNTACTIC_ANALYSIS); + EXPECT_TRUE(result.severity == SEVERITY_ERROR); + // prbbly later add message check also + + } TEST_END(); + +} + +DESCRIBE(function_declaration, "Function declaration parsing") { + Lexer lexer; + Lexer_constructor(&lexer); + + Parser parser; + Parser_constructor(&parser, &lexer); + + ParserResult result; + + TEST_BEGIN("No parameters empty body") { + Lexer_setSource(&lexer, "func empty_function(){}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_FUNCTION_DECLARATION); + + FunctionDeclarationASTNode *declaration = (FunctionDeclarationASTNode*)statement; + + IdentifierASTNode *id = declaration->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "empty_function")); + + // return type + EXPECT_NULL(declaration->returnType); + + // parameters + ParameterListASTNode *list = declaration->parameterList; + EXPECT_NOT_NULL(list->parameters); + + Array *arr = list->parameters; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // body + BlockASTNode *body = declaration->body; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("With simple parameters empty body") { + Lexer_setSource(&lexer, "func parameters_function(a: Int, b: Int = 10, c: String = \"hello\"){}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_FUNCTION_DECLARATION); + + FunctionDeclarationASTNode *declaration = (FunctionDeclarationASTNode*)statement; + + IdentifierASTNode *id = declaration->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "parameters_function")); + + // return type + EXPECT_NULL(declaration->returnType); + + // parameters + ParameterListASTNode *list = declaration->parameterList; + EXPECT_NOT_NULL(list->parameters); + + Array *arr = list->parameters; + EXPECT_NOT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 3); + + // first parameter a: Int + ParameterASTNode *parameter = Array_get(arr, 0); + EXPECT_NOT_NULL(parameter); + EXPECT_FALSE(parameter->isLabeless); + + EXPECT_NULL(parameter->externalId); + EXPECT_TRUE(String_equals(parameter->internalId->name, "a")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NULL(parameter->initializer); + + // second parameter b: Int = 10 + parameter = Array_get(arr, 1); + EXPECT_NOT_NULL(parameter); + EXPECT_FALSE(parameter->isLabeless); + + EXPECT_NULL(parameter->externalId); + EXPECT_TRUE(String_equals(parameter->internalId->name, "b")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NOT_NULL(parameter->initializer); + + LiteralExpressionASTNode *initializer = (LiteralExpressionASTNode*)parameter->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(initializer->value.integer, 10); + + // third parameter c = "hello" + parameter = Array_get(arr, 2); + EXPECT_NOT_NULL(parameter); + EXPECT_FALSE(parameter->isLabeless); + + EXPECT_NULL(parameter->externalId); + EXPECT_TRUE(String_equals(parameter->internalId->name, "c")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "String")); + + EXPECT_NOT_NULL(parameter->initializer); + + initializer = (LiteralExpressionASTNode*)parameter->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_STRING); + EXPECT_TRUE(String_equals(initializer->value.string, "hello")); + + // body + BlockASTNode *body = declaration->body; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("With advanced parameters empty body") { + Lexer_setSource(&lexer, "func parameters_function(a_external a_internal: Int, _ b_internal: Int = 100, _ _: Double = 12.3){}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_FUNCTION_DECLARATION); + + FunctionDeclarationASTNode *declaration = (FunctionDeclarationASTNode*)statement; + + IdentifierASTNode *id = declaration->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "parameters_function")); + + // return type + EXPECT_NULL(declaration->returnType); + + // parameters + ParameterListASTNode *list = declaration->parameterList; + EXPECT_NOT_NULL(list->parameters); + + Array *arr = list->parameters; + EXPECT_NOT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 3); + + // first parameter a_external a_internal: Int + ParameterASTNode *parameter = Array_get(arr, 0); + EXPECT_NOT_NULL(parameter); + EXPECT_FALSE(parameter->isLabeless); + + EXPECT_TRUE(String_equals(parameter->externalId->name, "a_external")); + EXPECT_TRUE(String_equals(parameter->internalId->name, "a_internal")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NULL(parameter->initializer); + + // second parameter _ b_internal: Int = 100 + parameter = Array_get(arr, 1); + EXPECT_NOT_NULL(parameter); + EXPECT_TRUE(parameter->isLabeless); + + EXPECT_TRUE(String_equals(parameter->externalId->name, "_")); + EXPECT_TRUE(String_equals(parameter->internalId->name, "b_internal")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NOT_NULL(parameter->initializer); + + LiteralExpressionASTNode *initializer = (LiteralExpressionASTNode*)parameter->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(initializer->value.integer, 100); + + // third parameter _ _: Double = 12.3 + parameter = Array_get(arr, 2); + EXPECT_NOT_NULL(parameter); + EXPECT_TRUE(parameter->isLabeless); + + EXPECT_TRUE(String_equals(parameter->externalId->name, "_")); + EXPECT_TRUE(String_equals(parameter->internalId->name, "_")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Double")); + + EXPECT_NOT_NULL(parameter->initializer); + + initializer = (LiteralExpressionASTNode*)parameter->initializer; + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(initializer->type == LITERAL_FLOATING); + EXPECT_EQUAL_INT(initializer->value.floating, 12.3); + + // body + BlockASTNode *body = declaration->body; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("With parameters and simple block") { + Lexer_setSource(&lexer, "func isGreater(_ a:Int, _ b:Int) -> Bool { return a > b}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_FUNCTION_DECLARATION); + + FunctionDeclarationASTNode *declaration = (FunctionDeclarationASTNode*)statement; + + IdentifierASTNode *id = declaration->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "isGreater")); + + // return type + EXPECT_NOT_NULL(declaration->returnType); + EXPECT_TRUE(declaration->returnType->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(declaration->returnType->id->name, "Bool")); + + // parameters + ParameterListASTNode *list = declaration->parameterList; + EXPECT_NOT_NULL(list->parameters); + + Array *arr = list->parameters; + EXPECT_NOT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 2); + + // first parameter a: Int + ParameterASTNode *parameter = Array_get(arr, 0); + EXPECT_NOT_NULL(parameter); + EXPECT_TRUE(parameter->isLabeless); + + EXPECT_TRUE(String_equals(parameter->externalId->name, "_")); + EXPECT_TRUE(String_equals(parameter->internalId->name, "a")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NULL(parameter->initializer); + + // second parameter a: Int + parameter = Array_get(arr, 1); + EXPECT_NOT_NULL(parameter); + EXPECT_TRUE(parameter->isLabeless); + + EXPECT_TRUE(String_equals(parameter->externalId->name, "_")); + EXPECT_TRUE(String_equals(parameter->internalId->name, "b")); + + EXPECT_NOT_NULL(parameter->type); + EXPECT_TRUE(parameter->type->_type == NODE_TYPE_REFERENCE); + EXPECT_TRUE(String_equals(parameter->type->id->name, "Int")); + + EXPECT_NULL(parameter->initializer); + + // body + BlockASTNode *body = declaration->body; + EXPECT_NOT_NULL(body->statements); + + arr = body->statements; + EXPECT_NOT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 1); + + StatementASTNode *body_statement = Array_get(arr, 0); + EXPECT_TRUE(body_statement->_type == NODE_RETURN_STATEMENT); + ReturnStatementASTNode *return_statement = (ReturnStatementASTNode*)body_statement; + + BinaryExpressionASTNode *function_return = (BinaryExpressionASTNode*)return_statement->expression; + EXPECT_NOT_NULL(function_return); + EXPECT_TRUE(function_return->_type == NODE_BINARY_EXPRESSION); + + EXPECT_TRUE(function_return->left); + + IdentifierASTNode *left = (IdentifierASTNode*)function_return->left; + EXPECT_NOT_NULL(left); + EXPECT_TRUE(left->_type == NODE_IDENTIFIER); + EXPECT_TRUE(String_equals(left->name, "a")); + + IdentifierASTNode *right = (IdentifierASTNode*)function_return->right; + EXPECT_NOT_NULL(right); + EXPECT_TRUE(right->_type == NODE_IDENTIFIER); + EXPECT_TRUE(String_equals(right->name, "b")); + + EXPECT_TRUE(function_return->operator == OPERATOR_GREATER); + + } TEST_END(); +} + +DESCRIBE(if_statement, "If statement parsing") { + Lexer lexer; + Lexer_constructor(&lexer); + + Parser parser; + Parser_constructor(&parser, &lexer); + + ParserResult result; + + TEST_BEGIN("Simple condition no body, no else") { + Lexer_setSource(&lexer, "if (true) {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_IF_STATEMENT); + + IfStatementASTNode *if_statement = (IfStatementASTNode*)statement; + + EXPECT_NOT_NULL(if_statement->condition); + EXPECT_TRUE(if_statement->condition->_type == NODE_CONDITION); + + LiteralExpressionASTNode *condition_expression = (LiteralExpressionASTNode*)if_statement->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(condition_expression->type == LITERAL_BOOLEAN); + EXPECT_TRUE(condition_expression->value.boolean); + + // if body + BlockASTNode *body = if_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // else + EXPECT_NULL(if_statement->alternate); + + } TEST_END(); + + TEST_BEGIN("Simple condition no parens, no body") { + Lexer_setSource(&lexer, "if true {} else {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_IF_STATEMENT); + + IfStatementASTNode *if_statement = (IfStatementASTNode*)statement; + + EXPECT_NOT_NULL(if_statement->condition); + EXPECT_TRUE(if_statement->condition->_type == NODE_CONDITION); + + LiteralExpressionASTNode *condition_expression = (LiteralExpressionASTNode*)if_statement->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(condition_expression->type == LITERAL_BOOLEAN); + EXPECT_TRUE(condition_expression->value.boolean); + + // if body + BlockASTNode *body = if_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // else body + EXPECT_TRUE(if_statement->alternate->_type == NODE_BLOCK); + body = (BlockASTNode*)if_statement->alternate; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("Else if condition, no body") { + Lexer_setSource(&lexer, "if a > 10 {} else if (a < 10) {} else {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_IF_STATEMENT); + + // if + IfStatementASTNode *if_statement = (IfStatementASTNode*)statement; + + EXPECT_NOT_NULL(if_statement->condition); + EXPECT_TRUE(if_statement->condition->_type == NODE_CONDITION); + + BinaryExpressionASTNode *condition_expression = (BinaryExpressionASTNode*)if_statement->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_BINARY_EXPRESSION); + + IdentifierASTNode *left = (IdentifierASTNode*)condition_expression->left; + EXPECT_NOT_NULL(left); + EXPECT_TRUE(left->_type == NODE_IDENTIFIER); + EXPECT_TRUE(String_equals(left->name, "a")); + + LiteralExpressionASTNode *right = (LiteralExpressionASTNode*)condition_expression->right; + EXPECT_NOT_NULL(right); + EXPECT_TRUE(right->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(right->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(right->value.integer, 10); + + EXPECT_TRUE(condition_expression->operator == OPERATOR_GREATER); + + // if body + BlockASTNode *body = if_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // else if + EXPECT_NOT_NULL(if_statement->alternate); + + EXPECT_TRUE(if_statement->alternate->_type == NODE_IF_STATEMENT); + + IfStatementASTNode *elseif = (IfStatementASTNode*)if_statement->alternate; + + EXPECT_NOT_NULL(elseif->condition); + EXPECT_TRUE(elseif->condition->_type == NODE_CONDITION); + + condition_expression = (BinaryExpressionASTNode*)elseif->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_BINARY_EXPRESSION); + + left = (IdentifierASTNode*)condition_expression->left; + EXPECT_NOT_NULL(left); + EXPECT_TRUE(left->_type == NODE_IDENTIFIER); + EXPECT_TRUE(String_equals(left->name, "a")); + + right = (LiteralExpressionASTNode*)condition_expression->right; + EXPECT_NOT_NULL(right); + EXPECT_TRUE(right->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(right->type == LITERAL_INTEGER); + EXPECT_EQUAL_INT(right->value.integer, 10); + + EXPECT_TRUE(condition_expression->operator == OPERATOR_LESS); + + // else if body + body = elseif->body; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // else body + EXPECT_TRUE(elseif->alternate->_type == NODE_BLOCK); + body = (BlockASTNode*)elseif->alternate; + EXPECT_NOT_NULL(body->statements); + arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("Binding condition with parantheses") { + Lexer_setSource(&lexer, "if (let b = a) {}"); + result = Parser_parse(&parser); + + EXPECT_FALSE(result.success); + EXPECT_NULL(result.node); + + EXPECT_TRUE(result.type == RESULT_ERROR_SYNTACTIC_ANALYSIS); + EXPECT_TRUE(result.severity == SEVERITY_ERROR); + // prbbly later add message check also + + } TEST_END(); + + TEST_BEGIN("Binding condition no body, no else") { + Lexer_setSource(&lexer, "if let b = a {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_IF_STATEMENT); + + IfStatementASTNode *if_statement = (IfStatementASTNode*)statement; + + EXPECT_NOT_NULL(if_statement->condition); + EXPECT_TRUE(if_statement->condition->_type == NODE_CONDITION); + + EXPECT_NULL(if_statement->condition->expression); + + OptionalBindingConditionASTNode *binding_condition = (OptionalBindingConditionASTNode*)if_statement->condition->optionalBindingCondition; + EXPECT_NOT_NULL(binding_condition); + EXPECT_TRUE(binding_condition->isConstant); + EXPECT_TRUE(binding_condition->_type == NODE_OPTIONAL_BINDING_CONDITION); + + PatternASTNode *pattern = binding_condition->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NULL(pattern->type); + + IdentifierASTNode *id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "b")); + + IdentifierASTNode *initializer = (IdentifierASTNode*)binding_condition->initializer; + + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_IDENTIFIER); + EXPECT_NOT_NULL(initializer->name); + EXPECT_TRUE(String_equals(initializer->name, "a")); + + // if body + BlockASTNode *body = if_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + // else + EXPECT_NULL(if_statement->alternate); + + } TEST_END(); +} + +DESCRIBE(while_statement, "While statement parsing") { + Lexer lexer; + Lexer_constructor(&lexer); + + Parser parser; + Parser_constructor(&parser, &lexer); + + ParserResult result; + + TEST_BEGIN("Simple condition no body") { + Lexer_setSource(&lexer, "while (true) {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_WHILE_STATEMENT); + + WhileStatementASTNode *while_statement = (WhileStatementASTNode*)statement; + + EXPECT_NOT_NULL(while_statement->condition); + EXPECT_TRUE(while_statement->condition->_type == NODE_CONDITION); + + LiteralExpressionASTNode *condition_expression = (LiteralExpressionASTNode*)while_statement->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(condition_expression->type == LITERAL_BOOLEAN); + EXPECT_TRUE(condition_expression->value.boolean); + + // while body + BlockASTNode *body = while_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("Simple condition no parens, no body") { + Lexer_setSource(&lexer, "while true {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_WHILE_STATEMENT); + + WhileStatementASTNode *while_statement = (WhileStatementASTNode*)statement; + + EXPECT_NOT_NULL(while_statement->condition); + EXPECT_TRUE(while_statement->condition->_type == NODE_CONDITION); + + LiteralExpressionASTNode *condition_expression = (LiteralExpressionASTNode*)while_statement->condition->expression; + EXPECT_NOT_NULL(condition_expression); + EXPECT_TRUE(condition_expression->_type == NODE_LITERAL_EXPRESSION); + EXPECT_TRUE(condition_expression->type == LITERAL_BOOLEAN); + EXPECT_TRUE(condition_expression->value.boolean); + + // while body + BlockASTNode *body = while_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); + + TEST_BEGIN("Binding condition with parantheses") { + Lexer_setSource(&lexer, "while (let hello = world) {}"); + result = Parser_parse(&parser); + + EXPECT_FALSE(result.success); + EXPECT_NULL(result.node); + + EXPECT_TRUE(result.type == RESULT_ERROR_SYNTACTIC_ANALYSIS); + EXPECT_TRUE(result.severity == SEVERITY_ERROR); + // prbbly later add message check also + + } TEST_END(); + + TEST_BEGIN("Binding condition no body") { + Lexer_setSource(&lexer, "while let hello = world {}"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_WHILE_STATEMENT); + + WhileStatementASTNode *while_statement = (WhileStatementASTNode*)statement; + + EXPECT_NOT_NULL(while_statement->condition); + EXPECT_TRUE(while_statement->condition->_type == NODE_CONDITION); + + EXPECT_NULL(while_statement->condition->expression); + + OptionalBindingConditionASTNode *binding_condition = (OptionalBindingConditionASTNode*)while_statement->condition->optionalBindingCondition; + EXPECT_NOT_NULL(binding_condition); + EXPECT_TRUE(binding_condition->isConstant); + EXPECT_TRUE(binding_condition->_type == NODE_OPTIONAL_BINDING_CONDITION); + + PatternASTNode *pattern = binding_condition->pattern; + EXPECT_NOT_NULL(pattern); + EXPECT_NULL(pattern->type); + + IdentifierASTNode *id = pattern->id; + EXPECT_NOT_NULL(id); + EXPECT_TRUE(String_equals(id->name, "hello")); + + IdentifierASTNode *initializer = (IdentifierASTNode*)binding_condition->initializer; + + EXPECT_NOT_NULL(initializer); + EXPECT_TRUE(initializer->_type == NODE_IDENTIFIER); + EXPECT_NOT_NULL(initializer->name); + EXPECT_TRUE(String_equals(initializer->name, "world")); + + // while body + BlockASTNode *body = while_statement->body; + EXPECT_NOT_NULL(body->statements); + Array *arr = body->statements; + EXPECT_NULL(arr->data); + EXPECT_EQUAL_INT(arr->size, 0); + + } TEST_END(); +} + +DESCRIBE(statement_separation, "Validity of statement separation") { + Lexer lexer; + Lexer_constructor(&lexer); + + Parser parser; + Parser_constructor(&parser, &lexer); + + ParserResult result; + + TEST_BEGIN("Single statement") { + Lexer_setSource(&lexer, "var a = 10"); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENT(result.node, NODE_VARIABLE_DECLARATION); + } TEST_END(); + + TEST_BEGIN("Multiple statements on new lines") { + Lexer_setSource( + &lexer, + "var a = 10" LF + "var b = 20" LF + ); + result = Parser_parse(&parser); + + EXPECT_TRUE(result.success); + EXPECT_STATEMENTS(result.node, 2); + } TEST_END(); + + TEST_BEGIN("Multiple statements on same line") { + Lexer_setSource( + &lexer, + "var a = 10 var b = 20" LF + ); + result = Parser_parse(&parser); + + EXPECT_FALSE(result.success); + } TEST_END(); + +} diff --git a/test/compiler/parser/parser_assertions.h b/test/compiler/parser/parser_assertions.h new file mode 100644 index 0000000..cef597a --- /dev/null +++ b/test/compiler/parser/parser_assertions.h @@ -0,0 +1,43 @@ +#include "compiler/parser/Parser.h" +#include "unit.h" + +#ifndef PARSER_ASSERTIONS_H +#define PARSER_ASSERTIONS_H + +#define LF "\n" + +/** + * Validates program body and looks for a single statemtnt of a given type. + * This creates `_program`, `_block`, `_statements` and `statement` variables to use. + */ +#define EXPECT_STATEMENT(node, type) EXPECT_NOT_NULL(node); \ + \ + ProgramASTNode *_program = (ProgramASTNode*)node; \ + EXPECT_NOT_NULL(_program->block); \ + \ + BlockASTNode *_block = _program->block; \ + EXPECT_NOT_NULL(_block->statements); \ + \ + Array *_statements = _block->statements; \ + EXPECT_EQUAL_INT(_statements->size, 1); \ + \ + StatementASTNode *statement = Array_get(_statements, 0); \ + EXPECT_NOT_NULL(statement); \ + EXPECT_TRUE(statement->_type == type); + +/** + * Validates program body and looks for a statement list of a given size. + * This creates `_program`, `_block` and `statements` variables to use. + */ +#define EXPECT_STATEMENTS(node, count) EXPECT_NOT_NULL(node); \ + \ + ProgramASTNode *_program = (ProgramASTNode*)node; \ + EXPECT_NOT_NULL(_program->block); \ + \ + BlockASTNode *_block = _program->block; \ + EXPECT_NOT_NULL(_block->statements); \ + \ + Array *statements = _block->statements; \ + EXPECT_EQUAL_INT(statements->size, count); + +#endif diff --git a/test/internal/Array.test.c b/test/internal/Array.test.c index 459faba..b8f7b05 100644 --- a/test/internal/Array.test.c +++ b/test/internal/Array.test.c @@ -2,6 +2,8 @@ #include "unit.h" #include +#define TEST_PRIORITY 100 + DESCRIBE(get, "Array_get") { Array *arr = NULL; diff --git a/test/internal/HashMap.test.c b/test/internal/HashMap.test.c index 9fdb6e8..6c68de7 100644 --- a/test/internal/HashMap.test.c +++ b/test/internal/HashMap.test.c @@ -2,6 +2,8 @@ #include "unit.h" #include +#define TEST_PRIORITY 100 + DESCRIBE(map_alloc, "HashMap_alloc/HashMap_resize") { HashMap *map = NULL; diff --git a/test/internal/String.test.c b/test/internal/String.test.c index 1b863f4..2c04a48 100644 --- a/test/internal/String.test.c +++ b/test/internal/String.test.c @@ -2,6 +2,8 @@ #include "unit.h" #include +#define TEST_PRIORITY 100 + DESCRIBE(equals, "String_equals") { String *str = NULL; diff --git a/test/register_tests.js b/test/register_tests.js index b15ea2e..346fd43 100644 --- a/test/register_tests.js +++ b/test/register_tests.js @@ -18,7 +18,8 @@ const MAX_ID_LENGTH = C_MAX_ID_LENGTH - TEST_SUIT_PREFIX.length - 1; path: file, relative: path.relative(root, file), base: path.basename(file), - functions: /**@type {{name: string, description: string, line: number}[]}*/([]) + functions: /**@type {{name: string, description: string, line: number}[]}*/([]), + priority: 0 })); const testNames = new Set(); @@ -27,6 +28,11 @@ const MAX_ID_LENGTH = C_MAX_ID_LENGTH - TEST_SUIT_PREFIX.length - 1; const content = fs.readFileSync(file.path, "utf8"); const regex = /DESCRIBE\s*\(([_a-zA-Z][_a-zA-Z0-9]*)\s*,\s*("(?:\\"|.)*?")\)/g; // 32 - 1 - "unit__".length (6) = 25 + // Get priority of test file + const [m, priority] = content.match(/^\s*#define\s+TEST_PRIORITY\s+([+-]?[0-9]+)/m) || [null, "0"]; + file.priority = parseInt(priority); + + // Get all test functions let match; while((match = regex.exec(content)) !== null) { const [m, name, description] = match; @@ -44,6 +50,9 @@ const MAX_ID_LENGTH = C_MAX_ID_LENGTH - TEST_SUIT_PREFIX.length - 1; } } + // Sort files by priority + files.sort((a, b) => b.priority - a.priority); + fs.writeFileSync(path.join(__dirname, MAIN_FILENAME), `/** * @file test/${MAIN_FILENAME} * @author test/register_tests.js