From e5f9d49e39210fe634305d57f1b01e013d66aa71 Mon Sep 17 00:00:00 2001 From: Kimplul Date: Tue, 9 Apr 2024 02:50:34 +0300 Subject: simplify ast definition + Makes it a lot nicer to work with. --- Makefile | 2 +- include/ek/actualize.h | 61 +- include/ek/ast.h | 1680 +++++++++++++--------------------------- include/ek/debug.h | 20 +- include/ek/lower.h | 2 +- include/ek/parser.h | 2 +- include/ek/scope.h | 61 +- include/ek/vec.h | 3 + src/actualize.c | 1865 ++++++++++++++++++++------------------------ src/ast.c | 2021 ++++++++++-------------------------------------- src/compiler.c | 12 +- src/debug.c | 133 ++-- src/lexer.l | 36 +- src/lower.c | 365 ++++----- src/parser.y | 265 ++++--- src/scope.c | 118 ++- tests/if2.ek | 6 +- tests/loop.ek | 3 +- 18 files changed, 2285 insertions(+), 4370 deletions(-) diff --git a/Makefile b/Makefile index fe2837c..4ded5db 100644 --- a/Makefile +++ b/Makefile @@ -1,7 +1,7 @@ DO != echo -n > deps.mk DEBUGFLAGS != [ $(RELEASE) ] && echo "-flto=auto -O2 -DNODEBUG" || echo "-O0 -DDEBUG" -CFLAGS = -Wall -Wextra -Wconversion -g +CFLAGS = -Wall -Wextra -Wconversion -ggdb3 DEPFLAGS = -MT $@ -MMD -MP -MF $@.d LINTFLAGS = -fsyntax-only INCLUDEFLAGS = -Iinclude diff --git a/include/ek/actualize.h b/include/ek/actualize.h index 486f19e..9dc5948 100644 --- a/include/ek/actualize.h +++ b/include/ek/actualize.h @@ -25,47 +25,7 @@ * @param b Type to compare against \p a. * @return \c 1 if types match, \c 0 otherwise. */ -int types_match(struct ast_node *a, struct ast_node *b); - -/** - * Replace type in \p type that matches with \p from - * with \p to. - * - * @param type Whole type to replace part in. - * @param from Part of type to replace. - * @param to Type to replace \p from with. - */ -void replace_type(struct ast_node *type, - struct ast_node *from, struct ast_node *to); - -/** - * Loop over parameters and replace types that match with \p param_type - * with \p arg_type. - * - * @param param Parameters to replace types for. - * @param param_type Type to replace with \p arg_type. - * @param arg_type Type to replace \p param_type with. - */ -void replace_param_types(struct ast_node *param, - struct ast_node *param_type, - struct ast_node *arg_type); - -/** - * Extract trait type, that is strip pointer/expression/alias stuff - * and get the trait itself from the type. - * - * @param type Type to extract trait from. - * @return Pointer to trait node when found, \c NULL otherwise. - */ -struct ast_node *extract_trait(struct ast_node *type); - -/** - * Extract typeof expression. - * - * @param type Type to extract typeof from. - * @return Pointer to typeof expression node when found, \c NULL otherwise. - */ -struct ast_node *extract_typeof(struct ast_node *type); +int types_match(struct type *a, struct type *b); /** * Analyze raw AST, assumed to be file scope. @@ -77,23 +37,6 @@ struct ast_node *extract_typeof(struct ast_node *type); * @param tree Raw AST to analyze. * @return \c 0 when successful, non-zero otherwise. */ -int analyze_root(struct scope *scope, struct ast_node *tree); - -/** - * Start actual compilation by trying to actualize main. - * - * @param scope Scope \c main() should be visible to, likely root scope. - * @return \c 0 when succesful, non-zero otherwise. - */ -int actualize_main(struct scope *scope); - -/** - * Create temporary actualization context and actualizes \p type within it. - * - * @param scope Scope to actualize \p type in. - * @param type Type to actualize. - * @return \c 0 when succesful, non-zero otherwise. - */ -int actualize_temp_type(struct scope *scope, struct ast_node *type); +int analyze_root(struct scope *scope, struct ast *tree); #endif /* ANALYZE_H */ diff --git a/include/ek/ast.h b/include/ek/ast.h index f7c6201..27e82e3 100644 --- a/include/ek/ast.h +++ b/include/ek/ast.h @@ -5,6 +5,7 @@ #define AST_H #include +#include /** * @file ast.h @@ -12,118 +13,6 @@ * Abstract syntax tree handling. */ -#define AST_ID(x) x->_id -#define AST_AS(x) x->_as -#define AST_DOT(x) x->_dot -#define AST_UNOP(x) x->_unop -#define AST_IMPORT(x) x->_import -#define AST_ALIAS(x) x->_alias -#define AST_TRAIT(x) x->_trait -#define AST_CAST(x) x->_cast -#define AST_PROC(x) x->_proc -#define AST_VAR(x) x->_var -#define AST_RETURN(x) x->_return -#define AST_STRUCT(x) x->_struct -#define AST_ENUM(x) x->_enum -#define AST_CALL(x) x->_call -#define AST_CONST(x) x->_const -#define AST_ASSIGN(x) x->_assign -#define AST_BLOCK(x) x->_block -#define AST_ARR_ACCESS(x) x->_arr_access -#define AST_MACRO_CONSTRUCT(x) x->_macro_construct -#define AST_MACRO_EXPAND(x) x->_macro_expand -#define AST_TYPE_EXPAND(x) x->_type_expand -#define AST_IF(x) x->_if -#define AST_CASE(x) x->_case -#define AST_SWITCH(x) x->_switch -#define AST_VAL(x) x->_val -#define AST_EMBED(x) x->_embed -#define AST_CTRL(x) x->_ctrl -#define AST_WHILE(x) x->_while -#define AST_FOR(x) x->_for -#define AST_DEFER(x) x->_defer -#define AST_BINOP(x) x->_binop -#define AST_LABEL(x) x->_label -#define AST_GOTO(x) x->_goto -#define AST_INIT(x) x->_init -#define AST_SIZEOF(x) x->_sizeof -#define AST_FETCH(x) x->_fetch - -#define AST_TYPE(x) x->_type -#define AST_ID_TYPE(x) x->_type._id -#define AST_CONSTRUCT_TYPE(x) x->_type._construct -#define AST_TRAIT_TYPE(x) x->_type._trait -/** @todo is sign and proc type the same ? */ -#define AST_PROC_TYPE(x) x->_type._proc -#define AST_ARR_TYPE(x) x->_type._arr -#define AST_SIGN_TYPE(x) x->_type._sign -#define AST_ENUM_TYPE(x) x->_type._enum -#define AST_UNION_TYPE(x) x->_type._union -#define AST_STRUCT_TYPE(x) x->_type._struct -/* might rename primitive to something else */ -#define AST_PRIMITIVE_TYPE(x) x->_type._primitive -#define AST_PTR_TYPE(x) x->_type._ptr - -/** Binary operands, that is they take two arguments and produce a result. */ -enum ast_binops { - /** Add, \c + */ - AST_ADD, - /** Subtract, \c - */ - AST_SUB, - /** Multiply, \ * */ - AST_MUL, - /** Divide, \c \ */ - AST_DIV, - /** Remainder, \c % */ - AST_REM, - /** Logical and, \c && */ - AST_LAND, - /** Logical or, \c ||*/ - AST_LOR, - /** Left shift (logical), \c << @todo add arithmetic shifts? */ - AST_LSHIFT, - /** Right shift (logical), \c >> */ - AST_RSHIFT, - /** Assigning add, \c += */ - AST_ASSIGN_ADD, - /** Assigning subtraction, \c -= */ - AST_ASSIGN_SUB, - /** Assigning multiplication, \c *= */ - AST_ASSIGN_MUL, - /** Assigning division, \c /= */ - AST_ASSIGN_DIV, - /** Assigning remainder, \c %= */ - AST_ASSIGN_REM, - /** Assigning logical left shift, \c >>= */ - AST_ASSIGN_LSHIFT, - /** Assigning logical right shift, \c <<= */ - AST_ASSIGN_RSHIFT, - /** Less than, \c < */ - AST_LT, - /** Greater than, \c > */ - AST_GT, - /** Less than or equal, \c <= */ - AST_LE, - /** Greater than or equal, \c >= */ - AST_GE, - /** Not equal, \c != */ - AST_NE, - /** Equal, \c == */ - AST_EQ, -}; - -/** Unary operations, that is they take one argument and produce a result. */ -enum ast_unops { - /** Negation, \c - */ - AST_NEG, - /** Logical negation, \c ! */ - AST_LNOT, - /** Referencing, \c & */ - AST_REF, - /** Dereferencing, \c ' */ - AST_DEREF, -}; - #define NULL_LOC() ((struct src_loc){0, 0, 0, 0}) /** Represents a source location, spanning over some bit of code. */ @@ -139,20 +28,16 @@ struct src_loc { }; /** Possible AST node types. We reserve node 0 as an illegal value. */ -enum ast_node_type { - /** Binary operation. */ - AST_BINOP = 1, - /** Unary operation. */ - AST_UNOP, +enum ast_kind { /** Fetch enum constant. */ - AST_FETCH, + AST_FETCH = 1, /** Value initialization. */ AST_INIT, /** Assignment. */ AST_ASSIGN, /** Call procedure. */ AST_CALL, - AST_ARR_ACCESS, + AST_ARR, /** Sizeof. */ AST_SIZEOF, /** Cast. */ @@ -160,17 +45,17 @@ enum ast_node_type { /** Defer. */ AST_DEFER, /** Macro definition. */ - AST_MACRO_CONSTRUCT, + AST_MACRO_DEF, AST_MACRO_EXPAND, AST_TYPE_EXPAND, /** Procedure definition. */ - AST_PROC, + AST_PROC_DEF, /** Goto. */ AST_GOTO, /** Goto label. */ AST_LABEL, /** Variable declaration/definition. */ - AST_VAR, + AST_VAR_DEF, /** For loop. */ AST_FOR, /** Embed file contents. */ @@ -182,88 +67,99 @@ enum ast_node_type { * \c AST_DELAYED is active for \c do loops. */ AST_WHILE, + AST_DO_WHILE, /** Control statement, \c break or \c continue. */ - AST_CTRL, + AST_BREAK, + AST_CONTINUE, /** Return. */ AST_RETURN, /** Alias definition. */ - AST_ALIAS, + AST_ALIAS_DEF, /** More like trait. @todo really come up with consistent naming. */ - AST_TRAIT, + AST_TRAIT_DEF, /** Structure definition. */ - AST_STRUCT, + AST_STRUCT_DEF, /** If. */ AST_IF, - /** Special 'meta' node that represents a type. */ - AST_TYPE, /** Block. E.g. \c {} */ AST_BLOCK, /** Import. */ AST_IMPORT, /** Enum definition. */ - AST_ENUM, + AST_ENUM_DEF, /** Enum constant value. */ AST_VAL, /** Switch. */ AST_SWITCH, /** Switch case. */ AST_CASE, - /** Constant value. */ - AST_CONST, /** Any ID, variable or whatever. */ AST_ID, /** As statement, used to determine types. */ AST_AS, /** Empty. Essentially noop. */ AST_EMPTY, -}; - -/** Whether \c AST_CTRL is break or continue. */ -enum ast_ctrl_kind { - /** Break. */ - AST_CTRL_BREAK, - /** Continue. */ - AST_CTRL_CONTINUE -}; - -/** Constant value kind. */ -enum ast_const_kind { - /** Integer, i27 */ - AST_CONST_INTEGER, - /** String. */ - AST_CONST_STRING, -}; - -/** Type representation. */ -enum ast_type_kind { - /** ID, can refer to pretty much anything. */ - AST_TYPE_ID, - AST_TYPE_PRIMITIVE, - /** Array. */ - AST_TYPE_ARR, - AST_TYPE_CONSTRUCT, - /** Trait. */ - AST_TYPE_TRAIT, - /** Pointer to a type. */ - AST_TYPE_POINTER, - /** Structure. */ - AST_TYPE_STRUCT, - /** Enum. */ - AST_TYPE_ENUM, - /** Signature, i.e. procedure signature. */ - AST_TYPE_SIGN, -}; - -/** Whether typedef makes an alias on trait. */ -enum ast_typedef_kind { - /** Alias. */ - AST_TYPEDEF_ALIAS, - /** Trait. */ - AST_TYPEDEF_TRAIT, + /** Add, \c + */ + AST_ADD, + /** Subtract, \c - */ + AST_SUB, + /** Multiply, \ * */ + AST_MUL, + /** Divide, \c \ */ + AST_DIV, + /** Remainder, \c % */ + AST_REM, + /** Logical and, \c && */ + AST_LAND, + /** Logical or, \c ||*/ + AST_LOR, + /** Left shift (logical), \c << @todo add arithmetic shifts? */ + AST_LSHIFT, + /** Right shift (logical), \c >> */ + AST_RSHIFT, + /** Assigning add, \c += */ + AST_ASSIGN_ADD, + /** Assigning subtraction, \c -= */ + AST_ASSIGN_SUB, + /** Assigning multiplication, \c *= */ + AST_ASSIGN_MUL, + /** Assigning division, \c /= */ + AST_ASSIGN_DIV, + /** Assigning remainder, \c %= */ + AST_ASSIGN_REM, + /** Assigning logical left shift, \c >>= */ + AST_ASSIGN_LSHIFT, + /** Assigning logical right shift, \c <<= */ + AST_ASSIGN_RSHIFT, + /** Less than, \c < */ + AST_LT, + /** Greater than, \c > */ + AST_GT, + /** Less than or equal, \c <= */ + AST_LE, + /** Greater than or equal, \c >= */ + AST_GE, + /** Not equal, \c != */ + AST_NE, + /** Equal, \c == */ + AST_EQ, + /** Negation, \c - */ + AST_NEG, + /** Logical negation, \c ! */ + AST_LNOT, + AST_NOT, + /** Referencing, \c & */ + AST_REF, + /** Dereferencing, \c ' */ + AST_DEREF, + AST_CONST_INT, + AST_CONST_CHAR, + AST_CONST_BOOL, + AST_CONST_STR, }; /** Flags an AST node can have. */ -enum ast_flag { +enum ast_flags { /** Node is mutable. Mostly used for variables. */ AST_FLAG_MUTABLE = (1 << 0), /** Value is constant. Constant if, for, etc. */ @@ -300,1005 +196,461 @@ enum ast_flag { AST_FLAG_DOEXPR = (1 << 15), }; -struct ast_node; - -/** - * AST if node. - * - * \verbatim - * if cond {body} [els] - * \endverbatim - */ -struct ast_if { - /** Conditional block. */ - struct ast_node *cond; - /** Body. */ - struct ast_node *body; - /** Else block. */ - struct ast_node *els; -}; - -/** AST fetch enum member node. */ -struct ast_fetch { - /** Name of member to fetch. */ - struct ast_node *id; - /** Enum type to fetch from. */ - struct ast_node *type; -}; - -/** Goto label. */ -struct ast_label { - /** Label name. */ - struct ast_node *id; - /** - * List of active defers. - * Used to determine which defer statements should be executed - * when a goto jumps to this label. - */ - struct ast_node *defers; -}; - -struct ast_arr_access { - struct ast_node *base; - struct ast_node *idx; -}; - -/** Goto node. */ -struct ast_goto { - /** Name of label to jump to. */ - struct ast_node *label; - /** - * List of active defers. - * With the list in the corresponding label, we can detect which - * defers should be triggered and which ones shouldn't. - * See actualize_goto_defers(). - */ - struct ast_node *defers; -}; - -/** Alias definition. */ -struct ast_alias { - /** Name of alias. */ - struct ast_node *id; - /** Type to alias. */ - struct ast_node *type; -}; - -/** List of types that implements the template list belongs to. */ -struct trait_implemented { - /** A type that implements the template. */ - struct ast_node *type; - /** Next type that implements the template. */ - struct trait_implemented *next; -}; - -/** A trait definition. */ -struct ast_trait { - /** Name of trait. */ - struct ast_node *id; - /** Parameters to construct concrete type from trait. */ - struct ast_node *params; - /** Raw body before expansion. */ - struct ast_node *raw_body; - /** Body of trait. */ - struct ast_node *body; -}; - -/** Cast. */ -struct ast_cast { - /** Expression. */ - struct ast_node *expr; - /** Type to cast expression result to. */ - struct ast_node *type; -}; - -/** Binary operation. */ -struct ast_binop { - /** Which operation. */ - enum ast_binops op; - /** Left operand. */ - struct ast_node *left; - /** Right operand. */ - struct ast_node *right; -}; - -/** Unary operation. */ -struct ast_unop { - /** Which operation. */ - enum ast_unops op; - /** Expression. */ - struct ast_node *expr; -}; - -/** A call. */ -struct ast_call { - /** Name to call, whatever it may be. */ - struct ast_node *expr; - /** List of arguments to call. */ - struct ast_node *args; -}; - -/** Defer. */ -struct ast_defer { - /** Expression to defer. */ - struct ast_node *expr; -}; - -/** Macro definition. */ -struct ast_macro_construct { - /** Name of macro. */ - struct ast_node *id; - /** Parameters macro takes. */ - struct ast_node *params; - /** Macro body. */ - struct ast_node *body; -}; - -struct ast_macro_expand { - struct ast_node *id; - struct ast_node *args; -}; - -struct ast_type_expand { - struct ast_node *id; - struct ast_node *args; -}; - -/** Procedure definition. */ -struct ast_proc { - /** Procedure name. */ - struct ast_node *id; - /** Procedure signature. */ - struct ast_node *sign; - /** Procedure body. */ - struct ast_node *body; -}; - -/** Dot. */ -struct ast_dot { - /** Expression to do dot operation on. */ - struct ast_node *expr; - /** Name to dot. */ - struct ast_node *id; -}; - -/** As. */ -struct ast_as { - /** Type to use for resolution. */ - struct ast_node *type; -}; - -/** Sizeof. */ -struct ast_sizeof { - /** Expression to get type of. */ - struct ast_node *expr; -}; - -/** Variable declaration. */ -struct ast_var { - /** Name of variable. */ - struct ast_node *id; - /** Type of variable. */ - struct ast_node *type; - /** Initialization expression. */ - struct ast_node *init; -}; - -/** Lambda definition. */ -struct ast_lambda { - /** Which variables to capture from the context. */ - struct ast_node *captures; - /** Signature of lambda. */ - struct ast_node *sign; - /** Body of lambda. */ - struct ast_node *body; -}; - -/** - * For loop. - * - * \verbatim - * for (pre; cond; post) {body} - * \endverbatim - */ -struct ast_for { - /** Initialization. */ - struct ast_node *pre; - /** Condition. */ - struct ast_node *cond; - /** Iteration. */ - struct ast_node *post; - /** Body. */ - struct ast_node *body; -}; - -/** While loop. */ -struct ast_while { - /** Condition. */ - struct ast_node *cond; - /** Body. */ - struct ast_node *body; -}; - -/** Control. */ -struct ast_ctrl { - /** Break or continue. */ - enum ast_ctrl_kind kind; - /** - * List of active defers. - * Similarly to goto, used to figure out which defers to trigger. - */ - struct ast_node *defers; -}; - -/** Return. */ -struct ast_return { - /** Expression to return. */ - struct ast_node *expr; - /** List of active defers. */ - struct ast_node *defers; -}; - -enum ast_primitive { - AST_VOID, AST_BOOL, AST_I9, AST_I27, AST_STR -}; - -/** - * Type. - * I'm not entirely happy with the current type system, for one - * some parts are only used until they get initialized, which makes it - * sort of difficult to follow what each kind means. - * - * @todo Could be better to have separate type nodes instead of co-opting - * AST nodes. - */ -struct ast_type { - /** Type kind. */ - enum ast_type_kind kind; - /** - * Next type element in whole type. I.e. *i9 is two elements, one - * AST_TYPE_POINTER and one AST_TYPE_ID. - */ - struct ast_node *next; - struct ast_node *as; - struct ast_node *aliased; - - /** Data relevant to kind. */ - union { - /** Name of a type, to be converted later. */ - struct { - struct ast_node *id; - } _id; - - struct { - struct ast_node *id; - struct ast_node *args; - } _construct; - - struct { - enum ast_primitive type; - struct ast_node *def; // for possible user defined - // member functions - } _primitive; - - /** Array type. */ - struct { - struct ast_node *size; - struct ast_node *base; - } _arr; - - struct { - struct ast_node *base; - } _ptr; - - /** Procedure. */ - struct { - /** Name. */ - struct ast_node *id; - /** Parameters. */ - struct ast_node *params; - /** Return type. */ - struct ast_node *ret; - } _proc; - - /** Trait. */ - struct { - /** Trait definition. */ - struct ast_node *def; - } _trait; - - /** Structure. */ - struct { - /** Structure definition. */ - struct ast_node *def; - } _struct; - - /** Enumeration. */ - struct { - /** Enum definition. */ - struct ast_node *def; - } _enum; - - /** Union. */ - struct { - /** Name of union. */ - struct ast_node *id; - /** Arguments for type parameters. */ - struct ast_node *impls; - } _union; - - struct { - /** Parameter types. */ - struct ast_node *params; - /** Return type. */ - struct ast_node *ret; - } _sign; - }; -}; - -/** Block. */ -struct ast_block { - /** Body of block. */ - struct ast_node *body; - /** List of defers. */ - struct ast_node *defers; -}; - -/** Import. */ -struct ast_import { - /** File to import. */ - const char *file; -}; - -/** Embed. @todo figure out how embedding should be done, just *u8? */ -struct ast_embed { - /** File to embed. */ - const char *file; -}; - -/** Enum definition. */ -struct ast_enum { - /** Name of enum. */ - struct ast_node *id; - /** Type enum is convertible to/from. */ - struct ast_node *type; - /** Body. */ - struct ast_node *body; -}; - -/** Structure definition. */ -struct ast_struct { - /** Name of structure. */ - struct ast_node *id; - /** List of type parameters, if any. */ - struct ast_node *generics; - /** Body. */ - struct ast_node *body; - struct trait_implemented *implemented_by; -}; +struct ast; -/** Enum member constant value. */ -struct ast_val { - /** Name of member. */ - struct ast_node *id; - /** Constant value of member. */ - struct ast_node *val; +enum type_kind { + TYPE_VOID = 1, TYPE_BOOL, TYPE_I9, TYPE_I27, TYPE_STR, TYPE_PTR, + TYPE_ID, TYPE_CONSTRUCT, TYPE_STRUCT, TYPE_ENUM, TYPE_CALLABLE, + TYPE_TRAIT, }; -/** Switch. */ -struct ast_switch { - /** Condition. */ - struct ast_node *cond; - /** List of cases. */ - struct ast_node *cases; -}; +struct type { + enum type_kind k; -/** Switch case. */ -struct ast_case { - /** Condition. */ - struct ast_node *cond; - /** Body. */ - struct ast_node *body; -}; + /* arg */ + struct type *t0; + struct type *t1; -/** Constant value. */ -struct ast_const { - /** Constant kind. */ - enum ast_const_kind kind; - union { - /** Integer. */ - long long integer; - /** String. */ - const char *str; - }; -}; + /* definition */ + struct ast *d; + /* alias */ + struct ast *a; + /* id */ + char *id; + /* next */ + struct type *n; -/** Value initialization. */ -struct ast_init { - /** Body. @todo maybe come up with a better name? */ - struct ast_node *body; + struct src_loc loc; + struct scope *scope; }; -/** Assignment. */ -struct ast_assign { - /** Where to assign to. */ - struct ast_node *to; - /** What to assign from. */ - struct ast_node *from; -}; +struct ast { + enum ast_kind k; + struct type *t; + long long v; + char *s; + struct ast *a0; + struct ast *a1; + struct ast *a2; + struct ast *a3; + struct type *t2; + enum ast_flags f; -/** - * An ID. - * @todo One optimization would be to reduce number of ast_ids, - * replacing them with raw strings. - */ -struct ast_id { - /** Actual ID. */ - const char *id; -}; + struct ast *n; + long long uses; -/** Empty node. */ -struct ast_empty { -}; - -/** AST node. */ -struct ast_node { - /** Node type. */ - enum ast_node_type node_type; - /** Node flags. */ - enum ast_flag flags; - /** Next node, when applicable. I.e. body statements. */ - struct ast_node *next; - /** Source location. */ struct src_loc loc; - /** Scope node belongs to. */ struct scope *scope; - /** Ek type. */ - struct ast_node *type; - - size_t uses; - - /** Data relevant to kind. */ - union { - struct ast_arr_access _arr_access; - /** Binary operation. */ - struct ast_binop _binop; - /** Unary operation. */ - struct ast_unop _unop; - /** Call. */ - struct ast_call _call; - /** Cast. */ - struct ast_cast _cast; - /** Macro definition. */ - struct ast_macro_construct _macro_construct; - struct ast_macro_expand _macro_expand; - struct ast_type_expand _type_expand; - /** Procedure definition. */ - struct ast_proc _proc; - /** Goto. */ - struct ast_goto _goto; - /** Goto label. */ - struct ast_label _label; - /** Variable. */ - struct ast_var _var; - /** Lambda. */ - struct ast_lambda _lambda; - /** If. */ - struct ast_if _if; - /** For. */ - struct ast_for _for; - /** While. */ - struct ast_while _while; - /** Break/continue. */ - struct ast_ctrl _ctrl; - /** Defer. */ - struct ast_defer _defer; - /** Type. */ - struct ast_type _type; - /** Dot. */ - struct ast_dot _dot; - /** Block. */ - struct ast_block _block; - /** Import. */ - struct ast_import _import; - /** Embed. */ - struct ast_embed _embed; - /** Return. */ - struct ast_return _return; - /** Switch. */ - struct ast_switch _switch; - /** Alias. */ - struct ast_alias _alias; - /** Trait definition. */ - struct ast_trait _trait; - /** Enum definition. */ - struct ast_enum _enum; - /** Structure definition. */ - struct ast_struct _struct; - /** Enum value. */ - struct ast_val _val; - /** Switch case. */ - struct ast_case _case; - /** Constant value. */ - struct ast_const _const; - /** Initialization. */ - struct ast_init _init; - /** Assignment. */ - struct ast_assign _assign; - /** ID. */ - struct ast_id _id; - /** As type. */ - struct ast_as _as; - /** Sizeof. */ - struct ast_sizeof _sizeof; - /** Fetch enum member. */ - struct ast_fetch _fetch; - /** Empty. */ - struct ast_empty _empty; - }; }; -struct ast_node *gen_arr_access(struct ast_node *base, struct ast_node *idx, - struct src_loc loc); - -/** - * Generate binary operation node. - * - * @param op Which operation. - * @param left Left operand. - * @param right Right operand. - * @return Corresponding AST node. - */ -struct ast_node *gen_binop(enum ast_binops op, - struct ast_node *left, - struct ast_node *right, - struct src_loc loc); - -/** - * Generate unary operation. - * - * @param op Which operation. - * @param expr Expression. - * @return Corresponding AST node. - */ -struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, - struct src_loc loc); - -/** - * Generate call. - * - * @param id ID to call. - * @param args Arguments to call. - * @return Corresponding AST node. - */ -struct ast_node *gen_call(struct ast_node *id, struct ast_node *args, - struct src_loc loc); - -/** - * Generate ID. - * - * @param id ID. - * @return Corresponding AST node. - */ -struct ast_node *gen_id(const char *id, struct src_loc loc); - -/** - * Generate constant integer. - * - * @param integer Integer. - * @return Corresponding AST node. - */ -struct ast_node *gen_int(long long integer, struct src_loc loc); - -/** - * Generate constant string. - * - * @param str String. - * @return Corresponding AST node. - */ -struct ast_node *gen_string(const char *str, struct src_loc loc); - -/** - * Generate constant float. - * - * @param dbl Double. - * @return Corresponding AST node. - */ -struct ast_node *gen_float(double dbl, struct src_loc loc); - -/** - * Generate assignment. - * - * @param to Where to assign to. - * @param from Where to assign from. - * @return Corresponding AST node. - */ -struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, - struct src_loc loc); - -/** - * Generate initialization. - * - * @param body Body of initialization. - * @return Corresponding AST node. - */ -struct ast_node *gen_init(struct ast_node *body, struct src_loc loc); - -/** - * Generate while loop. - * - * @param cond Condition. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, - struct src_loc loc); - -/** - * Generate for loop. - * - * @param pre Pre condition. - * @param cond Condition. - * @param post Post condition. - * @param body Loop body. - * @return Corresponding AST node. - */ -struct ast_node *gen_for(struct ast_node *pre, struct ast_node *cond, - struct ast_node *post, struct ast_node *body, - struct src_loc loc); - -/** - * Generate return. - * - * @param expr Expression. - * @return Corresponding AST node. - */ -struct ast_node *gen_return(struct ast_node *expr, struct src_loc loc); - -/** - * Generate control statement. - * @note Some generators take source location info, which is then - * propagated to higher level nodes. - * - * @todo not sure where to best place the note about location - * - * @param kind Break or continue. - * @param loc Source location. - * @return Corresponding AST node. - */ -struct ast_node *gen_ctrl(enum ast_ctrl_kind kind, struct src_loc loc); - -/** - * Generate macro definition. - * - * @param id Macro name. - * @param params Macro parameters. - * @param body Macro body. - * @return Corresponding AST node. - */ -struct ast_node *gen_macro_construct(struct ast_node *id, - struct ast_node *params, - struct ast_node *body, struct src_loc loc); - -struct ast_node *gen_macro_expand(struct ast_node *id, struct ast_node *args, - struct src_loc loc); - -/** @todo change args to type type when I figure out how it should be - * constructed */ -struct ast_node *gen_type_expand(struct ast_node *id, - struct ast_node *args, - struct src_loc loc); - -/** - * Generate if. - * - * @param cond Condition. - * @param body Body. - * @param els Else. - * @return Corresponding AST node. - */ -struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body, - struct ast_node *els, struct src_loc loc); - -/** - * Generate switch. - * - * @param cond Condition. - * @param cases List of cases. - * @return Corresponding AST node. - */ -struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, - struct src_loc loc); - -/** - * Generate switch case. - * - * @param expr Condition. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_case(struct ast_node *expr, struct ast_node *body, - struct src_loc loc); - -struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, - struct src_loc loc); -/** - * Generate Ek type (besides primitive). - * - * @param kind Type kind. - * @param id Name of type. - * @param decl List of parameters, when applicable. - * @param rets Return type, when applicable. - * @return Corresponding AST node. - */ -struct ast_node *gen_type(enum ast_type_kind kind, struct ast_node *t2, - struct ast_node *t1, - struct src_loc loc); - -/** - * Generate block. - * - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_block(struct ast_node *body, struct src_loc loc); - -/** - * Generate variable. - * - * @param id Name of variable. - * @param type Type of varibable. - * @param init Initialization, when applicable. - * @return Corresponding AST node. - */ -struct ast_node *gen_var(struct ast_node *id, struct ast_node *type, - struct ast_node *init, struct src_loc loc); - -/** - * Generate lambda. - * - * @param captures List of captures. - * @param type Signature of lambda. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_lambda(struct ast_node *captures, - struct ast_node *type, struct ast_node *body, - struct src_loc loc); - -/** - * Generate procedure definition. - * - * @param id Name of procedure. - * @param type Signature of procedure. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_proc(struct ast_node *id, struct ast_node *type, - struct ast_node *body, struct src_loc loc); - -/** - * Generate dot operation. - * - * @param expr Expression to do dot operation to. - * @param id Name to do dot with. - * @return Corresponding AST node. - */ -struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, - struct src_loc loc); - -/** - * Generate enum definition. - * - * @param id Name of enumeration type. - * @param type Type of enum. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type, - struct ast_node *body, struct src_loc loc); - -/** - * Generate enum member value. - * - * @param id Name of enumeration member. - * @param val Value of enumeration member. - * @return Corresponding AST node. - */ -struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, - struct src_loc loc); - -/** - * Generate alias definition. - * - * @param id Name of alias. - * @param type Type to alias. - * @return Corresponding AST node. - */ -struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, - struct src_loc loc); - -/** - * Generate trait definition. - * - * @param id Name of trait. - * @param body Body. - * @return Corresponding AST node. - */ -struct ast_node *gen_trait(struct ast_node *id, struct ast_node *params, - struct ast_node *raw_body, struct ast_node *body, - struct src_loc loc); - -/** - * Generate import; - * - * @param file File to import. - * @return Corresponding AST node. - */ -struct ast_node *gen_import(const char *file, struct src_loc loc); - -/** - * Generate cast. - * - * @param expr Expression. - * @param type Type to cast expression result to. - * @return Corresponding AST node. - */ -struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, - struct src_loc loc); - -/** - * Generate embed. - * - * @param file File to embed. - * @return Corresponding AST node. - */ -struct ast_node *gen_embed(const char *file, struct src_loc loc); - -/** - * Generate goto. - * - * @param label Name of label to jump to. - * @return Corresponding AST node. - */ -struct ast_node *gen_goto(struct ast_node *label, struct src_loc loc); - -/** - * Generate goto label. - * - * @param id Name of label. - * @return Corresponding AST node. - */ -struct ast_node *gen_label(struct ast_node *id, struct src_loc loc); - -/** - * Generate defer. - * - * @param expr Expression to defer. - * @return Corresponding AST node. - */ -struct ast_node *gen_defer(struct ast_node *expr, struct src_loc loc); - -/** - * Generate as. - * - * @param type Type to resolve with. - * @return Corresponding AST node. - */ -struct ast_node *gen_as(struct ast_node *type, struct src_loc loc); - -/** - * Generate sizeof. - * - * @param expr Expression to take type of. - * @return Corresponding AST node. - */ -struct ast_node *gen_sizeof(struct ast_node *expr, struct src_loc loc); - -/** - * Generate structure definition. - * - * @param id Name of structure. - * @param generics List of type parameters. - * @param body Body of structure. - * @return Corresponding AST node. - */ -struct ast_node *gen_struct(struct ast_node *id, struct ast_node *generics, - struct ast_node *body, struct src_loc loc); - -/** - * Generate enum member fetch. - * - * @param id Name of enum member to fetch. - * @param type Enum type to fetch from. - * @return Corresponding AST node. - */ -struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, - struct src_loc loc); - -/** - * Generate empty AST node. - * - * @return Corresponding AST node. - */ -struct ast_node *gen_empty(); +struct ast *gen_ast(enum ast_kind kind, + struct ast *a0, + struct ast *a1, + struct ast *a2, + struct ast *a3, + struct type *t1, + char *s, + long long v, + struct src_loc loc); + +struct type *tgen_type(enum type_kind kind, + struct type *t0, + struct type *t1, + struct ast *d, + struct ast *a, + char *id, + struct src_loc loc); + +#define tgen_primitive(kind, id, def, loc)\ + tgen_type(kind, NULL, NULL, def, NULL, id, loc) + +#define tgen_id(id, loc)\ + tgen_type(TYPE_ID, NULL, NULL, NULL, NULL, id, loc) + +#define tgen_struct(id, def, loc)\ + tgen_type(TYPE_STRUCT, NULL, NULL, def, NULL, id, loc) + +#define tgen_trait(id, def, loc)\ + tgen_type(TYPE_TRAIT, NULL, NULL, def, NULL, id, loc) + +static inline bool is_binop(struct ast *x) +{ + switch (x->k) { + case AST_ADD: + case AST_SUB: + case AST_MUL: + case AST_DIV: + case AST_REM: + case AST_LSHIFT: + case AST_RSHIFT: + return true; + default: + }; -/** - * Clone AST node. - * - * @param node Node to clone. - * @return A clone of \p node. - */ -struct ast_node *clone_ast_node(struct ast_node *node); + return false; +} + +static inline bool is_opassign(struct ast *x) +{ + switch (x->k) { + case AST_ASSIGN_ADD: + case AST_ASSIGN_SUB: + case AST_ASSIGN_MUL: + case AST_ASSIGN_DIV: + case AST_ASSIGN_REM: + case AST_ASSIGN_LSHIFT: + case AST_ASSIGN_RSHIFT: + return true; + default: + }; + return false; +} + +static inline bool is_unop(struct ast *x) +{ + switch (x->k) { + case AST_REF: + case AST_DEREF: + case AST_NOT: + case AST_NEG: + return true; + default: + } + + return false; +} + +static inline bool is_comparison(struct ast *x) +{ + switch (x->k) { + case AST_LT: + case AST_GT: + case AST_LE: + case AST_GE: + case AST_NE: + case AST_EQ: + return true; + default: + } + + return false; +} + +static inline bool is_const(struct ast *x) +{ + /* note that const strings are sort of their own entity */ + switch (x->k) { + case AST_CONST_INT: + case AST_CONST_CHAR: + case AST_CONST_BOOL: + return true; + default: + } + + return false; +} + +static inline bool is_primitive(struct type *t) +{ + switch (t->k) { + case TYPE_I27: + case TYPE_I9: + case TYPE_BOOL: + case TYPE_PTR: + return true; + default: + } + + return false; +} + +#define gen_str_type1(k, s, t, a, loc) gen_ast(k, a, NULL, NULL, NULL, t, s, 0, loc) +#define gen_str_type(k, s, t, loc) gen_str_type1(k, s, t, NULL, loc) +#define gen_type(k, a, type, loc) gen_ast(k, a, NULL, NULL, NULL, type, NULL, 0, loc) +#define gen_str2(k, s, a, b, loc) gen_ast(k, a, b, NULL, NULL, NULL, s, 0, loc) +#define gen_str1(k, s, a, loc) gen_str2(k, s, a, NULL, loc) +#define gen_str(k, s, loc) gen_ast(k, NULL, NULL, NULL, NULL, NULL, s, 0, loc) + + +#define gen4(k, a, b, c, d, loc) gen_ast(k, a, b, c, d, NULL, NULL, 0, loc) +#define gen3(k, a, b, c, loc) gen4(k, a, b, c, NULL, loc) +#define gen2(k, a, b, loc) gen3(k, a, b, NULL, loc) +#define gen1(k, a, loc) gen2(k, a, NULL, loc) + +#define tgen2(k, a, b, loc) tgen_type(k, a, b, NULL, NULL, NULL, loc) +#define tgen1(k, a, loc) tgen2(k, a, NULL, loc) + +#define tgen_str1(k, s, a, loc) tgen_type(k, a, NULL, NULL, NULL, s, loc) + +/* kind of hacky but I guess it works, and allows us to check that the type is + * correct every time */ +#define return_s(x, kind) *({assert((x)->k == kind); &(x)->s;}) +#define return_a0(x, kind) *({assert((x)->k == kind); &(x)->a0;}) +#define return_a1(x, kind) *({assert((x)->k == kind); &(x)->a1;}) +#define return_a2(x, kind) *({assert((x)->k == kind); &(x)->a2;}) +#define return_a3(x, kind) *({assert((x)->k == kind); &(x)->a3;}) + +#define return_t0(x, kind) *({assert((x)->k == kind); &(x)->t0;}) +#define return_t1(x, kind) *({assert((x)->k == kind); &(x)->t1;}) +/* note that this one is in ast, the other two are in type */ +#define return_t2(x, kind) *({assert((x)->k == kind); &(x)->t2;}) + +#define if_cond(x) return_a0(x, AST_IF) +#define if_body(x) return_a1(x, AST_IF) +#define if_else(x) return_a2(x, AST_IF) +#define gen_if(cond, body, else, loc)\ + gen3(AST_IF, cond, body, else, loc) + +#define fetch_id(x) return_s(x, AST_FETCH) +#define fetch_type(x) return_t2(x, AST_FETCH) +#define gen_fetch(id, type, loc)\ + gen_str_type(AST_FETCH, id, type, loc) + +#define label_id(x) return_s(x, AST_LABEL) +#define label_defers(x) return_a0(x, AST_LABEL) +#define gen_label(id, defers, loc)\ + gen_str1(AST_LABEL, id, defers, loc) + +#define arr_base(x) return_a0(x, AST_ARR) +#define arr_idx(x) return_a1(x, AST_ARR) +#define gen_arr(base, idx, loc)\ + gen2(AST_ARR, base, idx, loc) + +#define goto_label(x) return_s(x, AST_GOTO) +#define goto_defers(x) return_a0(x, AST_GOTO) +#define gen_goto(label, defers, loc)\ + gen_str1(AST_GOTO, label, defers, loc) + +#define alias_id(x) return_s(x, AST_ALIAS_DEF) +#define alias_type(x) return_t2(x, AST_ALIAS_DEF) +#define gen_alias(id, type, loc)\ + gen_str_type(AST_ALIAS_DEF, id, type, loc) + +#define trait_id(x) return_s(x, AST_TRAIT_DEF) +#define trait_params(x) return_a0(x, AST_TRAIT_DEF) +#define trait_raw_body(x) return_a1(x, AST_TRAIT_DEF) +#define trait_body(x) return_a2(x, AST_TRAIT_DEF) +#define gen_trait(id, params, body, loc)\ + gen_str2(AST_TRAIT_DEF, id, params, body, loc) + +#define cast_expr(x) return_a0(x, AST_CAST) +#define cast_type(x) return_t2(x, AST_CAST) +#define gen_cast(expr, type, loc)\ + gen_type(AST_CAST, expr, type, loc) + +#define opassign_left(x) ({assert(is_opassign(x)); x->a0;}) +#define opassign_right(x) ({assert(is_opassign(x)); x->a1;}) +#define gen_opassign(op, left, right, loc)\ + gen2(op, left, right, loc) + +#define binop_left(x) ({assert(is_binop(x)); x->a0;}) +#define binop_right(x) ({assert(is_binop(x)); x->a1;}) +#define gen_binop(op, left, right, loc)\ + gen2(op, left, right, loc) + +#define comparison_left(x) ({assert(is_comparison(x)); x->a0;}) +#define comparison_right(x) ({assert(is_comparison(x)); x->a1;}) +#define gen_comparison(op, left, right, loc)\ + gen2(op, left, right, loc) + +#define unop_expr(x) ({assert(is_unop(x)); x->a0;}) +#define gen_unop(op, expr, loc)\ + gen1(op, expr, loc) + +#define call_expr(x) return_a0(x, AST_CALL) +#define call_args(x) return_a1(x, AST_CALL) +#define gen_call(expr, args, loc)\ + gen2(AST_CALL, expr, args, loc) + +#define defer_expr(x) return_a0(x, AST_DEFER) +#define gen_defer(expr, loc)\ + gen1(AST_DEFER, expr, loc) + +#define macro_def_id(x) return_s(x, AST_MACRO_DEF) +#define macro_def_params(x) return_a0(x, AST_MACRO_DEF) +#define macro_def_body(x) return_a1(x, AST_MACRO_DEF) +#define gen_macro_def(id, params, body, loc)\ + gen_str2(AST_MACRO_DEF, id, params, body, loc) + +#define macro_expand_id(x) return_s(x, AST_MACRO_EXPAND) +#define macro_expand_args(x) return_a0(x, AST_MACRO_EXPAND) +#define gen_macro_expand(id, args, loc)\ + gen_str1(AST_MACRO_EXPAND, id, args, loc) + +#define type_expand_id(x) return_s(x, AST_TYPE_EXPAND) +#define type_expand_args(x) return_t2(x, AST_TYPE_EXPAND) +#define gen_type_expand(id, args, loc)\ + gen_str_type(AST_TYPE_EXPAND, id, args, loc) + +#define proc_id(x) return_s(x, AST_PROC_DEF) +#define proc_params(x) return_a0(x, AST_PROC_DEF) +#define proc_rtype(x) return_t2(x, AST_PROC_DEF) +#define proc_body(x) return_a1(x, AST_PROC_DEF) +#define gen_proc(id, params, rtype, body, loc)\ + gen_ast(AST_PROC_DEF, params, body, NULL, NULL, rtype, id, 0, loc) + +#define dot_id(x) return_s(x, AST_DOT) +#define dot_expr(x) return_a0(x, AST_DOT) +#define gen_dot(id, expr, loc)\ + gen_str1(AST_DOT, id, expr, loc) + +#define as_type(x) return_t2(x, AST_AS) +#define gen_as(type, loc)\ + gen_type(NULL, type, loc) + +#define sizeof_expr(x) return_a0(x, AST_SIZEOF) +#define gen_sizeof(expr, loc)\ + gen1(AST_SIZEOF, expr, loc) + +#define var_id(x) return_s(x, AST_VAR_DEF) +#define var_type(x) return_t2(x, AST_VAR_DEF) +#define var_init(x) return_a0(x, AST_VAR_DEF) +#define gen_var(id, type, init, loc)\ + gen_ast(AST_VAR_DEF, init, NULL, NULL, NULL, type, id, 1, loc) + +#define for_pre(x) return_a0(x, AST_FOR) +#define for_cond(x) return_a1(x, AST_FOR) +#define for_post(x) return_a2(x, AST_FOR) +#define for_body(x) return_a3(x, AST_FOR) +#define gen_for(pre, cond, post, body, loc)\ + gen4(AST_FOR, pre, cond, post, body, loc) + +#define while_cond(x) return_a0(x, AST_WHILE) +#define while_body(x) return_a1(x, AST_WHILE) +#define gen_while(cond, body, loc)\ + gen2(AST_WHILE, cond, body, loc) + +#define do_while_cond(x) return_a0(x, AST_DO_WHILE) +#define do_while_body(x) return_a1(x, AST_DO_WHILE) +#define gen_do_while(cond, body, loc)\ + gen2(AST_DO_WHILE, cond, body, loc) + +#define continue_defers(x) return_a0(x, AST_CONTINUE) +#define gen_continue(defers, loc)\ + gen1(AST_CONTINUE, defers, loc) + +#define break_defers(x) return_a0(x, AST_BREAK) +#define gen_break(defers, loc)\ + gen1(AST_BREAK, defers, loc) + +#define return_expr(x) return_a0(x, AST_RETURN) +#define return_defers(x) return_a1(x, AST_RETURN) +#define gen_return(expr, defers, loc)\ + gen2(AST_RETURN, expr, defers, loc) + +#define block_body(x) return_a0(x, AST_BLOCK) +#define block_defers(x) return_a1(x, AST_BLOCK) +#define gen_block(body, defers, loc)\ + gen2(AST_BLOCK, body, defers, loc) + +#define import_file(x) return_s(x, AST_IMPORT) +#define gen_import(f, loc)\ + gen_str(AST_IMPORT, f, loc) + +#define embed_file(x) return_s(x, AST_EMBED) +#define gen_embed(f, loc)\ + gen_str(AST_EMBED, f, loc) + +#define enum_id(x) return_s(x, AST_ENUM_DEF) +#define enum_type(x) return_t2(x, AST_ENUM_DEF) +#define enum_body(x) return_a0(x, AST_ENUM_DEF) +#define gen_enum(id, type, body, loc)\ + gen_ast(AST_ENUM_DEF, body, NULL, NULL, NULL, type, id, 0, loc) + +#define struct_id(x) return_s(x, AST_STRUCT_DEF) +#define struct_params(x) return_a0(x, AST_STRUCT_DEF) +#define struct_body(x) return_a1(x, AST_STRUCT_DEF) +#define gen_struct(id, params, body, loc)\ + gen_str2(AST_STRUCT_DEF, id, params, body, loc) + +#define val_id(x) return_s(x, AST_VAL) +#define val_val(x) return_a0(x, AST_VAL) +#define gen_val(id, val, loc)\ + gen_str1(AST_VAL, id, val, loc) + +#define switch_cond(x) return_a0(x, AST_SWITCH) +#define switch_cases(x) return_a1(x, AST_SWITCH) +#define gen_switch(cond, cases, loc)\ + gen2(AST_SWITCH, cond, cases, loc) + +#define case_cond(x) return_a0(x, AST_CASE) +#define case_body(x) return_a1(x, AST_CASE) +#define gen_case(cond, body, loc)\ + gen2(AST_CASE, cond, body, loc) + +#define str_val(x) return_s(x, AST_CONST_STR) +#define gen_const_str(s, loc)\ + gen_ast(AST_CONST_STR, NULL, NULL, NULL, NULL, NULL, s, 0, loc) + +#define int_val(x) *({assert(x->k == AST_CONST_INT); &x->v;}) +#define gen_const_int(i, loc)\ + gen_ast(AST_CONST_INT, NULL, NULL, NULL, NULL, NULL, NULL, i, loc) + +#define char_val(x) *({assert(x->k == AST_CONST_CHAR); &x->v;}) +#define gen_const_char(i, loc)\ + gen_ast(AST_CONST_CHAR, NULL, NULL, NULL, NULL, NULL, NULL, i, loc) + +#define bool_val(x) *({assert(x->k == AST_CONST_CHAR); &x->v;}) +#define gen_const_bool(i, loc)\ + gen_ast(AST_CONST_BOOL, NULL, NULL, NULL, NULL, NULL, NULL, i, loc) + +#define init_body(x) return_a0(x, AST_ASSIGN) +#define gen_init(body, loc)\ + gen1(AST_INIT, body, loc) + +#define assign_to(x) return_a0(x, AST_ASSIGN) +#define assign_from(x) return_a1(x, AST_ASSIGN) +#define gen_assign(from, to, loc)\ + gen2(AST_ASSIGN, from, to, loc) + +#define id_str(x) return_s(x, AST_ID) +#define gen_id(id, loc)\ + gen_str(AST_ID, id, loc) + +#define gen_empty(loc)\ + gen1(AST_EMPTY, NULL, loc) + +/* types */ +#define callable_ptypes(x) return_t0(x, TYPE_CALLABLE) +#define callable_rtype(x) return_t0(x, TYPE_CALLABLE) +#define tgen_callable(ptypes, rtype, loc)\ + tgen2(TYPE_CALLABLE, ptypes, rtype, loc) + +#define ptr_base(x) return_t0(x, TYPE_PTR) +#define tgen_ptr(base, loc)\ + tgen1(TYPE_PTR, base, loc) -/** - * Check if two nodes are identical. - * - * @param exact Ignore some less important checks, such as flags, location and - * scope. - * @param left One AST node to compare. - * @param right Other AST node to compare. - * @return \c 0 if nodes aren't identical, \c 1 otherwise. - */ -int identical_ast_nodes(int exact, struct ast_node *left, - struct ast_node *right); +#define construct_id(x) return_t0(x, TYPE_CONSTRUCT) +#define construct_atypes(x) return_t1(x, TYPE_CONSTRUCT) +#define tgen_construct(id, atypes, loc)\ + tgen_str1(TYPE_CONSTRUCT, id, atypes, loc) -/** - * Dump textual representation of AST to stdout. - * - * @param depth How many spaces to prepend. - * @param root AST node to dump. - */ -void dump_ast(int depth, struct ast_node *root); -void dump_ast_node(int depth, struct ast_node *node); +struct ast *clone_ast(struct ast *n); +struct ast *clone_ast_list(struct ast *l); -/** - * Add \p elem to end of \p list. - * - * @param list List of AST nodes. - * @param elem Add to end of \p list. - */ -void ast_append(struct ast_node *list, struct ast_node *elem); +struct type *clone_type(struct type *n); +struct type *clone_type_list(struct type *l); -/** - * Set AST node flags. - * - * @param node Node to set flags for. - * @param flags Flags to set. - */ -void ast_set_flags(struct ast_node *node, enum ast_flag flags); +void ast_dump_list(int depth, struct ast *root); +void ast_dump(int depth, struct ast *node); + +void type_dump_list(struct type *root); +void type_dump(struct type *node); -/** - * Clear AST node flags. - * - * @param node Node to clear flags for. - * @param flags Flags to set. - */ -void ast_clear_flags(struct ast_node *node, enum ast_flag flags); +void ast_append(struct ast *list, struct ast *elem); +void type_append(struct type *list, struct type *elem); -/** - * Check if \p node has \p flags set. - * All flags have to be set. - * - * @param node Node to check flags for. - * @param flags Flags to check. - * @return \c 1 if all \p flags are set, \c 0 othewise. - */ -unsigned ast_flags(struct ast_node *node, enum ast_flag flags); +void ast_set_flags(struct ast *node, enum ast_flags flags); +void ast_clear_flags(struct ast *node, enum ast_flags flags); +unsigned ast_flags(struct ast *node, enum ast_flags flags); -/** - * Call external callback on all nodes in tree. - * - * @param call Callback to call. - * @param node Node whose all subnodes should be passed to \p call. - * @param data Extra data to pass to \p call. - * @return Whatever \p call returns. - */ -int ast_call_on(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data); +typedef int (*ast_callback_t)(struct ast *, void *); +typedef int (*type_callback_t)(struct type *, void *); +int ast_visit(ast_callback_t before, ast_callback_t after, struct ast *node, void *data); +int ast_visit_list(ast_callback_t before, ast_callback_t after, struct ast *node, void *data); -int ast_call_on_chain(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data); +int type_visit(type_callback_t before, type_callback_t after, struct type *node, void *data); +int type_visit_list(type_callback_t before, type_callback_t after, struct type *node, void *data); /** * Number of elements in AST list. @@ -1306,7 +658,7 @@ int ast_call_on_chain(int (*call)(struct ast_node *, void *), * @param list List whose elements to count. * @return Number of elements in \p list. */ -size_t ast_list_len(struct ast_node *list); +size_t ast_list_len(struct ast *list); /** * Get last nose in ASt list. @@ -1314,7 +666,7 @@ size_t ast_list_len(struct ast_node *list); * @param list List whose last element to get. * @return Last node in \p list. */ -struct ast_node *ast_last_node(struct ast_node *list); +struct ast *ast_last(struct ast *list); /** * Get last element in block. @@ -1322,16 +674,22 @@ struct ast_node *ast_last_node(struct ast_node *list); * @param block Block whose last element to get. * @return Last node in block. */ -struct ast_node *ast_block_last(struct ast_node *block); +struct ast *ast_block_last(struct ast *block); + +void destroy_allocs(); +const char *primitive_str(struct type *kind); -void destroy_ast_nodes(); -const char *primitive_str(enum ast_primitive type); +int same_id(char *id1, char *id2); +int equiv_nodes(struct ast *n1, struct ast *n2); +int equiv_node_lists(struct ast *c1, struct ast *c2); -int same_id(struct ast_node *id1, struct ast_node *id2); -int equiv_nodes(struct ast_node *n1, struct ast_node *n2); -int equiv_node_chains(struct ast_node *c1, struct ast_node *c2); +int equiv_types(struct type *t1, struct type *t2); +int equiv_type_lists(struct type *t1, struct type *t2); #define foreach_node(iter, nodes) \ - for (struct ast_node *iter = nodes; iter; iter = iter->next) + for (struct ast *iter = nodes; iter; iter = iter->n) + +#define foreach_type(iter, nodes) \ + for (struct type *iter = nodes; iter; iter = iter->n) #endif /* AST_H */ diff --git a/include/ek/debug.h b/include/ek/debug.h index 588fc6b..79cc1d1 100644 --- a/include/ek/debug.h +++ b/include/ek/debug.h @@ -65,15 +65,7 @@ struct file_ctx { * @param type Type to generate string representation of. * @return \p type as string. */ -char *type_str(struct ast_node *type); - -/** - * Generate string representation of call. - * - * @param call Call to generate string representation of. - * @return \p call as string. - */ -char *call_str(struct ast_node *call); +char *type_str(struct type *type); /** * Print info that relates to a specific AST node. @@ -84,8 +76,8 @@ char *call_str(struct ast_node *call); * @param node AST node to print message with. * @param fmt Format string. Follows standard printf() formatting. */ -void semantic_info(struct file_ctx ctx, struct ast_node *node, const char *fmt, - ...); +void semantic_info(struct file_ctx ctx, struct ast *node, const char *fmt, ...); +void type_info(struct file_ctx ctx, struct type *node, const char *fmt, ...); /** * Print warning that relates to a specific AST node. @@ -100,7 +92,7 @@ void semantic_info(struct file_ctx ctx, struct ast_node *node, const char *fmt, * @param node AST node to print message with. * @param fmt Format string. Follows standard printf() formatting. */ -void semantic_warn(struct file_ctx ctx, struct ast_node *node, const char *fmt, +void semantic_warn(struct file_ctx ctx, struct ast *node, const char *fmt, ...); /** @@ -111,8 +103,8 @@ void semantic_warn(struct file_ctx ctx, struct ast_node *node, const char *fmt, * @param node AST node to print message with. * @param fmt Format string. Follows standard printf() formatting. */ -void semantic_error(struct file_ctx ctx, struct ast_node *node, const char *fmt, - ...); +void semantic_error(struct file_ctx ctx, struct ast *node, const char *fmt, ...); +void type_error(struct file_ctx ctx, struct type *type, const char *fmt, ...); /** * Print internal error. diff --git a/include/ek/lower.h b/include/ek/lower.h index af203d6..d635178 100644 --- a/include/ek/lower.h +++ b/include/ek/lower.h @@ -4,6 +4,6 @@ #include #include -int lower_actuals(struct scope *root); +int lower(struct scope *root); #endif /* EK_OPS_H */ diff --git a/include/ek/parser.h b/include/ek/parser.h index c548f7f..49d9302 100644 --- a/include/ek/parser.h +++ b/include/ek/parser.h @@ -30,7 +30,7 @@ struct parser { /** How deeply we've nested comments. */ size_t comment_nesting; /** Raw AST. */ - struct ast_node *tree; + struct ast *tree; }; /** diff --git a/include/ek/scope.h b/include/ek/scope.h index 4cef3a1..4858b55 100644 --- a/include/ek/scope.h +++ b/include/ek/scope.h @@ -34,9 +34,9 @@ enum scope_flags { */ struct visible { /** Name of the visible node. */ - struct ast_node *id; + char *id; /** AST node that is visible. */ - struct ast_node *node; + struct ast *node; /** Next visible object in the scope we're in. */ struct visible *next; }; @@ -44,15 +44,15 @@ struct visible { /** Actualized nodes visible to scope. */ struct actual { /** Actualized AST node. */ - struct ast_node *node; + struct ast *node; /** Next actual node. */ struct actual *next; }; -struct types { - struct ast_node *id; - struct ast_node *type; - struct ast_node *next; +struct type_defs { + char *id; + struct ast *type_def; + struct type_defs *next; }; /** @@ -160,7 +160,7 @@ void scope_destroy_defaults(struct scope *root); * @param scratch Scratch node to add to \p scope. * @return \c 0 when successful, non-zero otherwise. */ -int scope_add_scratch(struct scope *scope, struct ast_node *scratch); +int scope_add_scratch(struct scope *scope, struct ast *scratch); /** * Set scope flags. @@ -178,7 +178,7 @@ void scope_set_flags(struct scope *scope, enum scope_flags flags); * @return \c 1 if flags are set, \c 0 if flags are unset. * @note All flags have to be set for the result to be \c 1. */ -int scope_flags(struct scope *scope, enum scope_flags flags); +unsigned scope_flags(struct scope *scope, enum scope_flags flags); /** * Add child scope to \p parent. @@ -196,7 +196,7 @@ void scope_add_scope(struct scope *parent, struct scope *child); * @param node Actualized AST node. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_actual(struct scope *scope, struct ast_node *node); +int scope_add_actual(struct scope *scope, struct ast *node); /** * Add variable to scope. @@ -206,7 +206,7 @@ int scope_add_actual(struct scope *scope, struct ast_node *node); * @param var Variable to add to scope. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_var(struct scope *scope, struct ast_node *var); +int scope_add_var(struct scope *scope, struct ast *var); /** * Add type to scope. @@ -216,8 +216,7 @@ int scope_add_var(struct scope *scope, struct ast_node *var); * @param type Type to add to scope. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_type(struct scope *scope, struct ast_node *id, - struct ast_node *type); +int scope_add_type(struct scope *scope, char *id, struct ast *type); /** * Add procedure to scope. @@ -227,7 +226,7 @@ int scope_add_type(struct scope *scope, struct ast_node *id, * @param proc Procedure to add to scope. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_proc(struct scope *scope, struct ast_node *proc); +int scope_add_proc(struct scope *scope, struct ast *proc); /** * Add macro to scope. @@ -237,7 +236,7 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc); * @param macro Macro to add to scope. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_macro(struct scope *scope, struct ast_node *macro); +int scope_add_macro(struct scope *scope, struct ast *macro); /** * Add template to scope. @@ -247,7 +246,7 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro); * @param type_template Template to add to scope. * @return \c 0 when succesful, non-zero otherwise. */ -int scope_add_trait(struct scope *scope, struct ast_node *trait); +int scope_add_trait(struct scope *scope, struct ast *trait); int scope_resolve(struct scope *scope); @@ -261,7 +260,7 @@ int scope_resolve(struct scope *scope); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_var(struct scope *scope, struct ast_node *id); +struct ast *scope_find_var(struct scope *scope, char *id); /** * Find a type with ID in \p scope. @@ -273,7 +272,7 @@ struct ast_node *scope_find_var(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_type(struct scope *scope, struct ast_node *id); +struct ast *scope_find_type(struct scope *scope, char *id); /** * Find a procedure with ID in \p scope. @@ -285,7 +284,7 @@ struct ast_node *scope_find_type(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_proc(struct scope *scope, struct ast_node *id); +struct ast *scope_find_proc(struct scope *scope, char *id); /** * Find a macro with ID in \p scope. @@ -297,7 +296,7 @@ struct ast_node *scope_find_proc(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_macro(struct scope *scope, struct ast_node *id); +struct ast *scope_find_macro(struct scope *scope, char *id); /** * Find an alias with ID in \p scope. @@ -309,7 +308,7 @@ struct ast_node *scope_find_macro(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_alias(struct scope *scope, struct ast_node *id); +struct ast *scope_find_alias(struct scope *scope, char *id); /** * Find a template with ID in \p scope. @@ -321,7 +320,7 @@ struct ast_node *scope_find_alias(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *scope_find_trait(struct scope *scope, struct ast_node *id); +struct ast *scope_find_trait(struct scope *scope, char *id); /** * Find a variable with ID visible to \p scope. @@ -331,7 +330,7 @@ struct ast_node *scope_find_trait(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_var(struct scope *scope, struct ast_node *id); +struct ast *file_scope_find_var(struct scope *scope, char *id); /** * Find a type with ID visible to \p scope. @@ -341,7 +340,7 @@ struct ast_node *file_scope_find_var(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_type(struct scope *scope, struct ast_node *id); +struct ast *file_scope_find_type(struct scope *scope, char *id); /** * Find a procedure with ID visible to \p scope. @@ -351,7 +350,7 @@ struct ast_node *file_scope_find_type(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_proc(struct scope *scope, struct ast_node *id); +struct ast *file_scope_find_proc(struct scope *scope, char *id); /** * Find a macro with ID visible to \p scope. @@ -361,8 +360,7 @@ struct ast_node *file_scope_find_proc(struct scope *scope, struct ast_node *id); * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_macro(struct scope *scope, - struct ast_node *id); +struct ast *file_scope_find_macro(struct scope *scope, char *id); /** * Find a alias with ID visible to \p scope. * @@ -371,8 +369,7 @@ struct ast_node *file_scope_find_macro(struct scope *scope, * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_alias(struct scope *scope, - struct ast_node *id); +struct ast *file_scope_find_alias(struct scope *scope, char *id); /** * Find a template with ID visible to \p scope. @@ -382,7 +379,9 @@ struct ast_node *file_scope_find_alias(struct scope *scope, * @return Pointer to the AST node corresponding to \p id if found, * otherwise \c NULL. */ -struct ast_node *file_scope_find_trait(struct scope *scope, - struct ast_node *id); +struct ast *file_scope_find_trait(struct scope *scope, char *id); + +#define foreach_visible(iter, init)\ + for (struct visible *iter = init; iter; iter = iter->next) #endif /* SCOPE_H */ diff --git a/include/ek/vec.h b/include/ek/vec.h index 8d5cae1..d9048fe 100644 --- a/include/ek/vec.h +++ b/include/ek/vec.h @@ -35,4 +35,7 @@ void vec_append(struct vec *v, void *n); #define vect_pop(type, v) \ *(type *)vec_pop(&v) +#define vec_uninit(v) \ + (v.buf == NULL) + #endif /* VEC_H */ diff --git a/src/actualize.c b/src/actualize.c index 744dc1a..6d85640 100644 --- a/src/actualize.c +++ b/src/actualize.c @@ -18,11 +18,8 @@ #define UNUSED(x) do { (void)(x); } while (0) -static int replace_type_id(struct ast_node *nodes, struct ast_node *id, - struct ast_node *replacement); - struct act_stack { - struct ast_node *node; + struct ast *node; struct act_stack *next; }; @@ -35,23 +32,36 @@ enum act_flags { struct act_state { enum act_flags flags; - struct ast_node *last_var; - struct ast_node *cur_trait; - struct ast_node *cur_proc; + struct ast *cur_proc; struct act_stack *defer_stack; struct act_stack *goto_stack; struct act_stack *label_stack; }; -static int is_lvalue(struct ast_node *n) +static int replace_type_id(struct ast *nodes, char *id, + struct type *replacement); + +static int actualize(struct act_state *state, struct scope *scope, + struct ast *node); + +static int actualize_list(struct act_state *state, struct scope *scope, + struct ast *l); + +static int actualize_type(struct act_state *state, struct scope *scope, + struct type *node); + +static int actualize_type_list(struct act_state *state, struct scope *scope, + struct type *node); + +static int is_lvalue(struct ast *n) { - if (n->node_type == AST_ARR_ACCESS) + if (n->k == AST_ARR) return 1; - if (n->node_type == AST_ID) + if (n->k == AST_ID) return 1; - if (n->node_type == AST_UNOP && AST_UNOP(n).op == AST_DEREF) + if (n->k == AST_DEREF) return 1; return 0; @@ -68,85 +78,65 @@ static enum act_flags act_flags(struct act_state *state, enum act_flags flags) return state->flags & flags; } -/* TODO: this could be improved if we get a global void thing */ -static int is_void(struct ast_node *type) +static bool is_void(struct type *t) { - assert(type->node_type == AST_TYPE); - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) - return 0; + /* missing return is void */ + if (!t) + return 1; - return AST_PRIMITIVE_TYPE(type).type == AST_VOID; + return t->k == TYPE_VOID; } -static struct ast_node *void_type() +static struct type *void_type() { - struct ast_node *v = gen_primitive(AST_VOID, NULL, NULL_LOC()); - ast_set_flags(v, AST_FLAG_INIT | AST_FLAG_ACTUAL); - v->type = v; - return v; + return tgen_primitive(TYPE_VOID, strdup("void"), NULL, NULL_LOC()); } -static struct ast_node *i27_type(struct scope *scope) +static struct type *i27_type(struct scope *scope) { - struct ast_node *i27 = gen_id(strdup("i27"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, i27); + struct ast *def = file_scope_find_type(scope, "i27"); if (!def) { error("missing definition of type 'i27'"); return NULL; } - struct ast_node *a = gen_primitive(AST_I27, def, def->loc); - ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); - a->type = a; - return a; + return tgen_primitive(TYPE_I27, strdup("i27"), def, def->loc); } -static struct ast_node *i9_type(struct scope *scope) +static struct type *i9_type(struct scope *scope) { - struct ast_node *i27 = gen_id(strdup("i9"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, i27); + struct ast *def = file_scope_find_type(scope, "i9"); if (!def) { error("missing definition of type 'i9'"); return NULL; } - struct ast_node *a = gen_primitive(AST_I9, def, def->loc); - ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); - a->type = a; - return a; + return tgen_primitive(TYPE_I9, strdup("i9"), def, def->loc); } -static struct ast_node *str_type(struct scope *scope) +static struct type *str_type(struct scope *scope) { - struct ast_node *str = gen_id(strdup("str"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, str); + struct ast *def = file_scope_find_type(scope, "str"); if (!def) { error("missing definition of type 'str'"); return NULL; } - struct ast_node *a = gen_primitive(AST_STR, def, def->loc); - ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); - a->type = a; - return a; + return tgen_primitive(TYPE_STR, strdup("str"), def, def->loc); } -static struct ast_node *bool_type(struct scope *scope) +static struct type *bool_type(struct scope *scope) { - struct ast_node *b = gen_id(strdup("bool"), NULL_LOC()); - struct ast_node *def = file_scope_find_type(scope, b); + struct ast *def = file_scope_find_type(scope, "bool"); if (!def) { error("missing definition of type 'bool'"); return NULL; } - struct ast_node *a = gen_primitive(AST_BOOL, def, def->loc); - ast_set_flags(a, AST_FLAG_INIT | AST_FLAG_ACTUAL); - a->type = a; - return a; + return tgen_primitive(TYPE_BOOL, strdup("bool"), def, def->loc); } -static int push_defer(struct act_state *state, struct ast_node *expr) +static int push_defer(struct act_state *state, struct ast *expr) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -160,7 +150,7 @@ static int push_defer(struct act_state *state, struct ast_node *expr) return 0; } -static int push_label(struct act_state *state, struct ast_node *label) +static int push_label(struct act_state *state, struct ast *label) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -174,23 +164,20 @@ static int push_label(struct act_state *state, struct ast_node *label) return 0; } -static struct ast_node *find_label(struct act_state *state, - struct ast_node *label) +static struct ast *find_label(struct act_state *state, char *label) { - assert(label->node_type == AST_LABEL); struct act_stack *prev = state->label_stack, *cur; if (prev) do { cur = prev->next; - if (same_id(AST_LABEL(prev->node).id, - AST_LABEL(label).id)) + if (same_id(label_id(prev->node), label)) return prev->node; } while ((prev = cur)); return NULL; } -static int push_goto(struct act_state *state, struct ast_node *got) +static int push_goto(struct act_state *state, struct ast *got) { struct act_stack *new = calloc(1, sizeof(struct act_stack)); if (!new) { @@ -204,17 +191,17 @@ static int push_goto(struct act_state *state, struct ast_node *got) return 0; } -static struct ast_node *clone_defers(struct act_state *state, +static struct ast *clone_defers(struct act_state *state, struct act_stack *to) { struct act_stack *from = state->defer_stack; /* maintain reverse order */ - struct ast_node *defers = NULL, *prev = NULL; + struct ast *defers = NULL, *prev = NULL; while (from != to) { - struct ast_node *defer = clone_ast_node(from->node); + struct ast *defer = clone_ast(from->node); if (prev) - prev->next = defer; + prev->n = defer; if (!defers) defers = defer; @@ -263,130 +250,78 @@ static void destroy_act_state(struct act_state *state) clear_gotos(state, NULL); } -static int actualize(struct act_state *state, struct scope *scope, - struct ast_node *node); -static int analyze(struct scope *scope, struct ast_node *tree); +static void type_mismatch(struct scope *scope, struct ast *node, struct type *a, struct type *b) +{ + char *left_type = type_str(a); + char *right_type = type_str(b); + semantic_error(scope->fctx, node, + "type mismatch: %s vs %s", + left_type, right_type); + free(left_type); + free(right_type); +} + +static int analyze(struct scope *scope, struct ast *tree); -static int eval_const_if(struct scope *scope, struct ast_node *node) +static int eval_const_if(struct scope *scope, struct ast *node) { assert(node - && node->node_type == AST_IF + && node->k == AST_IF && ast_flags(node, AST_FLAG_CONST)); - int eval = 0; - struct ast_node *cond = node->_if.cond; - struct ast_node *next = node->next; - - switch (cond->node_type) { - /* for now just check if the variable exists, might change in - * the future */ - case AST_ID: eval = file_scope_find_var(scope, cond) != NULL; - break; - default: semantic_error(scope->fctx, node, - "const if conditional at file scope unimplemented"); - return -1; - } - - if (eval) { - /* condition evaluated true, so keep the if block */ - struct ast_node *body = node->_if.body; - ast_last_node(body)->next = node->next; - - /* an if must have a body, otherwise the parser messed up */ - assert(body); - - *node = *body; - free(body); - return 0; - } - - struct ast_node *els = node->_if.els; - if (els) { - *node = *els; - ast_last_node(els)->next = next; - free(els); - return 0; - } - - if (next) { - /* copy the next node into our place */ - *node = *next; - free(next); - return 0; - } - - free(node); - return 0; + semantic_error(scope->fctx, node, + "const if unimplemented"); + return -1; } -static int analyze_visibility(struct scope *scope, struct ast_node *node) +static int analyze_visibility(struct scope *scope, struct ast *node) { if (!node) return 0; - int ret = 0; node->scope = scope; - /* TODO: add error checking */ - switch (node->node_type) { - case AST_PROC: { - ret |= scope_add_proc(scope, node); - break; - } - - case AST_VAR: { - ret |= scope_add_var(scope, node); - break; - } + switch (node->k) { + case AST_PROC_DEF: return scope_add_proc(scope, node); + case AST_MACRO_DEF: return scope_add_macro(scope, node); + case AST_VAR_DEF: return scope_add_var(scope, node); case AST_IMPORT: { - const char *file = AST_IMPORT(node).file; - ret |= process_file(&scope, + const char *file = import_file(node); + return process_file(&scope, (int)ast_flags(node, AST_FLAG_PUBLIC), file); - break; } case AST_IF: { assert(ast_flags(node, AST_FLAG_CONST)); - ret |= eval_const_if(scope, node); - if (ret) + if (eval_const_if(scope, node)) return -1; /* since a const if likely replaced the current node with * something else, we have to analyze the replacement */ - ret = analyze(scope, node); - break; + return analyze(scope, node); } - case AST_STRUCT: { + case AST_STRUCT_DEF: { /* we shouldn't get any anonymous structs at this stage */ - struct ast_node *id = AST_STRUCT(node).id; - ret |= scope_add_type(scope, id, node); - break; + char *id = struct_id(node); + return scope_add_type(scope, id, node); } - case AST_ENUM: { - struct ast_node *id = AST_ENUM(node).id; - ret |= scope_add_type(scope, id, node); - break; + case AST_ENUM_DEF: { + char *id = enum_id(node); + return scope_add_type(scope, id, node); } - case AST_ALIAS: { - struct ast_node *id = AST_ALIAS(node).id; - ret |= scope_add_type(scope, id, node); - break; + case AST_ALIAS_DEF: { + char *id = alias_id(node); + return scope_add_type(scope, id, node); } - case AST_TRAIT: { - struct ast_node *id = AST_TRAIT(node).id; - ret |= scope_add_type(scope, id, node); - break; - } - - case AST_MACRO_CONSTRUCT: { - ret |= scope_add_macro(scope, node); - break; + case AST_TRAIT_DEF: { + char *id = trait_id(node); + return scope_add_type(scope, id, node); } case AST_EMPTY: { @@ -394,145 +329,188 @@ static int analyze_visibility(struct scope *scope, struct ast_node *node) } default: - ret = -1; semantic_error(scope->fctx, node, "unknown top element"); - break; + return -1; }; - return ret; + return 0; } -static int analyze_var(struct scope *scope, struct ast_node *node) +static int analyze_var(struct scope *scope, struct ast *node) { struct act_state state = {0}; return actualize(&state, scope, node); } -static void set_type(struct ast_node *node, struct ast_node *type) +static void set_type(struct ast *node, struct type *type) +{ + node->t = clone_type_list(type); +} + +static void replace_type(struct type *t, struct type *r) { - assert(type->node_type == AST_TYPE); - node->type = clone_ast_node(type); + /* free strings before they get overwritten */ + if (t->id) + free(t->id); + + struct src_loc loc = t->loc; + struct scope *scope = t->scope; + + *t = *r; + + /* clone so we don't accidentally free same string twice later */ + if (t->id) + t->id = strdup(t->id); + + /* these things we generally don't want to replace */ + t->loc = loc; + t->scope = scope; } -static int analyze_proc(struct scope *scope, struct ast_node *node) +static void replace_ast(struct ast *n, struct ast *t) { - /* not sure if this is the best place for this */ - AST_PROC(node).id->scope = scope; + if (n->s) + free(n->s); + + struct src_loc loc = n->loc; + struct scope *scope = n->scope; + *n = *t; + + if (n->s) + n->s = strdup(n->s); + + n->loc = loc; + t->scope = scope; +} + +static int analyze_proc(struct scope *scope, struct ast *node) +{ struct scope *proc_scope = create_scope(); scope_add_scope(scope, proc_scope); node->scope = proc_scope; - struct ast_node *sign = AST_PROC(node).sign; - + struct ast *params = proc_params(node); struct act_state state = {0}; - int ret = actualize(&state, proc_scope, sign); - set_type(node, sign); - return ret; + if (actualize_list(&state, proc_scope, params)) + return -1; + + struct type *rtype = proc_rtype(node); + + if (actualize_type_list(&state, proc_scope, rtype)) + return -1; + + if (!rtype) + proc_rtype(node) = void_type(); + + struct type *callable = tgen_callable(NULL, proc_rtype(node), node->loc); + foreach_node(p, params) { + /* we must manually 'start' the chain **/ + if (!callable_ptypes(callable)) { + callable_ptypes(callable) = clone_type(p->t); + continue; + } + + type_append(callable_ptypes(callable), p->t); + } + + set_type(node, callable); + return 0; } -static struct ast_node *analyze_type_expand(struct scope *scope, - struct ast_node *n) +static struct ast *analyze_type_expand(struct scope *scope, + struct ast *n) { - assert(n->node_type == AST_TYPE_EXPAND); - struct ast_node *trait = file_scope_find_type(scope, - AST_TYPE_EXPAND(n).id); + assert(n->k == AST_TYPE_EXPAND); + struct ast *trait = file_scope_find_type(scope, type_expand_id(n)); if (!trait) { semantic_error(scope->fctx, n, "no such type"); return NULL; } - if (trait->node_type != AST_TRAIT) { + if (trait->k != AST_TRAIT_DEF) { semantic_error(scope->fctx, n, "not a trait"); return NULL; } semantic_info(scope->fctx, n, "FIXME: skipping type param check for now"); - struct ast_node *body = AST_TRAIT(trait).raw_body; - body = clone_ast_node(body); + struct ast *body = trait_raw_body(trait); + body = clone_ast(body); - struct ast_node *pa = AST_TYPE_EXPAND(n).args; - foreach_node(pt, AST_TRAIT(trait).params) { - replace_type_id(body, AST_VAR(pt).id, pa); - pa = pa->next; + struct type *pa = type_expand_args(n); + foreach_node(pt, trait_params(trait)) { + replace_type_id(body, var_id(pt), pa); + pa = pa->n; } return body; } -static int implements_trait(struct ast_node *body, struct ast_node *id) +static int implements_trait(struct ast *body, char *id) { foreach_node(n, body) { - if (n->node_type != AST_ID) + /* traits don't currently take generic parameters I guess? */ + if (n->k != AST_ID) continue; - if (same_id(n, id)) + if (same_id(id_str(n), id)) return 1; } return 0; } -static void mark_implemented(struct ast_node *n) +static int analyze_struct(struct scope *scope, struct ast *node) { - assert(n->node_type == AST_TYPE_EXPAND); - /** @todo very hacky but good enough for now */ - struct ast_node *id = AST_TYPE_EXPAND(n).id; - n->node_type = AST_ID; - AST_ID(n).id = strdup(AST_ID(id).id); -} - -static int analyze_struct(struct scope *scope, struct ast_node *node) -{ - assert(node->node_type == AST_STRUCT); - struct ast_node *generics = AST_STRUCT(node).generics; + assert(node->k == AST_STRUCT_DEF); + struct ast *params = struct_params(node); struct scope *struct_scope = create_scope(); if (!struct_scope) return -1; scope_add_scope(node->scope, struct_scope); node->scope = struct_scope; - if (generics) + if (params) ast_set_flags(node, AST_FLAG_GENERIC); - struct ast_node *type = gen_type(AST_TYPE_STRUCT, node, NULL, - node->loc); + struct type *type = tgen_type(TYPE_STRUCT, + NULL, NULL, + node, NULL, + strdup(struct_id(node)), + node->loc); - foreach_node(n, AST_STRUCT(node).body) { - if (n->node_type != AST_TYPE_EXPAND) + foreach_node(n, struct_body(node)) { + if (n->k != AST_TYPE_EXPAND) continue; - if (implements_trait(AST_STRUCT(node).body, - AST_TYPE_EXPAND(n).id)) { - n->node_type = AST_EMPTY; + if (implements_trait(struct_body(node), type_expand_id(n))) { + n->k = AST_EMPTY; continue; } - if (same_id(AST_STRUCT(node).id, AST_TYPE_EXPAND(n).id)) { + if (same_id(struct_id(node), type_expand_id(n))) { semantic_error(scope->fctx, n, "recursive trait implementations not allowed"); return -1; } - struct ast_node *body = analyze_type_expand(scope, n); + struct ast *body = analyze_type_expand(scope, n); if (!body) { - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - replace_type_id(body, AST_TYPE_EXPAND(n).id, type); - ast_block_last(body)->next = n->next; - n->next = body; - - mark_implemented(n); + replace_type_id(body, type_expand_id(n), type); + ast_block_last(body)->n = n->n; + n->n = body; } - foreach_node(n, AST_STRUCT(node).body) { - switch (n->node_type) { + foreach_node(n, struct_body(node)) { + switch (n->k) { case AST_EMPTY: continue; case AST_ID: continue; /* prototypes are checked later */ - case AST_PROC: if (!AST_PROC(n).body) continue; + case AST_PROC_DEF: if (!proc_body(n)) continue; default: } @@ -540,9 +518,9 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) return -1; } - foreach_node(n, AST_STRUCT(node).body) { + foreach_node(n, struct_body(node)) { /* also checks prototypes */ - if (n->node_type != AST_PROC) + if (n->k != AST_PROC_DEF) continue; if (analyze_proc(struct_scope, n)) @@ -550,38 +528,47 @@ static int analyze_struct(struct scope *scope, struct ast_node *node) } /* check that all prototypes are implemented */ - foreach_node(n, AST_STRUCT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, struct_body(node)) { + if (n->k != AST_PROC_DEF) continue; - if (AST_PROC(n).body) + if (proc_body(n)) continue; - struct ast_node *proc = scope_find_proc(struct_scope, - AST_PROC(n).id); + struct ast *proc = scope_find_proc(struct_scope, proc_id(n)); if (!proc) { semantic_error(scope->fctx, n, "missing implementation"); return -1; } - if (!equiv_nodes(AST_PROC(n).sign, AST_PROC(proc).sign)) { + if (!types_match(n->t, proc->t)) { semantic_error(scope->fctx, n, "mismatched signatures"); semantic_info(scope->fctx, proc, "note: here"); return -1; } } + char *id = strdup(struct_id(node)); + if (same_id(id, "i27")) + node->t = tgen_primitive(TYPE_I27, id, node, node->loc); + else if (same_id(id, "i9")) + node->t = tgen_primitive(TYPE_I9, id, node, node->loc); + else if (same_id(id, "bool")) + node->t = tgen_primitive(TYPE_BOOL, id, node, node->loc); + else + node->t = tgen_struct(id, node, node->loc); + /** @todo there is the possibility that two different traits add the * same prototype, which is reported in traits but not structs? */ return 0; } /* quite a lot of overlap with analyze_struct, kind of ugly I guess */ -static int analyze_trait(struct scope *scope, struct ast_node *node) +static int analyze_trait(struct scope *scope, struct ast *node) { - assert(node->node_type == AST_TRAIT); - struct ast_node *generics = AST_TRAIT(node).params; + assert(node->k == AST_TRAIT_DEF); + struct ast *params = trait_params(node); struct scope *trait_scope = create_scope(); if (!trait_scope) return -1; @@ -589,54 +576,55 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) scope_add_scope(node->scope, trait_scope); node->scope = trait_scope; /** @todo should probably add in aliases for the traits in scope? */ - if (generics) + if (params) ast_set_flags(node, AST_FLAG_GENERIC); - struct ast_node *type = gen_type(AST_TYPE_TRAIT, node, NULL, node->loc); + struct type *type = tgen_type(TYPE_TRAIT, NULL, NULL, + node, NULL, strdup(trait_id(node)), + node->loc); + + /* copy body */ + node->a2 = clone_ast(trait_raw_body(node)); - AST_TRAIT(node).body = clone_ast_node(AST_TRAIT(node).raw_body); /* do type expansions */ - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_TYPE_EXPAND) + foreach_node(n, trait_body(node)) { + if (n->k != AST_TYPE_EXPAND) continue; /* don't re-expand already implemented traits */ - if (implements_trait(AST_TRAIT(node).body, - AST_TYPE_EXPAND(n).id)) { + if (implements_trait(trait_body(node), type_expand_id(n))) { /* not sure about this, but at least we don't have stray * type expands everywhere */ - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - if (same_id(AST_TRAIT(node).id, AST_TYPE_EXPAND(n).id)) { + if (same_id(trait_id(node), type_expand_id(n))) { semantic_error(scope->fctx, n, "recursive trait implementations not allowed"); return -1; } - struct ast_node *body = analyze_type_expand(scope, n); + struct ast *body = analyze_type_expand(scope, n); if (!body) { - n->node_type = AST_EMPTY; + n->k = AST_EMPTY; continue; } - replace_type_id(body, AST_TYPE_EXPAND(n).id, type); - ast_last_node(body)->next = n->next; - n->next = body; - - mark_implemented(n); + replace_type_id(body, type_expand_id(n), type); + ast_last(body)->n = n->n; + n->n = body; } /* add all procedure definitions to scope */ - foreach_node(n, AST_TRAIT(node).body) { + foreach_node(n, trait_body(node)) { /* kind of a hack but these shouldn't be shown to * analyze_visibility */ - switch (n->node_type) { + switch (n->k) { case AST_EMPTY: continue; case AST_ID: continue; /* prototypes are added later */ - case AST_PROC: if (!AST_PROC(n).body) continue; + case AST_PROC_DEF: if (!proc_body(n)) continue; default: } @@ -649,47 +637,49 @@ static int analyze_trait(struct scope *scope, struct ast_node *node) * would be. Add a prototypes -list to scopes? */ /* add all prototypes that don't have matching definition to scope */ - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, trait_body(node)) { + if (n->k != AST_PROC_DEF) continue; - if (AST_PROC(n).body) + if (proc_body(n)) continue; /* prototypes are checked only if there's no implementation */ - if (scope_find_proc(trait_scope, AST_PROC(n).id)) + if (scope_find_proc(trait_scope, proc_id(n))) continue; if (analyze_visibility(trait_scope, n)) return -1; } - foreach_node(n, AST_TRAIT(node).body) { - if (n->node_type != AST_PROC) + foreach_node(n, trait_body(node)) { + if (n->k != AST_PROC_DEF) continue; if (analyze_proc(trait_scope, n)) return -1; } + node->t = tgen_trait(strdup(struct_id(node)), node, node->loc); + return 0; } -static int analyze_signs(struct scope *scope, struct ast_node *node) +static int analyze_signs(struct scope *scope, struct ast *node) { /** @todo aliases? */ - switch (node->node_type) { - case AST_VAR: return analyze_var(scope, node); break; - case AST_PROC: return analyze_proc(scope, node); break; - case AST_STRUCT: return analyze_struct(scope, node); break; - case AST_TRAIT: return analyze_trait(scope, node); break; + switch (node->k) { + case AST_VAR_DEF: return analyze_var(scope, node); break; + case AST_PROC_DEF: return analyze_proc(scope, node); break; + case AST_STRUCT_DEF: return analyze_struct(scope, node); break; + case AST_TRAIT_DEF: return analyze_trait(scope, node); break; default: } return 0; } -static int analyze(struct scope *scope, struct ast_node *tree) +static int analyze(struct scope *scope, struct ast *tree) { foreach_node(node, tree) { if (analyze_visibility(scope, node)) @@ -707,13 +697,13 @@ static int analyze(struct scope *scope, struct ast_node *tree) return -1; printf("//actualized:\n"); - dump_ast_node(0, node); + ast_dump(0, node); } return 0; } -int analyze_root(struct scope *scope, struct ast_node *tree) +int analyze_root(struct scope *scope, struct ast *tree) { if (analyze(scope, tree)) return -1; @@ -721,22 +711,13 @@ int analyze_root(struct scope *scope, struct ast_node *tree) return 0; } -static int structs_match(struct ast_node *a, struct ast_node *b) +static int structs_match(struct type *a, struct type *b) { - /** @todo: iterate over elements in structure */ - /** @todo: check tag as well? */ - return 0; -} - -static int primitives_match(struct ast_node *a, struct ast_node *b) -{ - enum ast_primitive at = AST_PRIMITIVE_TYPE(a).type; - enum ast_primitive bt = AST_PRIMITIVE_TYPE(b).type; - - return at == bt; + /* dunno, let's go with this for now */ + return a->d == b->d; } -int types_match(struct ast_node *a, struct ast_node *b) +int types_match(struct type *a, struct type *b) { if (!a && !b) return 1; @@ -744,111 +725,85 @@ int types_match(struct ast_node *a, struct ast_node *b) if (!a || !b) return 0; - assert(a->node_type == AST_TYPE); - assert(b->node_type == AST_TYPE); - /* if the type kind doesn't match, we're done. */ - if (AST_TYPE(a).kind != AST_TYPE(b).kind) + if (a->k != b->k) return 0; - if (AST_TYPE(a).kind == AST_TYPE_STRUCT) + if (a->k == TYPE_STRUCT) return structs_match(a, b); - if (AST_TYPE(a).kind == AST_TYPE_POINTER) - return types_match(AST_PTR_TYPE(a).base, - AST_PTR_TYPE(b).base); - - if (AST_TYPE(a).kind == AST_TYPE_PRIMITIVE) - return primitives_match(a, b); + if (a->k == TYPE_PTR) + return types_match(ptr_base(a), ptr_base(b)); - return 0; + return 1; } -static int _replace_id(struct ast_node *node, void *data) +static int _replace_id(struct ast *node, void *data) { if (!node) return 0; - if (node->node_type != AST_ID) - return ast_call_on(_replace_id, node, data); + if (node->k != AST_ID) + return 0; - struct ast_node **pair = data; - struct ast_node *id = pair[0]; - struct ast_node *expr = pair[1]; + struct ast **pair = data; + struct ast *id = pair[0]; + struct ast *expr = pair[1]; - if (!same_id(node, id)) - return ast_call_on(_replace_id, node, data); + /* no match, continue */ + if (!same_id(id_str(node), id_str(id))) + return 0; - struct ast_node *clone = clone_ast_node(expr); + struct ast *clone = clone_ast(expr); if (!clone) { internal_error("failed cloning replacement expr"); return -1; } - clone->next = node->next; + clone->n = node->n; clone->scope = node->scope; - *node = *clone; + replace_ast(node, clone); /* a succesful replacement needs no futher replacements, I think */ return 0; } -static int replace_id(struct ast_node *body, struct ast_node *id, - struct ast_node *expr) +static int replace_id(struct ast *body, struct ast *id, + struct ast *expr) { - struct ast_node *pair[2] = {id, expr}; - return ast_call_on(_replace_id, body, pair); + struct ast *pair[2] = {id, expr}; + return ast_visit(_replace_id, NULL, body, pair); } -static int actualize_macro_construct(struct act_state *state, - struct scope *scope, struct ast_node *n) +static int actualize_macro_def(struct act_state *state, + struct scope *scope, struct ast *n) { UNUSED(state); /* macro bodies, arguments, etc aren't expanded upon until the macro is * called, so just try to add it to the local scope */ - assert(n && n->node_type == AST_MACRO_CONSTRUCT); + assert(n && n->k == AST_MACRO_DEF); return scope_add_macro(scope, n); } -struct ast_node *extract_trait(struct ast_node *type) -{ - if (!type) - return 0; - - assert(type->node_type == AST_TYPE); - if (type->_type.kind == AST_TYPE_TRAIT) - return type; - - return extract_trait(type->_type.next); -} - -static void actualize_trait_types(struct ast_node *params, - struct ast_node *args) -{ - /** @todo replace trait types with arg types, should probably be merged - * */ - assert(!args && !params); -} - static int actualize_macro_expand(struct act_state *state, struct scope *scope, - struct ast_node *macro_expand) + struct ast *macro_expand) { - assert(macro_expand->node_type == AST_MACRO_EXPAND); - struct ast_node *id = AST_MACRO_EXPAND(macro_expand).id; - struct ast_node *macro = file_scope_find_macro(scope, id); + assert(macro_expand->k == AST_MACRO_EXPAND); + char *id = macro_expand_id(macro_expand); + struct ast *macro = file_scope_find_macro(scope, id); if (!macro) { semantic_error(scope->fctx, macro_expand, "no such macro"); return -1; } - assert(macro->node_type == AST_MACRO_CONSTRUCT); + assert(macro->k == AST_MACRO_DEF); if (ast_flags(macro, AST_FLAG_VARIADIC)) { semantic_error(scope->fctx, macro, "variadic macros not yet implemented"); return -1; } - struct ast_node *body = clone_ast_node(AST_MACRO_CONSTRUCT(macro).body); + struct ast *body = clone_ast(macro_def_body(macro)); if (!body) { internal_error("failed allocating body for macro expansion"); return -1; @@ -856,63 +811,79 @@ static int actualize_macro_expand(struct act_state *state, /** @todo update all macro IDs to the correct scope */ body->scope = macro_expand->scope; - body->next = macro_expand->next; + body->n = macro_expand->n; - struct ast_node *param = AST_MACRO_CONSTRUCT(macro).params; - struct ast_node *arg = AST_MACRO_EXPAND(macro_expand).args; + struct ast *param = macro_def_params(macro); + struct ast *arg = macro_expand_args(macro_expand); - /* TODO: actual replacements */ while (param && arg) { /* feels slightly hacky, but essentially replace each individual * component in the arg list by breaking it out of the list * temporarily. After the replacement, insert it back into the * list so the cleanup is easier. */ - struct ast_node *next_arg = arg->next; - arg->next = NULL; + struct ast *next_arg = arg->n; + arg->n = NULL; if (replace_id(body, param, arg)) { semantic_error(scope->fctx, macro_expand, "failed replacing params with args"); - arg->next = next_arg; + arg->n = next_arg; return -1; } - param = param->next; - arg = arg->next = next_arg; + param = param->n; + arg = arg->n = next_arg; } + free(macro_expand_id(macro_expand)); *macro_expand = *body; /* actualize the new content */ return actualize(state, scope, macro_expand); } static int actualize_call(struct act_state *state, - struct scope *scope, struct ast_node *call) + struct scope *scope, struct ast *call) { - assert(call && call->node_type == AST_CALL); + assert(call && call->k == AST_CALL); - /* check that arguments exist, make sure they have types etc. */ - /* TODO: procedure callbacks? */ - int ret = actualize(state, scope, AST_CALL(call).args); - if (ret) - return ret; + if (actualize_list(state, scope, call_args(call))) + return -1; - ret = actualize(state, scope, AST_CALL(call).expr); - if (ret) - return ret; + if (actualize_list(state, scope, call_expr(call))) + return -1; - semantic_info(scope->fctx, call, "FIXME: skipping type checks for now"); - struct ast_node *expr = AST_CALL(call).expr; - if (AST_TYPE(expr->type).kind != AST_TYPE_SIGN) { - char *tstr = type_str(expr->type); - semantic_info(scope->fctx, call, "not a callable type: %s", + struct ast *expr = call_expr(call); + if (expr->t->k != TYPE_CALLABLE) { + char *tstr = type_str(expr->t); + semantic_error(scope->fctx, call, "not a callable type: %s", tstr); free(tstr); return -1; } - struct ast_node *sign = expr->type; - set_type(call, AST_SIGN_TYPE(sign).ret); + struct type *callable = expr->t; + struct type *ptypes = callable_ptypes(callable); + struct ast *arg = call_args(call); + foreach_type(p, ptypes) { + if (!arg) { + semantic_error(scope->fctx, call, "too many arguments"); + return -1; + } + + if (!types_match(p, arg->t)) { + type_mismatch(scope, arg, p, arg->t); + return -1; + } + + arg = arg->n; + } + + if (arg) { + semantic_error(scope->fctx, arg, "too many arguments"); + return -1; + } + + set_type(call, callable_rtype(callable)); return 0; } @@ -923,7 +894,7 @@ static void warn_unused_labels(struct act_state *state, struct scope *scope) return; do { - struct ast_node *label = labels->node; + struct ast *label = labels->node; if (!ast_flags(label, AST_FLAG_ACTUAL)) semantic_warn(scope->fctx, label, "unused label"); @@ -939,7 +910,7 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) return ret; do { - struct ast_node *got = gotos->node; + struct ast *got = gotos->node; if (!ast_flags(got, AST_FLAG_ACTUAL)) { semantic_warn(scope->fctx, got, "undefined label"); @@ -952,39 +923,35 @@ static int undefined_gotos(struct act_state *state, struct scope *scope) } static int actualize_proc(struct act_state *state, - struct scope *scope, struct ast_node *proc) + struct scope *scope, struct ast *proc) { + UNUSED(state); /* actualize_proc is called on trait procs as well, but I believe * that's fine? */ - assert(proc && proc->node_type == AST_PROC); - struct ast_node *sign = AST_PROC(proc).sign; + assert(proc && proc->k == AST_PROC_DEF); - /* signature should already be typed etc. */ struct act_state new_state = {0}; - if (actualize(&new_state, proc->scope, sign)) - return -1; - - set_type(proc, sign); + /* params should already have been actualized, should maybe check */ /* actualize body */ new_state.cur_proc = proc; - if (actualize(&new_state, proc->scope, AST_PROC(proc).body)) + if (actualize(&new_state, proc->scope, proc_body(proc))) return -1; if (!act_flags(&new_state, ACT_HAS_RETURN)) { - if (!is_void(AST_SIGN_TYPE(sign).ret)) { + if (!is_void(proc_rtype(proc))) { semantic_error(scope->fctx, proc, "no return with non-void return type"); return -1; } /* add 'implicit' return */ - struct ast_node *body = AST_PROC(proc).body; - struct ast_node *r = gen_return(NULL, NULL_LOC()); + struct ast *body = proc_body(proc); + struct ast *r = gen_return(NULL, NULL, NULL_LOC()); r->scope = body->scope; - ast_append(AST_BLOCK(body).body, r); + ast_append(block_body(body), r); } - else if (ast_block_last(AST_PROC(proc).body)->node_type != AST_RETURN) { + else if (ast_block_last(proc_body(proc))->k != AST_RETURN) { /* TODO: something more sophisticated than this */ semantic_warn(scope->fctx, proc, "unable to determine explicit return for all branches"); @@ -996,63 +963,41 @@ static int actualize_proc(struct act_state *state, return -1; /* if we're main, don't mangle the entry point */ - struct ast_node *id = AST_PROC(proc).id; - if (strcmp("main", AST_ID(id).id) == 0) - ast_set_flags(id, AST_FLAG_NOMANGLE); + char *id = proc_id(proc); + if (strcmp("main", id) == 0) + ast_set_flags(proc, AST_FLAG_NOMANGLE); /* we have successfully actualized the procedure */ return 0; } static int actualize_binop(struct act_state *state, - struct scope *scope, struct ast_node *binop) + struct scope *scope, struct ast *binop) { - assert(binop && binop->node_type == AST_BINOP); - - struct ast_node *left = AST_BINOP(binop).left; - struct ast_node *right = AST_BINOP(binop).right; + assert(binop && is_binop(binop)); - int ret = 0; - ret |= actualize(state, scope, left); - ret |= actualize(state, scope, right); - if (ret) - return ret; + struct ast *left = binop_left(binop); + struct ast *right = binop_right(binop); - if (!left->type) { - semantic_error(scope->fctx, binop, - "unable to detect lefthand type"); + if (actualize(state, scope, left)) return -1; - } - if (!right->type) { - semantic_error(scope->fctx, binop, - "unable to detect righthand type"); + if (actualize(state, scope, right)) return -1; - } - if (!types_match(left->type, right->type)) { - char *left_type = type_str(left); - char *right_type = type_str(right); - semantic_error(scope->fctx, binop, - "type mismatch (%s vs %s)", left_type, - right_type); - free(left_type); - free(right_type); + if (!types_match(left->t, right->t)) { + type_mismatch(scope, binop, left->t, right->t); return -1; } - /* TODO: also check trait types, just because two traits collapse - * to the same actual type doesn't mean that the two trait types - * should be allowed to operate on eachother */ - /* types are the same, so the type of this expression is whichever */ - set_type(binop, left->type); + set_type(binop, left->t); return 0; } static int actualize_block(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { struct scope *block_scope = scope; if (!ast_flags(node, AST_FLAG_UNHYGIENIC)) { @@ -1066,20 +1011,21 @@ static int actualize_block(struct act_state *state, } struct act_stack *defers = state->defer_stack; - foreach_node(pt, node->_block.body) { + foreach_node(pt, block_body(node)) { if (actualize(state, block_scope, pt)) return -1; } - if (node->_block.body == NULL) { - node->type = void_type(); - node->_block.body = gen_empty(); + if (block_body(node) == NULL) { + node->t = void_type(); + /* still not a huge fan of directly mucking about with ast slots */ + block_body(node) = gen_empty(NULL_LOC()); return 0; } /* the block type is the last statement in the block's type */ - set_type(node, ast_last_node(node->_block.body)->type); - if (!node->type) { + set_type(node, ast_last(block_body(node))->t); + if (!node->t) { semantic_error(scope->fctx, node, "unable to detect block type"); return -1; @@ -1088,8 +1034,8 @@ static int actualize_block(struct act_state *state, /* TODO: currently defers are sort of duplicated after a return, unsure * if they should be handled here or somewhere else */ if (state->defer_stack) { - node->_block.defers = clone_defers(state, defers); - if (!node->_block.defers) { + block_defers(node) = clone_defers(state, defers); + if (!block_defers(node)) { internal_error("failed cloning defers"); return -1; } @@ -1101,10 +1047,10 @@ static int actualize_block(struct act_state *state, } static int actualize_id(struct act_state *state, - struct scope *scope, struct ast_node *id) + struct scope *scope, struct ast *id) { UNUSED(state); - assert(id && id->node_type == AST_ID); + assert(id && id->k == AST_ID); id->scope = scope; /** @todo vars and procs kind of override eachother, i.e. @@ -1119,16 +1065,16 @@ static int actualize_id(struct act_state *state, * Either add in some syntax to distinguish procedure calls and * pointer calls or make procs and vars share the same namespace. * */ - struct ast_node *decl = file_scope_find_var(scope, id); + struct ast *decl = file_scope_find_var(scope, id_str(id)); if (decl) { - set_type(id, decl->type); + set_type(id, decl->t); decl->uses++; return 0; } - decl = file_scope_find_proc(scope, id); + decl = file_scope_find_proc(scope, id_str(id)); if (decl) { - set_type(id, decl->type); + set_type(id, decl->t); decl->uses++; return 0; } @@ -1138,320 +1084,268 @@ static int actualize_id(struct act_state *state, } static int actualize_var(struct act_state *state, - struct scope *scope, struct ast_node *var) + struct scope *scope, struct ast *var) { - assert(var && var->node_type == AST_VAR); - struct ast_node *init = AST_VAR(var).init; - struct ast_node *type = AST_VAR(var).type; + assert(var && var->k == AST_VAR_DEF); + struct ast *init = var_init(var); + struct type *type = var_type(var); /* one of these must be defined, otherwise the parser fucked up */ assert(type || init); - if (init && actualize(state, scope, init)) + if (init && actualize_list(state, scope, init)) return -1; - if (type && actualize(state, scope, type)) + if (type && actualize_type_list(state, scope, type)) return -1; - if (init && init->node_type == AST_INIT) { - assert(!init->type); + if (init && init->k == AST_INIT) { + assert(!init->t); set_type(init, type); /* TODO: some kind of check_init() */ } if (init && type) { /* make sure the asked type and the actualized types match */ - if (!types_match(init->type, type)) { - char *init_type = type_str(init->type); - char *req_type = type_str(type); - semantic_error(scope->fctx, var, - "type mismatch (%s vs %s)", - req_type, init_type); - free(init_type); - free(req_type); + if (!types_match(init->t, type)) { + type_mismatch(scope, var, init->t, type); return -1; } } - /* this is important for lowering */ - AST_VAR(var).id->scope = scope; var->scope = scope; if (init) /* infer */ - set_type(var, init->type); + set_type(var, init->t); if (type) - /* TODO: should there be some default value? */ + /* TODO: should there be some default zero value? */ /* declare */ set_type(var, type); /* an unnamed var is a var in a signature that should not produce a * warning on not being used (if I ever get around to adding those kinds * of warnings) */ - if (AST_VAR(var).id && !ast_flags(var, AST_FLAG_MEMBER)) + if (var_id(var) && !ast_flags(var, AST_FLAG_MEMBER)) return scope_add_var(scope, var); - /* TODO: we should make sure the type is fully qualified in bodies */ return 0; } -#define ENTER_ACT() \ - enum act_flags old_flags = state->flags; \ - struct ast_node *old_trait = state->cur_trait; - -#define EXIT_ACT(r) \ - do { \ - state->cur_trait = old_trait; \ - state->flags = old_flags; \ - return r; \ - } while (0); - -static enum ast_primitive id_to_primitive(struct ast_node *id) +static int actualize_tid(struct act_state *state, struct scope *scope, struct type *t) { - const char *name = AST_ID(id).id; - if (strcmp(name, "i27") == 0) - return AST_I27; + UNUSED(state); - if (strcmp(name, "i9") == 0) - return AST_I9; + /* no id means void */ + if (!t->id) { + replace_type(t, void_type()); + return 0; + } - if (strcmp(name, "bool") == 0) - return AST_BOOL; + struct src_loc l = t->loc; - /** @todo some better error reporting should probably be used */ - internal_error( "illegal primitive: %s\n", name); - return AST_VOID; -} + struct ast *def = file_scope_find_type(scope, t->id); + if (!def) { + type_error(scope->fctx, t, "no such type"); + return -1; + } -static int struct_is_primitive(struct ast_node *s) -{ - struct ast_node *id = AST_STRUCT(s).id; - const char *name = AST_ID(id).id; - if (strcmp(name, "i27") == 0) - return 1; + assert(t->n == NULL); + assert(def->k != AST_TRAIT_DEF); - if (strcmp(name, "i9") == 0) - return 1; + if (def->k == AST_ALIAS_DEF) { + replace_type(t, clone_type_list(def->t)); + t->a = def; + return 0; + } + else if (def->k == AST_STRUCT_DEF) { + replace_type(t, clone_type_list(def->t)); + return 0; + } + else if (def->k == AST_ENUM_DEF) { + replace_type(t, clone_type_list(def->t)); + return 0; + } - if (strcmp(name, "bool") == 0) - return 1; + return -1; +} - /* special case of a special case? - if (strcmp(name, "str")) - return 1; - */ +static int actualize_ptr(struct act_state *state, struct scope *scope, struct type *t) +{ + assert(ptr_base(t)); + if (actualize_type(state, scope, ptr_base(t))) + return -1; return 0; } -static int actualize_type(struct act_state *state, - struct scope *scope, struct ast_node *type) -{ - /* TODO: there's gotta be a better way to handle flags. Maybe macros - * like ENTER_ACTUALIZE and EXIT_ACTUALIZE? */ - /* TODO: get rid of aliases, expand implements, fill out structures, - * etc. */ - assert(type->node_type == AST_TYPE); - ENTER_ACT(); - - if (actualize(state, scope, AST_TYPE(type).next)) { - EXIT_ACT(-1); - } - - switch (AST_TYPE(type).kind) { - case AST_TYPE_ID: { - /* type IDs can really only be aliases to something else, or if - * they're missing, void */ - if (!AST_ID_TYPE(type).id) { - /* no ID means void */ - AST_ID_TYPE(type).id = - gen_id(strdup("void"), NULL_LOC()); - } +static int actualize_callable(struct act_state *state, struct scope *scope, struct type *t) +{ + struct type *ptypes = callable_ptypes(t); + struct type *rtype = callable_rtype(t); - struct ast_node *exists = file_scope_find_type(scope, - AST_ID_TYPE( - type).id); - if (!exists) { - semantic_error(scope->fctx, type, "no such type"); - EXIT_ACT(-1); - } + foreach_type(p, ptypes){ + if (actualize_type(state, scope, p)) + return -1; + } - if (exists->node_type == AST_TYPE) { - assert(AST_TYPE(exists).kind == AST_TYPE_PRIMITIVE); - AST_TYPE(type) = AST_TYPE(exists); - break; - } + if (!rtype) + callable_rtype(t) = void_type(); - /* - if (actualize(state, exists->scope, exists)) - EXIT_ACT(-1); - */ + if (actualize_type(state, scope, rtype)) + return -1; - assert(AST_TYPE(type).next == NULL); - if (exists->node_type == AST_ALIAS) { - AST_TYPE(type).aliased = exists; - AST_TYPE(type) = AST_TYPE(exists); - } - else if (exists->node_type == AST_TRAIT) { - /* this is kind of weird, have to think about it */ - *type = *gen_type(AST_TYPE_TRAIT, exists, - NULL, exists->loc); - } - else if (exists->node_type == AST_STRUCT) { - if (struct_is_primitive(exists)) { - *type = *gen_primitive( - id_to_primitive(AST_STRUCT(exists).id), - exists, - exists->loc); - } else { - *type = *gen_type(AST_TYPE_STRUCT, exists, - NULL, exists->loc); - } - } - else if (exists->node_type == AST_ENUM) { - *type = *gen_type(AST_TYPE_ENUM, exists, - NULL, exists->loc); - } + return 0; +} - break; +static int actualize_i27(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; + + struct ast *def = file_scope_find_type(scope, "i27"); + if (!def) { + error("missing definition of type 'i27'"); + return NULL; } - case AST_TYPE_CONSTRUCT: - /** @todo fully qualified constructs should be added to the - * actual list for code generation */ - semantic_info(scope->fctx, type, - "constructs unimplemented, continuing with compilation to see what breaks"); - break; + t->d = def; + return 0; +} - case AST_TYPE_ARR: - /* TODO: expression should be expandable to integer constant */ - semantic_info(scope->fctx, type, - "arrays unimplemented, continuing with compilation to see what breaks"); - break; +static int actualize_i9(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; - case AST_TYPE_POINTER: - assert(AST_PTR_TYPE(type).base); - if (actualize(state, scope, AST_PTR_TYPE(type).base)) - EXIT_ACT(-1); - break; + struct ast *def = file_scope_find_type(scope, "i9"); + if (!def) { + error("missing definition of type 'i9'"); + return NULL; + } - case AST_TYPE_SIGN: { - struct ast_node *params = AST_SIGN_TYPE(type).params; - struct ast_node *ret = AST_SIGN_TYPE(type).ret; + t->d = def; + return 0; +} - foreach_node(p, params){ - if (actualize(state, scope, p)) - EXIT_ACT(-1); - } +static int actualize_bool(struct act_state *state, struct scope *scope, struct type *t) +{ + /* not much to do */ + if (t->d) + return 0; - if (!ret) - ret = AST_SIGN_TYPE(type).ret = void_type(); + struct ast *def = file_scope_find_type(scope, "bool"); + if (!def) { + error("missing definition of type 'bool'"); + return NULL; + } - if (actualize(state, scope, ret)) - EXIT_ACT(-1); + t->d = def; + return 0; +} - break; - } +static int actualize_type(struct act_state *state, + struct scope *scope, + struct type *t) +{ + if (!t) + return 0; - case AST_TYPE_TRAIT: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping trait type checks"); - break; - } + t->scope = scope; - case AST_TYPE_STRUCT: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping struct type checks"); - break; - } + if (actualize_type(state, scope, t->n)) + return -1; - case AST_TYPE_PRIMITIVE: { - assert(ast_flags(type, AST_FLAG_ACTUAL)); - semantic_info(scope->fctx, type, - "FIXME skipping primitive type checks"); - break; - } + switch (t->k) { + case TYPE_I27: return actualize_i27(state, scope, t); + case TYPE_I9: return actualize_i9(state, scope, t); + case TYPE_BOOL: return actualize_bool(state, scope, t); + case TYPE_ID: return actualize_tid(state, scope, t); + case TYPE_PTR: return actualize_ptr(state, scope, t); + case TYPE_CALLABLE: return actualize_callable(state, scope, t); + case TYPE_VOID: return 0; /* void is by default actualized */ default: - semantic_error(scope->fctx, type, "unimplemented type"); - EXIT_ACT(-1); + type_info(scope->fctx, t, + "unimplemented type"); + type_info(scope->fctx, t, + "continuing with compilation to see what breaks"); + return 0; } - /* generally speaking */ - EXIT_ACT(0); + return 0; } -static int actualize_empty(struct act_state *state, - struct scope *scope, struct ast_node *node) +static int actualize_type_list(struct act_state *state, + struct scope *scope, + struct type *l) { - UNUSED(state); - /* TODO: converting to void is common enough that it might be worth - * creating a function for */ - struct ast_node *void_id = gen_id(strdup("void"), NULL_LOC()); - if (!void_id) { - internal_error( - "couldn't allocate type id for empty statement\n"); - return -1; - } - - node->type = gen_type(AST_TYPE_ID, void_id, NULL, NULL_LOC()); - if (!node->type) { - internal_error("couldn't allocate type for empty statement\n"); - return -1; + foreach_type(t, l) { + if (actualize_type(state, scope, t)) + return -1; } return 0; } -static int integral_type(struct ast_node *type) +static int actualize_empty(struct act_state *state, + struct scope *scope, struct ast *node) { - assert(type->node_type == AST_TYPE); - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) - return 0; + UNUSED(state); + UNUSED(scope); + node->t = void_type(); + return 0; +} - /* here would be awesome with an enum of our base types */ - enum ast_primitive p = AST_PRIMITIVE_TYPE(type).type; - switch (p) { - case AST_I9: return 1; - case AST_I27: return 1; - default: return 0; +static int integral_type(struct type *type) +{ + switch (type->k) { + case TYPE_I27: + case TYPE_I9: + case TYPE_BOOL: + return true; /* maybe? */ + default: } - return 0; + return false; } -static int pointer_type(struct ast_node *type) +static int pointer_type(struct type *type) { - assert(type->node_type == AST_TYPE); - return type->_type.kind == AST_TYPE_POINTER; + return type->k == TYPE_PTR; } -static int pointer_conversion(struct ast_node *a, struct ast_node *b) +static int pointer_conversion(struct type *a, struct type *b) { - assert(a->node_type == AST_TYPE); - assert(b->node_type == AST_TYPE); if (pointer_type(a)) { - if (AST_TYPE(b).kind != AST_TYPE_PRIMITIVE) + if (!is_primitive(b)) return 0; /* for now */ - return AST_PRIMITIVE_TYPE(b).type == AST_I27; + return b->k == TYPE_I27; } return 0; } -static size_t member_count(struct ast_node *exists) +static size_t member_count(struct ast *exists) { - assert(exists->node_type == AST_STRUCT); - struct ast_node *body = exists->_struct.body; - return ast_list_len(body); + assert(exists->k == AST_STRUCT_DEF); + struct ast *body = struct_body(exists); + + size_t count = 0; + foreach_node(n, body) { + if (n->k == AST_VAR_DEF) + count++; + } + + return count; } -static struct ast_node *lookup_member_idx(struct ast_node *body, - struct ast_node *find, +static struct ast *lookup_member_idx(struct ast *body, + char *find, size_t *idx) { /* micro-optimisation, likely way premature but speeds up selection @@ -1459,27 +1353,26 @@ static struct ast_node *lookup_member_idx(struct ast_node *body, (void)(find); assert(idx); size_t i = *idx; - struct ast_node *m = body; + struct ast *m = body; while (i != 0 && m) { - m = m->next; + m = m->n; i--; } return m; } -static struct ast_node *lookup_member_name(struct ast_node *body, - struct ast_node *find, +static struct ast *lookup_member_name(struct ast *body, + char *find, size_t *idx) { - assert(find->node_type == AST_ID); size_t i = 0; - struct ast_node *m = body; + struct ast *m = body; while (m) { - assert(m->node_type == AST_VAR); - if (same_id(find, AST_VAR(m).id)) + assert(m->k == AST_VAR_DEF); + if (same_id(find, var_id(m))) break; - m = m->next; + m = m->n; i++; } @@ -1489,25 +1382,25 @@ static struct ast_node *lookup_member_name(struct ast_node *body, return m; } -static struct ast_node *lookup_struct_member(struct ast_node *struc, - struct ast_node *find, size_t *idx) +static struct ast *lookup_struct_member(struct ast *struc, + char *find, size_t *idx) { if (find) - return lookup_member_name(struc->_struct.body, find, idx); + return lookup_member_name(struct_body(struc), find, idx); - return lookup_member_idx(struc->_struct.body, find, idx); + return lookup_member_idx(struct_body(struc), find, idx); } -static struct ast_node *lookup_enum_member(struct ast_node *enu, - struct ast_node *find) +static struct ast *lookup_enum_member(struct ast *enu, + char *find) { size_t i = 0; - struct ast_node *m = enu->_enum.body; + struct ast *m = enum_body(enu); while (m) { - assert(m->node_type == AST_VAL); - if (same_id(find, AST_VAL(m).id)) + assert(m->k == AST_VAL); + if (same_id(find, val_id(m))) break; - m = m->next; + m = m->n; i++; } @@ -1515,7 +1408,7 @@ static struct ast_node *lookup_enum_member(struct ast_node *enu, } static int init_struct(struct act_state *state, struct scope *scope, - struct ast_node *exists, struct ast_node *init) + struct ast *exists, struct ast *init) { size_t i = 0; size_t mcount = member_count(exists); @@ -1527,7 +1420,7 @@ static int init_struct(struct act_state *state, struct scope *scope, return -1; } - struct ast_node *args = init->_init.body; + struct ast *args = init_body(init); while (args) { if (i >= mcount) { semantic_error(scope->fctx, args, @@ -1546,15 +1439,15 @@ static int init_struct(struct act_state *state, struct scope *scope, if ((ret = actualize(state, scope, args))) break; - struct ast_node *find = NULL; + char *find = NULL; if (ast_flags(args, AST_FLAG_MEMBER)) - find = args->_var.id; + find = var_id(args); - struct ast_node *member = + struct ast *member = lookup_struct_member(exists, find, &i); if (!member) { - char *sstr = type_str(exists->type); + char *sstr = type_str(exists->t); semantic_error(scope->fctx, args, "no such member in %s", sstr); @@ -1563,9 +1456,9 @@ static int init_struct(struct act_state *state, struct scope *scope, break; } - if (!types_match(args->type, member->type)) { - char *astr = type_str(args->type); - char *mstr = type_str(member->type); + if (!types_match(args->t, member->t)) { + char *astr = type_str(args->t); + char *mstr = type_str(member->t); semantic_error(scope->fctx, args, "%s does not match %s", astr, mstr); free(astr); @@ -1575,7 +1468,7 @@ static int init_struct(struct act_state *state, struct scope *scope, } initd[i] = 1; - args = args->next; + args = args->n; i++; } @@ -1585,108 +1478,70 @@ static int init_struct(struct act_state *state, struct scope *scope, static int actualize_struct_init_cast(struct act_state *state, struct scope *scope, - struct ast_node *init, - struct ast_node *actual) + struct ast *init, + struct type *type) { - struct ast_node *def = AST_STRUCT_TYPE(actual).def; + struct ast *def = type->d; return init_struct(state, scope, def, init); } static int actualize_init_cast(struct act_state *state, - struct scope *scope, struct ast_node *init, - struct ast_node *type) + struct scope *scope, struct ast *init, + struct type *type) { - if (AST_TYPE(type).kind == AST_TYPE_STRUCT) + if (type->k == TYPE_STRUCT) return actualize_struct_init_cast(state, scope, init, type); - semantic_error(scope->fctx, type, + type_error(scope->fctx, type, "type is not a struct"); return -1; } -static int proc_pointer(struct ast_node *type) -{ - if (type->_type.kind != AST_TYPE_POINTER) - return 0; - - struct ast_node *next = type->_type.next; - if (next->_type.kind != AST_TYPE_SIGN) - return 0; - - return 1; -} - -static int proc_choice(struct ast_node *expr, struct ast_node *type) -{ - if (expr->node_type != AST_ID) - return 0; - - if (!proc_pointer(type)) - return 0; - - return 1; -} - -/* still slightly unsure about this, but hey ho */ -static int match_proc(struct act_state *state, struct scope *scope, - struct ast_node *cast) -{ - (void)(state); - semantic_error(scope->fctx, cast, - "procedure signature casts not yet implemented"); - return -1; -} - static int actualize_cast(struct act_state *state, - struct scope *scope, struct ast_node *cast) + struct scope *scope, struct ast *cast) { - assert(cast->node_type == AST_CAST); - struct ast_node *expr = cast->_cast.expr; - struct ast_node *type = cast->_cast.type; + assert(cast->k == AST_CAST); + struct ast *expr = cast_expr(cast); + struct type *type = cast_type(cast); - if (actualize(state, scope, type)) + if (actualize_type(state, scope, type)) return -1; - if (proc_choice(expr, type)) { - set_type(cast, type); - return match_proc(state, scope, cast); - } - if (actualize(state, scope, expr)) return -1; - if (expr->node_type == AST_INIT) { + if (expr->k == AST_INIT) { set_type(cast, type); return actualize_init_cast(state, scope, expr, type); } - if (types_match(expr->type, type)) { + if (types_match(expr->t, type)) { set_type(cast, type); return 0; } - if (integral_type(expr->type) && integral_type(type)) { + if (integral_type(expr->t) && integral_type(type)) { set_type(cast, type); return 0; } - if (pointer_type(expr->type) && pointer_type(type)) { + if (pointer_type(expr->t) && pointer_type(type)) { set_type(cast, type); return 0; } - if (pointer_conversion(expr->type, type) - || pointer_conversion(type, expr->type)) { + if (pointer_conversion(expr->t, type) + || pointer_conversion(type, expr->t)) { set_type(cast, type); return 0; } /* TODO: arrays? */ - char *left_type = type_str(expr); + char *left_type = type_str(expr->t); char *right_type = type_str(type); - semantic_error(scope->fctx, cast, "illegal cast (%s vs %s)", + semantic_error(scope->fctx, cast, "illegal cast: %s vs %s", left_type, right_type); free(left_type); free(right_type); @@ -1694,17 +1549,22 @@ static int actualize_cast(struct act_state *state, } static int actualize_const(struct act_state *state, struct scope *scope, - struct ast_node *cons) + struct ast *cons) { UNUSED(state); - assert(cons->node_type == AST_CONST); - if (AST_CONST(cons).kind == AST_CONST_INTEGER) - cons->type = i27_type(scope); + if (cons->k == AST_CONST_INT) + cons->t = i27_type(scope); + + else if (cons->k == AST_CONST_CHAR) + cons->t = i9_type(scope); - else if (AST_CONST(cons).kind == AST_CONST_STRING) - cons->type = str_type(scope); + else if (cons->k == AST_CONST_BOOL) + cons->t = bool_type(scope); - if (cons->type) + else if (cons->k == AST_CONST_STR) + cons->t = str_type(scope); + + if (cons->t) return 0; semantic_error(scope->fctx, cons, "unimplemented constant"); @@ -1712,13 +1572,13 @@ static int actualize_const(struct act_state *state, struct scope *scope, } static int actualize_alias(struct act_state *state, struct scope *scope, - struct ast_node *alias) + struct ast *alias) { /* I shall have to think about things, as currently very deeply nested * aliases might be a bit cumbersome to work with. Still, this works * well enough I suppose. */ - assert(alias->node_type == AST_ALIAS); - if (actualize(state, scope, AST_ALIAS(alias).type)) { + assert(alias->k == AST_ALIAS_DEF); + if (actualize_type(state, scope, alias_type(alias))) { /* usually we don't want to output errors upon errors, but this * is likely a useful message as it might show where a loop is * occuring */ @@ -1731,9 +1591,9 @@ static int actualize_alias(struct act_state *state, struct scope *scope, } static int actualize_defer(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - struct ast_node *expr = node->_defer.expr; + struct ast *expr = defer_expr(node); /* TODO: should the actualization only happen when the defers are * called? */ if (actualize(state, scope, expr)) @@ -1742,61 +1602,54 @@ static int actualize_defer(struct act_state *state, if (push_defer(state, expr)) return -1; - node->type = void_type(); + node->t = void_type(); return 0; } static int actualize_return(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { act_set_flags(state, ACT_HAS_RETURN); - struct ast_node *expr = node->_return.expr; + struct ast *expr = return_expr(node); if (expr) { if (actualize(state, scope, expr)) return -1; - set_type(node, expr->type); + set_type(node, expr->t); } else { - node->type = void_type(); + node->t = void_type(); } assert(state->cur_proc); - struct ast_node *cur_proc = state->cur_proc; - struct ast_node *sign = AST_PROC(cur_proc).sign; - struct ast_node *ret = AST_SIGN_TYPE(sign).ret; - if (!types_match(node->type, ret)) { - char *rt = type_str(ret); - char *et = type_str(node); - semantic_error(scope->fctx, node, - "return type mismatch: %s", et); - - semantic_info(scope->fctx, ret, - "vs %s", rt); - free(rt); - free(et); + struct ast *cur_proc = state->cur_proc; + struct type *rtype = proc_rtype(cur_proc); + if (!types_match(node->t, rtype)) { + /* hmm, should this be "return type mismatch? */ + type_mismatch(scope, node, rtype, node->t); return -1; } if (state->defer_stack) { - AST_RETURN(node).defers = clone_defers(state, NULL); - if (!node->_return.defers) { + return_defers(node) = clone_defers(state, NULL); + if (!return_defers(node)) { internal_error("failed cloning return defers"); return -1; } } + return 0; } /* Still slightly unsure if this works in all cases, but a good start * nonetheless. */ -static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) +static void actualize_goto_defer(struct ast *got, struct ast *label) { - struct ast_node *goto_defers = AST_GOTO(got).defers; - struct ast_node *label_defers = AST_LABEL(label).defers; + struct ast *goto_defers = goto_defers(got); + struct ast *label_defers = label_defers(label); /* since we're dealing with singly linked lists, keep a reference to one * node before the current goto defer. */ - struct ast_node *prev_defer = NULL; + struct ast *prev_defer = NULL; /* this goto has a defined label */ ast_set_flags(got, AST_FLAG_ACTUAL); @@ -1808,12 +1661,12 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) /* find first common defer statement */ while (goto_len > label_len) { prev_defer = goto_defers; - goto_defers = goto_defers->next; + goto_defers = goto_defers->n; goto_len--; } while (label_len > goto_len) { - label_defers = label_defers->next; + label_defers = label_defers->n; label_len--; } @@ -1823,15 +1676,15 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) * unlikely that goto stuff would be a major bottleneck. */ while (!equiv_nodes(goto_defers, label_defers)) { prev_defer = goto_defers; - label_defers = label_defers->next; - goto_defers = goto_defers->next; + label_defers = label_defers->n; + goto_defers = goto_defers->n; } /* only the defers below the common defer should be executed by the goto */ if (goto_defers) { /* fuck, I actually need the one previous to this */ - assert(prev_defer->next == goto_defers); - prev_defer->next = NULL; + assert(prev_defer->n == goto_defers); + prev_defer->n = NULL; } /* nothing to do */ @@ -1839,16 +1692,16 @@ static void actualize_goto_defer(struct ast_node *got, struct ast_node *label) static int actualize_goto(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_GOTO); + assert(node->k == AST_GOTO); push_goto(state, node); /* clone all defers as we don't know where the label might be */ - node->_goto.defers = clone_defers(state, NULL); - node->type = void_type(); + goto_defers(node) = clone_defers(state, NULL); + node->t = void_type(); - struct ast_node *label = find_label(state, node->_goto.label); + struct ast *label = find_label(state, goto_label(node)); /* this is a jump backwards, i.e. we can already do it */ if (label) actualize_goto_defer(node, label); @@ -1858,24 +1711,24 @@ static int actualize_goto(struct act_state *state, struct scope *scope, } static void actualize_goto_defers(struct act_state *state, - struct ast_node *label) + struct ast *label) { struct act_stack *prev = state->goto_stack, *cur; if (prev) do { cur = prev->next; - struct ast_node *got = prev->node; - if (equiv_nodes(AST_GOTO(got).label, label)) + struct ast *got = prev->node; + if (same_id(goto_label(got), label_id(label))) actualize_goto_defer(got, label); } while ((prev = cur)); } static int actualize_label(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_LABEL); - struct ast_node *prev = find_label(state, node); + assert(node->k == AST_LABEL); + struct ast *prev = find_label(state, label_id(node)); if (prev) { semantic_error(scope->fctx, node, "label redefined"); semantic_info(scope->fctx, prev, "previous definition"); @@ -1884,60 +1737,65 @@ static int actualize_label(struct act_state *state, struct scope *scope, push_label(state, node); /* clone all defers */ - node->_label.defers = clone_defers(state, NULL); - node->type = void_type(); + label_defers(node) = clone_defers(state, NULL); + node->t = void_type(); actualize_goto_defers(state, node); return 0; } static int actualize_unop(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_UNOP); - struct ast_node *expr = AST_UNOP(node).expr; + assert(is_unop(node)); + struct ast *expr = unop_expr(node); if (actualize(state, scope, expr)) return -1; /* generally speaking */ - set_type(node, expr->type); + set_type(node, expr->t); - switch (node->_unop.op) { + switch (node->k) { case AST_DEREF: { - struct ast_node *type = expr->type; - if (AST_TYPE(type).kind != AST_TYPE_POINTER) { - /* TODO: or array */ + struct type *type = expr->t; + if (type->k!= TYPE_PTR) { + /** @todo or array? */ + char *tstr = type_str(type); semantic_error(scope->fctx, expr, - "trying to dereference something that's not a pointer"); + "not a pointer: %s", + tstr); + free(tstr); return -1; } - set_type(node, AST_PTR_TYPE(type).base); - assert(node->type); + set_type(node, ptr_base(type)); + assert(node->t); break; } case AST_REF: { - node->type = gen_type(AST_TYPE_POINTER, NULL, NULL, - NULL_LOC()); - set_type(node->AST_TYPE(type).next, expr->type); + /** @todo array pointer decay? */ + node->t = tgen_ptr(clone_type(expr->t), node->loc); break; } case AST_LNOT: { - if (AST_TYPE(expr->type).kind != AST_TYPE_PRIMITIVE) { + if (is_primitive(expr->t)) { + char *tstr = type_str(expr->t); semantic_error(scope->fctx, node, - "'!' only implemented for primitive types"); + "'!' only implemented for primitive types: %s", + tstr); + free(tstr); return -1; } - if (AST_PRIMITIVE_TYPE(expr->type).type == AST_VOID) { + if (expr->t->k == TYPE_VOID) { semantic_error(scope->fctx, node, "'!' not implemented for void"); return -1; } - node->type = bool_type(scope); + node->t = bool_type(scope); break; } @@ -1950,79 +1808,90 @@ static int actualize_unop(struct act_state *state, } static int actualize_as(struct act_state *state, - struct scope *scope, struct ast_node *as) + struct scope *scope, struct ast *as) { - assert(as->node_type == AST_AS); - struct ast_node *type = AST_AS(as).type; - if (actualize(state, scope, type)) + assert(as->k == AST_AS); + struct type *type = as->t; + if (actualize_type(state, scope, type)) return -1; set_type(as, type); return 0; } -static int _replace_type_id(struct ast_node *node, void *data) -{ - if (!node) - return 0; +struct replace_data { + char *id; + struct type *replacement; +}; - struct ast_node **pair = data; - struct ast_node *id = pair[0]; - struct ast_node *replacement = pair[1]; +static int _replace_type_id(struct type *type, void *data) +{ + struct replace_data *pair = data; + struct type *replacement = pair->replacement; + char *id = pair->id; - if (node->node_type != AST_TYPE) - goto next; + switch (type->k) { + case TYPE_ID: { + replace_type(type, clone_type(replacement)); + break; + } - switch (AST_TYPE(node).kind) { - case AST_TYPE_ID: *node = *clone_ast_node(replacement); break; - case AST_TYPE_TRAIT: { - struct ast_node *def = AST_TRAIT_TYPE(node).def; + case TYPE_TRAIT: { + struct ast *def = type->d; assert(def); - struct ast_node *name = AST_TRAIT(def).id; + + char *name = trait_id(def); if (same_id(id, name)) - *node = *clone_ast_node(replacement); + replace_type(type, clone_type(replacement)); break; } - case AST_TYPE_STRUCT: { - struct ast_node *def = AST_STRUCT_TYPE(node).def; + case TYPE_STRUCT: { + struct ast *def = type->d; assert(def); - struct ast_node *name = AST_STRUCT(def).id; + char *name = struct_id(def); if (same_id(id, name)) - *node = *clone_ast_node(replacement); + replace_type(type, clone_type(replacement)); break; } + default: } -next: - return ast_call_on(_replace_type_id, node, data); + return 0; } -static int replace_type_id(struct ast_node *nodes, struct ast_node *id, - struct ast_node *replacement) +static int _replace_ast_type_id(struct ast *node, void *data) { - assert(replacement->node_type == AST_TYPE); - struct ast_node *pair[2] = {id, replacement}; - return ast_call_on(_replace_type_id, nodes, pair); + if (!node) + return 0; + + return type_visit_list(_replace_type_id, NULL, node->t, data); +} + +static int replace_type_id(struct ast *nodes, char *id, + struct type *replacement) +{ + struct replace_data pair = {id, replacement}; + return ast_visit(_replace_ast_type_id, NULL, nodes, &pair); } /* lots of overlap with actualize_struct, kind of ugly... */ static int actualize_trait(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_TRAIT); - foreach_node(n, AST_TRAIT(node).body) { + assert(node->k == AST_TRAIT_DEF); + foreach_node(n, trait_body(node)) { /* there's really only prodcedure body actualization left I * guess, as type stuff was taken care of in the analysis phase * */ - if (n->node_type != AST_PROC) + if (n->k != AST_PROC_DEF) continue; /* don't actualize prototypes, duh */ - if (!AST_PROC(n).body) + if (!proc_body(n)) continue; if (actualize(state, node->scope, n)) @@ -2033,18 +1902,18 @@ static int actualize_trait(struct act_state *state, struct scope *scope, } static int actualize_struct(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_STRUCT); - foreach_node(n, AST_STRUCT(node).body) { + assert(node->k == AST_STRUCT_DEF); + foreach_node(n, struct_body(node)) { /* there's really only prodcedure body actualization left I * guess, as type stuff was taken care of in the analysis phase * */ - if (n->node_type != AST_PROC) + if (n->k != AST_PROC_DEF) continue; /* don't actualize prototypes, duh */ - if (!AST_PROC(n).body) + if (!proc_body(n)) continue; if (actualize(state, node->scope, n)) @@ -2057,23 +1926,23 @@ static int actualize_struct(struct act_state *state, /* could maybe be renamed, but essentially dot in copper works as either * -> or . in C, so allow structures or traits and single level pointers to * structures or traits. */ -static int has_members(struct ast_node *type) +static int has_members(struct type *type) { /* most likely */ - if (AST_TYPE(type).kind == AST_TYPE_STRUCT) + if (type->k == TYPE_STRUCT) return 1; - if (AST_TYPE(type).kind == AST_TYPE_TRAIT) + if (type->k == TYPE_TRAIT) return 1; - if (AST_TYPE(type).kind == AST_TYPE_CONSTRUCT) + if (type->k == TYPE_CONSTRUCT) return 1; return 0; } static int actualize_dot(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { /* TODO: handle enums as well, idea is something like * enum whatever {A_FLAG} @@ -2083,19 +1952,18 @@ static int actualize_dot(struct act_state *state, * possibly also if the expr is of type whatever then .A_FLAG just gets * the corresponding constant? **/ - assert(node->node_type == AST_DOT); - struct ast_node *expr = AST_DOT(node).expr; + assert(node->k == AST_DOT); + struct ast *expr = dot_expr(node); if (actualize(state, scope, expr)) return -1; - struct ast_node *id = AST_DOT(node).id; - struct ast_node *type = expr->type; + char *id = dot_id(node); + struct type *type = expr->t; - struct ast_node *def = NULL; - switch (AST_TYPE(type).kind) { - case AST_TYPE_PRIMITIVE: def = AST_PRIMITIVE_TYPE(type).def; break; - case AST_TYPE_TRAIT: def = AST_TRAIT_TYPE(type).def; break; - case AST_TYPE_STRUCT: def = AST_STRUCT_TYPE(type).def; break; + struct ast *def = NULL; + switch (type->k) { + case TYPE_TRAIT: + case TYPE_STRUCT: def = type->d; break; default: { char *tstr = type_str(type); semantic_error(scope->fctx, node, @@ -2106,17 +1974,17 @@ static int actualize_dot(struct act_state *state, } } - struct ast_node *exists = scope_find_var(def->scope, id); + struct ast *exists = scope_find_var(def->scope, id); if (exists) { - assert(exists->type); - set_type(node, exists->type); + assert(exists->t); + set_type(node, exists->t); return 0; } exists = scope_find_proc(def->scope, id); if (exists) { - assert(exists->type); - set_type(node, exists->type); + assert(exists->t); + set_type(node, exists->t); return 0; } @@ -2126,42 +1994,33 @@ static int actualize_dot(struct act_state *state, } static int actualize_init(struct act_state *state, - struct scope *scope, struct ast_node *node) + struct scope *scope, struct ast *node) { - assert(node->node_type == AST_INIT); + assert(node->k == AST_INIT); /* for now just do the types, the named stuff will be checked later */ /* TODO: how to make sure all members are initialized? */ - int ret = 0; - enum act_flags old_flags = state->flags; - ret = actualize(state, scope, node->_init.body); - state->flags = old_flags; - return ret; + return actualize_list(state, scope, init_body(node)); } static int actualize_assign(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_ASSIGN); - struct ast_node *to = node->_assign.to; + assert(node->k == AST_ASSIGN); + struct ast *to = assign_to(node); if (actualize(state, scope, to)) return -1; - struct ast_node *from = node->_assign.from; - if (actualize(state, scope, from)) + struct ast *from = assign_from(node); + if (actualize_list(state, scope, from)) return -1; - if (from->node_type == AST_INIT) { - set_type(node, to->type); - return actualize_init_cast(state, scope, from, to->type); + if (from->k == AST_INIT) { + set_type(node, to->t); + return actualize_init_cast(state, scope, from, to->t); } - if (!types_match(to->type, from->type)) { - char *tostr = type_str(to->type); - char *fromstr = type_str(from->type); - semantic_error(scope->fctx, node, "type mismatch (%s vs %s)", - tostr, fromstr); - free(tostr); - free(fromstr); + if (!types_match(to->t, from->t)) { + type_mismatch(scope, node, to->t, from->t); return -1; } @@ -2172,84 +2031,77 @@ static int actualize_assign(struct act_state *state, struct scope *scope, return -1; } - set_type(node, to->type); + set_type(node, to->t); return 0; } static int actualize_fetch(struct act_state *state, struct scope *scope, - struct ast_node *fetch) + struct ast *fetch) { - assert(fetch->node_type == AST_FETCH); - struct ast_node *type = fetch->_fetch.type; - if (actualize(state, scope, type)) + assert(fetch->k == AST_FETCH); + struct type *type = fetch_type(fetch); + if (actualize_type(state, scope, type)) return -1; - if (type->_type.kind != AST_TYPE_ENUM) { - semantic_error(scope->fctx, type, "type is not an enum"); + if (type->k != TYPE_ENUM) { + type_error(scope->fctx, type, "type is not an enum"); return -1; } - struct ast_node *id = fetch->_fetch.id; - struct ast_node *def = - file_scope_find_type(scope, AST_ID_TYPE(type).id); + char *id = fetch_id(fetch); + struct ast *def = type->d; assert(def); - struct ast_node *member = lookup_enum_member(def, id); + struct ast *member = lookup_enum_member(def, id); if (!member) { char *estr = type_str(type); - semantic_error(scope->fctx, id, "no such member in enum %s"); + semantic_error(scope->fctx, fetch, "no such member in enum %s"); free(estr); return -1; } - set_type(fetch, def->type); + set_type(fetch, def->t); return 0; } static int actualize_enum(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_ENUM); - struct ast_node *type = node->_enum.type; + assert(node->k == AST_ENUM_DEF); + struct type *type = enum_type(node); struct scope *enum_scope = node->scope; /* TODO: here we could save space by choosing the smallest type that * fits */ if (!type) { type = i27_type(scope); - node->_enum.type = type; - } else if (actualize(state, enum_scope, type)) + node->t = type; + } else if (actualize_type(state, enum_scope, type)) return -1; long long counter = 0; - node->type = type; - struct ast_node *members = node->_enum.body; + node->t = type; + struct ast *members = enum_body(node); while (members) { set_type(members, type); - if (members->_val.val) { - struct ast_node *val = members->_val.val; + if (val_val(members)) { + struct ast *val = val_val(members); if (actualize(state, enum_scope, val)) return -1; - if (val->node_type != AST_CONST) { + if (val->k != AST_CONST_INT) { semantic_error(scope->fctx, members, "unable to process nonconstant expression"); return -1; } - if (val->_const.kind != AST_CONST_INTEGER) { - semantic_error(scope->fctx, members, - "not expandable to an integer constant"); - return -1; - } - - counter = val->_const.integer; + counter = int_val(val); } else { - members->_val.val = gen_int(counter, NULL_LOC()); + val_val(members) = gen_const_int(counter, NULL_LOC()); } - members = members->next; + members = members->n; counter++; } @@ -2258,21 +2110,21 @@ static int actualize_enum(struct act_state *state, struct scope *scope, } static int actualize_if(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_IF); - if (actualize(state, scope, AST_IF(node).cond)) + assert(node->k == AST_IF); + if (actualize(state, scope, if_cond(node))) return -1; - if (actualize(state, scope, AST_IF(node).body)) + if (actualize(state, scope, if_body(node))) return -1; - if (actualize(state, scope, AST_IF(node).els)) + if (actualize(state, scope, if_else(node))) return -1; if (ast_flags(node, AST_FLAG_DOEXPR)) { - struct ast_node *tt = ast_last_node(AST_IF(node).body)->type; - struct ast_node *ft = ast_last_node(AST_IF(node).els)->type; + struct type *tt = ast_last(if_body(node))->t; + struct type *ft = ast_last(if_else(node))->t; if (!types_match(tt, ft)) { semantic_error(scope->fctx, node, "mismatched if/else body values"); @@ -2283,143 +2135,124 @@ static int actualize_if(struct act_state *state, struct scope *scope, return 0; } - node->type = void_type(); + node->t = void_type(); return 0; } static int actualize_for(struct act_state *state, struct scope *scope, - struct ast_node *node) + struct ast *node) { - assert(node->node_type == AST_FOR); - if (actualize(state, scope, AST_FOR(node).pre)) + assert(node->k == AST_FOR); + if (actualize_list(state, scope, for_pre(node))) return -1; - if (actualize(state, scope, AST_FOR(node).post)) + if (actualize_list(state, scope, for_post(node))) return -1; - if (actualize(state, scope, AST_FOR(node).cond)) + if (actualize_list(state, scope, for_cond(node))) return -1; - if (actualize(state, scope, AST_FOR(node).body)) + if (actualize_list(state, scope, for_body(node))) return -1; - node->type = void_type(); + node->t = void_type(); return 0; } -static int actualize(struct act_state *state, struct scope *scope, - struct ast_node *node) +static int actualize_comparison(struct act_state *state, struct scope *scope, struct ast *node) { - int ret = 0; - if (!node) - return ret; - - if (!node->scope) - node->scope = scope; + assert(is_comparison(node)); + struct ast *left = comparison_left(node); + struct ast *right = comparison_right(node); - /* this node was already actualized, presumed to be fine */ - if (ast_flags(node, AST_FLAG_ACTUAL)) { - return 0; - } - - /* actualization started */ - ast_set_flags(node, AST_FLAG_INIT); - - /* actualization done for this node (optimistic) */ - ast_set_flags(node, AST_FLAG_ACTUAL); - - switch (node->node_type) { - case AST_PROC: - ret |= actualize_proc(state, scope, node); - break; + if (actualize(state, scope, left)) + return -1; - case AST_TRAIT: ret |= actualize_trait(state, scope, node); break; - case AST_ALIAS: ret |= actualize_alias(state, scope, node); break; - case AST_MACRO_CONSTRUCT: ret |= actualize_macro_construct(state, scope, - node); break; - case AST_MACRO_EXPAND: ret |= - actualize_macro_expand(state, scope, node); break; - case AST_CALL: ret |= actualize_call(state, scope, node); break; - case AST_BINOP: ret |= actualize_binop(state, scope, node); break; - case AST_BLOCK: ret |= actualize_block(state, scope, node); break; - case AST_ID: ret |= actualize_id(state, scope, node); break; - case AST_VAR: ret |= actualize_var(state, scope, node); break; - case AST_TYPE: ret |= actualize_type(state, scope, node); break; - case AST_EMPTY: ret |= actualize_empty(state, scope, node); break; - case AST_CAST: ret |= actualize_cast(state, scope, node); break; - case AST_CONST: ret |= actualize_const(state, scope, node); break; - case AST_DEFER: ret |= actualize_defer(state, scope, node); break; - case AST_RETURN: ret |= actualize_return(state, scope, node); break; - case AST_GOTO: ret |= actualize_goto(state, scope, node); break; - case AST_LABEL: ret |= actualize_label(state, scope, node); break; - case AST_UNOP: ret |= actualize_unop(state, scope, node); break; - case AST_AS: ret |= actualize_as(state, scope, node); break; - case AST_STRUCT: ret |= actualize_struct(state, scope, node); break; - case AST_DOT: ret |= actualize_dot(state, scope, node); break; - case AST_INIT: ret |= actualize_init(state, scope, node); break; - case AST_ASSIGN: ret |= actualize_assign(state, scope, node); break; - case AST_FETCH: ret |= actualize_fetch(state, scope, node); break; - case AST_ENUM: ret |= actualize_enum(state, scope, node); break; - case AST_IF: ret |= actualize_if(state, scope, node); break; - case AST_FOR: ret |= actualize_for(state, scope, node); break; + if (actualize(state, scope, right)) + return -1; - default: - /* more like internal_error, maybe? */ - semantic_error(scope->fctx, node, - "unimplemented actualization"); - break; + if (!is_primitive(left->t)) { + type_error(scope->fctx, left->t, "primitive type required"); + return -1; } - return ret; -} -int actualize_main(struct scope *root) -{ - struct ast_node *main_id = gen_id(strdup("main"), NULL_LOC()); - - struct act_state state = {0}; + if (!is_primitive(right->t)) { + type_error(scope->fctx, right->t, "primitive type required"); + return -1; + } - /* skip checking signature for now */ - struct ast_node *main = file_scope_find_proc(root, main_id); - if (!main) { - /* libraries are not really compilable... */ - error("no main"); + if (!types_match(left->t, right->t)) { + type_mismatch(scope, node, left->t, right->t); return -1; } - int ret = actualize(&state, root, main); - destroy_act_state(&state); - return ret; + + set_type(node, bool_type(scope)); + return 0; } -void replace_type(struct ast_node *type, struct ast_node *from, - struct ast_node *to) +static int actualize(struct act_state *state, struct scope *scope, + struct ast *node) { - if (!type) - return; + if (!node) + return 0; - assert(type->node_type == AST_TYPE); - assert(from->node_type == AST_TYPE); - assert(to->node_type == AST_TYPE); + if (!node->scope) + node->scope = scope; - if (types_match(type, from)) { - assert(type->_type.next == NULL); - struct ast_node *clone = clone_ast_node(to); - *type = *clone; - return; + if (is_unop(node)) + return actualize_unop(state, scope, node); + + if (is_binop(node)) + return actualize_binop(state, scope, node); + + if (is_comparison(node)) + return actualize_comparison(state, scope, node); + + if (is_const(node)) + return actualize_const(state, scope, node); + + switch (node->k) { + case AST_PROC_DEF: return actualize_proc(state, scope, node); + case AST_TRAIT_DEF: return actualize_trait(state, scope, node); + case AST_ALIAS_DEF: return actualize_alias(state, scope, node); + case AST_ENUM_DEF: return actualize_enum(state, scope, node); + case AST_MACRO_DEF: return actualize_macro_def(state, scope, node); + case AST_STRUCT_DEF: return actualize_struct(state, scope, node); + case AST_VAR_DEF: return actualize_var(state, scope, node); + case AST_CALL: return actualize_call(state, scope, node); + case AST_BLOCK: return actualize_block(state, scope, node); + case AST_ID: return actualize_id(state, scope, node); + case AST_EMPTY: return actualize_empty(state, scope, node); + case AST_CAST: return actualize_cast(state, scope, node); + case AST_DEFER: return actualize_defer(state, scope, node); + case AST_RETURN: return actualize_return(state, scope, node); + case AST_GOTO: return actualize_goto(state, scope, node); + case AST_LABEL: return actualize_label(state, scope, node); + case AST_AS: return actualize_as(state, scope, node); + case AST_DOT: return actualize_dot(state, scope, node); + case AST_INIT: return actualize_init(state, scope, node); + case AST_ASSIGN: return actualize_assign(state, scope, node); + case AST_FETCH: return actualize_fetch(state, scope, node); + case AST_IF: return actualize_if(state, scope, node); + case AST_FOR: return actualize_for(state, scope, node); + case AST_MACRO_EXPAND: return actualize_macro_expand(state, scope, node); + default: + /* more like internal_error, maybe? */ + semantic_error(scope->fctx, node, + "unimplemented actualization"); + return -1; } - replace_type(type->_type.next, from, to); + return 0; } -void replace_param_types(struct ast_node *param, struct ast_node *param_type, - struct ast_node *arg_type) +static int actualize_list(struct act_state *state, struct scope *scope, + struct ast *l) { - if (arg_type->_type.kind == AST_TYPE_TRAIT) { - replace_param_types(param, param_type, arg_type); - return; + foreach_node(n, l) { + if (actualize(state, scope, n)) + return -1; } - while (param) { - replace_type(param->type, param_type, arg_type); - param = param->next; - } + return 0; } diff --git a/src/ast.c b/src/ast.c index e77714c..18a98bc 100644 --- a/src/ast.c +++ b/src/ast.c @@ -15,585 +15,162 @@ #include #include +#include #include -static struct { - size_t n; - size_t s; - struct ast_node **v; -} ast_nodes = {0}; +static struct vec nodes = {0}; +static struct vec types = {0}; -static void destroy_ast_node(struct ast_node *node) +static void destroy_ast_node(struct ast *n) { - if (!node) + if (!n) return; - switch (node->node_type) { - case AST_ID: free((void *)AST_ID(node).id); break; - case AST_CONST: - if (AST_CONST(node).kind == AST_CONST_STRING) - free((void *)AST_CONST(node).str); - break; - default: - } - - free(node); -} - -void destroy_ast_nodes() -{ - for (size_t i = 0; i < ast_nodes.n; ++i) - destroy_ast_node(ast_nodes.v[i]); + if (n->s) + free(n->s); - free(ast_nodes.v); + free(n); } -/* eventually we might want to pass in a context or something */ -static struct ast_node *create_ast_node() +static void destroy_type(struct type *n) { - if (ast_nodes.s == 0) { - ast_nodes.s = 1; - ast_nodes.n = 0; - ast_nodes.v = calloc(1, sizeof(struct ast_node *)); - } - - else if (ast_nodes.n >= ast_nodes.s) { - ast_nodes.s *= 2; - ast_nodes.v = - realloc(ast_nodes.v, - ast_nodes.s * sizeof(struct ast_node *)); - } - - struct ast_node *n = calloc(1, sizeof(struct ast_node)); - ast_nodes.v[ast_nodes.n++] = n; - return n; -} - -/** @todo alloc should maybe also keep track of all nodes in a vector or - * something and mass free all AST at a time to keep my sanity */ -#define ALLOC_NODE(n, type) \ - struct ast_node *n = create_ast_node(); \ - if (!n) { \ - fprintf(stderr, "failed allocating" type "\n"); \ - return NULL; \ - } - -#define DESTROY_LIST(x) \ - { \ - struct ast_node *prev = x, *cur; \ - if (prev) \ - do { \ - cur = prev->next; \ - destroy_ast_node(prev); \ - } while ((prev = cur)); \ - } - -struct ast_node *gen_arr_access(struct ast_node *base, struct ast_node *idx, - struct src_loc loc) -{ - ALLOC_NODE(n, "arr_access"); - n->node_type = AST_ARR_ACCESS; - AST_ARR_ACCESS(n).base = base; - AST_ARR_ACCESS(n).idx = idx; - n->loc = loc; - return n; -} - -struct ast_node *gen_macro_expand(struct ast_node *id, struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "macro_expand"); - n->node_type = AST_MACRO_EXPAND; - AST_MACRO_EXPAND(n).id = id; - AST_MACRO_EXPAND(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_type_expand(struct ast_node *id, - struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "type_expand"); - n->node_type = AST_TYPE_EXPAND; - AST_TYPE_EXPAND(n).id = id; - AST_TYPE_EXPAND(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_binop(enum ast_binops op, - struct ast_node *left, - struct ast_node *right, - struct src_loc loc) -{ - ALLOC_NODE(n, "binop"); - n->node_type = AST_BINOP; - AST_BINOP(n).op = op; - AST_BINOP(n).left = left; - AST_BINOP(n).right = right; - n->loc = loc; - return n; -} - -struct ast_node *gen_unop(enum ast_unops op, struct ast_node *expr, - struct src_loc loc) -{ - ALLOC_NODE(n, "unop"); - n->node_type = AST_UNOP; - AST_UNOP(n).op = op; - AST_UNOP(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_call(struct ast_node *expr, struct ast_node *args, - struct src_loc loc) -{ - ALLOC_NODE(n, "call"); - n->node_type = AST_CALL; - AST_CALL(n).expr = expr; - AST_CALL(n).args = args; - n->loc = loc; - return n; -} - -struct ast_node *gen_id(const char *id, struct src_loc loc) -{ - ALLOC_NODE(n, "id"); - n->node_type = AST_ID; - AST_ID(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_assign(struct ast_node *to, struct ast_node *from, - struct src_loc loc) -{ - ALLOC_NODE(n, "assign"); - n->node_type = AST_ASSIGN; - AST_ASSIGN(n).to = to; - AST_ASSIGN(n).from = from; - n->loc = loc; - return n; -} - -struct ast_node *gen_init(struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "struct init"); - n->node_type = AST_INIT; - AST_INIT(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_int(long long integer, struct src_loc loc) -{ - ALLOC_NODE(n, "int"); - n->node_type = AST_CONST; - AST_CONST(n).kind = AST_CONST_INTEGER; - AST_CONST(n).integer = integer; - n->loc = loc; - return n; -} - -struct ast_node *gen_string(const char *str, struct src_loc loc) -{ - ALLOC_NODE(n, "string"); - n->node_type = AST_CONST; - AST_CONST(n).kind = AST_CONST_STRING; - AST_CONST(n).str = str; - n->loc = loc; - return n; -} - -struct ast_node *gen_while(struct ast_node *cond, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "while"); - n->node_type = AST_WHILE; - AST_WHILE(n).cond = cond; - AST_WHILE(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_for(struct ast_node *pre, struct ast_node *cond, - struct ast_node *post, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "for"); - n->node_type = AST_FOR; - AST_FOR(n).pre = pre; - AST_FOR(n).cond = cond; - AST_FOR(n).post = post; - AST_FOR(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_return(struct ast_node *expr, struct src_loc loc) -{ - ALLOC_NODE(n, "return"); - n->node_type = AST_RETURN; - AST_RETURN(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_goto(struct ast_node *label, struct src_loc loc) -{ - ALLOC_NODE(n, "goto"); - n->node_type = AST_GOTO; - AST_GOTO(n).label = label; - n->loc = loc; - return n; -} - -struct ast_node *gen_dot(struct ast_node *expr, struct ast_node *id, - struct src_loc loc) -{ - ALLOC_NODE(n, "dot"); - n->node_type = AST_DOT; - AST_DOT(n).expr = expr; - AST_DOT(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_label(struct ast_node *id, struct src_loc loc) -{ - ALLOC_NODE(n, "label"); - n->node_type = AST_LABEL; - AST_LABEL(n).id = id; - n->loc = loc; - return n; -} - -struct ast_node *gen_ctrl(enum ast_ctrl_kind kind, struct src_loc loc) -{ - ALLOC_NODE(n, "ctrl"); - n->node_type = AST_CTRL; - AST_CTRL(n).kind = kind; - n->loc = loc; - return n; -} - -struct ast_node *gen_fetch(struct ast_node *id, struct ast_node *type, - struct src_loc loc) -{ - ALLOC_NODE(n, "fetch"); - n->node_type = AST_FETCH; - AST_FETCH(n).id = id; - AST_FETCH(n).type = type; - n->loc = loc; - return n; -} - -struct ast_node *gen_macro_construct(struct ast_node *id, - struct ast_node *params, - struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "macro_construct"); - n->node_type = AST_MACRO_CONSTRUCT; - AST_MACRO_CONSTRUCT(n).id = id; - AST_MACRO_CONSTRUCT(n).params = params; - AST_MACRO_CONSTRUCT(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_if(struct ast_node *cond, struct ast_node *body, - struct ast_node *els, struct src_loc loc) -{ - ALLOC_NODE(n, "if"); - n->node_type = AST_IF; - AST_IF(n).cond = cond; - AST_IF(n).body = body; - AST_IF(n).els = els; - n->loc = loc; - return n; -} - -struct ast_node *gen_switch(struct ast_node *cond, struct ast_node *cases, - struct src_loc loc) -{ - ALLOC_NODE(n, "switch"); - n->node_type = AST_SWITCH; - AST_SWITCH(n).cond = cond; - AST_SWITCH(n).cases = cases; - n->loc = loc; - return n; -} + if (!n) + return; -struct ast_node *gen_case(struct ast_node *cond, struct ast_node *body, - struct src_loc loc) -{ - ALLOC_NODE(n, "case"); - /* TODO: a macro to map proc name to node type would make sure I don't - * make any dumb mixups... */ - n->node_type = AST_CASE; - AST_CASE(n).cond = cond; - AST_CASE(n).body = body; - /* TODO: where should I check the fallthrough flag? In the - * actualization stage, I guess */ - n->loc = loc; - return n; -} + if (n->id) + free(n->id); -struct ast_node *gen_primitive(enum ast_primitive type, struct ast_node *def, - struct src_loc loc) -{ - ALLOC_NODE(n, "primitive"); - n->node_type = AST_TYPE; - AST_TYPE(n).kind = AST_TYPE_PRIMITIVE; - AST_PRIMITIVE_TYPE(n).type = type; - AST_PRIMITIVE_TYPE(n).def = def; - n->loc = loc; - return n; + free(n); } -struct ast_node *gen_type(enum ast_type_kind kind, - struct ast_node *t0, - struct ast_node *t1, - struct src_loc loc) +void destroy_ast_nodes() { - ALLOC_NODE(n, "type"); - n->node_type = AST_TYPE; - AST_TYPE(n).kind = kind; - n->loc = loc; - - switch (kind) { - case AST_TYPE_TRAIT: - AST_TRAIT_TYPE(n).def = t0; - break; - - case AST_TYPE_ID: - AST_ID_TYPE(n).id = t0; - break; - - case AST_TYPE_CONSTRUCT: - AST_CONSTRUCT_TYPE(n).id = t0; - AST_CONSTRUCT_TYPE(n).args = t1; - break; - - case AST_TYPE_ARR: - AST_ARR_TYPE(n).size = t0; - AST_ARR_TYPE(n).base = t1; - break; - - case AST_TYPE_POINTER: - AST_PTR_TYPE(n).base = t0; - break; - - case AST_TYPE_STRUCT: - AST_STRUCT_TYPE(n).def = t0; - break; - - case AST_TYPE_ENUM: - AST_ENUM_TYPE(n).def = t0; - break; - - case AST_TYPE_SIGN: - AST_SIGN_TYPE(n).params = t0; - AST_SIGN_TYPE(n).ret = t1; - break; - - default: abort(); + foreach_vec(ni, nodes) { + struct ast *n = vect_at(struct ast *, nodes, ni); + destroy_ast_node(n); } - return n; -} - -struct ast_node *gen_block(struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "block"); - n->node_type = AST_BLOCK; - AST_BLOCK(n).body = body; - n->loc = loc; - return n; -} - -struct ast_node *gen_sizeof(struct ast_node *expr, struct src_loc loc) -{ - ALLOC_NODE(n, "sizeof"); - n->node_type = AST_SIZEOF; - AST_SIZEOF(n).expr = expr; - n->loc = loc; - return n; -} - -struct ast_node *gen_as(struct ast_node *type, struct src_loc loc) -{ - ALLOC_NODE(n, "as"); - n->node_type = AST_AS; - AST_AS(n).type = type; - n->loc = loc; - return n; + vec_destroy(&nodes); } -struct ast_node *gen_defer(struct ast_node *expr, struct src_loc loc) +void destroy_types() { - ALLOC_NODE(n, "defer"); - n->node_type = AST_DEFER; - AST_DEFER(n).expr = expr; - n->loc = loc; - return n; -} - -void destroy_defer(struct ast_node *defer) -{ - assert(defer->node_type == AST_DEFER); - destroy_ast_node(AST_DEFER(defer).expr); - free(defer); -} + foreach_vec(ti, types) { + struct type *t = vect_at(struct type *, types, ti); + destroy_type(t); + } -struct ast_node *gen_var(struct ast_node *id, struct ast_node *type, - struct ast_node *init, struct src_loc loc) -{ - ALLOC_NODE(n, "var"); - n->node_type = AST_VAR; - AST_VAR(n).id = id; - AST_VAR(n).type = type; - AST_VAR(n).init = init; - n->loc = loc; - return n; + vec_destroy(&types); } -struct ast_node *gen_proc(struct ast_node *id, struct ast_node *sign, - struct ast_node *body, struct src_loc loc) +void destroy_allocs() { - ALLOC_NODE(n, "proc"); - n->node_type = AST_PROC; - AST_PROC(n).id = id; - AST_PROC(n).sign = sign; - AST_PROC(n).body = body; - n->loc = loc; - return n; + destroy_ast_nodes(); + destroy_types(); } -struct ast_node *gen_struct(struct ast_node *id, - struct ast_node *generics, struct ast_node *body, - struct src_loc loc) +static struct ast *create_empty_ast() { - ALLOC_NODE(n, "struct"); - n->node_type = AST_STRUCT; - AST_STRUCT(n).id = id; - AST_STRUCT(n).generics = generics; - AST_STRUCT(n).body = body; - n->loc = loc; - return n; -} + if (vec_uninit(nodes)) { + nodes = vec_create(sizeof(struct ast *)); + } -struct ast_node *gen_enum(struct ast_node *id, struct ast_node *type, - struct ast_node *body, struct src_loc loc) -{ - ALLOC_NODE(n, "enum"); - n->node_type = AST_ENUM; - AST_ENUM(n).id = id; - AST_ENUM(n).type = type; - AST_ENUM(n).body = body; - n->loc = loc; + struct ast *n = calloc(1, sizeof(struct ast)); + /* just to be safe */ + n->k = AST_EMPTY; + vect_append(struct ast *, nodes, &n); return n; } -struct ast_node *gen_cast(struct ast_node *expr, struct ast_node *type, - struct src_loc loc) +static struct type *create_empty_type() { - ALLOC_NODE(n, "cast"); - n->node_type = AST_CAST; - AST_CAST(n).expr = expr; - AST_CAST(n).type = type; - n->loc = loc; - return n; -} + if (vec_uninit(types)) { + types = vec_create(sizeof(struct type *)); + } -struct ast_node *gen_val(struct ast_node *id, struct ast_node *val, - struct src_loc loc) -{ - ALLOC_NODE(n, "val"); - n->node_type = AST_VAL; - AST_VAL(n).id = id; - AST_VAL(n).val = val; - n->loc = loc; + struct type *n = calloc(1, sizeof(struct type)); + /* just to be safe */ + n->k = TYPE_VOID; + vect_append(struct ast *, types, &n); return n; } -struct ast_node *gen_alias(struct ast_node *id, struct ast_node *type, - struct src_loc loc) +struct ast *gen_ast(enum ast_kind kind, + struct ast *a0, + struct ast *a1, + struct ast *a2, + struct ast *a3, + struct type *t2, + char *s, + long long v, + struct src_loc loc) { - ALLOC_NODE(n, "alias"); - n->node_type = AST_ALIAS; - AST_ALIAS(n).id = id; - AST_ALIAS(n).type = type; + struct ast *n = create_empty_ast(); + n->k = kind; + n->a0 = a0; + n->a1 = a1; + n->a2 = a2; + n->a3 = a3; + n->t2 = t2; + n->s = s; + n->v = v; n->loc = loc; return n; } -struct ast_node *gen_trait(struct ast_node *id, - struct ast_node *params, - struct ast_node *raw_body, - struct ast_node *body, - struct src_loc loc) +struct type *tgen_type(enum type_kind kind, + struct type *t0, + struct type *t1, + struct ast *d, + struct ast *a, + char *id, + struct src_loc loc) { - ALLOC_NODE(n, "trait"); - n->node_type = AST_TRAIT; - AST_TRAIT(n).id = id; - AST_TRAIT(n).params = params; - AST_TRAIT(n).body = body; - AST_TRAIT(n).raw_body = raw_body; + struct type *n = create_empty_type(); + n->k = kind; + n->t0 = t0; + n->t1 = t1; + n->d = d; + n->a = a; + n->id = id; n->loc = loc; return n; } -struct ast_node *gen_import(const char *file, struct src_loc loc) +void ast_set_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "import"); - n->node_type = AST_IMPORT; - AST_IMPORT(n).file = file; - n->loc = loc; - return n; + node->f |= flags; } -struct ast_node *gen_embed(const char *file, struct src_loc loc) +void ast_clear_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "embed"); - n->node_type = AST_EMBED; - AST_EMBED(n).file = file; - n->loc = loc; - return n; + node->f &= ~(flags); } -struct ast_node *gen_empty() +unsigned ast_flags(struct ast *node, enum ast_flags flags) { - ALLOC_NODE(n, "empty"); - n->node_type = AST_EMPTY; - /* TODO: location? */ - return n; + return node->f & flags; } -void ast_set_flags(struct ast_node *node, enum ast_flag flags) +void ast_append(struct ast *list, struct ast *elem) { - node->flags |= flags; -} + struct ast *cur = list; + while (cur->n) + cur = cur->n; -void ast_clear_flags(struct ast_node *node, enum ast_flag flags) -{ - node->flags &= ~(flags); + cur->n = elem; } -void ast_append(struct ast_node *list, struct ast_node *elem) +void type_append(struct type *list, struct type *elem) { - struct ast_node *cur = list; - while (cur->next) - cur = cur->next; + struct type *cur = list; + while (cur->n) + cur = cur->n; - cur->next = elem; + cur->n = elem; } - -static const char *binop_symbol(int op) +static const char *binop_symbol(enum ast_kind op) { switch (op) { case AST_ADD: return "+"; @@ -618,18 +195,20 @@ static const char *binop_symbol(int op) case AST_GE: return ">="; case AST_NE: return "!="; case AST_EQ: return "=="; + default: } return "UNKNOWN"; } -static const char *unop_symbol(int op) +static const char *unop_symbol(enum ast_kind op) { switch (op) { case AST_NEG: return "-"; case AST_LNOT: return "!"; case AST_REF: return "&"; case AST_DEREF: return "*"; + default: } return "UNKNOWN"; @@ -641,19 +220,19 @@ static void dump(int depth, const char *fmt, ...) va_start(args, fmt); printf("//"); for (int i = 0; i < depth; ++i) - putchar('\t'); + printf(" "); vprintf(fmt, args); va_end(args); } -static void dump_flags(struct ast_node *node) +static void dump_flags(struct ast *node) { if (node->scope) printf(" %zu:", node->scope->number); - enum ast_flag flags = node->flags; + enum ast_flags flags = node->f; if (flags & AST_FLAG_MUTABLE) printf(" MUT"); @@ -679,1252 +258,462 @@ static void dump_flags(struct ast_node *node) printf(" FALLTHROUGH"); } -const char *primitive_str(enum ast_primitive type) +const char *primitive_str(struct type *type) { - switch (type) { - case AST_VOID: return "void"; - case AST_I9: return "i9"; - case AST_I27: return "i27"; - case AST_BOOL: return "bool"; + switch (type->k) { + case TYPE_VOID: return "void"; + case TYPE_I9: return "i9"; + case TYPE_I27: return "i27"; + case TYPE_BOOL: return "bool"; default: return "unimp"; } return "unimp"; } -static void __dump_ast(int depth, struct ast_node *node) +void ast_dump(int depth, struct ast *n) { - switch (node->node_type) { - case AST_FETCH: - dump(depth, "{FETCH:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_FETCH(node).id); - dump_ast(depth + 1, AST_FETCH(node).type); - - dump(depth, "}\n"); - break; - - case AST_ASSIGN: - dump(depth, "{ASSIGN:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ASSIGN(node).to); - dump_ast(depth + 1, AST_ASSIGN(node).from); - - dump(depth, "}\n"); - break; - - case AST_INIT: - dump(depth, "{INIT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_INIT(node).body); - - dump(depth, "}\n"); - break; - - case AST_SIZEOF: - dump(depth, "{SIZEOF:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_SIZEOF(node).expr); - - dump(depth, "}\n"); - break; - - case AST_DOT: - dump(depth, "{DOT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_DOT(node).expr); - dump_ast(depth + 1, AST_DOT(node).id); - - dump(depth, "}\n"); - break; - - case AST_GOTO: - dump(depth, "{GOTO:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_GOTO(node).defers); - dump_ast(depth + 1, AST_GOTO(node).label); - - dump(depth, "}\n"); - break; - - case AST_LABEL: - dump(depth, "{LABEL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_LABEL(node).id); - - dump(depth, "}\n"); - break; - - case AST_BINOP: - dump(depth, "{BINOP:"); - dump_flags(node); - printf(" %s\n", binop_symbol(AST_BINOP(node).op)); - - dump_ast(depth + 1, AST_BINOP(node).left); - dump_ast(depth + 1, AST_BINOP(node).right); - - dump(depth, "}\n"); - break; - - case AST_UNOP: - dump(depth, "{UNOP:"); - dump_flags(node); - printf(" %s\n", unop_symbol(AST_UNOP(node).op)); - - dump_ast(depth + 1, AST_UNOP(node).expr); - - dump(depth, "}\n"); - break; - - case AST_CALL: - dump(depth, "{CALL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_CALL(node).expr); - dump_ast(depth + 1, AST_CALL(node).args); - - dump(depth, "}\n"); - break; - - case AST_DEFER: - dump(depth, "{DEFER:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_DEFER(node).expr); - - dump(depth, "}\n"); - break; + if (!n) { + dump(depth, "{NULL}\n"); + return; + } - case AST_CAST: - dump(depth, "{CAST:"); - dump_flags(node); - putchar('\n'); +#define DUMP(x) case x: dump(depth, #x); break; + switch (n->k) { + DUMP(AST_FETCH); + DUMP(AST_INIT); + DUMP(AST_ASSIGN); + DUMP(AST_CALL); + DUMP(AST_ARR); + DUMP(AST_SIZEOF); + DUMP(AST_CAST); + DUMP(AST_DEFER); + DUMP(AST_MACRO_DEF); + DUMP(AST_MACRO_EXPAND); + DUMP(AST_TYPE_EXPAND); + DUMP(AST_PROC_DEF); + DUMP(AST_GOTO); + DUMP(AST_LABEL); + DUMP(AST_VAR_DEF); + DUMP(AST_FOR); + DUMP(AST_EMBED); + DUMP(AST_DOT); + DUMP(AST_WHILE); + DUMP(AST_DO_WHILE); + DUMP(AST_BREAK); + DUMP(AST_CONTINUE); + DUMP(AST_RETURN); + DUMP(AST_ALIAS_DEF); + DUMP(AST_TRAIT_DEF); + DUMP(AST_STRUCT_DEF); + DUMP(AST_IF); + DUMP(AST_BLOCK); + DUMP(AST_IMPORT); + DUMP(AST_ENUM_DEF); + DUMP(AST_VAL); + DUMP(AST_SWITCH); + DUMP(AST_CASE); + DUMP(AST_ID); + DUMP(AST_AS); + DUMP(AST_EMPTY); + DUMP(AST_ADD); + DUMP(AST_SUB); + DUMP(AST_MUL); + DUMP(AST_DIV); + DUMP(AST_REM); + DUMP(AST_LAND); + DUMP(AST_LOR); + DUMP(AST_LSHIFT); + DUMP(AST_RSHIFT); + DUMP(AST_ASSIGN_ADD); + DUMP(AST_ASSIGN_SUB); + DUMP(AST_ASSIGN_MUL); + DUMP(AST_ASSIGN_DIV); + DUMP(AST_ASSIGN_REM); + DUMP(AST_ASSIGN_LSHIFT); + DUMP(AST_ASSIGN_RSHIFT); + DUMP(AST_LT); + DUMP(AST_GT); + DUMP(AST_LE); + DUMP(AST_GE); + DUMP(AST_NE); + DUMP(AST_EQ); + DUMP(AST_NEG); + DUMP(AST_LNOT); + DUMP(AST_NOT); + DUMP(AST_REF); + DUMP(AST_DEREF); + DUMP(AST_CONST_INT); + DUMP(AST_CONST_CHAR); + DUMP(AST_CONST_BOOL); + DUMP(AST_CONST_STR); + } +#undef DUMP - dump_ast(depth + 1, AST_CAST(node).expr); - dump_ast(depth + 1, AST_CAST(node).type); + depth++; - dump(depth, "}\n"); - break; + if (n->t) + type_dump_list(n->t); - case AST_MACRO_CONSTRUCT: - dump(depth, "{MACRO_CONSTRUCT:"); - dump_flags(node); - putchar('\n'); + printf("\n"); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).id); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).params); - dump_ast(depth + 1, AST_MACRO_CONSTRUCT(node).body); + if (n->s) + dump(depth, "%s\n", n->s); - dump(depth, "}\n"); - break; + if (n->k == AST_CONST_INT) + dump(depth, "%lli\n", n->v); - case AST_MACRO_EXPAND: - dump(depth, "{MACRO_EXPAND:"); - dump_flags(node); - putchar('\n'); + if (n->a0) + ast_dump_list(depth, n->a0); - dump_ast(depth + 1, AST_MACRO_EXPAND(node).id); - dump_ast(depth + 1, AST_MACRO_EXPAND(node).args); + if (n->a1) + ast_dump_list(depth, n->a1); - dump(depth, "}\n"); - break; + if (n->a2) + ast_dump_list(depth, n->a2); - case AST_PROC: - dump(depth, "{PROC:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_PROC(node).id); - dump_ast(depth + 1, AST_PROC(node).sign); - dump_ast(depth + 1, AST_PROC(node).body); - - dump(depth, "}\n"); - break; - - case AST_VAR: - dump(depth, "{VAR:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_VAR(node).id); - dump_ast(depth + 1, AST_VAR(node).type); - dump_ast(depth + 1, AST_VAR(node).init); - - dump(depth, "}\n"); - break; - - case AST_ID: - dump(depth, "{ID:"); - dump_flags(node); - printf(" %s}\n", AST_ID(node).id); - break; - - case AST_AS: - dump(depth, "{AS:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_AS(node).type); - dump(depth, "}\n"); - break; - - case AST_BLOCK: - dump(depth, "{BLOCK:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_BLOCK(node).body); - dump_ast(depth + 1, AST_BLOCK(node).defers); - - dump(depth, "}\n"); - break; - - case AST_RETURN: - dump(depth, "{RETURN:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_RETURN(node).defers); - dump_ast(depth + 1, AST_RETURN(node).expr); - - dump(depth, "}\n"); - break; - - case AST_TYPE: - dump(depth, "{TYPE:"); - dump_flags(node); - - switch (node->_type.kind) { - case AST_TYPE_PRIMITIVE: - printf(" PRIMITIVE %s\n", - primitive_str(AST_PRIMITIVE_TYPE(node).type)); - break; - - case AST_TYPE_TRAIT: { - printf(" TRAIT\n"); - struct ast_node *def = AST_TRAIT_TYPE(node).def; - /* this should be enough, avoid print loops */ - dump_ast(depth + 1, AST_TRAIT(def).id); - break; - } - - case AST_TYPE_CONSTRUCT: { - printf(" CONSTRUCT\n"); - dump_ast(depth + 1, AST_CONSTRUCT_TYPE(node).id); - dump_ast(depth + 1, AST_CONSTRUCT_TYPE(node).args); - break; - } - - case AST_TYPE_ID: - printf(" ID\n"); - dump_ast(depth + 1, AST_ID_TYPE(node).id); - break; - - case AST_TYPE_ARR: - printf(" ARR\n"); - dump_ast(depth + 1, AST_ARR_TYPE(node).size); - dump_ast(depth + 1, AST_ARR_TYPE(node).base); - break; - - case AST_TYPE_POINTER: - printf(" PTR\n"); - dump_ast(depth + 1, AST_PTR_TYPE(node).base); - break; - - case AST_TYPE_STRUCT: { - printf(" STRUCT\n"); - struct ast_node *def = AST_STRUCT_TYPE(node).def; - /* oh yeah, struc is at least right now just an ID that - * we can use to fetch the actual struct with. */ - dump_ast(depth + 1, AST_STRUCT(def).id); - break; - } - - case AST_TYPE_ENUM: { - printf(" ENUM\n"); - struct ast_node *def = AST_ENUM_TYPE(node).def; - dump_ast(depth + 1, AST_ENUM(def).id); - break; - } - - case AST_TYPE_SIGN: - printf(" SIGN\n"); - dump_ast(depth + 1, AST_SIGN_TYPE(node).params); - dump_ast(depth + 1, AST_SIGN_TYPE(node).ret); - break; - } - - dump_ast(depth + 1, AST_TYPE(node).next); - - dump(depth, "}\n"); - break; - - case AST_EMPTY: - dump(depth, "{EMPTY:"); - dump_flags(node); - printf("}\n"); - break; - - case AST_FOR: - dump(depth, "{FOR:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_FOR(node).pre); - dump_ast(depth + 1, AST_FOR(node).cond); - dump_ast(depth + 1, AST_FOR(node).post); - dump_ast(depth + 1, AST_FOR(node).body); - - dump(depth, "}\n"); - break; - - case AST_WHILE: - dump(depth, "{WHILE:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_WHILE(node).cond); - dump_ast(depth + 1, AST_WHILE(node).body); - - dump(depth, "}\n"); - break; - - case AST_CTRL: - dump(depth, "{CTRL:"); - dump_flags(node); - switch(AST_CTRL(node).kind) { - case AST_CTRL_BREAK: printf(" BREAK"); break; - case AST_CTRL_CONTINUE: printf(" CONTINUE"); break; - } - printf("}\n"); - break; - - case AST_IF: - dump(depth, "{IF:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_IF(node).cond); - dump_ast(depth + 1, AST_IF(node).body); - dump_ast(depth + 1, AST_IF(node).els); - - dump(depth, "}\n"); - break; - - case AST_IMPORT: - dump(depth, "{IMPORT:"); - dump_flags(node); - printf(" %s}\n", AST_IMPORT(node).file); - break; - - case AST_EMBED: - dump(depth, "{EMBED:"); - dump_flags(node); - printf(" %s}\n", AST_EMBED(node).file); - break; - - case AST_ENUM: - dump(depth, "{ENUM:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ENUM(node).id); - dump_ast(depth + 1, AST_ENUM(node).type); - dump_ast(depth + 1, AST_ENUM(node).body); - - dump(depth, "}\n"); - break; - - case AST_STRUCT: - dump(depth, "{STRUCT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_STRUCT(node).id); - dump_ast(depth + 1, AST_STRUCT(node).generics); - dump_ast(depth + 1, AST_STRUCT(node).body); - - dump(depth, "}\n"); - break; - - case AST_VAL: - dump(depth, "{VAL:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_VAL(node).id); - dump_ast(depth + 1, AST_VAL(node).val); - - dump(depth, "}\n"); - break; - - case AST_SWITCH: - dump(depth, "{SWITCH:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_SWITCH(node).cond); - dump_ast(depth + 1, AST_SWITCH(node).cases); - - dump(depth, "}\n"); - break; - - case AST_CASE: - dump(depth, "{CASE:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_CASE(node).cond); - dump_ast(depth + 1, AST_CASE(node).body); - - dump(depth, "}\n"); - break; - - case AST_CONST: - dump(depth, "{CONST:"); - dump_flags(node); - switch (node->_const.kind) { - case AST_CONST_INTEGER: - printf(" %lli", AST_CONST(node).integer); - break; - case AST_CONST_STRING: - printf(" \"%s\"", AST_CONST(node).str); - break; - } - printf("}\n"); - break; - - case AST_ALIAS: - dump(depth, "{ALIAS:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_ALIAS(node).id); - dump_ast(depth + 1, AST_ALIAS(node).type); - - dump(depth, "}\n"); - break; - - case AST_TRAIT: - dump(depth, "{TRAIT:"); - dump_flags(node); - putchar('\n'); - - dump_ast(depth + 1, AST_TRAIT(node).id); - dump_ast(depth + 1, AST_TRAIT(node).body); - - dump(depth, "}\n"); - break; + if (n->a3) + ast_dump_list(depth, n->a3); - default: - dump(depth, "{UNIMP}\n"); + if (n->t2) { + type_dump_list(n->t2); printf("\n"); } } -void dump_ast_node(int depth, struct ast_node *n) +void ast_dump_list(int depth, struct ast *root) { - if (!n) { + if (!root) { dump(depth, "{NULL}\n"); return; } - __dump_ast(depth, n); + foreach_node(n, root) { + ast_dump(depth, n); + } } -void dump_ast(int depth, struct ast_node *root) +void type_dump(struct type *n) { - if (!root) { - dump(depth, "{NULL}\n"); + if (!n) { + printf(" {NULL}"); return; } - struct ast_node *n = root; - do { - dump_ast_node(depth, n); - } while ((n = n->next)); + printf(" "); + +#define DUMP(x) case x: printf(#x); break; + switch (n->k) { + DUMP(TYPE_VOID); + DUMP(TYPE_BOOL); + DUMP(TYPE_I9); + DUMP(TYPE_I27); + DUMP(TYPE_STR); + DUMP(TYPE_PTR); + DUMP(TYPE_ID); + DUMP(TYPE_CONSTRUCT); + DUMP(TYPE_STRUCT); + DUMP(TYPE_ENUM); + DUMP(TYPE_CALLABLE); + DUMP(TYPE_TRAIT); + } } -struct ast_node *clone_ast_node(struct ast_node *node) +void type_dump_list(struct type *root) { - if (!node) - return NULL; + if (!root) + return; - assert(node->node_type); - struct ast_node *new = NULL; - switch (node->node_type) { - case AST_ARR_ACCESS: - new = gen_arr_access( - clone_ast_node(AST_ARR_ACCESS(node).base), - clone_ast_node(AST_ARR_ACCESS(node).idx), - node->loc); - break; - - case AST_TYPE_EXPAND: - new = gen_type_expand( - clone_ast_node(AST_TYPE_EXPAND(node).id), - clone_ast_node(AST_TYPE_EXPAND(node).args), - node->loc); - break; - - case AST_FETCH: - new = gen_fetch(clone_ast_node(AST_FETCH(node).id), - clone_ast_node(AST_FETCH(node).type), - node->loc); - break; - - case AST_ASSIGN: - new = gen_assign(clone_ast_node(AST_ASSIGN(node).to), - clone_ast_node(AST_ASSIGN(node).from), - node->loc); - break; - - case AST_INIT: new = gen_init(clone_ast_node(AST_INIT(node).body), - node->loc); - break; - - case AST_SIZEOF: new = gen_sizeof(clone_ast_node(AST_SIZEOF(node).expr), - node->loc); - break; - - case AST_DOT: new = gen_dot(clone_ast_node(AST_DOT(node).expr), - clone_ast_node(AST_DOT(node).id), - node->loc); - break; - - case AST_AS: new = gen_as(clone_ast_node(AST_AS(node).type), node->loc); - break; - - case AST_GOTO: new = gen_goto(clone_ast_node(AST_GOTO(node).label), - node->loc); - break; - - case AST_LABEL: new = gen_label(clone_ast_node(AST_LABEL(node).id), - node->loc); - break; - - case AST_BINOP: new = gen_binop(AST_BINOP(node).op, - clone_ast_node(AST_BINOP(node).left), - clone_ast_node(AST_BINOP(node).right), - node->loc); - break; - - case AST_UNOP: new = gen_unop(AST_UNOP(node).op, - clone_ast_node(AST_UNOP(node).expr), - node->loc); - break; - - case AST_CALL: new = gen_call(clone_ast_node(AST_CALL(node).expr), - clone_ast_node(AST_CALL(node).args), - node->loc); - break; - - case AST_DEFER: new = gen_defer(clone_ast_node(AST_DEFER(node).expr), - node->loc); - break; - - case AST_MACRO_CONSTRUCT: new = gen_macro_construct( - clone_ast_node(AST_MACRO_CONSTRUCT(node).id), - clone_ast_node(AST_MACRO_CONSTRUCT(node).params), - clone_ast_node(AST_MACRO_CONSTRUCT(node).body), - node->loc); - break; - - case AST_MACRO_EXPAND: new = gen_macro_expand( - clone_ast_node(AST_MACRO_EXPAND(node).id), - clone_ast_node(AST_MACRO_EXPAND(node).args), - node->loc); - break; - - case AST_CAST: new = gen_cast(clone_ast_node(AST_CAST(node).expr), - clone_ast_node(AST_CAST(node).type), - node->loc); - break; - - case AST_PROC: new = gen_proc(clone_ast_node(AST_PROC(node).id), - clone_ast_node(AST_PROC(node).sign), - clone_ast_node(AST_PROC(node).body), - node->loc); - break; - - case AST_VAR: new = gen_var(clone_ast_node(AST_VAR(node).id), - clone_ast_node(AST_VAR(node).type), - clone_ast_node(AST_VAR(node).init), - node->loc); - break; - - case AST_FOR: new = gen_for(clone_ast_node(AST_FOR(node).pre), - clone_ast_node(AST_FOR(node).cond), - clone_ast_node(AST_FOR(node).post), - clone_ast_node(AST_FOR(node).body), - node->loc); - break; - - case AST_WHILE: new = gen_while(clone_ast_node(AST_WHILE(node).cond), - clone_ast_node(AST_WHILE(node).body), - node->loc); - break; - - case AST_CTRL: new = gen_ctrl(AST_CTRL(node).kind, node->loc); - break; - - case AST_RETURN: new = gen_return(clone_ast_node(AST_RETURN(node).expr), - node->loc); - break; - - case AST_TYPE: - switch (node->_type.kind) { - case AST_TYPE_PRIMITIVE: - new = gen_primitive(AST_PRIMITIVE_TYPE(node).type, - AST_PRIMITIVE_TYPE(node).def, - node->loc); - break; - - case AST_TYPE_TRAIT: - new = gen_type(AST_TYPE_TRAIT, - AST_TRAIT_TYPE(node).def, - NULL, - node->loc); - break; - - case AST_TYPE_CONSTRUCT: - new = gen_type(AST_TYPE_CONSTRUCT, - clone_ast_node(AST_CONSTRUCT_TYPE( - node).id), - clone_ast_node(AST_CONSTRUCT_TYPE( - node).args), - node->loc); - break; - - case AST_TYPE_ID: - new = gen_type(AST_TYPE_ID, - clone_ast_node(AST_ID_TYPE(node).id), - NULL, - node->loc); - break; - - case AST_TYPE_ARR: - new = gen_type(AST_TYPE_ARR, - clone_ast_node(AST_ARR_TYPE(node).size), - clone_ast_node(AST_ARR_TYPE(node).base), - node->loc); - break; - - case AST_TYPE_POINTER: - new = gen_type(AST_TYPE_POINTER, - clone_ast_node(AST_PTR_TYPE(node).base), - NULL, - node->loc); - break; - - case AST_TYPE_STRUCT: - new = gen_type(AST_TYPE_STRUCT, - AST_STRUCT_TYPE(node).def, - NULL, node->loc); - break; - - case AST_TYPE_ENUM: - new = gen_type(AST_TYPE_ENUM, - AST_ENUM_TYPE(node).def, - NULL, - node->loc); - break; - - case AST_TYPE_SIGN: - new = gen_type(AST_TYPE_SIGN, - clone_ast_node(AST_SIGN_TYPE( - node).params), - clone_ast_node(AST_SIGN_TYPE(node).ret), - node->loc); - break; - - } - - assert(new); - AST_TYPE(new).next = clone_ast_node(AST_TYPE(node).next); - break; - - case AST_BLOCK: - /* TODO: should defers also be cloned? Probably? */ - new = gen_block(clone_ast_node(AST_BLOCK(node).body), - node->loc); - break; - - case AST_IMPORT: - new = gen_import(strdup(AST_IMPORT(node).file), node->loc); - break; - - case AST_EMBED: - new = gen_embed(strdup(AST_EMBED(node).file), node->loc); - break; - - case AST_ENUM: - new = gen_enum(clone_ast_node(AST_ENUM(node).id), - clone_ast_node(AST_ENUM(node).type), - clone_ast_node(AST_ENUM(node).body), - node->loc); - break; - - case AST_STRUCT: - new = gen_struct(clone_ast_node(AST_STRUCT(node).id), - clone_ast_node(AST_STRUCT(node).generics), - clone_ast_node(AST_STRUCT(node).body), - node->loc); - break; - - case AST_VAL: - new = gen_val(clone_ast_node(AST_VAL(node).id), - clone_ast_node(AST_VAL(node).val), - node->loc); - break; - - case AST_SWITCH: - new = gen_switch(clone_ast_node(AST_SWITCH(node).cond), - clone_ast_node(AST_SWITCH(node).cases), - node->loc); - break; - - case AST_CASE: - new = gen_case(clone_ast_node(AST_CASE(node).cond), - clone_ast_node(AST_CASE(node).body), - node->loc); - break; - - case AST_CONST: - switch (node->_const.kind) { - case AST_CONST_INTEGER: - new = gen_int(AST_CONST(node).integer, node->loc); - break; - - case AST_CONST_STRING: - new = gen_string(strdup(AST_CONST(node).str), - node->loc); - break; - } - break; - - case AST_ID: - new = gen_id(strdup(AST_ID(node).id), node->loc); - break; - - case AST_EMPTY: - new = gen_empty(); - break; - - case AST_ALIAS: - new = gen_alias(clone_ast_node(AST_ALIAS(node).id), - clone_ast_node(AST_ALIAS(node).type), - node->loc); - break; - - case AST_TRAIT: - new = gen_trait(clone_ast_node(AST_TRAIT(node).id), - clone_ast_node(AST_TRAIT(node).params), - clone_ast_node(AST_TRAIT(node).raw_body), - clone_ast_node(AST_TRAIT(node).body), - node->loc); - break; - - case AST_IF: - new = gen_if(clone_ast_node(AST_IF(node).cond), - clone_ast_node(AST_IF(node).body), - clone_ast_node(AST_IF(node).els), - node->loc); - break; + foreach_type(t, root) { + type_dump(t); } - - /* if we run out of memory, this assert is likely a bit dumb... */ - assert(new); - new->scope = node->scope; - new->flags = node->flags; - new->uses = node->uses; - new->loc = node->loc; - new->next = clone_ast_node(node->next); - - if (node->type != node) - node->type = clone_ast_node(node->type); - else - node->type = node; - - return new; } -unsigned ast_flags(struct ast_node *node, enum ast_flag flags) +struct ast *clone_ast(struct ast *n) { - return node->flags & flags; -} + if (!n) + return NULL; -static int call_on_assign(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_ASSIGN(node).to, data); - ret |= call(AST_ASSIGN(node).from, data); - return ret; -} + assert(n->k); + struct ast *new = create_empty_ast(); + new->scope = n->scope; + new->uses = n->uses; + new->loc = n->loc; + new->k = n->k; + new->v = n->v; + new->f = n->f; -static int call_on_init(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_INIT(node).body, data); -} + if (n->t) + new->t = clone_type_list(n->t); -static int call_on_sizeof(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(AST_SIZEOF(node).expr, data); -} + if (n->s) + new->s = strdup(n->s); -static int call_on_dot(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_DOT(node).expr, data); - ret |= call(AST_DOT(node).id, data); - return ret; -} + if (n->a0) + new->a0 = clone_ast_list(n->a0); -static int call_on_as(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_AS(node).type, data); -} + if (n->a1) + new->a1 = clone_ast_list(n->a1); -static int call_on_cast(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_CAST(node).expr, data); - ret |= call(AST_CAST(node).type, data); - return ret; -} + if (n->a2) + new->a2 = clone_ast_list(n->a2); -static int call_on_defer(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_DEFER(node).expr, data); -} + if (n->a3) + new->a3 = clone_ast_list(n->a3); -static int call_on_var(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_VAR(node).id, data); - ret |= call(AST_VAR(node).type, data); - ret |= call(AST_VAR(node).init, data); - return ret; -} + if (n->t2) + new->t2 = clone_type_list(n->t2); -static int call_on_for(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_for.pre, data); - ret |= call(node->_for.cond, data); - ret |= call(node->_for.post, data); - ret |= call(node->_for.body, data); - return ret; + return new; } -static int call_on_while(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +struct type *clone_type(struct type *n) { - int ret = 0; - ret |= call(node->_while.cond, data); - ret |= call(node->_while.body, data); - return ret; -} + if (!n) + return NULL; -static int call_on_return(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(node->_return.expr, data); -} + assert(n->k); + struct type *new = create_empty_type(); + new->scope = n->scope; + new->loc = n->loc; + new->k = n->k; + new->a = n->a; + new->d = n->d; -static int call_on_alias(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_alias.id, data); - ret |= call(node->_alias.type, data); - return ret; -} + if (n->id) + new->id = strdup(n->id); -static int call_on_trait(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_trait.id, data); - ret |= call(node->_trait.body, data); - return ret; -} + if (n->t0) + new->t0 = clone_type_list(n->t0); -static int call_on_if(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_if.cond, data); - ret |= call(node->_if.body, data); - ret |= call(node->_if.els, data); - return ret; -} + if (n->t1) + new->t1 = clone_type_list(n->t1); -static int call_on_enum(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_enum.id, data); - ret |= call(node->_enum.type, data); - ret |= call(node->_enum.body, data); - return ret; + return new; } -static int call_on_struct(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) +struct ast *clone_ast_list(struct ast *root) { - int ret = 0; - ret |= call(node->_struct.id, data); - ret |= call(node->_struct.generics, data); - ret |= call(node->_struct.body, data); - return ret; -} + struct ast *n = root, *new_root = NULL, *prev = NULL; + while (n) { + struct ast *new = clone_ast(n); -static int call_on_val(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_val.id, data); - ret |= call(node->_val.val, data); - return ret; -} + if (prev) prev->n = new; + else new_root = new; -static int call_on_switch(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(node->_switch.cond, data); - ret |= call(node->_switch.cases, data); - return ret; -} + prev = new; + n = n->n; + } -static int call_on_case(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_case.cond, data); - ret |= call(node->_case.body, data); - return ret; + return new_root; } -static int call_on_type_id(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) +struct type *clone_type_list(struct type *root) { - return call(AST_ID_TYPE(node).id, data); -} + struct type *n = root, *new_root = NULL, *prev = NULL; + while (n) { + struct type *new = clone_type(n); -static int call_on_type_arr(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - return call(AST_ARR_TYPE(node).size, data); -} + if (prev) prev->n = new; + else new_root = new; -static int call_on_type_sign(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_SIGN_TYPE(node).params, data); - ret |= call(AST_SIGN_TYPE(node).ret, data); - return ret; + prev = new; + n = n->n; + } + + return new_root; } -static int call_on_type_construct(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) +int type_visit(type_callback_t before, type_callback_t after, struct type *n, void *d) { int ret = 0; - ret |= call(AST_CONSTRUCT_TYPE(node).id, data); - ret |= call(AST_CONSTRUCT_TYPE(node).args, data); - return ret; -} + if (!n) + return ret; -static int call_on_type_pointer(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) -{ - return call(AST_PTR_TYPE(node).base, data); -} + if (before && (ret = before(n, d))) + return ret; -static int call_on_type(int (*call)(struct ast_node *, void *), - struct ast_node *node, void *data) -{ - int ret = 0; - switch (AST_TYPE(node).kind) { - case AST_TYPE_ENUM: break; - case AST_TYPE_TRAIT: break; - case AST_TYPE_ID: ret = call_on_type_id(call, node, data); break; - case AST_TYPE_ARR: ret = call_on_type_arr(call, node, data); break; - case AST_TYPE_STRUCT: break; - case AST_TYPE_SIGN: ret = call_on_type_sign(call, node, data); break; - case AST_TYPE_CONSTRUCT: ret = call_on_type_construct(call, node, data); - break; - case AST_TYPE_POINTER: ret = call_on_type_pointer(call, node, data); - break; - case AST_TYPE_PRIMITIVE: break; - } + if (n->t0 && (ret = type_visit_list(before, after, n->t0, d))) + return ret; - return ret; -} + if (n->t1 && (ret = type_visit_list(before, after, n->t1, d))) + return ret; -static int call_on_goto(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_GOTO(node).label, data); -} + if (after && (ret = after(n, d))) + return ret; -static int call_on_label(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_LABEL(node).id, data); + return ret; } -static int call_on_binop(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +int ast_visit(ast_callback_t before, ast_callback_t after, struct ast *n, void *d) { int ret = 0; - ret |= call(AST_BINOP(node).left, data); - ret |= call(AST_BINOP(node).right, data); - return ret; -} + if (!n) + return ret; -static int call_on_unop(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(AST_UNOP(node).expr, data); -} + if (before && (ret = before(n, d))) + return ret; -static int call_on_call(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(AST_CALL(node).expr, data); - ret |= call(AST_CALL(node).args, data); - return ret; -} + if (n->a0 && (ret = ast_visit_list(before, after, n->a0, d))) + return ret; -static int call_on_macro_construct(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(AST_MACRO_CONSTRUCT(node).id, data); - ret |= call(AST_MACRO_CONSTRUCT(node).params, data); - ret |= call(AST_MACRO_CONSTRUCT(node).body, data); - return ret; -} + if (n->a1 && (ret = ast_visit_list(before, after, n->a1, d))) + return ret; -static int call_on_proc(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_proc.id, data); - ret |= call(node->_proc.sign, data); - ret |= call(node->_proc.body, data); - return ret; -} + if (n->a2 && (ret = ast_visit_list(before, after, n->a2, d))) + return ret; -static int call_on_block(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - return call(node->_block.body, data); -} + if (n->a3 && (ret = ast_visit_list(before, after, n->a3, d))) + return ret; -static int call_on_fetch(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) -{ - int ret = 0; - ret |= call(node->_fetch.id, data); - ret |= call(node->_fetch.type, data); - return ret; -} + if (after && (ret = after(n, d))) + return ret; -static int call_on_macro_expand(int (*call)(struct ast_node *, - void *), struct ast_node *node, - void *data) -{ - int ret = 0; - ret |= call(node->_macro_expand.id, data); - ret |= call(node->_macro_expand.args, data); return ret; } -static int call_on_type_expand(int (*call)(struct ast_node *, - void *), - struct ast_node *type_expand, void *data) +int ast_visit_list(ast_callback_t before, ast_callback_t after, struct ast *l, void *d) { int ret = 0; - ret |= call(AST_TYPE_EXPAND(type_expand).id, data); - ret |= call(AST_TYPE_EXPAND(type_expand).args, data); + foreach_node(n, l) { + if ((ret = ast_visit(before, after, n, d))) + return ret; + } + return ret; } -/* I guess this works, but it's not exactly optimal as the caller sort of has to - * know when to continue to call on, and when it would cause an infinite loop. - * I.e. a call on an ID that is forwarded results in an infinite loop. - * - * Maybe add in something like ast_continue_call_on() that can check for the - * user if there's any point in continuing? - */ -int ast_call_on(int (*call)(struct ast_node *, - void *), struct ast_node *node, void *data) +int type_visit_list(type_callback_t before, type_callback_t after, struct type *l, void *d) { int ret = 0; - if (!node) - return ret; - - switch (node->node_type) { - case AST_ARR_ACCESS: - case AST_TYPE_EXPAND: ret = call_on_type_expand(call, node, data); - break; - case AST_FETCH: ret = call_on_fetch(call, node, data); break; - case AST_ASSIGN: ret = call_on_assign(call, node, data); break; - case AST_INIT: ret = call_on_init(call, node, data); break; - case AST_SIZEOF: ret = call_on_sizeof(call, node, data); break; - case AST_DOT: ret = call_on_dot(call, node, data); break; - case AST_AS: ret = call_on_as(call, node, data); break; - case AST_CAST: ret = call_on_cast(call, node, data); break; - case AST_DEFER: ret = call_on_defer(call, node, data); break; - case AST_VAR: ret = call_on_var(call, node, data); break; - case AST_FOR: ret = call_on_for(call, node, data); break; - case AST_WHILE: ret = call_on_while(call, node, data); break; - case AST_RETURN: ret = call_on_return(call, node, data); break; - case AST_ALIAS: ret = call_on_alias(call, node, data); break; - case AST_TRAIT: ret = call_on_trait(call, node, data); break; - case AST_IF: ret = call_on_if(call, node, data); break; - case AST_ENUM: ret = call_on_enum(call, node, data); break; - case AST_STRUCT: ret = call_on_struct(call, node, data); break; - case AST_VAL: ret = call_on_val(call, node, data); break; - case AST_SWITCH: ret = call_on_switch(call, node, data); break; - case AST_CASE: ret = call_on_case(call, node, data); break; - case AST_TYPE: ret = call_on_type(call, node, data); break; - case AST_GOTO: ret = call_on_goto(call, node, data); break; - case AST_LABEL: ret = call_on_label(call, node, data); break; - case AST_BINOP: ret = call_on_binop(call, node, data); break; - case AST_UNOP: ret = call_on_unop(call, node, data); break; - case AST_CALL: ret = call_on_call(call, node, data); break; - case AST_MACRO_CONSTRUCT: ret = - call_on_macro_construct(call, node, data); break; - case AST_MACRO_EXPAND: ret = call_on_macro_expand(call, node, data); - break; - case AST_PROC: ret = call_on_proc(call, node, data); break; - case AST_BLOCK: ret = call_on_block(call, node, data); break; - case AST_EMBED: break; - case AST_CTRL: break; - case AST_IMPORT: break; - case AST_CONST: break; - case AST_ID: break; - case AST_EMPTY: break; + foreach_type(n, l) { + if ((ret = type_visit(before, after, n, d))) + return ret; } - ret |= call(node->next, data); return ret; } -size_t ast_list_len(struct ast_node *node) +size_t ast_list_len(struct ast *node) { size_t count = 0; while (node) { count++; - node = node->next; + node = node->n; } return count; } -struct ast_node *ast_last_node(struct ast_node *list) +struct ast *ast_last(struct ast *list) { if (!list) return NULL; - while (list->next) - list = list->next; + while (list->n) + list = list->n; return list; } -struct ast_node *ast_block_last(struct ast_node *block) +struct ast *ast_block_last(struct ast *block) { - struct ast_node *b = ast_last_node(block); - if (b && b->node_type == AST_BLOCK) - return ast_block_last(b->_block.body); + struct ast *b = ast_last(block); + if (b && b->k == AST_BLOCK) + return ast_block_last(block_body(b)); return b; } -int same_id(struct ast_node *id1, struct ast_node *id2) +int same_id(char *id1, char *id2) { - assert(id1->node_type == AST_ID); - assert(id2->node_type == AST_ID); - return equiv_nodes(id1, id2); + return strcmp(id1, id2) == 0; } -int equiv_nodes(struct ast_node *n1, struct ast_node *n2) +int equiv_types(struct type *n1, struct type *n2) { - if (n1->node_type != n2->node_type) + if (n1 == n2) + return 1; + + if (n1 && !n2) return 0; - switch (n1->node_type) { - case AST_ID: - if (strcmp(AST_ID(n1).id, AST_ID(n2).id) != 0) - return 0; + if (!n1 && n2) + return 0; - break; - default: - internal_error("unimplemented equivalency"); + if (n1->k != n2->k) + return 0; + + if (n1->id && strcmp(n1->id, n2->id) != 0) + return 0; + + if (n1->t0 && !equiv_type_lists(n1->t0, n2->t0)) + return 0; + + if (n1->t1 && !equiv_type_lists(n1->t1, n2->t1)) return 0; - } return 1; } -int equiv_node_chains(struct ast_node *c1, struct ast_node *c2) +int equiv_nodes(struct ast *n1, struct ast *n2) { - if (c1 && !c2) + if (n1 && !n2) return 0; - if (!c1 && c2) + + if (!n1 && n2) return 0; - if (!c1 && !c2) + if (!n1 && !n2) return 1; + if (n1->k != n2->k) + return 0; + + if (n1->s && strcmp(n1->s, n2->s) != 0) + return 0; + + if (n1->a0 && !equiv_node_lists(n1->a0, n2->a0)) + return 0; + + if (n1->a1 && !equiv_node_lists(n1->a1, n2->a1)) + return 0; + + if (n1->a2 && !equiv_node_lists(n1->a2, n2->a2)) + return 0; + + if (n1->a3 && !equiv_node_lists(n1->a3, n2->a3)) + return 0; + + if (n1->t2 && !equiv_type_lists(n1->t2, n2->t2)) + return 0; + + return 1; +} + +int equiv_node_lists(struct ast *c1, struct ast *c2) +{ do { if (!equiv_nodes(c1, c2)) return 0; - c1 = c1->next; - c2 = c2->next; + c1 = c1->n; + c2 = c2->n; } while (c1 && c2); return 1; } + +int equiv_type_lists(struct type *t1, struct type *t2) +{ + do { + if (!equiv_types(t1, t2)) + return 0; + + t1 = t1->n; + t2 = t2->n; + + } while (t1 && t2); + + return 1; +} diff --git a/src/compiler.c b/src/compiler.c index b345e16..5aa6787 100644 --- a/src/compiler.c +++ b/src/compiler.c @@ -85,14 +85,14 @@ static int process(struct scope **parent, int public, const char *file) if (!p) return -1; parse(p, file, buf); - struct ast_node *tree = p->tree; + struct ast *tree = p->tree; bool failed = p->failed; destroy_parser(p); if (failed) return -1; - dump_ast(0, tree); + ast_dump_list(0, tree); struct scope *scope = create_scope(); if (!scope) @@ -167,19 +167,19 @@ int compile(const char *input) { struct scope *root = NULL; if (process_file(&root, 0, input)) { destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); error("compilation of %s stopped due to errors", input); return ret; } - if ((ret = lower_actuals(root))) { + if ((ret = lower(root))) { destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); error("compilation of %s stopped due to errors", input); return ret; } destroy_scope(root); - destroy_ast_nodes(); + destroy_allocs(); return 0; } diff --git a/src/debug.c b/src/debug.c index 2c873a2..f4b481d 100644 --- a/src/debug.c +++ b/src/debug.c @@ -120,7 +120,7 @@ void src_issue(struct src_issue issue, const char *err_msg, ...) va_end(args); } -void semantic_error(struct file_ctx fctx, struct ast_node *node, +void semantic_error(struct file_ctx fctx, struct ast *node, const char *fmt, ...) { va_list args; @@ -133,7 +133,20 @@ void semantic_error(struct file_ctx fctx, struct ast_node *node, va_end(args); } -void semantic_warn(struct file_ctx fctx, struct ast_node *node, const char *fmt, +void type_error(struct file_ctx fctx, struct type *node, + const char *fmt, ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_ERROR; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void semantic_warn(struct file_ctx fctx, struct ast *node, const char *fmt, ...) { va_list args; @@ -146,7 +159,20 @@ void semantic_warn(struct file_ctx fctx, struct ast_node *node, const char *fmt, va_end(args); } -void semantic_info(struct file_ctx fctx, struct ast_node *node, const char *fmt, +void semantic_info(struct file_ctx fctx, struct ast *node, const char *fmt, + ...) +{ + va_list args; + va_start(args, fmt); + struct src_issue issue; + issue.level = SRC_INFO; + issue.loc = node->loc; + issue.fctx = fctx; + _issue(issue, fmt, args); + va_end(args); +} + +void type_info(struct file_ctx fctx, struct type *node, const char *fmt, ...) { va_list args; @@ -185,117 +211,60 @@ void internal_warn(const char *fmt, ...) * @param fp File pointer to write string representation to. * @param type Type to generate string representation for. */ -static void _type_str(FILE *fp, struct ast_node *type) +static void _type_str(FILE *fp, struct type *type) { if (!type) return; - assert(type->node_type == AST_TYPE); - - switch (AST_TYPE(type).kind) { - case AST_TYPE_POINTER: + switch (type->k) { + case TYPE_PTR: fputc('*', fp); - _type_str(fp, AST_PTR_TYPE(type).base); + _type_str(fp, ptr_base(type)); break; - case AST_TYPE_ID: { - struct ast_node *id = AST_ID_TYPE(type).id; - fprintf(fp, "%s", AST_ID(id).id); + case TYPE_ID: { + fprintf(fp, "%s", type->id); break; } - case AST_TYPE_TRAIT: { - struct ast_node *def = AST_TRAIT_TYPE(type).def; - if (AST_TRAIT(def).id) { - struct ast_node *name = AST_TRAIT(def).id; - fprintf(fp, "%s ", AST_ID(name).id); + case TYPE_TRAIT: { + struct ast *def = type->d; + if (trait_id(def)) { + fprintf(fp, "%s ", trait_id(def)); } fprintf(fp, "(trait)"); break; } - case AST_TYPE_STRUCT: { - struct ast_node *def = AST_STRUCT_TYPE(type).def; - if (AST_STRUCT(def).id) { - struct ast_node *name = AST_STRUCT(def).id; - fprintf(fp, "%s ", AST_ID(name).id); + case TYPE_STRUCT: { + struct ast *def = type->d; + if (struct_id(def)) { + fprintf(fp, "%s ", struct_id(def)); } fprintf(fp, "(struct)"); - /** @todo print out anonymous structs with members? */ - break; - } - - case AST_TYPE_PRIMITIVE: { - fprintf(fp, "%s", primitive_str(AST_PRIMITIVE_TYPE(type).type)); break; } default: - fprintf(fp, "NOT YET IMPLEMENTED"); + if (is_primitive(type)) + fprintf(fp, "%s", primitive_str(type)); + else + fprintf(fp, "UNKNOWN TYPE"); } - - _type_str(fp, AST_TYPE(type).next); } -char *type_str(struct ast_node *node) +char *type_str(struct type *t) { /* maybe hacky? */ - if (!node) + if (!t) return strdup("void"); char *buf = NULL; size_t size = 0; + /* hehe */ FILE *memstream = open_memstream(&buf, &size); - /* TODO: improve trait detection */ - /* we were given a plain type, pass it directly along to _type_str */ - if (node->node_type == AST_TYPE) - _type_str(memstream, node); - else - /* otherwise, try to fish out the type of the node */ - _type_str(memstream, node->type); + _type_str(memstream, t); fclose(memstream); return buf; } - -/** - * Workhorse for call_str(). - * - * @param f File pointer to write string representation to. - * @param call Call to generate string representation for. - */ -static void _call_str(FILE *f, struct ast_node *call) -{ - struct ast_node *expr = AST_CALL(call).expr; - if (expr->node_type == AST_ID) { - const char *id_str = AST_ID(expr).id; - fprintf(f, "%s", id_str); - } - - struct ast_node *args = AST_CALL(call).args; - fprintf(f, "("); - - while (args) { - char *type = type_str(args); - fprintf(f, "%s", type); - free(type); - - args = args->next; - if (args) - fprintf(f, ", "); - else - break; - } - - fprintf(f, ")"); -} - -char *call_str(struct ast_node *call) -{ - assert(call->node_type == AST_CALL); - char *buf = NULL; size_t size = 0; - FILE *memstream = open_memstream(&buf, &size); - _call_str(memstream, call); - fclose(memstream); - return buf; -} diff --git a/src/lexer.l b/src/lexer.l index c819328..b38833d 100644 --- a/src/lexer.l +++ b/src/lexer.l @@ -84,36 +84,26 @@ STRING \"(\\.|[^"\\])*\" "%" {return REM;} "^" {return XOR;} -'[^'\\]' { - /* regular character constant, 'a' */ - yylval->integer = yytext[1]; - return INT; +"true" { + yylval->integer = 1; + return BOOL; } -'\\x[0-9a-fA-F]+' { - /* hex character constant, '\xef' */ - /* handling is slightly different from C, here it's more or less just - * another way to specify a hex integer */ - yylval->integer = strtoll(yytext + 3, NULL, 16); - return INT; -} - -'\\[0-8]+' { - /* octal character constant, '\033' */ - yylval->integer = strtoll(yytext + 2, NULL, 8); - return INT; +"false" { + yylval->integer = 0; + return BOOL; } -'\\b[01]+' { - /* binary character constant, '\b101' */ - yylval->integer = strtoll(yytext + 3, NULL, 2); - return INT; +'[^'\\]' { + /* regular character constant, 'a' */ + yylval->integer = yytext[1]; + return CHAR; } '\\.' { /* escaped character constant */ yylval->integer = match_escape(yytext[2]); - return INT; + return CHAR; } "?" {return QUESTION;} @@ -185,7 +175,7 @@ STRING \"(\\.|[^"\\])*\" } {ID} { - yylval->str = yytext; + yylval->str = strdup(yytext); return ID; } @@ -194,7 +184,7 @@ STRING \"(\\.|[^"\\])*\" char *s = yytext + strlen(yytext); s[-1] = '\0'; - yylval->str = yytext; + yylval->str = strdup(yytext); return APPLY; } diff --git a/src/lower.c b/src/lower.c index c78ac96..bf36fae 100644 --- a/src/lower.c +++ b/src/lower.c @@ -108,12 +108,17 @@ static bool retval_is_const(struct retval r) return r.kind == CONST_I9 || r.kind == CONST_I27; } -static bool is_i9(struct ast_node *n) +static bool is_small_type(struct type *type) { - if (AST_TYPE(n->type).kind != AST_TYPE_PRIMITIVE) - return false; + switch (type->k) { + case TYPE_I9: + case TYPE_BOOL: + return true; - return AST_PRIMITIVE_TYPE(n->type).type == AST_I9; + default: + } + + return false; } #define retval_create() \ @@ -157,26 +162,27 @@ char *build_str(const char *fmt, ...) { return buf; } -static size_t get_scope_number(struct ast_node *id) +static size_t get_scope_number(struct ast *id) { /** @todo this mirrors what's in actualize.c:actualize_id, same comments * apply */ - struct ast_node *def = file_scope_find_var(id->scope, id); + struct ast *def = file_scope_find_var(id->scope, id->s); if (def) return def->scope->number; - def = file_scope_find_proc(id->scope, id); + def = file_scope_find_proc(id->scope, id->s); if (def) return def->scope->number; return 0; } -static char *mangle_idx(struct ast_node *id, size_t idx) +static char *mangle_idx(struct ast *id, size_t idx) { - assert(id->node_type == AST_ID); assert(id->scope); - const char *name = AST_ID(id).id; + assert(id->s); + + const char *name = id->s; /* oh wait, I need to do a variable lookup on the ID, not use the ID's * scope number, duh */ size_t number = get_scope_number(id); @@ -187,89 +193,74 @@ static char *mangle_idx(struct ast_node *id, size_t idx) return build_str("%s_s%zif%zi", name, number, idx); } -static char *mangle(struct ast_node *id) +static char *mangle(struct ast *id) { return mangle_idx(id, 0); } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval); -static int lower_statement(struct lower_state *s, struct ast_node *n); +static int lower_statement(struct lower_state *s, struct ast *n); -static void output_id(struct ast_node *id) +static void output_ast_id(struct ast *id) { + assert(id->s); char *name = mangle(id); printf("%s", name); free(name); } -static int lower_global_var(struct ast_node *n) +static int lower_global_var(struct ast *n) { /* trivial types are reasonably easy, but stuff like compound types need * a lot of work */ - struct ast_node *type = AST_VAR(n).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + struct type *type = var_type(n); + if (is_primitive(type)) { semantic_error(n->scope->fctx, n, "only primitive globals currently implemented"); return -1; } - struct ast_node *id = AST_VAR(n).id; - struct ast_node *init = AST_VAR(n).init; - if (init->node_type != AST_CONST) { + struct ast *init = var_init(n); + if (init->k != AST_CONST_INT) { semantic_error(n->scope->fctx, n, - "constant expressions currently not implemented"); + "only constant expressions currently implemented"); return -1; } - output_id(id); + output_ast_id(n); printf(" = "); /* hmm, this might be useful elsewhere as well */ - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 %lli", AST_CONST(init).integer); break; - case AST_I9: printf("i9 %lli", AST_CONST(init).integer); break; - default: - semantic_error(n->scope->fctx, n, - "unhandled primitive type"); - return -1; - } - + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s %lli", t, int_val(init)); printf(";\n"); return 0; } -static int lower_param(struct lower_state *s, struct ast_node *p) +static int lower_param(struct lower_state *s, struct ast *p) { UNUSED(s); - assert(p->node_type == AST_VAR); - struct ast_node *type = AST_VAR(p).type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE) { + assert(p->k == AST_VAR_DEF); + struct type *type = var_type(p); + if (!is_primitive(type)) { semantic_error(p->scope->fctx, p, "only primitive params currently implemented"); return -1; } - assert(AST_VAR(p).init == NULL); - - switch (AST_PRIMITIVE_TYPE(type).type) { - case AST_I27: printf("i27 "); break; - case AST_I9: printf("i9 "); break; - default: - semantic_error(p->scope->fctx, p, - "unhandled primitive type"); - return -1; - } + assert(var_init(p) == NULL); - struct ast_node *id = AST_VAR(p).id; - output_id(id); + char *t = is_small_type(type) ? "i9" : "i27"; + printf("%s ", t); + output_ast_id(p); printf(","); return 0; } -static int lower_params(struct lower_state *s, struct ast_node *params) +static int lower_params(struct lower_state *s, struct ast *params) { - for (struct ast_node *p = params; p; p = p->next) { + foreach_node(p, params) { if (lower_param(s, p)) return -1; } @@ -277,20 +268,19 @@ static int lower_params(struct lower_state *s, struct ast_node *params) return 0; } -static int lower_var(struct lower_state *s, struct ast_node *v, +static int lower_var(struct lower_state *s, struct ast *v, struct vec *retval) { - assert(v->node_type == AST_VAR); + assert(v->k == AST_VAR_DEF); struct vec input = retval_create(); - if (lower_expr(s, AST_VAR(v).init, &input)) + if (lower_expr(s, var_init(v), &input)) return -1; - struct ast_node *id = AST_VAR(v).id; /* if we have a struct, we should add the member name to the base name * */ foreach_retval(ri, input) { struct retval r = retval_at(input, ri); - char *name = mangle_idx(id, ri); + char *name = mangle_idx(v, ri); /* I assume we're always dealing with i27 for now */ /** @todo qbt could maybe skip the type stuff except for casts */ printf("i27 %s = %s;\n", name, r.s); @@ -355,27 +345,24 @@ static void do_store(struct lower_state *s, struct vec *from, struct vec *to, } } -static int lower_cast(struct lower_state *s, struct ast_node *e, +static int lower_cast(struct lower_state *s, struct ast *e, struct vec *retval) { - assert(e->node_type == AST_CAST); + assert(e->k == AST_CAST); /** @todo make sure actualize removes casts that aren't of these types * */ - assert(AST_TYPE(e->type).kind == AST_TYPE_PRIMITIVE - || AST_TYPE(e->type).kind == AST_TYPE_POINTER); + assert(is_primitive(e->t)); - if (lower_expr(s, AST_CAST(e).expr, retval)) + if (lower_expr(s, cast_expr(e), retval)) return -1; - enum retval_kind kind = REG_I27; - if (is_i9(e)) - kind = REG_I9; + enum retval_kind kind = is_small_type(e->t) ? REG_I9 : REG_I27; foreach_retval(ri, *retval) { struct retval r = retval_at(*retval, ri); /* build new temporary cast result and replace the previous * retval */ - char *s = build_str("%s%s", "cast_", r.s); + char *s = build_str("%s%s", "cast", r.s); printf("%s %s = %s;\n", retval_kind_str(kind), s, r.s); free(r.s); @@ -388,49 +375,35 @@ static int lower_cast(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_const(struct lower_state *s, struct ast_node *c, +static int lower_const(struct lower_state *s, struct ast *c, struct vec *retval) { UNUSED(s); - assert(c->node_type == AST_CONST); - if (AST_CONST(c).kind == AST_CONST_STRING) { - /* requires pushing strings as variables and replacing them with - * references */ - semantic_error(c->scope->fctx, c, - "string constant lowering not yet implemented"); - return -1; - } - - enum retval_kind type = CONST_I27; - if (AST_PRIMITIVE_TYPE(c->type).type == AST_I9) - type = CONST_I9; - - char *str = build_str("%lli", (long long int)AST_CONST(c).integer); - struct retval r = build_retval(type, str); + assert(is_const(c)); + char *str = build_str("%lli", (long long)c->v); + enum retval_kind kind = is_small_type(c->t) ? CONST_I9: CONST_I27; + struct retval r = build_retval(kind, str); vec_append(retval, &r); return 0; } -static int lower_assign(struct lower_state *s, struct ast_node *a, +static int lower_assign(struct lower_state *s, struct ast *a, struct vec *retval) { -#define IS_DEREF(t) (t->node_type == AST_UNOP && AST_UNOP(t).op == AST_DEREF) -#define IS_ARR(t) (t->node_type == AST_ARR_ACCESS) - - assert(a->node_type == AST_ASSIGN); - if (lower_expr(s, AST_ASSIGN(a).from, retval)) + assert(a->k == AST_ASSIGN); + if (lower_expr(s, assign_from(a), retval)) return -1; struct vec loc = retval_create(); struct vec off = retval_create(); - struct ast_node *to = AST_ASSIGN(a).to; - struct ast_node *base = to; - if (IS_DEREF(to)) - base = AST_UNOP(to).expr; - else if (IS_ARR(to)) { - base = AST_ARR_ACCESS(to).base; - if (lower_expr(s, AST_ARR_ACCESS(to).idx, &off)) { + struct ast *to = assign_to(a); + struct ast *base = to; + if (to->k == AST_DEREF) + base = unop_expr(to); + else if (to->k == AST_ARR) { + base = arr_base(to); + if (lower_expr(s, arr_idx(to), &off)) { retval_destroy(&loc); retval_destroy(&off); return -1; @@ -443,10 +416,10 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, return -1; } - if (IS_DEREF(to)) { + if (to->k == AST_DEREF) { do_store(s, retval, &loc, NULL); } - else if (IS_ARR(to)) { + else if (to->k == AST_ARR) { do_store(s, retval, &loc, &off); } else { assert(vec_len(retval) == vec_len(&loc)); @@ -464,29 +437,25 @@ static int lower_assign(struct lower_state *s, struct ast_node *a, #undef IS_ARR } -static int lower_id(struct lower_state *s, struct ast_node *id, +static int lower_id(struct lower_state *s, struct ast *id, struct vec *retval) { UNUSED(s); - assert(id->node_type == AST_ID); + assert(id->k == AST_ID); char *m = mangle(id); - struct ast_node *type = id->type; - if (AST_TYPE(type).kind != AST_TYPE_PRIMITIVE - && AST_TYPE(type).kind != AST_TYPE_POINTER - && AST_TYPE(type).kind != AST_TYPE_SIGN) { + struct type *type = id->t; + if (!is_primitive(type) && type->k != TYPE_CALLABLE) { semantic_error(id->scope->fctx, id, "only primitive ids currently implemented"); return -1; } - enum retval_kind kind = REG_I27; - if (is_i9(id)) - kind = REG_I9; + enum retval_kind kind = is_small_type(type) ? REG_I9 : REG_I27; /* this likely isn't enough and we need to add the & to most things we * want to take the address of */ - if (AST_TYPE(type).kind == AST_TYPE_SIGN) { + if (type->k == TYPE_CALLABLE) { char *o = m; m = build_str("&%s", m); free(o); @@ -497,11 +466,12 @@ static int lower_id(struct lower_state *s, struct ast_node *id, return 0; } -static int lower_return(struct lower_state *s, struct ast_node *r, +static int lower_return(struct lower_state *s, struct ast *r, struct vec *retval) { - assert(r->node_type == AST_RETURN); - if (lower_expr(s, AST_RETURN(r).expr, retval)) + assert(r->k == AST_RETURN); + /** @todo defers, should they be handled here or in ast? */ + if (lower_expr(s, return_expr(r), retval)) return -1; printf("=> ( "); @@ -515,11 +485,11 @@ static int lower_return(struct lower_state *s, struct ast_node *r, return 0; } -static int lower_if(struct lower_state *s, struct ast_node *i, +static int lower_if(struct lower_state *s, struct ast *i, struct vec *retval) { - assert(i->node_type == AST_IF); - if (lower_expr(s, AST_IF(i).cond, retval)) + assert(i->k == AST_IF); + if (lower_expr(s, if_cond(i), retval)) return -1; assert(vec_len(retval) == 1); @@ -533,7 +503,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("! %s -> %s;\n", (retval_at(*retval, 0)).s, bottom); /* a block counts as a statement in this case */ - if (lower_statement(s, AST_IF(i).body)) { + if (lower_statement(s, if_body(i))) { free(bottom); return -1; } @@ -542,7 +512,7 @@ static int lower_if(struct lower_state *s, struct ast_node *i, printf("%s:\n", bottom); free(bottom); - if (AST_IF(i).els && lower_statement(s, AST_IF(i).els)) { + if (if_else(i) && lower_statement(s, if_else(i))) { free(out); return -1; } @@ -552,11 +522,11 @@ static int lower_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_for(struct lower_state *s, struct ast_node *f, +static int lower_for(struct lower_state *s, struct ast *f, struct vec *retval) { - assert(f->node_type == AST_FOR); - if (lower_statement(s, AST_FOR(f).pre)) + assert(f->k == AST_FOR); + if (lower_statement(s, for_pre(f))) return -1; long long uniq = s->uniq++; @@ -570,19 +540,19 @@ static int lower_for(struct lower_state *s, struct ast_node *f, printf("-> %s;\n", out); printf("%s:\n", top); - if (lower_statement(s, AST_FOR(f).body)) { + if (lower_statement(s, for_body(f))) { pop_loop(s); return -1; } printf("%s:\n", bottom); - if (lower_statement(s, AST_FOR(f).post)) { + if (lower_statement(s, for_post(f))) { pop_loop(s); return -1; } printf("%s:\n", out); - if (lower_expr(s, AST_FOR(f).cond, retval)) { + if (lower_expr(s, for_cond(f), retval)) { pop_loop(s); return -1; } @@ -594,7 +564,7 @@ static int lower_for(struct lower_state *s, struct ast_node *f, return 0; } -static int lower_expr_if(struct lower_state *s, struct ast_node *i, +static int lower_expr_if(struct lower_state *s, struct ast *i, struct vec *retval) { semantic_error(i->scope->fctx, i, @@ -602,19 +572,19 @@ static int lower_expr_if(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_binop(struct lower_state *s, struct ast_node *i, +static int lower_binop(struct lower_state *s, struct ast *i, struct vec *retval) { struct vec l = retval_create(); struct vec r = retval_create(); - if (lower_expr(s, AST_BINOP(i).left, &l)) { + if (lower_expr(s, binop_left(i), &l)) { retval_destroy(&l); retval_destroy(&r); return -1; } - if (lower_expr(s, AST_BINOP(i).right, &r)) { + if (lower_expr(s, binop_right(i), &r)) { retval_destroy(&l); retval_destroy(&r); return -1; @@ -623,12 +593,12 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, assert(vec_len(&l) == 1); assert(vec_len(&r) == 1); - char *name = build_str("tmp%lli", (long long)s->uniq++); + char *name = build_str("binop%lli", (long long)s->uniq++); struct retval ret = build_retval(REG_I27, name); vec_append(retval, &ret); char *op = ""; - switch (AST_BINOP(i).op) { + switch (i->k) { case AST_ADD: op = "+"; break; case AST_SUB: op = "-"; break; case AST_MUL: op = "*"; break; @@ -636,6 +606,50 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_REM: op = "%"; break; case AST_LSHIFT: op = "<<"; break; case AST_RSHIFT: op = ">>"; break; + default: semantic_error(i->scope->fctx, i, + "unimplemented binary operation"); + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + printf("i27 %s = %s %s %s;\n", name, + (retval_at(l, 0)).s, + op, + (retval_at(r, 0)).s); + + retval_destroy(&l); + retval_destroy(&r); + return 0; +} + +static int lower_comparison(struct lower_state *s, struct ast *i, struct vec *retval) +{ + /* very similar to lower_binop, hmm */ + struct vec l = retval_create(); + struct vec r = retval_create(); + + if (lower_expr(s, comparison_left(i), &l)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + if (lower_expr(s, comparison_right(i), &r)) { + retval_destroy(&l); + retval_destroy(&r); + return -1; + } + + assert(vec_len(&l) == 1); + assert(vec_len(&r) == 1); + + char *name = build_str("comp%lli", (long long)s->uniq++); + struct retval ret = build_retval(REG_I27, name); + vec_append(retval, &ret); + + char *op = ""; + switch (i->k) { case AST_LT: op = "<"; break; case AST_GT: op = ">"; break; case AST_LE: op = "<="; break; @@ -643,13 +657,13 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, case AST_NE: op = "!="; break; case AST_EQ: op = "=="; break; default: semantic_error(i->scope->fctx, i, - "unimplemented binary operation"); + "unimplemented comparison operation"); retval_destroy(&l); retval_destroy(&r); return -1; } - printf("i27 %s = %s %s %s;\n", name, + printf("i9 %s = %s %s %s;\n", name, (retval_at(l, 0)).s, op, (retval_at(r, 0)).s); @@ -659,20 +673,20 @@ static int lower_binop(struct lower_state *s, struct ast_node *i, return 0; } -static int lower_call(struct lower_state *s, struct ast_node *c, +static int lower_call(struct lower_state *s, struct ast *c, struct vec *retval) { - assert(c->node_type == AST_CALL); + assert(c->k == AST_CALL); struct vec call = retval_create(); - if (lower_expr(s, AST_CALL(c).expr, &call)) { + if (lower_expr(s, call_expr(c), &call)) { retval_destroy(&call); return -1; } /* collect all args */ struct vec args = retval_create(); - foreach_node(a, AST_CALL(c).args) { + foreach_node(a, call_args(c)) { struct vec arg = retval_create(); if (lower_expr(s, a, &arg)) { retval_destroy(&arg); @@ -699,37 +713,51 @@ static int lower_call(struct lower_state *s, struct ast_node *c, printf("%s, ", r.s); } -#define IS_VOID(t) \ - (t->node_type == AST_TYPE && AST_TYPE(t).kind == AST_TYPE_PRIMITIVE && \ - AST_PRIMITIVE_TYPE(t).type == AST_VOID) - - if (!IS_VOID(c->type)) { + if (!is_primitive(c->t) && c->t->k != TYPE_VOID) { semantic_error(c->scope->fctx, c, - "only void return type implemented"); + "only primitive return types implemented"); retval_destroy(&args); return -1; } - printf(") => ();\n"); + printf(") => ( "); + + int i = 0; + foreach_type(t, c->t) { + char *s = build_str("r%i\n", i); + enum retval_kind k = is_small_type(t) ? REG_I9 : REG_I27; + struct retval r = build_retval(k, s); + vec_append(retval, &r); + i++; + } + + printf(" );\n"); retval_destroy(&args); return 0; } -static int lower_expr(struct lower_state *s, struct ast_node *e, +static int lower_expr(struct lower_state *s, struct ast *e, struct vec *retval) { if (!e) return 0; - switch (e->node_type) { + if (is_const(e)) + return lower_const(s, e, retval); + + if (is_binop(e)) + return lower_binop(s, e, retval); + + if (is_comparison(e)) + return lower_comparison(s, e, retval); + + switch (e->k) { + case AST_VAR_DEF: return lower_var(s, e, retval); case AST_ID: return lower_id(s, e, retval); /* var is considered an expression in this case */ - case AST_VAR: return lower_var(s, e, retval); case AST_CAST: return lower_cast(s, e, retval); - case AST_CONST: return lower_const(s, e, retval); case AST_RETURN: return lower_return(s, e, retval); case AST_ASSIGN: return lower_assign(s, e, retval); - case AST_BINOP: return lower_binop(s, e, retval); case AST_CALL: return lower_call(s, e, retval); case AST_IF: return lower_expr_if(s, e, retval); default: @@ -741,25 +769,24 @@ static int lower_expr(struct lower_state *s, struct ast_node *e, return 0; } -static int lower_block(struct lower_state *s, struct ast_node *body) +static int lower_block(struct lower_state *s, struct ast *block) { - assert(body->node_type == AST_BLOCK); - assert(!ast_flags(body, AST_FLAG_DOEXPR)); - struct ast_node *stmt = AST_BLOCK(body).body; - for (; stmt; stmt = stmt->next) { - if (lower_statement(s, stmt)) + assert(block->k == AST_BLOCK); + assert(!ast_flags(block, AST_FLAG_DOEXPR)); + foreach_node(n, block_body(block)) { + if (lower_statement(s, n)) return -1; } return 0; } -static int lower_statement(struct lower_state *s, struct ast_node *n) +static int lower_statement(struct lower_state *s, struct ast *n) { struct vec retval = retval_create(); int ret = 0; - switch (n->node_type) { + switch (n->k) { case AST_RETURN: ret = lower_return(s, n, &retval); break; case AST_IF: ret = lower_if(s, n, &retval); break; case AST_FOR: ret = lower_for(s, n, &retval); break; @@ -771,24 +798,22 @@ static int lower_statement(struct lower_state *s, struct ast_node *n) return ret; } -static int lower_proc(struct ast_node *n) +static int lower_proc(struct ast *n) { - assert(n->node_type == AST_PROC); + assert(n->k == AST_PROC_DEF); /* nobody uses the proc, so no need to do anything */ - if (n->uses == 0 && !ast_flags(AST_PROC(n).id, AST_FLAG_NOMANGLE)) + if (n->uses == 0 && !ast_flags(n, AST_FLAG_NOMANGLE)) return 0; struct lower_state state = create_state(); /* name */ - struct ast_node *id = AST_PROC(n).id; - output_id(id); + output_ast_id(n); /* args */ printf("("); - struct ast_node *sign = AST_PROC(n).sign; - if (lower_params(&state, AST_SIGN_TYPE(sign).params)) { + if (lower_params(&state, proc_params(n))) { destroy_state(&state); return -1; } @@ -799,7 +824,7 @@ static int lower_proc(struct ast_node *n) /* body */ printf("{\n"); - if (lower_block(&state, AST_PROC(n).body)) { + if (lower_block(&state, proc_body(n))) { destroy_state(&state); return -1; } @@ -809,19 +834,13 @@ static int lower_proc(struct ast_node *n) return 0; } -static int lower_actual(struct ast_node *n) -{ - assert(AST_TYPE(n).kind == AST_TYPE_CONSTRUCT); - return 0; -} - -static int _lower_actuals(struct scope *root) +int lower(struct scope *root) { /* go through all child scopes but only do actual work on file-scope * includes are allowed inside procs etc to make something only locally * visible */ for (struct scope *c = root->children; c; c = c->next) { - if (_lower_actuals(c)) + if (lower(c)) return -1; } @@ -842,17 +861,3 @@ static int _lower_actuals(struct scope *root) return 0; } - -int lower_actuals(struct scope *root) -{ - int ret = _lower_actuals(root); - /* actuals are currently global, would it make more sense for them to be - * scope-local? */ - for (struct actual *a = root->actuals; a; a = a->next) { - assert(a->node); - if (lower_actual(a->node)) - return -1; - } - - return ret; -} diff --git a/src/parser.y b/src/parser.y index d6aefc8..51d950c 100644 --- a/src/parser.y +++ b/src/parser.y @@ -11,6 +11,8 @@ #include #include #include +#include + #include %} @@ -26,12 +28,15 @@ %parse-param {void *scanner} {struct parser* parser} %union { - struct ast_node *node; + struct ast *node; + struct type *type; long long integer; char *str; }; %token INT +%token CHAR +%token BOOL %token FLOAT %token STRING %token ID @@ -122,16 +127,18 @@ %left "::" /* why doesn't bison allow <*> for %nterm? would be so much easier */ -%nterm import binop unop decls expr +%nterm import binop unop decls opt_decls expr %nterm while do_while statement statements body references macro %nterm exprs if for case cases switch const -%nterm func_sign type var_decl var +%nterm var_decl var %nterm var_init proc %nterm alias trait enum_val enums enum top unit id %nterm embed param_decl members %nterm top_if const_if const_for defer goto assign %nterm construct construct_args construct_arg -%nterm statelet apply types +%nterm statelet apply + +%nterm types type opt_type %nterm tagged_struct expr_if @@ -140,14 +147,15 @@ %nterm macro_expand type_expand -%nterm type_params type_param opt_for_inits for_inits for_init +%nterm type_params opt_type_params type_param opt_for_inits for_inits for_init /* array stuff */ %nterm arr arr_inits arr_init /* optional stuff */ %nterm opt_exprs proc_decl member opt_members -%nterm opt_statements opt_types opt_type_params +%nterm opt_statements +%nterm opt_types opt_sign_decls sign_decls sign_decl sign_var_decl %{ @@ -209,7 +217,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer, * @param c Escape character without backslash. * @return Corresponding value. */ -static long long match_escape(char c); +static char match_escape(char c); /** * Similar to strdup() but skips quotation marks that would @@ -219,18 +227,18 @@ static long long match_escape(char c); * @param s String to clone, with quotation marks surrounding it. * @return Identical string but without quotation marks around it. */ -static const char *clone_string(const char *s); +static char *strip(const char *s); %} %start input; %% id - : ID {$$ = gen_id(strdup($1), src_loc(@$));} + : ID {$$ = gen_id($1, src_loc(@$));} apply : APPLY { - $$ = gen_id(strdup($1), src_loc(@$)); + $$ = gen_id($1, src_loc(@$)); } var @@ -238,10 +246,10 @@ var | var_init embed - : "embed" "(" STRING ")" { $$ = gen_embed(clone_string($3), src_loc(@$)); } + : "embed" "(" STRING ")" { $$ = gen_embed(strip($3), src_loc(@$)); } import - : "import" STRING { $$ = gen_import(clone_string($2), src_loc(@$)); } + : "import" STRING { $$ = gen_import(strip($2), src_loc(@$)); } assign : expr "=" expr { $$ = gen_assign($1, $3, src_loc(@$)); } @@ -265,12 +273,12 @@ binop | expr ">>=" expr { $$ = gen_binop(AST_ASSIGN_RSHIFT, $1, $3, src_loc(@$)); } - | expr "<" expr { $$ = gen_binop(AST_LT, $1, $3, src_loc(@$)); } - | expr ">" expr { $$ = gen_binop(AST_GT, $1, $3, src_loc(@$)); } - | expr "<=" expr { $$ = gen_binop(AST_LE, $1, $3, src_loc(@$)); } - | expr ">=" expr { $$ = gen_binop(AST_GE, $1, $3, src_loc(@$)); } - | expr "!=" expr { $$ = gen_binop(AST_NE, $1, $3, src_loc(@$)); } - | expr "==" expr { $$ = gen_binop(AST_EQ, $1, $3, src_loc(@$)); } + | expr "<" expr { $$ = gen_comparison(AST_LT, $1, $3, src_loc(@$)); } + | expr ">" expr { $$ = gen_comparison(AST_GT, $1, $3, src_loc(@$)); } + | expr "<=" expr { $$ = gen_comparison(AST_LE, $1, $3, src_loc(@$)); } + | expr ">=" expr { $$ = gen_comparison(AST_GE, $1, $3, src_loc(@$)); } + | expr "!=" expr { $$ = gen_comparison(AST_NE, $1, $3, src_loc(@$)); } + | expr "==" expr { $$ = gen_comparison(AST_EQ, $1, $3, src_loc(@$)); } unop : "-" expr { $$ = gen_unop(AST_NEG, $2, src_loc(@$)); } @@ -280,15 +288,17 @@ unop arr_init : "=>" const_expr "..." const_expr "=" expr { - $$ = gen_var($2, $4, $6, src_loc(@$)); + assert(0 && "range array init is unimplemented"); + abort(); } | "=>" const_expr "=" expr { - $$ = gen_var($2, NULL, $4, src_loc(@$)); + assert(0 && "range array init is unimplemented"); + abort(); } | expr arr_inits - : arr_init "," arr_inits { $$ = $1; $1->next = $3; } + : arr_init "," arr_inits { $$ = $1; $1->n = $3; } | arr_init arr @@ -298,10 +308,22 @@ param_decl : type { $$ = gen_var(NULL, $1, NULL, src_loc(@$)); } | var_decl +sign_decl + : type + | sign_var_decl + decls - : param_decl "," decls { $$ = $1; $1->next = $3; } + : param_decl "," decls { $$ = $1; $1->n = $3; } | param_decl +sign_decls + : sign_decl "," sign_decls { $$ = $1; $1->n = $3; } + | sign_decl + +opt_decls + : decls + | {$$ = NULL;} + defer : "defer" body { $$ = gen_defer($2, src_loc(@$)); } @@ -352,19 +374,20 @@ const_unop const_expr : "(" const_expr ")" { $$ = $2; } - | INT { $$ = gen_int($1, src_loc(@$)); } + | INT { $$ = gen_const_int($1, src_loc(@$)); } + | CHAR { $$ = gen_const_char($1, src_loc(@$)); } + | BOOL { $$ = gen_const_bool($1, src_loc(@$)); } | const_binop | const_unop | id /* TODO: concatenate multiple strings together? Or is that the lexer's job? */ expr - : expr "." id { $$ = gen_dot($1, $3, src_loc(@$)); } - | "..." id { $$ = $2; } - | INT { $$ = gen_int($1, src_loc(@$)); } - | STRING { - $$ = gen_string(clone_string($1), src_loc(@$)); - } + : expr "." ID { $$ = gen_dot($3, $1, src_loc(@$)); } + | INT { $$ = gen_const_int($1, src_loc(@$)); } + | CHAR { $$ = gen_const_char($1, src_loc(@$)); } + | BOOL { $$ = gen_const_bool($1, src_loc(@$)); } + | STRING {$$ = gen_const_str(strip($1), src_loc(@$));} | "(" expr ")" { $$ = $2; } /* special rule, user is allowed to define new variables in if * statements etc but it should stand out, which is why we require @@ -381,10 +404,10 @@ expr | "do" switch { $$ = $2; ast_set_flags($$, AST_FLAG_DOEXPR); } | "do" "const" switch { $$ = $3; ast_set_flags($$, AST_FLAG_DOEXPR); } | expr "(" opt_exprs ")" { $$ = gen_call($1, $3, src_loc(@$)); } - | expr "[" expr "]" { $$ = gen_arr_access($1, $3, src_loc(@$)); } + | expr "[" expr "]" { $$ = gen_arr($1, $3, src_loc(@$)); } | "sizeof" expr { $$ = gen_sizeof($2, src_loc(@$)); } | expr "as" type { $$ = gen_cast($1, $3, src_loc(@$)); } - | id "::" type { $$ = gen_fetch($1, $3, src_loc(@$)); } + | ID "::" type { $$ = gen_fetch($1, $3, src_loc(@$)); } | macro_expand | construct | assign @@ -404,13 +427,13 @@ do_while } goto - : "goto" id { $$ = gen_goto(gen_label($2, src_loc(@$)), src_loc(@$)); } + : "goto" ID { $$ = gen_goto($[ID], NULL, src_loc(@$)); } statelet - : "return" exprs { $$ = gen_return($2, src_loc(@$)); } - | "return" { $$ = gen_return(NULL, src_loc(@$)); } - | "break" { $$ = gen_ctrl(AST_CTRL_BREAK, src_loc(@$)); } - | "continue" { $$ = gen_ctrl(AST_CTRL_CONTINUE, src_loc(@$)); } + : "return" exprs { $$ = gen_return($2, NULL, src_loc(@$)); } + | "return" { $$ = gen_return(NULL, NULL, src_loc(@$)); } + | "break" { $$ = gen_break(NULL, src_loc(@$)); } + | "continue" { $$ = gen_continue(NULL, src_loc(@$)); } | trait | import | alias @@ -420,7 +443,7 @@ statelet | error { /* TODO: figure out how to destroy any and all possible ast nodes we * may have generated up until the error */ - $$ = gen_empty(); + $$ = gen_empty(src_loc(@$)); parser->failed = true; /* If we're failing to parse a statement in a block, continue by trying to * parse the next statement in the block */ @@ -444,11 +467,11 @@ statement | const | enum | macro - | ";" { $$ = gen_empty(); } - | id ":" { $$ = gen_label($1, src_loc(@$)); } + | ";" { $$ = gen_empty(src_loc(@$)); } + | ID ":" { $$ = gen_label($[ID], NULL, src_loc(@$)); } statements - : statement statements { $$ = $1; $1->next = $2; } + : statement statements { $$ = $1; $1->n = $2; } | statement | statelet @@ -457,49 +480,49 @@ opt_statements | {$$ = NULL;} body - : "{" opt_statements "}" { $$ = gen_block($2, src_loc(@$)); } + : "{" opt_statements "}" { $$ = gen_block($2, NULL, src_loc(@$)); } references - : id "," references { $$ = $1; $$->next = $3; } + : id "," references { $$ = $1; $$->n = $3; } | "..." id { $$ = $2; ast_set_flags($$, AST_FLAG_VARIADIC); } | id macro - : "define" id "(" references ")" body { - $$ = gen_macro_construct($2, $4, $6, src_loc(@$)); + : "define" ID "(" references ")" body { + $$ = gen_macro_def($[ID], $[references], $[body], src_loc(@$)); ast_set_flags($6, AST_FLAG_UNHYGIENIC); } - | "define" id "(" references "..." id ")" body { + | "define" ID "(" references "..." id ")" body { ast_append($4, $6); - $$ = gen_macro_construct($2, $4, $8, src_loc(@$)); + $$ = gen_macro_def($[ID], $4, $8, src_loc(@$)); ast_set_flags($$, AST_FLAG_VARIADIC); ast_set_flags($8, AST_FLAG_UNHYGIENIC); } - | "define" id "(" ")" body { - $$ = gen_macro_construct($2, NULL, $5, src_loc(@$)); + | "define" ID "(" ")" body { + $$ = gen_macro_def($[ID], NULL, $5, src_loc(@$)); ast_set_flags($5, AST_FLAG_UNHYGIENIC); } exprs - : expr "," exprs { $$ = $1; $1->next = $3; } + : expr "," exprs { $$ = $1; $1->n = $3; } | expr construct_arg - : "." id "=" expr { + : "." ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_MEMBER); } construct_args - : construct_arg "," construct_args { $$ = $1; $1->next = $3; } + : construct_arg "," construct_args { $$ = $1; $1->n = $3; } | construct_arg construct - : apply "{" construct_args "}" { + : APPLY "{" construct_args "}" { /** @todo add type info? */ $$ = gen_init($3, src_loc(@$)); } - | apply "[" opt_types "]" "{" construct_args "}" { + | APPLY "[" opt_types "]" "{" construct_args "}" { $$ = gen_init($6, src_loc(@$)); } @@ -521,7 +544,7 @@ for_init | var_init for_inits - : for_init "," for_inits { $$ = $1; $$->next = $3; } + : for_init "," for_inits { $$ = $1; $$->n = $3; } | for_init opt_for_inits @@ -543,7 +566,7 @@ case } cases - : case cases { $$ = $1; $1->next = $2; } + : case cases { $$ = $1; $1->n = $2; } | case switch @@ -578,41 +601,43 @@ const : "const" const_if { $$ = $2; ast_set_flags($$, AST_FLAG_CONST); } | "const" const_for { $$ = $2; ast_set_flags($$, AST_FLAG_CONST); } -func_sign - : "(" decls "=>" type ")" { - $$ = gen_type(AST_TYPE_SIGN, $2, $4, src_loc(@$)); - } - | "(" decls ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, src_loc(@$)); } - | "(" decls "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, $2, NULL, src_loc(@$)); } - | "(" "=>" type ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, $3, src_loc(@$)); } - | "(" "=>" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, src_loc(@$)); } - | "(" ")" { $$ = gen_type(AST_TYPE_SIGN, NULL, NULL, src_loc(@$)); } +opt_sign_decls + : sign_decls + | {$$ = NULL;} type - : id { $$ = gen_type(AST_TYPE_ID, $1, NULL, src_loc(@$)); } - | "^" func_sign { + : ID { $$ = tgen_id($1, src_loc(@$)); } + | "^" "(" opt_sign_decls "=>" opt_type ")" { /* still not entirely sold on this signature, but it's not terrible I * guess */ - $$ = gen_type(AST_TYPE_POINTER, $2, NULL, src_loc(@$)); + $$ = tgen_callable($[opt_sign_decls], $[opt_type], src_loc(@$)); + } + | "^" "(" opt_sign_decls ")" { + $$ = tgen_callable($[opt_sign_decls], NULL, src_loc(@$)); } | "*" type { - $$ = gen_type(AST_TYPE_POINTER, $2, NULL, src_loc(@$)); + $$ = tgen_ptr($2, src_loc(@$)); } | "[" const_expr "]" type { - $$ = gen_type(AST_TYPE_ARR, $2, $4, src_loc(@$)); + assert(0 && "arrays unimplemented"); + abort(); } | "const" type { $$ = $2; } | "mut" type { - $$ = $2; ast_set_flags($$, AST_FLAG_MUTABLE); + $$ = $2; /* ignored for now */ } - | apply "[" opt_types "]" { - $$ = gen_type(AST_TYPE_CONSTRUCT, $1, $3, src_loc(@$)); + | APPLY "[" opt_types "]" { + $$ = tgen_construct($[APPLY], $[opt_types], src_loc(@$)); } +opt_type + : type + | {$$ = NULL;} + types - : type "," types { $$ = $1; $$->next = $3; } + : type "," types { $$ = $1; $$->n = $3; } | type opt_types @@ -620,32 +645,56 @@ opt_types | { $$ = NULL; } type_expand - : apply "[" opt_types "]" { $$ = gen_type_expand($1, $3, src_loc(@$)); } + : APPLY "[" opt_types "]" { $$ = gen_type_expand($1, $3, src_loc(@$)); } var_decl - : type id { $$ = gen_var($2, $1, NULL, src_loc(@$)); } + : type ID { $$ = gen_var($2, $1, NULL, src_loc(@$)); } + +sign_var_decl + : type ID { $$ = $1; free((void *)$[ID]);} var_init - : var_decl "=" expr { $$ = $1; $$->_var.init = $3; } - | "const" id "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); } - | "mut" id "=" expr { + : var_decl "=" expr { $$ = $1; var_init($$) = $3; } + | "const" ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); } + | "mut" ID "=" expr { $$ = gen_var($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_MUTABLE); } proc_decl - : id func_sign { - $$ = gen_proc($1, $2, NULL, src_loc(@$)); + : ID "(" opt_decls "=>" opt_type ")" { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + NULL, + src_loc(@$)); + } + | ID "(" opt_decls ")" { + $$ = gen_proc($[ID], $[opt_decls], NULL, NULL, src_loc(@$)); } + proc - : id func_sign body { - $$ = gen_proc($1, $2, $3, src_loc(@$)); - ast_set_flags($$, $2->flags); - ast_set_flags($3, AST_FLAG_UNHYGIENIC); + : ID "(" opt_decls "=>" opt_type ")" body { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + $[body], + src_loc(@$)); + } + | ID "(" opt_decls ")" body { + $$ = gen_proc($[ID], $[opt_decls], NULL, $[body], src_loc(@$)); + } + | "extern" ID "(" opt_decls "=>" opt_type ")" { + $$ = gen_proc($[ID], + $[opt_decls], + $[opt_type], + NULL, + src_loc(@$)); + + ast_set_flags($$, AST_FLAG_EXTERN); } - | "extern" id func_sign { - /* todo check that we don't have a variadic function */ - $$ = gen_proc($2, $3, NULL, src_loc(@$)); + | "extern" ID "(" opt_decls ")" { + $$ = gen_proc($[ID], $[opt_decls], NULL, NULL, src_loc(@$)); ast_set_flags($$, AST_FLAG_EXTERN); } @@ -657,7 +706,7 @@ member ; members - : member members { $$ = $1; $1->next = $2; } + : member members { $$ = $1; $1->n = $2; } | member opt_members @@ -665,31 +714,31 @@ opt_members | {$$ = NULL;} macro_expand - : apply "(" opt_exprs ")" { + : APPLY "(" opt_exprs ")" { $$ = gen_macro_expand($1, $3, src_loc(@$)); } tagged_struct - : "typedef" id "[" opt_type_params "]" "{" opt_members "}" { + : "typedef" ID "[" opt_type_params "]" "{" opt_members "}" { $$ = gen_struct($2, $4, $7, src_loc(@$)); } - | "typedef" id "{" opt_members "}" { + | "typedef" ID "{" opt_members "}" { $$ = gen_struct($2, NULL, $4, src_loc(@$)); } alias - : "typedef" id type { + : "typedef" ID type { $$ = gen_alias($2, $3, src_loc(@$)); } type_param - : id id { - struct ast_node *t = gen_type(AST_TYPE_ID, $1, NULL, src_loc(@1)); + : ID ID { + struct type *t = tgen_id($1, src_loc(@1)); $$ = gen_var($2, t, NULL, src_loc(@$)); } type_params - : type_param "," type_params { $$ = $1; $1->next = $3; } + : type_param "," type_params { $$ = $1; $1->n = $3; } | type_param opt_type_params @@ -697,28 +746,28 @@ opt_type_params | { $$ = NULL; } trait - : "define" id "[" opt_type_params "]" "{" opt_members "}" { - $$ = gen_trait($2, $4, $7, NULL, src_loc(@$)); + : "define" ID "[" opt_type_params "]" "{" opt_members "}" { + $$ = gen_trait($2, $4, $7, src_loc(@$)); } enum_val - : id { + : ID { $$ = gen_val($1, NULL, src_loc(@$)); } - | id "=" expr { + | ID "=" expr { $$ = gen_val($1, $3, src_loc(@$)); } enums - : enum_val "," enums { $$ = $1; $1->next = $3; } + : enum_val "," enums { $$ = $1; $1->n = $3; } | enum_val "," { $$ = $1; } | enum_val { $$ = $1; } enum - : "enum" id ":" type "{" enums "}" { + : "enum" ID ":" type "{" enums "}" { $$ = gen_enum($2, $4, $6, src_loc(@$)); } - | "enum" id "{" enums "}" { + | "enum" ID "{" enums "}" { $$ = gen_enum($2, NULL, $4, src_loc(@$)); ast_set_flags($$, AST_FLAG_UNTYPED); } @@ -755,9 +804,9 @@ top | "pub" import { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" alias { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } | "pub" trait { $$ = $2; ast_set_flags($2, AST_FLAG_PUBLIC); } - | ";" { $$ = gen_empty(); } + | ";" { $$ = gen_empty(src_loc(@$)); } | error { - $$ = gen_empty(); + $$ = gen_empty(src_loc(@$)); parser->failed = true; /* ignore any content inside a top level thing and just move onto * the next one */ @@ -770,7 +819,7 @@ top unit : top { $$ = $1; } - | top unit { $$ = $1; $1->next = $2; } + | top unit { $$ = $1; $1->n = $2; } input : unit { parser->tree = $1; } @@ -832,7 +881,7 @@ static void yyerror(YYLTYPE *yylloc, void *lexer, src_issue(issue, msg); } -static long long match_escape(char c) +static char match_escape(char c) { switch (c) { case '\'': return '\''; @@ -849,13 +898,14 @@ static long long match_escape(char c) return c; } -static const char *clone_string(const char *str) +static char *strip(const char *str) { const size_t len = strlen(str) + 1; char *buf = malloc(len); if (!buf) { /* should probably try to handle the error in some way... */ internal_error("failed allocating buffer for string clone"); + free((void *)str); return NULL; } @@ -871,6 +921,7 @@ static const char *clone_string(const char *str) } buf[j] = 0; + free((void *)str); return buf; } diff --git a/src/scope.c b/src/scope.c index 205e858..13c3dce 100644 --- a/src/scope.c +++ b/src/scope.c @@ -74,14 +74,13 @@ void scope_set_flags(struct scope *scope, enum scope_flags flags) scope->flags |= flags; } -int scope_flags(struct scope *scope, enum scope_flags flags) +unsigned scope_flags(struct scope *scope, enum scope_flags flags) { assert(scope); return scope->flags & flags; } -static struct visible *create_visible(struct ast_node *id, - struct ast_node *node) +static struct visible *create_visible(char *id, struct ast *node) { struct visible *visible = calloc(1, sizeof(struct visible)); visible->id = id; @@ -89,8 +88,7 @@ static struct visible *create_visible(struct ast_node *id, return visible; } -struct visible *create_type(struct scope *scope, struct ast_node *id, - struct ast_node *type) +struct visible *create_type(struct scope *scope, char *id, struct ast *type) { struct visible *n = create_visible(id, type); if (!n) @@ -102,8 +100,7 @@ struct visible *create_type(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_var(struct scope *scope, struct ast_node *id, - struct ast_node *var) +struct visible *create_var(struct scope *scope, char *id, struct ast *var) { struct visible *n = create_visible(id, var); if (!n) @@ -115,8 +112,7 @@ struct visible *create_var(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_macro(struct scope *scope, struct ast_node *id, - struct ast_node *macro) +struct visible *create_macro(struct scope *scope, char *id, struct ast *macro) { struct visible *n = create_visible(id, macro); if (!n) @@ -128,8 +124,7 @@ struct visible *create_macro(struct scope *scope, struct ast_node *id, return n; } -struct visible *create_proc(struct scope *scope, struct ast_node *id, - struct ast_node *proc) +struct visible *create_proc(struct scope *scope, char *id, struct ast *proc) { struct visible *n = create_visible(id, proc); if (!n) @@ -141,16 +136,16 @@ struct visible *create_proc(struct scope *scope, struct ast_node *id, return n; } -int scope_add_var(struct scope *scope, struct ast_node *var) +int scope_add_var(struct scope *scope, struct ast *var) { - struct ast_node *exists = file_scope_find_var(scope, AST_VAR(var).id); + struct ast *exists = file_scope_find_var(scope, var_id(var)); if (exists) { semantic_error(scope->fctx, var, "var redefined"); semantic_info(scope->fctx, exists, "previously here"); return -1; } - create_var(scope, AST_VAR(var).id, var); + create_var(scope, var_id(var), var); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(var, AST_FLAG_PUBLIC)) return scope_add_var(scope->parent, var); @@ -158,10 +153,9 @@ int scope_add_var(struct scope *scope, struct ast_node *var) return 0; } -int scope_add_type(struct scope *scope, struct ast_node *id, - struct ast_node *type) +int scope_add_type(struct scope *scope, char *id, struct ast *type) { - struct ast_node *exists = file_scope_find_type(scope, id); + struct ast *exists = file_scope_find_type(scope, id); if (exists) { semantic_error(scope->fctx, type, "type redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -176,12 +170,10 @@ int scope_add_type(struct scope *scope, struct ast_node *id, return 0; } -int scope_add_macro(struct scope *scope, struct ast_node *macro) +int scope_add_macro(struct scope *scope, struct ast *macro) { - assert(macro->node_type == AST_MACRO_CONSTRUCT); - struct ast_node *exists = file_scope_find_macro(scope, - AST_MACRO_CONSTRUCT( - macro).id); + assert(macro->k == AST_MACRO_DEF); + struct ast *exists = file_scope_find_macro(scope, macro_def_id(macro)); if (exists) { semantic_error(scope->fctx, macro, "macro redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -189,7 +181,7 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro) } /* always add to scope, do resolve checking later */ - create_macro(scope, AST_MACRO_CONSTRUCT(macro).id, macro); + create_macro(scope, macro_def_id(macro), macro); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(macro, AST_FLAG_PUBLIC)) return scope_add_macro(scope->parent, macro); @@ -197,11 +189,10 @@ int scope_add_macro(struct scope *scope, struct ast_node *macro) return 0; } -int scope_add_proc(struct scope *scope, struct ast_node *proc) +int scope_add_proc(struct scope *scope, struct ast *proc) { - assert(proc->node_type == AST_PROC); - struct ast_node *exists = - file_scope_find_proc(scope, AST_PROC(proc).id); + assert(proc->k == AST_PROC_DEF); + struct ast *exists = file_scope_find_proc(scope, proc_id(proc)); if (exists) { semantic_error(scope->fctx, proc, "proc redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -209,7 +200,7 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) } /* always add to scope, do resolve checking later */ - create_proc(scope, AST_PROC(proc).id, proc); + create_proc(scope, proc_id(proc), proc); if (scope->parent && scope_flags(scope, SCOPE_FILE) && ast_flags(proc, AST_FLAG_PUBLIC)) return scope_add_proc(scope->parent, proc); @@ -217,12 +208,12 @@ int scope_add_proc(struct scope *scope, struct ast_node *proc) return 0; } -int scope_add_trait(struct scope *scope, struct ast_node *trait) +int scope_add_trait(struct scope *scope, struct ast *trait) { - assert(trait->node_type == AST_TRAIT); + assert(trait->k == AST_TRAIT_DEF); - struct ast_node *id = AST_TRAIT(trait).id; - struct ast_node *exists = file_scope_find_type(scope, id); + char *id = trait_id(trait); + struct ast *exists = file_scope_find_type(scope, id); if (exists) { semantic_error(scope->fctx, trait, "type redefined"); semantic_info(scope->fctx, exists, "previously here"); @@ -237,105 +228,96 @@ int scope_add_trait(struct scope *scope, struct ast_node *trait) return 0; } -static struct ast_node *scope_find_visible(struct visible *v, - struct ast_node *id) +static struct ast *scope_find_visible(struct visible *v, char *id) { if (!v) return NULL; - while (v) { - if (same_id(v->id, id)) - return v->node; - - v = v->next; + foreach_visible(n, v) { + struct ast *node = n->node; + if (same_id(node->s, id)) + return node; } return NULL; } -struct ast_node *scope_find_type(struct scope *scope, struct ast_node *type) +struct ast *scope_find_type(struct scope *scope, char *id) { - return scope_find_visible(scope->types, type); + return scope_find_visible(scope->types, id); } -struct ast_node *file_scope_find_type(struct scope *scope, - struct ast_node *type) +struct ast *file_scope_find_type(struct scope *scope, char *id) { - assert(type->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_type(scope, type); + struct ast *found = scope_find_type(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_type(scope->parent, type); + return file_scope_find_type(scope->parent, id); return NULL; } -struct ast_node *scope_find_macro(struct scope *scope, struct ast_node *macro) +struct ast *scope_find_macro(struct scope *scope, char *id) { - return scope_find_visible(scope->macros, macro); + return scope_find_visible(scope->macros, id); } -struct ast_node *file_scope_find_macro(struct scope *scope, - struct ast_node *macro) +struct ast *file_scope_find_macro(struct scope *scope, char *id) { - assert(macro->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_macro(scope, macro); + struct ast *found = scope_find_macro(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_macro(scope->parent, macro); + return file_scope_find_macro(scope->parent, id); return NULL; } -struct ast_node *scope_find_proc(struct scope *scope, struct ast_node *proc) +struct ast *scope_find_proc(struct scope *scope, char *id) { - return scope_find_visible(scope->procs, proc); + return scope_find_visible(scope->procs, id); } -struct ast_node *file_scope_find_proc(struct scope *scope, - struct ast_node *proc) +struct ast *file_scope_find_proc(struct scope *scope, char *id) { - assert(proc->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_proc(scope, proc); + struct ast *found = scope_find_proc(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_proc(scope->parent, proc); + return file_scope_find_proc(scope->parent, id); return NULL; } -struct ast_node *scope_find_var(struct scope *scope, struct ast_node *var) +struct ast *scope_find_var(struct scope *scope, char *id) { - return scope_find_visible(scope->vars, var); + return scope_find_visible(scope->vars, id); } -struct ast_node *file_scope_find_var(struct scope *scope, struct ast_node *var) +struct ast *file_scope_find_var(struct scope *scope, char *id) { - assert(var->node_type == AST_ID); if (!scope) return NULL; - struct ast_node *found = scope_find_var(scope, var); + struct ast *found = scope_find_var(scope, id); if (found) return found; if (!scope_flags(scope, SCOPE_FILE)) - return file_scope_find_var(scope->parent, var); + return file_scope_find_var(scope->parent, id); return NULL; } @@ -355,7 +337,7 @@ void scope_add_scope(struct scope *parent, struct scope *child) parent->children = child; } -static int add_actual(struct actual *actuals, struct ast_node *node) +static int add_actual(struct actual *actuals, struct ast *node) { if (!actuals->node) { /* fill empty first element */ @@ -374,7 +356,7 @@ static int add_actual(struct actual *actuals, struct ast_node *node) return 0; } -int scope_add_actual(struct scope *scope, struct ast_node *node) +int scope_add_actual(struct scope *scope, struct ast *node) { return add_actual(scope->actuals, node); } diff --git a/tests/if2.ek b/tests/if2.ek index b41b389..7589571 100644 --- a/tests/if2.ek +++ b/tests/if2.ek @@ -11,17 +11,17 @@ main() { if 0 { for i27 i = 0; i < 5; i = i + 1 { - putchar('0' + i); + putchar('0' + i as i9); } } else if 1 { for i27 i = 0; i < 5; i = i + 1 { - putchar('A' + i); + putchar('A' + i as i9); } } else { for i27 i = 0; i < 5; i = i + 1 { - putchar('K' + i); + putchar('K' + i as i9); } } diff --git a/tests/loop.ek b/tests/loop.ek index 301e7ea..265d09f 100644 --- a/tests/loop.ek +++ b/tests/loop.ek @@ -1,5 +1,6 @@ typedef i9 {} typedef i27 {} +typedef bool {} putchar(i9 c) { @@ -10,6 +11,6 @@ putchar(i9 c) main() { for i27 i = 0; i < 5; i = i + 1 { - putchar('A' + i); + putchar('A' + (i as i9)); } } -- cgit v1.3