diff options
| author | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 18:18:59 -0400 |
|---|---|---|
| committer | Bruce Hill <bruce@bruce-hill.com> | 2025-08-24 18:18:59 -0400 |
| commit | 7e5e03f191c4379bf7e34cccfa044ec928189eed (patch) | |
| tree | 3c6773e0838850fbce2086ea9021903b8a7b57b6 | |
| parent | 6cc848bfde0cc6bd702019f81c1467e409f35487 (diff) | |
Split reductions into their own file
| -rw-r--r-- | src/compile/expressions.c | 149 | ||||
| -rw-r--r-- | src/compile/reductions.c | 159 | ||||
| -rw-r--r-- | src/compile/reductions.h | 8 |
3 files changed, 169 insertions, 147 deletions
diff --git a/src/compile/expressions.c b/src/compile/expressions.c index 7c9779e6..50963fe7 100644 --- a/src/compile/expressions.c +++ b/src/compile/expressions.c @@ -20,6 +20,7 @@ #include "optionals.h" #include "pointers.h" #include "promotions.h" +#include "reductions.h" #include "sets.h" #include "statements.h" #include "structs.h" @@ -251,153 +252,7 @@ Text_t compile(env_t *env, ast_t *ast) { } case When: return compile_when_statement(env, ast); case If: return compile_if_expression(env, ast); - case Reduction: { - DeclareMatch(reduction, ast, Reduction); - ast_e op = reduction->op; - - type_t *iter_t = get_type(env, reduction->iter); - type_t *item_t = get_iterated_type(iter_t); - if (!item_t) - code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); - - static int64_t next_id = 1; - ast_t *item = FakeAST(Var, String("$it", next_id++)); - ast_t *body = LiteralCode(Text("{}")); // placeholder - ast_t *loop = FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = body); - env_t *body_scope = for_scope(env, loop); - if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan - || op == GreaterThanOrEquals) { - // Chained comparisons like ==, <, etc. - type_t *item_value_type = item_t; - ast_t *item_value = item; - if (reduction->key) { - set_binding(body_scope, "$", item_t, compile(body_scope, item)); - item_value = reduction->key; - item_value_type = get_type(body_scope, reduction->key); - } - - Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_value_type, Text("prev")), - ";\n" - "OptionalBool_t result = NONE_BOOL;\n"); - - ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, - .__data.Plus.lhs = LiteralCode(Text("prev"), .type = item_value_type), - .__data.Plus.rhs = item_value); - body->__data.InlineCCode.chunks = new ( - ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (result == NONE_BOOL) {\n" - " prev = ", - compile(body_scope, item_value), - ";\n" - " result = yes;\n" - "} else {\n" - " if (", - compile(body_scope, comparison), ") {\n", - " prev = ", compile(body_scope, item_value), ";\n", - " } else {\n" - " result = no;\n", - " break;\n", " }\n", "}\n"))); - code = Texts(code, compile_statement(env, loop), "\nresult;})"); - return code; - } else if (op == Min || op == Max) { - // Min/max: - Text_t superlative = op == Min ? Text("min") : Text("max"); - Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_t, superlative), - ";\n" - "Bool_t has_value = no;\n"); - - Text_t item_code = compile(body_scope, item); - ast_e cmp_op = op == Min ? LessThan : GreaterThan; - if (reduction->key) { - env_t *key_scope = fresh_scope(env); - set_binding(key_scope, "$", item_t, item_code); - type_t *key_type = get_type(key_scope, reduction->key); - Text_t superlative_key = op == Min ? Text("min_key") : Text("max_key"); - code = Texts(code, compile_declaration(key_type, superlative_key), ";\n"); - - ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, - .__data.Plus.lhs = LiteralCode(Text("key"), .type = key_type), - .__data.Plus.rhs = LiteralCode(superlative_key, .type = key_type)); - - body->__data.InlineCCode.chunks = new ( - ast_list_t, .ast = FakeAST(TextLiteral, Texts(compile_declaration(key_type, Text("key")), " = ", - compile(key_scope, reduction->key), ";\n", - "if (!has_value || ", compile(body_scope, comparison), - ") {\n" - " ", - superlative, " = ", compile(body_scope, item), - ";\n" - " ", - superlative_key, - " = key;\n" - " has_value = yes;\n" - "}\n"))); - } else { - ast_t *comparison = - new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, - .__data.Plus.lhs = item, .__data.Plus.rhs = LiteralCode(superlative, .type = item_t)); - body->__data.InlineCCode.chunks = new ( - ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (!has_value || ", compile(body_scope, comparison), - ") {\n" - " ", - superlative, " = ", compile(body_scope, item), - ";\n" - " has_value = yes;\n" - "}\n"))); - } - - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(item_t, superlative), - " : ", compile_none(item_t), ";})"); - return code; - } else { - // Accumulator-style reductions like +, ++, *, etc. - type_t *reduction_type = Match(get_type(env, ast), OptionalType)->type; - ast_t *item_value = item; - if (reduction->key) { - set_binding(body_scope, "$", item_t, compile(body_scope, item)); - item_value = reduction->key; - } - - Text_t code = Texts("({ // Reduction:\n", compile_declaration(reduction_type, Text("reduction")), - ";\n" - "Bool_t has_value = no;\n"); - - // For the special case of (or)/(and), we need to early out if we - // can: - Text_t early_out = EMPTY_TEXT; - if (op == Compare) { - if (reduction_type->tag != IntType || Match(reduction_type, IntType)->bits != TYPE_IBITS32) - code_err(ast, "<> reductions are only supported for Int32 " - "values"); - } else if (op == And) { - if (reduction_type->tag == BoolType) early_out = Text("if (!reduction) break;"); - else if (reduction_type->tag == OptionalType) - early_out = Texts("if (", check_none(reduction_type, Text("reduction")), ") break;"); - } else if (op == Or) { - if (reduction_type->tag == BoolType) early_out = Text("if (reduction) break;"); - else if (reduction_type->tag == OptionalType) - early_out = Texts("if (!", check_none(reduction_type, Text("reduction")), ") break;"); - } - - ast_t *combination = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, - .__data.Plus.lhs = LiteralCode(Text("reduction"), .type = reduction_type), - .__data.Plus.rhs = item_value); - body->__data.InlineCCode.chunks = - new (ast_list_t, - .ast = FakeAST(TextLiteral, Texts("if (!has_value) {\n" - " reduction = ", - compile(body_scope, item_value), - ";\n" - " has_value = yes;\n" - "} else {\n" - " reduction = ", - compile(body_scope, combination), ";\n", early_out, "}\n"))); - - code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", - promote_to_optional(reduction_type, Text("reduction")), " : ", compile_none(reduction_type), - ";})"); - return code; - } - } + case Reduction: return compile_reduction(env, ast); case FieldAccess: { DeclareMatch(f, ast, FieldAccess); type_t *fielded_t = get_type(env, f->fielded); diff --git a/src/compile/reductions.c b/src/compile/reductions.c new file mode 100644 index 00000000..163b083e --- /dev/null +++ b/src/compile/reductions.c @@ -0,0 +1,159 @@ +// This file defines how to compile reductions like `(+: nums)` + +#include "../ast.h" +#include "../config.h" +#include "../environment.h" +#include "../stdlib/text.h" +#include "../stdlib/util.h" +#include "../typecheck.h" +#include "declarations.h" +#include "expressions.h" +#include "optionals.h" +#include "statements.h" + +Text_t compile_reduction(env_t *env, ast_t *ast) { + DeclareMatch(reduction, ast, Reduction); + ast_e op = reduction->op; + + type_t *iter_t = get_type(env, reduction->iter); + type_t *item_t = get_iterated_type(iter_t); + if (!item_t) + code_err(reduction->iter, "I couldn't figure out how to iterate over this type: ", type_to_str(iter_t)); + + static int64_t next_id = 1; + ast_t *item = FakeAST(Var, String("$it", next_id++)); + ast_t *body = LiteralCode(Text("{}")); // placeholder + ast_t *loop = FakeAST(For, .vars = new (ast_list_t, .ast = item), .iter = reduction->iter, .body = body); + env_t *body_scope = for_scope(env, loop); + if (op == Equals || op == NotEquals || op == LessThan || op == LessThanOrEquals || op == GreaterThan + || op == GreaterThanOrEquals) { + // Chained comparisons like ==, <, etc. + type_t *item_value_type = item_t; + ast_t *item_value = item; + if (reduction->key) { + set_binding(body_scope, "$", item_t, compile(body_scope, item)); + item_value = reduction->key; + item_value_type = get_type(body_scope, reduction->key); + } + + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_value_type, Text("prev")), + ";\n" + "OptionalBool_t result = NONE_BOOL;\n"); + + ast_t *comparison = + new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("prev"), .type = item_value_type), .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (result == NONE_BOOL) {\n" + " prev = ", + compile(body_scope, item_value), + ";\n" + " result = yes;\n" + "} else {\n" + " if (", + compile(body_scope, comparison), ") {\n", + " prev = ", compile(body_scope, item_value), ";\n", + " } else {\n" + " result = no;\n", + " break;\n", " }\n", "}\n"))); + code = Texts(code, compile_statement(env, loop), "\nresult;})"); + return code; + } else if (op == Min || op == Max) { + // Min/max: + Text_t superlative = op == Min ? Text("min") : Text("max"); + Text_t code = Texts("({ // Reduction:\n", compile_declaration(item_t, superlative), + ";\n" + "Bool_t has_value = no;\n"); + + Text_t item_code = compile(body_scope, item); + ast_e cmp_op = op == Min ? LessThan : GreaterThan; + if (reduction->key) { + env_t *key_scope = fresh_scope(env); + set_binding(key_scope, "$", item_t, item_code); + type_t *key_type = get_type(key_scope, reduction->key); + Text_t superlative_key = op == Min ? Text("min_key") : Text("max_key"); + code = Texts(code, compile_declaration(key_type, superlative_key), ";\n"); + + ast_t *comparison = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = LiteralCode(Text("key"), .type = key_type), + .__data.Plus.rhs = LiteralCode(superlative_key, .type = key_type)); + + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts(compile_declaration(key_type, Text("key")), " = ", + compile(key_scope, reduction->key), ";\n", + "if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " ", + superlative_key, + " = key;\n" + " has_value = yes;\n" + "}\n"))); + } else { + ast_t *comparison = + new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = cmp_op, + .__data.Plus.lhs = item, .__data.Plus.rhs = LiteralCode(superlative, .type = item_t)); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (!has_value || ", compile(body_scope, comparison), + ") {\n" + " ", + superlative, " = ", compile(body_scope, item), + ";\n" + " has_value = yes;\n" + "}\n"))); + } + + code = Texts(code, compile_statement(env, loop), "\nhas_value ? ", promote_to_optional(item_t, superlative), + " : ", compile_none(item_t), ";})"); + return code; + } else { + // Accumulator-style reductions like +, ++, *, etc. + type_t *reduction_type = Match(get_type(env, ast), OptionalType)->type; + ast_t *item_value = item; + if (reduction->key) { + set_binding(body_scope, "$", item_t, compile(body_scope, item)); + item_value = reduction->key; + } + + Text_t code = Texts("({ // Reduction:\n", compile_declaration(reduction_type, Text("reduction")), + ";\n" + "Bool_t has_value = no;\n"); + + // For the special case of (or)/(and), we need to early out if we + // can: + Text_t early_out = EMPTY_TEXT; + if (op == Compare) { + if (reduction_type->tag != IntType || Match(reduction_type, IntType)->bits != TYPE_IBITS32) + code_err(ast, "<> reductions are only supported for Int32 " + "values"); + } else if (op == And) { + if (reduction_type->tag == BoolType) early_out = Text("if (!reduction) break;"); + else if (reduction_type->tag == OptionalType) + early_out = Texts("if (", check_none(reduction_type, Text("reduction")), ") break;"); + } else if (op == Or) { + if (reduction_type->tag == BoolType) early_out = Text("if (reduction) break;"); + else if (reduction_type->tag == OptionalType) + early_out = Texts("if (!", check_none(reduction_type, Text("reduction")), ") break;"); + } + + ast_t *combination = new (ast_t, .file = ast->file, .start = ast->start, .end = ast->end, .tag = op, + .__data.Plus.lhs = LiteralCode(Text("reduction"), .type = reduction_type), + .__data.Plus.rhs = item_value); + body->__data.InlineCCode.chunks = new ( + ast_list_t, .ast = FakeAST(TextLiteral, Texts("if (!has_value) {\n" + " reduction = ", + compile(body_scope, item_value), + ";\n" + " has_value = yes;\n" + "} else {\n" + " reduction = ", + compile(body_scope, combination), ";\n", early_out, "}\n"))); + + code = + Texts(code, compile_statement(env, loop), "\nhas_value ? ", + promote_to_optional(reduction_type, Text("reduction")), " : ", compile_none(reduction_type), ";})"); + return code; + } +} diff --git a/src/compile/reductions.h b/src/compile/reductions.h new file mode 100644 index 00000000..5c76c74f --- /dev/null +++ b/src/compile/reductions.h @@ -0,0 +1,8 @@ +// This file defines how to compile reductions like `(+: nums)` +#pragma once + +#include "../ast.h" +#include "../environment.h" +#include "../stdlib/datatypes.h" + +Text_t compile_reduction(env_t *env, ast_t *ast); |
