/* * abstract syntax tree types for a catskill source. */ enum Unary_Operation { UNARY_NONE, UNARY_MINUS, UNARY_NOT, UNARY_BITWISE_NOT, }; enum Unary_Operation unary_operation_from_token(const struct Token* token) { switch (token->kind) { case TOKEN_MINUS: return UNARY_MINUS; case TOKEN_BANG: return UNARY_NOT; case TOKEN_TILDE: return UNARY_BITWISE_NOT; default: return UNARY_NONE; } } const ascii* unary_operation_to_string(enum Unary_Operation operation) { switch (operation) { case UNARY_MINUS: return "-"; case UNARY_NOT: return "!"; case UNARY_BITWISE_NOT: return "~"; default: failure("unexpected unary operation passed to `unary_operation_to_string`"); return nil; } } enum Binary_Operation { BINARY_NONE, BINARY_PLUS, BINARY_MINUS, BINARY_MULTIPLY, BINARY_DIVIDE, BINARY_MODULO, BINARY_POWER, BINARY_EQUAL, BINARY_NOT_EQUAL, BINARY_GREATER_THAN, BINARY_GREATER_THAN_EQUAL, BINARY_LESS_THAN, BINARY_LESS_THAN_EQUAL, BINARY_AND, BINARY_OR, BINARY_RANGE, BINARY_BITWISE_AND, BINARY_BITWISE_OR, BINARY_BITWISE_XOR, BINARY_BITWISE_LEFT_SHIFT, BINARY_BITWISE_RIGHT_SHIFT, BINARY_ASSIGN, BINARY_ASSIGN_PLUS, BINARY_ASSIGN_MINUS, BINARY_ASSIGN_MULTIPLY, BINARY_ASSIGN_DIVIDE, BINARY_ASSIGN_MODULO, BINARY_ASSIGN_AND, BINARY_ASSIGN_OR, BINARY_ASSIGN_BITWISE_AND, BINARY_ASSIGN_BITWISE_OR, BINARY_ASSIGN_BITWISE_XOR, BINARY_ASSIGN_BITWISE_LEFT_SHIFT, BINARY_ASSIGN_BITWISE_RIGHT_SHIFT, }; enum Binary_Operation binary_operation_from_token(const struct Token* token) { switch (token->kind) { case TOKEN_PLUS: return BINARY_PLUS; case TOKEN_MINUS: return BINARY_MINUS; case TOKEN_STAR: return BINARY_MULTIPLY; case TOKEN_SLASH: return BINARY_DIVIDE; case TOKEN_PERCENT: return BINARY_MODULO; case TOKEN_STAR_STAR: return BINARY_POWER; case TOKEN_EQUAL: return BINARY_EQUAL; case TOKEN_NOT_EQUAL: return BINARY_NOT_EQUAL; case TOKEN_GREATER: return BINARY_GREATER_THAN; case TOKEN_GREATER_EQUAL: return BINARY_GREATER_THAN_EQUAL; case TOKEN_LESS: return BINARY_LESS_THAN; case TOKEN_LESS_EQUAL: return BINARY_LESS_THAN_EQUAL; case TOKEN_AND: return BINARY_AND; case TOKEN_OR: return BINARY_OR; case TOKEN_DOT_DOT: return BINARY_RANGE; case TOKEN_AMPERSAND: return BINARY_BITWISE_AND; case TOKEN_PIPE: return BINARY_BITWISE_OR; case TOKEN_CARET: return BINARY_BITWISE_XOR; case TOKEN_LEFT_SHIFT: return BINARY_BITWISE_LEFT_SHIFT; case TOKEN_RIGHT_SHIFT: return BINARY_BITWISE_RIGHT_SHIFT; case TOKEN_ASSIGN: return BINARY_ASSIGN; case TOKEN_ASSIGN_PLUS: return BINARY_ASSIGN_PLUS; case TOKEN_ASSIGN_MINUS: return BINARY_ASSIGN_MINUS; case TOKEN_ASSIGN_STAR: return BINARY_ASSIGN_MULTIPLY; case TOKEN_ASSIGN_SLASH: return BINARY_ASSIGN_DIVIDE; case TOKEN_ASSIGN_PERCENT: return BINARY_ASSIGN_MODULO; case TOKEN_ASSIGN_AND: return BINARY_ASSIGN_AND; case TOKEN_ASSIGN_OR: return BINARY_ASSIGN_OR; case TOKEN_ASSIGN_AMPERSAND: return BINARY_ASSIGN_BITWISE_AND; case TOKEN_ASSIGN_PIPE: return BINARY_ASSIGN_BITWISE_OR; case TOKEN_ASSIGN_CARET: return BINARY_ASSIGN_BITWISE_XOR; case TOKEN_ASSIGN_LEFT_SHIFT: return BINARY_ASSIGN_BITWISE_LEFT_SHIFT; case TOKEN_ASSIGN_RIGHT_SHIFT: return BINARY_ASSIGN_BITWISE_RIGHT_SHIFT; default: return BINARY_NONE; } } // return the precedence of the given binary operation. // lower values are weaker, with 1 being the weakest. // the values are taken mostly the same as other C-family languages. uint binary_operation_precedence(enum Binary_Operation operation) { switch (operation) { // weakest case BINARY_ASSIGN: case BINARY_ASSIGN_PLUS: case BINARY_ASSIGN_MINUS: case BINARY_ASSIGN_MULTIPLY: case BINARY_ASSIGN_DIVIDE: case BINARY_ASSIGN_MODULO: case BINARY_ASSIGN_AND: case BINARY_ASSIGN_OR: case BINARY_ASSIGN_BITWISE_AND: case BINARY_ASSIGN_BITWISE_OR: case BINARY_ASSIGN_BITWISE_XOR: case BINARY_ASSIGN_BITWISE_LEFT_SHIFT: case BINARY_ASSIGN_BITWISE_RIGHT_SHIFT: return 1; case BINARY_RANGE: return 2; case BINARY_OR: return 3; case BINARY_AND: return 4; case BINARY_BITWISE_OR: return 5; case BINARY_BITWISE_XOR: return 6; case BINARY_BITWISE_AND: return 7; case BINARY_EQUAL: case BINARY_NOT_EQUAL: return 8; case BINARY_GREATER_THAN: case BINARY_GREATER_THAN_EQUAL: case BINARY_LESS_THAN: case BINARY_LESS_THAN_EQUAL: return 9; case BINARY_BITWISE_LEFT_SHIFT: case BINARY_BITWISE_RIGHT_SHIFT: return 10; case BINARY_PLUS: case BINARY_MINUS: return 11; case BINARY_MULTIPLY: case BINARY_DIVIDE: case BINARY_MODULO: return 12; case BINARY_POWER: return 13; // strongest default: failure("unexpected binary operation passed to `binary_operation_precedence`"); return 0; } } enum Binary_Operation_Associativity { BINARY_ASSOCIATIVITY_NONE, BINARY_ASSOCIATIVITY_LEFT, BINARY_ASSOCIATIVITY_RIGHT, }; enum Binary_Operation_Associativity binary_operation_associativity(enum Binary_Operation operation) { switch (operation) { case BINARY_ASSIGN: case BINARY_ASSIGN_PLUS: case BINARY_ASSIGN_MINUS: case BINARY_ASSIGN_MULTIPLY: case BINARY_ASSIGN_DIVIDE: case BINARY_ASSIGN_MODULO: case BINARY_ASSIGN_AND: case BINARY_ASSIGN_OR: case BINARY_ASSIGN_BITWISE_AND: case BINARY_ASSIGN_BITWISE_OR: case BINARY_ASSIGN_BITWISE_XOR: case BINARY_ASSIGN_BITWISE_LEFT_SHIFT: case BINARY_POWER: return BINARY_ASSOCIATIVITY_RIGHT; case BINARY_RANGE: return BINARY_ASSOCIATIVITY_NONE; default: return BINARY_ASSOCIATIVITY_LEFT; } } const ascii* binary_operation_to_string(enum Binary_Operation operation) { switch (operation) { case BINARY_PLUS: return "+"; case BINARY_MINUS: return "-"; case BINARY_MULTIPLY: return "*"; case BINARY_DIVIDE: return "/"; case BINARY_MODULO: return "%"; case BINARY_POWER: return "**"; case BINARY_EQUAL: return "=="; case BINARY_NOT_EQUAL: return "!="; case BINARY_GREATER_THAN: return ">"; case BINARY_GREATER_THAN_EQUAL: return ">="; case BINARY_LESS_THAN: return "<"; case BINARY_LESS_THAN_EQUAL: return "<="; case BINARY_AND: return "&&"; case BINARY_OR: return "||"; case BINARY_RANGE: return ".."; case BINARY_BITWISE_AND: return "&"; case BINARY_BITWISE_OR: return "|"; case BINARY_BITWISE_XOR: return "^"; case BINARY_BITWISE_LEFT_SHIFT: return "<<"; case BINARY_BITWISE_RIGHT_SHIFT: return ">>"; case BINARY_ASSIGN: return "="; case BINARY_ASSIGN_PLUS: return "+="; case BINARY_ASSIGN_MINUS: return "-="; case BINARY_ASSIGN_MULTIPLY: return "*="; case BINARY_ASSIGN_DIVIDE: return "/="; case BINARY_ASSIGN_MODULO: return "%="; case BINARY_ASSIGN_AND: return "&&="; case BINARY_ASSIGN_OR: return "||="; case BINARY_ASSIGN_BITWISE_AND: return "&="; case BINARY_ASSIGN_BITWISE_OR: return "|="; case BINARY_ASSIGN_BITWISE_XOR: return "^="; case BINARY_ASSIGN_BITWISE_LEFT_SHIFT: return "<<="; case BINARY_ASSIGN_BITWISE_RIGHT_SHIFT: return ">>="; default: failure("unexpected binary operation passed to `binary_operation_to_string`"); return nil; } } enum Increment_Decrement_Operation { INCREMENT_DECREMENT_NONE, INCREMENT_DECREMENT_INCREMENT, INCREMENT_DECREMENT_DECREMENT, }; enum Increment_Decrement_Operation increment_decrement_operation_from_token(const struct Token* token) { switch (token->kind) { case TOKEN_PLUS_PLUS: return INCREMENT_DECREMENT_INCREMENT; case TOKEN_MINUS_MINUS: return INCREMENT_DECREMENT_DECREMENT; default: return INCREMENT_DECREMENT_NONE; } } const ascii* increment_decrement_operation_to_string(enum Increment_Decrement_Operation operation) { switch (operation) { case INCREMENT_DECREMENT_INCREMENT: return "++"; case INCREMENT_DECREMENT_DECREMENT: return "--"; default: failure("unexpected increment/decrement operation passed to " "`increment_decrement_operation_to_string`"); return nil; } } // nodes are parts of the syntax tree that are reused often // and in different places. // a block of code, enclosed in curly braces. // represents a sequence of statements that are executed in order. struct Block_Node { struct Statement* statements; struct Span span; struct Cursor location; }; void block_node_print(const struct Block_Node* block); // a function header, describing the parameters and return type of function. // used both as a type and in full function definitions. struct Function_Header_Node { // linked list of parameters. // name, if given, is included in the type node. struct Type_Node* parameters_type_and_name; struct Type_Node* return_type; struct Span span; }; void function_header_node_print(const struct Function_Header_Node* header); // a declaration of a variable, constant, or other binding, without a mutability // signifier, like `let` or `var`. // the mutability is determined by some outside context, where // a bare declaration in a for-loop, for example, is always mutable. struct Bare_Declaration_Node { struct String_Array names; struct Expression* initializer; struct Type_Node* type; struct Span span; struct Cursor location; }; void bare_declaration_node_print(const struct Bare_Declaration_Node* declaration); enum Type_Node_Type { TYPE_NODE_NONE, TYPE_NODE_NAME, TYPE_NODE_ARRAY, // an array of a type, `[int]`. TYPE_NODE_REFERENCE, // a reference to a type, `&int`. TYPE_NODE_MAYBE, // a type that may be null, `int?`. TYPE_NODE_TUPLE, // a tuple type, `(int string)`. TYPE_NODE_MAP, // a map type, `[string = int]`. TYPE_NODE_FUNCTION, // a function type, `fun (int) int`. TYPE_NODE_STRUCTURE, // a struct, invoked either with `type` or with `{}` when a type is inline. TYPE_NODE_VARIANT, // a tagged union. TYPE_NODE_CLASS, // a class of types, a.k.a. an interface. }; struct Type_Node_Name { struct String name; }; struct Type_Node_Array { struct Type_Node* element_type; }; struct Type_Node_Reference { struct Type_Node* referenced_type; }; struct Type_Node_Maybe { struct Type_Node* inner_type; }; struct Type_Node_Tuple { struct Type_Node* head; // the first type in the tuple, if any. }; struct Type_Node_Map { struct Type_Node* key_type; struct Type_Node* value_type; }; struct Type_Node_Function { struct Function_Header_Node header; }; struct Type_Node_Structure { // the fields of the structure, linked list of types and (required) names. struct Type_Node* fields; }; struct Type_Node_Variant { // the variants of the tagged union, linked list of (required) variant names and backing types. // if a variant has no backing type, it is TYPE_NODE_NONE. struct Type_Node* variants; }; struct Type_Node_Class { // linked list of the types of methods required to implement the class. // each node is required to have a name and be of TYPE_NODE_FUNCTION. struct Type_Node* methods; }; union Type_Node_Value { struct Type_Node_Name name; struct Type_Node_Array array; struct Type_Node_Reference reference; struct Type_Node_Maybe maybe; struct Type_Node_Tuple tuple; struct Type_Node_Map map; struct Type_Node_Function function; struct Type_Node_Structure structure; struct Type_Node_Variant variant; struct Type_Node_Class class; }; // a type node represents a type in the syntax tree. // currently, we only support types that are simple names. // or null types. // also includes the name of the field, member, parameter, etc., for which // the type is defined. struct Type_Node { enum Type_Node_Type type; union Type_Node_Value value; // note: we could also just include the token here i think? struct Span span; struct Cursor location; // usually a type is preceded by the name of the binding // it is assigned to, e.g. `x int`, `string y`, etc. // this is the name of that binding, for ease of listing. struct String value_name; // if type is within a group of multiple types, // points to the next type within the group. struct Type_Node* next; }; REGION(struct Type_Node, type_node) // allocates a new type node in the global type node region. struct Type_Node* type_node_new( enum Type_Node_Type type, union Type_Node_Value value, struct Span span, struct Cursor location) { check(region_type_node_cursor < REGION_SIZE, "out of type node memory"); struct Type_Node* type_node = ®ion_type_node[region_type_node_cursor++]; *type_node = (struct Type_Node){ .type = type, .value = value, .span = span, .location = location, .value_name = string_empty(), .next = nil, }; return type_node; } // allocates a new type node with no value, used for `none` types. // this is used for types that are not specified, note that it is still // fully allocated and can be used in the syntax tree. struct Type_Node* type_node_none(struct Span span, struct Cursor location) { return type_node_new(TYPE_NODE_NONE, (union Type_Node_Value){ 0 }, span, location); } bool type_node_is_none(const struct Type_Node* type_node) { return type_node->type == TYPE_NODE_NONE; } void type_node_print(const struct Type_Node* type_node) { printf("(type "); switch (type_node->type) { case TYPE_NODE_NONE: printf("none"); break; case TYPE_NODE_NAME: printf("name %s", type_node->value.name.name.data); break; case TYPE_NODE_ARRAY: printf("array of "); type_node_print(type_node->value.array.element_type); break; case TYPE_NODE_REFERENCE: printf("reference to "); type_node_print(type_node->value.reference.referenced_type); break; case TYPE_NODE_MAYBE: printf("maybe "); type_node_print(type_node->value.maybe.inner_type); break; case TYPE_NODE_TUPLE: { printf("tuple"); FOR_EACH(struct Type_Node*, current, type_node->value.tuple.head) { printf(" "); type_node_print(current); } break; } case TYPE_NODE_MAP: printf("map "); type_node_print(type_node->value.map.key_type); printf(" = "); type_node_print(type_node->value.map.value_type); break; case TYPE_NODE_FUNCTION: { printf("function "); function_header_node_print(&type_node->value.function.header); break; } case TYPE_NODE_STRUCTURE: { printf("structure"); FOR_EACH(struct Type_Node*, current, type_node->value.structure.fields) { printf(" (field %s) ", current->value_name.data); type_node_print(current); } break; } case TYPE_NODE_VARIANT: { printf("variant"); FOR_EACH(struct Type_Node*, current, type_node->value.variant.variants) { if (type_node_is_none(current)) { printf(" (variant %s)", current->value_name.data); } else { printf(" (variant %s of ", current->value_name.data); type_node_print(current); printf(")"); } } break; } case TYPE_NODE_CLASS: printf("class"); FOR_EACH(struct Type_Node*, current, type_node->value.class.methods) { check(current->type == TYPE_NODE_FUNCTION, "expected class method type node to be a function type"); printf(" (method %s ", current->value_name.data); function_header_node_print(¤t->value.function.header); printf(")"); } break; } printf(")"); } enum Expression_Kind { EXPRESSION_NONE, EXPRESSION_INTEGER_LITERAL, EXPRESSION_FLOAT_LITERAL, EXPRESSION_STRING_LITERAL, EXPRESSION_BOOLEAN_LITERAL, EXPRESSION_NAME, EXPRESSION_UNARY_OPERATION, EXPRESSION_BINARY_OPERATION, EXPRESSION_GROUP, EXPRESSION_CALL, EXPRESSION_SUBSCRIPT, EXPRESSION_MEMBER, EXPRESSION_INCREMENT_DECREMENT, EXPRESSION_FUNCTION, EXPRESSION_TYPE, }; struct Expression_Integer_Literal { int64 value; // might not fit entire number given in source. }; struct Expression_Float_Literal { float64 value; }; struct Expression_String_Literal { struct String value; }; struct Expression_Bool_Literal { bool value; }; struct Expression_Name { struct String name; }; struct Expression_Unary_Operator { enum Unary_Operation operation; struct Expression* operand; }; struct Expression_Binary_Operator { enum Binary_Operation operation; struct Expression* left_operand; struct Expression* right_operand; }; struct Expression_Group { struct Expression* inner_expression; }; struct Expression_Call { struct Expression* subject; struct Expression* arguments; // linked list of expressions. }; struct Expression_Subscript { struct Expression* subject; struct Expression* index; }; struct Expression_Member { struct Expression* subject; struct String name; }; struct Expression_Increment_Decrement { // whether the increment/decrement is a prefix or postfix operation. bool prefix; struct Expression* subject; enum Increment_Decrement_Operation operation; }; struct Expression_Function { struct Function_Header_Node header; struct Block_Node body; }; struct Expression_Type { struct Type_Node* type; }; union Expression_Value { struct Expression_Integer_Literal integer_literal; struct Expression_Float_Literal float_literal; struct Expression_String_Literal string_literal; struct Expression_Bool_Literal bool_literal; struct Expression_Name name; struct Expression_Unary_Operator unary_operator; struct Expression_Binary_Operator binary_operator; struct Expression_Group group; struct Expression_Call call; struct Expression_Subscript subscript; struct Expression_Member member; struct Expression_Increment_Decrement increment_decrement; struct Expression_Function function; struct Expression_Type type; }; struct Expression { enum Expression_Kind kind; union Expression_Value value; struct Span span; struct Cursor location; // if expression is within a group of multiple expressions, // points to the next expression within it. struct Expression* next; }; REGION(struct Expression, expression) struct Expression* expression_new( enum Expression_Kind kind, union Expression_Value value, struct Span span, struct Cursor location) { check(region_expression_cursor < REGION_SIZE, "out of expression memory"); struct Expression* expression = ®ion_expression[region_expression_cursor++]; *expression = (struct Expression){ .kind = kind, .value = value, .span = span, .location = location, .next = nil, }; return expression; } void function_header_node_print(const struct Function_Header_Node* header) { FOR_EACH(struct Type_Node*, current, header->parameters_type_and_name) { if (current != header->parameters_type_and_name) printf(" "); if (!string_is_empty(current->value_name)) printf("(param %s) ", current->value_name.data); else printf("(param) "); type_node_print(current); } if (header->return_type) { printf(" (returns "); type_node_print(header->return_type); printf(")"); } } void expression_print(const struct Expression* expression) { printf("(expr "); switch (expression->kind) { case EXPRESSION_NONE: printf("none"); break; case EXPRESSION_INTEGER_LITERAL: printf("%ld", expression->value.integer_literal.value); break; case EXPRESSION_FLOAT_LITERAL: printf("%lf", expression->value.float_literal.value); break; case EXPRESSION_STRING_LITERAL: printf("\"%s\"", expression->value.string_literal.value.data); break; case EXPRESSION_BOOLEAN_LITERAL: printf("%s", expression->value.bool_literal.value ? "true" : "false"); break; case EXPRESSION_NAME: printf("(name %s)", expression->value.name.name.data); break; case EXPRESSION_UNARY_OPERATION: printf("(unary %s ", unary_operation_to_string(expression->value.unary_operator.operation)); expression_print(expression->value.unary_operator.operand); printf(")"); break; case EXPRESSION_BINARY_OPERATION: printf( "(binary %s ", binary_operation_to_string(expression->value.binary_operator.operation)); expression_print(expression->value.binary_operator.left_operand); printf(" "); expression_print(expression->value.binary_operator.right_operand); printf(")"); break; case EXPRESSION_GROUP: printf("(group "); expression_print(expression->value.group.inner_expression); printf(")"); break; case EXPRESSION_CALL: printf("(call "); expression_print(expression->value.call.subject); FOR_EACH(struct Expression*, argument, expression->value.call.arguments) { printf(" "); expression_print(argument); } printf(")"); break; case EXPRESSION_SUBSCRIPT: printf("(subscript "); expression_print(expression->value.subscript.subject); printf(" "); expression_print(expression->value.subscript.index); printf(")"); break; case EXPRESSION_MEMBER: printf("(member "); expression_print(expression->value.member.subject); printf(")"); break; case EXPRESSION_INCREMENT_DECREMENT: { const struct Expression_Increment_Decrement* inc_dec = &expression->value.increment_decrement; const ascii* prefix_or_postfix = inc_dec->prefix ? "prefix" : "postfix"; printf("(increment/decrement %s %s ", increment_decrement_operation_to_string(inc_dec->operation), prefix_or_postfix); expression_print(inc_dec->subject); break; } case EXPRESSION_FUNCTION: { const struct Expression_Function* fun = &expression->value.function; printf("(function "); function_header_node_print(&fun->header); printf(" "); block_node_print(&fun->body); printf(")"); break; } case EXPRESSION_TYPE: type_node_print(expression->value.type.type); break; default: failure("unexpected expression kind passed to `expression_print`"); break; } printf(")"); } enum Statement_Kind { STATEMENT_NONE, STATEMENT_EXPRESSION, STATEMENT_DECLARATION, // NOTE: a block could be an expression in the future. STATEMENT_BLOCK, STATEMENT_CONDITIONAL, STATEMENT_LOOP, STATEMENT_RETURN, STATEMENT_BREAK, STATEMENT_CONTINUE, STATEMENT_DEFER, }; struct Statement_Value_Expression { struct Expression* inner; }; enum Statement_Declaration_Kind { STATEMENT_DECLARATION_NONE, STATEMENT_DECLARATION_VARIABLE, STATEMENT_DECLARATION_CONSTANT, }; enum Statement_Declaration_Kind statement_declaration_kind_from_token(const struct Token* token) { switch (token->kind) { case TOKEN_WORD_VAR: return STATEMENT_DECLARATION_VARIABLE; case TOKEN_WORD_LET: return STATEMENT_DECLARATION_CONSTANT; default: return STATEMENT_DECLARATION_NONE; } } struct Statement_Value_Declaration { enum Statement_Declaration_Kind kind; struct Bare_Declaration_Node inner; }; struct Statement_Value_Block { struct Block_Node inner; // the block of statements. }; #define STATEMENT_VALUE_CONDITIONAL_MAX 8 struct Statement_Value_Conditional { struct Statement_Conditional_Branch { // if nil, the condition is always true. struct Expression* when; struct Block_Node then; } conditions[STATEMENT_VALUE_CONDITIONAL_MAX]; uint condition_count; }; enum Statement_Loop_Style { STATEMENT_LOOP_STYLE_NONE, STATEMENT_LOOP_STYLE_C, // for i int = 0; i < 10; ++i {} STATEMENT_LOOP_STYLE_FOR_EACH, // for x Obj = list {} STATEMENT_LOOP_STYLE_WHILE, // while true {} STATEMENT_LOOP_STYLE_ENDLESS, // while {} }; // stands for both `for` and `while` loops. struct Statement_Value_Loop { enum Statement_Loop_Style style; struct Bare_Declaration_Node declaration; struct Expression* condition; struct Expression* iteration; struct Block_Node body; }; struct Statement_Value_Return { // nil if there is no return value. struct Expression* value; }; struct Statement_Value_Defer { // either a simple expression, or, if expression is nil, // a block of code to execute. struct Expression* expression; struct Block_Node block; }; union Statement_Value { struct Statement_Value_Expression expression; struct Statement_Value_Declaration declaration; struct Statement_Value_Block block; struct Statement_Value_Conditional conditional; struct Statement_Value_Loop loop; struct Statement_Value_Return return_value; struct Statement_Value_Defer defer; }; struct Statement { enum Statement_Kind kind; union Statement_Value value; struct Span span; struct Cursor location; // if statement is within a group of multiple statements, // points to the next statement within it. struct Statement* next; }; REGION(struct Statement, statement) void statement_print(const struct Statement* statement); void block_node_print(const struct Block_Node* block) { printf("(block \n"); FOR_EACH(struct Statement*, statement, block->statements) { printf("\t"); statement_print(statement); printf("\n"); } printf(")"); } void bare_declaration_node_print(const struct Bare_Declaration_Node* declaration) { printf("(declaration "); STRING_ARRAY_FOR_EACH(i, name, declaration->names) { printf("%s ", name.data); } if (!type_node_is_none(declaration->type)) { type_node_print(declaration->type); printf(" "); } if (declaration->initializer) { printf("(initializer "); expression_print(declaration->initializer); printf(")"); } printf(")"); } struct Statement* statement_new( enum Statement_Kind kind, union Statement_Value value, struct Span span, struct Cursor location) { check(region_statement_cursor < REGION_SIZE, "out of statement memory"); struct Statement* statement = ®ion_statement[region_statement_cursor++]; *statement = (struct Statement){ .kind = kind, .value = value, .span = span, .location = location, .next = nil, }; return statement; } void statement_print(const struct Statement* statement) { printf("(stmt "); switch (statement->kind) { case STATEMENT_NONE: printf("none"); break; case STATEMENT_EXPRESSION: { const struct Expression* expression = statement->value.expression.inner; expression_print(expression); break; } case STATEMENT_DECLARATION: { if (statement->value.declaration.kind == STATEMENT_DECLARATION_VARIABLE) printf("(variable "); else if (statement->value.declaration.kind == STATEMENT_DECLARATION_CONSTANT) printf("(constant "); bare_declaration_node_print(&statement->value.declaration.inner); printf(")"); break; } case STATEMENT_BLOCK: block_node_print(&statement->value.block.inner); break; case STATEMENT_CONDITIONAL: { printf("(conditional"); for (uint i = 0; i < statement->value.conditional.condition_count; ++i) { const struct Statement_Conditional_Branch* branch = &statement->value.conditional.conditions[i]; printf(" "); if (branch->when) { printf("(when "); expression_print(branch->when); printf(") "); } else { printf("(always) "); } block_node_print(&branch->then); } printf(")"); break; } case STATEMENT_LOOP: { printf("(loop "); switch (statement->value.loop.style) { case STATEMENT_LOOP_STYLE_C: printf("c-style "); bare_declaration_node_print(&statement->value.loop.declaration); printf(" (condition "); expression_print(statement->value.loop.condition); printf(") (iteration "); expression_print(statement->value.loop.iteration); printf(") "); break; case STATEMENT_LOOP_STYLE_FOR_EACH: printf("for-each "); bare_declaration_node_print(&statement->value.loop.declaration); printf(" "); break; case STATEMENT_LOOP_STYLE_WHILE: printf("while (condition "); expression_print(statement->value.loop.condition); printf(") "); break; case STATEMENT_LOOP_STYLE_ENDLESS: printf("endless "); break; default: failure("unexpected loop style in `statement_print`"); break; } block_node_print(&statement->value.loop.body); printf(")"); break; } case STATEMENT_RETURN: { printf("(return"); if (statement->value.return_value.value) { printf(" "); expression_print(statement->value.return_value.value); } printf(")"); break; } case STATEMENT_BREAK: printf("(break)"); break; case STATEMENT_CONTINUE: printf("(continue)"); break; case STATEMENT_DEFER: { printf("(defer "); if (statement->value.defer.expression) { expression_print(statement->value.defer.expression); } else { block_node_print(&statement->value.defer.block); } printf(")"); break; } default: failure("unexpected statement kind passed to `statement_print`"); break; } printf(")"); } // the top-level tree of a single catskill source file. struct Tree { struct Statement* top_level_statements; }; void tree_print(const struct Tree* tree) { FOR_EACH(struct Statement*, statement, tree->top_level_statements) { statement_print(statement); printf("\n"); } }