Metaprogramming =============== See also "compile_time_execution.txt". Use cases: * Centralizing definitions. E.g. an error message might have: - an identifier - a severity level - a message * "Functions" that can implicitly use variables or return from the outer function. * "Functions" that create variables or statements * "Functions" that create types or enum values * etc. Solutions: * Pre-processor * Typed pre-processor * Code generation * Code verification (write the code manually, but have an external tool to verify it) * Compile-time execution * Compile-time verification (write the code manually, but have the compiler verify it) * Dynamic dispatch / runtime interpretation (this might actually be faster than code generation due to better I-cache utilization!) * ...? Desirable properties: * Should be type safe * It would be nice if it can run in a loop to generate repeated code. Syntax idea 1: func print_operator(arena Output out, ref Operator op) { switch op.kind { %each %Operator %{ case %(Operator.enum_ident): print_string(%(Operator.name)) %if %(Operator.arity == 2) %{ print_expr(operator.operand1) print_expr(operator.operand2) %} %else %{ print_expr(operator.operand1) %} %} } } Syntax idea 2: func print_operator(arena Output out, ref Operator op) { switch op.kind { case %(Operator.enum_ident): print_string(%(Operator.name)) %if %(Operator.arity == 2) %{ print_expr(operator.operand1) print_expr(operator.operand2) %} %else %{ print_expr(operator.operand1) %} } } Syntax idea 3: func print_operator(arena Output out, ref Operator op) { switch op.kind { case %enum_ident: print_string(%OPERATOR_NAME(%enum_ident)) %if OPERATOR_ARITY(%enum_ident) == 2 %{ print_expr(operator.operand1) print_expr(operator.operand2) %} %else %{ print_expr(operator.operand1) %} } } Syntax idea 4 (dynamic). Best solution? type OpInfo = struct { string name int arity } data OpInfo[OperatorKind] opinfos = [ .plus = ("plus", 2) .minus = ("minus", 2) ... ] func print_operator(arena Output out, ref Operator op) { OpInfo opinfo = opinfo[op.kind] print_string(opinfo.name) if opinfo.arity == 2 { print_expr(operator.operand1) print_expr(operator.operand2) } else { print_expr(operator.operand1) } }