aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/ast.c121
-rw-r--r--src/ast.h9
-rw-r--r--src/compile/binops.c2
-rw-r--r--src/compile/files.c41
-rw-r--r--src/compile/reductions.c2
-rw-r--r--src/compile/statements.c49
-rw-r--r--src/environment.c6
-rw-r--r--src/formatter/formatter.c13
-rw-r--r--src/parse/expressions.c2
-rw-r--r--src/typecheck.c4
10 files changed, 104 insertions, 145 deletions
diff --git a/src/ast.c b/src/ast.c
index 5c55930e..37c5a514 100644
--- a/src/ast.c
+++ b/src/ast.c
@@ -36,83 +36,46 @@ const int op_tightness[NUM_AST_TAGS] = {
[Xor] = 1,
};
-static Text_t quoted_text(const char *text) { return Text$quoted(Text$from_str(text), false, Text("\"")); }
-
-CONSTFUNC const char *binop_method_name(ast_e tag) {
- switch (tag) {
- case Power:
- case PowerUpdate: return "power";
- case Multiply:
- case MultiplyUpdate: return "times";
- case Divide:
- case DivideUpdate: return "divided_by";
- case Mod:
- case ModUpdate: return "modulo";
- case Mod1:
- case Mod1Update: return "modulo1";
- case Plus:
- case PlusUpdate: return "plus";
- case Minus:
- case MinusUpdate: return "minus";
- case Concat:
- case ConcatUpdate: return "concatenated_with";
- case LeftShift:
- case LeftShiftUpdate: return "left_shifted";
- case RightShift:
- case RightShiftUpdate: return "right_shifted";
- case UnsignedLeftShift:
- case UnsignedLeftShiftUpdate: return "unsigned_left_shifted";
- case UnsignedRightShift:
- case UnsignedRightShiftUpdate: return "unsigned_right_shifted";
- case And:
- case AndUpdate: return "bit_and";
- case Or:
- case OrUpdate: return "bit_or";
- case Xor:
- case XorUpdate: return "bit_xor";
- default: return NULL;
- }
-}
-
-CONSTFUNC const char *binop_operator(ast_e tag) {
- switch (tag) {
- case Power: return "^";
- case PowerUpdate: return "^=";
- case Concat: return "++";
- case ConcatUpdate: return "++=";
- case Multiply: return "*";
- case MultiplyUpdate: return "*=";
- case Divide: return "/";
- case DivideUpdate: return "/=";
- case Mod: return "mod";
- case ModUpdate: return "mod=";
- case Mod1: return "mod1";
- case Mod1Update: return "mod1=";
- case Plus: return "+";
- case PlusUpdate: return "+=";
- case Minus: return "-";
- case MinusUpdate: return "-=";
- case LeftShift: return "<<";
- case LeftShiftUpdate: return "<<=";
- case RightShift: return ">>";
- case RightShiftUpdate: return ">>=";
- case And: return "and";
- case AndUpdate: return "and=";
- case Or: return "or";
- case OrUpdate: return "or=";
- case Xor: return "xor";
- case XorUpdate: return "xor=";
- case Equals: return "==";
- case NotEquals: return "!=";
- case LessThan: return "<";
- case LessThanOrEquals: return "<=";
- case GreaterThan: return ">";
- case GreaterThanOrEquals: return ">=";
- case Min: return "_min_";
- case Max: return "_max_";
- default: return NULL;
- }
-}
+const binop_info_t binop_info[NUM_AST_TAGS] = {
+ [Power] = {"power", "^"},
+ [PowerUpdate] = {"power", "^="},
+ [Multiply] = {"times", "*"},
+ [MultiplyUpdate] = {"times", "*="},
+ [Divide] = {"divided_by", "/"},
+ [DivideUpdate] = {"divided_by", "/="},
+ [Mod] = {"modulo", "mod"},
+ [ModUpdate] = {"modulo", "mod="},
+ [Mod1] = {"modulo1", "mod1"},
+ [Mod1Update] = {"modulo1", "mod1="},
+ [Plus] = {"plus", "+"},
+ [PlusUpdate] = {"plus", "+="},
+ [Minus] = {"minus", "-"},
+ [MinusUpdate] = {"minus", "-="},
+ [Concat] = {"concatenated_with", "++"},
+ [ConcatUpdate] = {"concatenated_with", "++="},
+ [LeftShift] = {"left_shifted", "<<"},
+ [LeftShiftUpdate] = {"left_shifted", "<<="},
+ [RightShift] = {"right_shifted", ">>"},
+ [RightShiftUpdate] = {"right_shifted", ">>="},
+ [UnsignedLeftShift] = {"unsigned_left_shifted", NULL},
+ [UnsignedLeftShiftUpdate] = {"unsigned_left_shifted", NULL},
+ [UnsignedRightShift] = {"unsigned_right_shifted", NULL},
+ [UnsignedRightShiftUpdate] = {"unsigned_right_shifted", NULL},
+ [And] = {"bit_and", "and"},
+ [AndUpdate] = {"bit_and", "and="},
+ [Or] = {"bit_or", "or"},
+ [OrUpdate] = {"bit_or", "or="},
+ [Xor] = {"bit_xor", "xor"},
+ [XorUpdate] = {"bit_xor", "xor="},
+ [Equals] = {NULL, "=="},
+ [NotEquals] = {NULL, "!="},
+ [LessThan] = {NULL, "<"},
+ [LessThanOrEquals] = {NULL, "<="},
+ [GreaterThan] = {NULL, ">"},
+ [GreaterThanOrEquals] = {NULL, ">="},
+ [Min] = {NULL, "_min_"},
+ [Max] = {NULL, "_max_"},
+};
static Text_t ast_list_to_sexp(ast_list_t *asts);
static Text_t arg_list_to_sexp(arg_ast_t *args);
@@ -122,6 +85,8 @@ static Text_t tags_to_sexp(tag_ast_t *tags);
static Text_t optional_sexp(const char *tag, ast_t *ast);
static Text_t optional_type_sexp(const char *tag, type_ast_t *ast);
+static Text_t quoted_text(const char *text) { return Text$quoted(Text$from_str(text), false, Text("\"")); }
+
Text_t ast_list_to_sexp(ast_list_t *asts) {
Text_t c = EMPTY_TEXT;
for (; asts; asts = asts->next) {
@@ -291,7 +256,7 @@ Text_t ast_to_sexp(ast_t *ast) {
")");
T(When, "(When ", ast_to_sexp(data.subject), when_clauses_to_sexp(data.clauses),
optional_sexp("else", data.else_body), ")");
- T(Reduction, "(Reduction ", quoted_text(binop_operator(data.op)), " ", ast_to_sexp(data.key), " ",
+ T(Reduction, "(Reduction ", quoted_text(binop_info[data.op].operator), " ", ast_to_sexp(data.key), " ",
ast_to_sexp(data.iter), ")");
T(Skip, "(Skip ", quoted_text(data.target), ")");
T(Stop, "(Stop ", quoted_text(data.target), ")");
diff --git a/src/ast.h b/src/ast.h
index 02c5ad74..7396555e 100644
--- a/src/ast.h
+++ b/src/ast.h
@@ -481,6 +481,13 @@ struct ast_s {
extern const int op_tightness[NUM_AST_TAGS];
+typedef struct {
+ const char *method_name;
+ const char *operator;
+} binop_info_t;
+
+extern const binop_info_t binop_info[NUM_AST_TAGS];
+
OptionalText_t ast_source(ast_t *ast);
Text_t ast_to_sexp(ast_t *ast);
@@ -490,7 +497,5 @@ Text_t type_ast_to_sexp(type_ast_t *ast);
PUREFUNC bool is_idempotent(ast_t *ast);
void visit_topologically(ast_list_t *ast, Closure_t fn);
CONSTFUNC bool is_update_assignment(ast_t *ast);
-CONSTFUNC const char *binop_method_name(ast_e tag);
-CONSTFUNC const char *binop_operator(ast_e tag);
CONSTFUNC ast_e binop_tag(ast_e tag);
CONSTFUNC bool is_binary_operation(ast_t *ast);
diff --git a/src/compile/binops.c b/src/compile/binops.c
index 87fd2c7a..ed4aaeba 100644
--- a/src/compile/binops.c
+++ b/src/compile/binops.c
@@ -67,7 +67,7 @@ Text_t compile_binary_op(env_t *env, ast_t *ast) {
}
}
} else if ((ast->tag == Divide || ast->tag == Mod || ast->tag == Mod1) && is_numeric_type(rhs_t)) {
- b = get_namespace_binding(env, binop.lhs, binop_method_name(ast->tag));
+ b = get_namespace_binding(env, binop.lhs, binop_info[ast->tag].method_name);
if (b && b->type->tag == FunctionType) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, lhs_t)) {
diff --git a/src/compile/files.c b/src/compile/files.c
index c250e6cc..3e91976b 100644
--- a/src/compile/files.c
+++ b/src/compile/files.c
@@ -12,7 +12,16 @@
#include "../types.h"
#include "compilation.h"
-static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
+static void initialize_vars_and_statics(env_t *env, ast_t *ast);
+static void initialize_namespace(env_t *env, const char *name, ast_t *namespace);
+static Text_t compile_top_level_code(env_t *env, ast_t *ast);
+static Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace);
+
+void initialize_namespace(env_t *env, const char *name, ast_t *namespace) {
+ initialize_vars_and_statics(namespace_env(env, name), namespace);
+}
+
+void initialize_vars_and_statics(env_t *env, ast_t *ast) {
if (!ast) return;
for (ast_list_t *stmt = Match(ast, Block)->statements; stmt; stmt = stmt->next) {
@@ -27,24 +36,20 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
if (t->tag == FunctionType) t = Type(ClosureType, t);
Text_t val_code = compile_declared_value(env, stmt->ast);
if ((decl->value && !is_constant(env, decl->value)) || (!decl->value && has_heap_memory(t))) {
- Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$$initialized"));
+ Text_t initialized_name = namespace_name(env, env->namespace, Texts(decl_name, "$initialized"));
env->code->variable_initializers =
Texts(env->code->variable_initializers,
with_source_info(env, stmt->ast,
Texts(full_name, " = ", val_code, ",\n", initialized_name, " = true;\n")));
}
} else if (stmt->ast->tag == StructDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, StructDef)->name),
- Match(stmt->ast, StructDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, StructDef)->name, Match(stmt->ast, StructDef)->namespace);
} else if (stmt->ast->tag == EnumDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, EnumDef)->name),
- Match(stmt->ast, EnumDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, EnumDef)->name, Match(stmt->ast, EnumDef)->namespace);
} else if (stmt->ast->tag == LangDef) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, LangDef)->name),
- Match(stmt->ast, LangDef)->namespace);
+ initialize_namespace(env, Match(stmt->ast, LangDef)->name, Match(stmt->ast, LangDef)->namespace);
} else if (stmt->ast->tag == Extend) {
- initialize_vars_and_statics(namespace_env(env, Match(stmt->ast, Extend)->name),
- Match(stmt->ast, Extend)->body);
+ initialize_namespace(env, Match(stmt->ast, Extend)->name, Match(stmt->ast, Extend)->body);
} else if (stmt->ast->tag == Use) {
continue;
} else {
@@ -54,7 +59,12 @@ static void initialize_vars_and_statics(env_t *env, ast_t *ast) {
}
}
-static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
+Text_t compile_namespace(env_t *env, const char *name, ast_t *namespace) {
+ env_t *ns_env = namespace_env(env, name);
+ return namespace ? compile_top_level_code(ns_env, namespace) : EMPTY_TEXT;
+}
+
+Text_t compile_top_level_code(env_t *env, ast_t *ast) {
if (!ast) return EMPTY_TEXT;
switch (ast->tag) {
@@ -111,15 +121,13 @@ static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
type_t *t = Table$str_get(*env->types, def->name);
assert(t && t->tag == StructType);
Text_t code = compile_struct_typeinfo(env, t, def->name, def->fields, def->secret, def->opaque);
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case EnumDef: {
DeclareMatch(def, ast, EnumDef);
Text_t code = compile_enum_typeinfo(env, ast);
code = Texts(code, compile_enum_constructors(env, ast));
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case LangDef: {
DeclareMatch(def, ast, LangDef);
@@ -127,8 +135,7 @@ static Text_t compile_top_level_code(env_t *env, ast_t *ast) {
Texts("public const TypeInfo_t ", namespace_name(env, env->namespace, Texts(def->name, "$$info")), " = {",
(int64_t)sizeof(Text_t), ", ", (int64_t)__alignof__(Text_t),
", .metamethods=Text$metamethods, .tag=TextInfo, .TextInfo={", quoted_str(def->name), "}};\n");
- env_t *ns_env = namespace_env(env, def->name);
- return Texts(code, def->namespace ? compile_top_level_code(ns_env, def->namespace) : EMPTY_TEXT);
+ return Texts(code, compile_namespace(env, def->name, def->namespace));
}
case Extend: {
DeclareMatch(extend, ast, Extend);
diff --git a/src/compile/reductions.c b/src/compile/reductions.c
index 1652384c..438e072b 100644
--- a/src/compile/reductions.c
+++ b/src/compile/reductions.c
@@ -12,7 +12,7 @@ public
Text_t compile_reduction(env_t *env, ast_t *ast) {
DeclareMatch(reduction, ast, Reduction);
ast_e op = reduction->op;
- const char *op_str = binop_operator(op);
+ const char *op_str = binop_info[op].operator;
type_t *iter_t = get_type(env, reduction->iter);
type_t *item_t = get_iterated_type(iter_t);
diff --git a/src/compile/statements.c b/src/compile/statements.c
index 4e37838c..bde9ae36 100644
--- a/src/compile/statements.c
+++ b/src/compile/statements.c
@@ -25,6 +25,14 @@ Text_t with_source_info(env_t *env, ast_t *ast, Text_t code) {
return Texts("\n#line ", line, "\n", code);
}
+static Text_t compile_simple_update_assignment(env_t *env, ast_t *ast, const char *op) {
+ binary_operands_t update = BINARY_OPERANDS(ast);
+ type_t *lhs_t = get_type(env, update.lhs);
+ if (is_idempotent(update.lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
+ return Texts(compile_lvalue(env, update.lhs), " ", op, "= ", compile_to_type(env, update.rhs, lhs_t), ";");
+ return compile_update_assignment(env, ast);
+}
+
static Text_t _compile_statement(env_t *env, ast_t *ast) {
switch (ast->tag) {
case When: return compile_when_statement(env, ast);
@@ -47,41 +55,12 @@ static Text_t _compile_statement(env_t *env, ast_t *ast) {
}
}
case Assign: return compile_assignment_statement(env, ast);
- case PlusUpdate: {
- DeclareMatch(update, ast, PlusUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " += ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case MinusUpdate: {
- DeclareMatch(update, ast, MinusUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " -= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case MultiplyUpdate: {
- DeclareMatch(update, ast, MultiplyUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " *= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case DivideUpdate: {
- DeclareMatch(update, ast, DivideUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " /= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
- case ModUpdate: {
- DeclareMatch(update, ast, ModUpdate);
- type_t *lhs_t = get_type(env, update->lhs);
- if (is_idempotent(update->lhs) && (lhs_t->tag == IntType || lhs_t->tag == NumType || lhs_t->tag == ByteType))
- return Texts(compile_lvalue(env, update->lhs), " %= ", compile_to_type(env, update->rhs, lhs_t), ";");
- return compile_update_assignment(env, ast);
- }
+ case PlusUpdate: return compile_simple_update_assignment(env, ast, "+");
+ case MinusUpdate: return compile_simple_update_assignment(env, ast, "-");
+ case MultiplyUpdate: return compile_simple_update_assignment(env, ast, "*");
+ case DivideUpdate: return compile_simple_update_assignment(env, ast, "/");
+ case ModUpdate: return compile_simple_update_assignment(env, ast, "%");
+
case PowerUpdate:
case Mod1Update:
case ConcatUpdate:
diff --git a/src/environment.c b/src/environment.c
index 5efedfbe..7ac54a7a 100644
--- a/src/environment.c
+++ b/src/environment.c
@@ -73,7 +73,9 @@ env_t *global_env(bool source_mapping) {
} ns_entry_t;
#define MAKE_TYPE(name, type, type_name, type_info, ...) \
- {name, type, type_name, type_info, TypedList(ns_entry_t, __VA_ARGS__)}
+ { \
+ name, type, type_name, type_info, TypedList(ns_entry_t, __VA_ARGS__) \
+ }
struct {
const char *name;
type_t *type;
@@ -736,7 +738,7 @@ PUREFUNC binding_t *get_constructor(env_t *env, type_t *t, arg_ast_t *args, bool
}
PUREFUNC binding_t *get_metamethod_binding(env_t *env, ast_e tag, ast_t *lhs, ast_t *rhs, type_t *ret) {
- const char *method_name = binop_method_name(tag);
+ const char *method_name = binop_info[tag].method_name;
if (!method_name) return NULL;
binding_t *b = get_namespace_binding(env, lhs, method_name);
if (!b || b->type->tag != FunctionType) return NULL;
diff --git a/src/formatter/formatter.c b/src/formatter/formatter.c
index 60714945..b68b3874 100644
--- a/src/formatter/formatter.c
+++ b/src/formatter/formatter.c
@@ -323,7 +323,8 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
if (reduction->key) {
return Texts("(", fmt_inline(reduction->key, comments), ": ", fmt_inline(reduction->iter, comments));
} else {
- return Texts("(", binop_operator(reduction->op), ": ", fmt_inline(reduction->iter, comments));
+ return Texts("(", Text$from_str(binop_info[reduction->op].operator), ": ",
+ fmt_inline(reduction->iter, comments));
}
}
/*inline*/ case None:
@@ -353,7 +354,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
}
/*inline*/ case BINOP_CASES: {
binary_operands_t operands = BINARY_OPERANDS(ast);
- const char *op = binop_operator(ast->tag);
+ const char *op = binop_info[ast->tag].operator;
Text_t lhs = fmt_inline(operands.lhs, comments);
Text_t rhs = fmt_inline(operands.rhs, comments);
@@ -368,7 +369,7 @@ OptionalText_t format_inline_code(ast_t *ast, Table_t comments) {
rhs = parenthesize(rhs, EMPTY_TEXT);
Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" ");
- return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs);
+ return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs);
}
/*inline*/ case Deserialize: {
DeclareMatch(deserialize, ast, Deserialize);
@@ -760,7 +761,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
return Texts("(", fmt(reduction->key, comments, Texts(indent, single_indent)), ": ",
fmt(reduction->iter, comments, Texts(indent, single_indent)));
} else {
- return Texts("(", binop_operator(reduction->op), ": ",
+ return Texts("(", binop_info[reduction->op].operator, ": ",
fmt(reduction->iter, comments, Texts(indent, single_indent)));
}
}
@@ -808,7 +809,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
/*multiline*/ case BINOP_CASES: {
if (inlined_fits) return inlined;
binary_operands_t operands = BINARY_OPERANDS(ast);
- const char *op = binop_operator(ast->tag);
+ const char *op = binop_info[ast->tag].operator;
Text_t lhs = fmt(operands.lhs, comments, indent);
Text_t rhs = fmt(operands.rhs, comments, indent);
@@ -822,7 +823,7 @@ Text_t format_code(ast_t *ast, Table_t comments, Text_t indent) {
rhs = parenthesize(rhs, indent);
Text_t space = op_tightness[ast->tag] >= op_tightness[Multiply] ? EMPTY_TEXT : Text(" ");
- return Texts(lhs, space, Text$from_str(binop_operator(ast->tag)), space, rhs);
+ return Texts(lhs, space, Text$from_str(binop_info[ast->tag].operator), space, rhs);
}
/*multiline*/ case Deserialize: {
if (inlined_fits) return inlined;
diff --git a/src/parse/expressions.c b/src/parse/expressions.c
index 32133fe9..df0a10a7 100644
--- a/src/parse/expressions.c
+++ b/src/parse/expressions.c
@@ -49,7 +49,7 @@ ast_t *parse_reduction(parse_ctx_t *ctx, const char *pos) {
ast_e op = match_binary_operator(&pos);
if (op == Unknown) return NULL;
- const char *op_str = binop_operator(op);
+ const char *op_str = binop_info[op].operator;
assert(op_str);
ast_t *key = NewAST(ctx->file, pos, pos, Var, .name = op_str);
for (bool progress = true; progress;) {
diff --git a/src/typecheck.c b/src/typecheck.c
index 2d805cdb..e34a85de 100644
--- a/src/typecheck.c
+++ b/src/typecheck.c
@@ -1356,7 +1356,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
}
}
} else if ((ast->tag == Divide || ast->tag == Mod || ast->tag == Mod1) && is_numeric_type(rhs_t)) {
- binding_t *b = get_namespace_binding(env, binop.lhs, binop_method_name(ast->tag));
+ binding_t *b = get_namespace_binding(env, binop.lhs, binop_info[ast->tag].method_name);
if (b && b->type->tag == FunctionType) {
DeclareMatch(fn, b->type, FunctionType);
if (type_eq(fn->ret, lhs_t)) {
@@ -1415,7 +1415,7 @@ type_t *get_type(env_t *env, ast_t *ast) {
code_err(reduction->iter, "I don't know how to do a reduction over ", type_to_str(iter_t), " values");
if (reduction->key && !(reduction->op == Min || reduction->op == Max)) {
env_t *item_scope = fresh_scope(env);
- const char *op_str = binop_operator(reduction->op);
+ const char *op_str = binop_info[reduction->op].operator;
set_binding(item_scope, op_str, iterated, EMPTY_TEXT);
iterated = get_type(item_scope, reduction->key);
}