Skip to content

Commit

Permalink
Add experimental tuple functionality
Browse files Browse the repository at this point in the history
  • Loading branch information
Maginor committed Feb 13, 2025
1 parent 65b90d3 commit 7b593cd
Show file tree
Hide file tree
Showing 10 changed files with 255 additions and 41 deletions.
20 changes: 10 additions & 10 deletions dev_notes/todo.txt
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@
We need an all_in_flux and all_out_flux or similar.
For instance, SimplyP could be broken if it is used with soil discharge along a connection since it uses out_flux (and it can't know about the connections).

If an input series is provided indexed over "subcatchment", but should be indexed over "water body" and "subcatchment" is a union member of "water body", that should be made to work. (and all similar cases) Just re-map the index.

If you load different modules using the same load name, you don't get a name clash!
This is as intended if the two loads are identical (so that you can extend two models that load the same module)
Expand Down Expand Up @@ -67,9 +66,6 @@
Lots of duplication between the different airsea things in general.
The FPV versions should be able to reuse some of what is in the plain versions without code duplication.

The combining of phytoplankton module for NIVAFjord and EasyLake does not work that well in the Vansjø setup
Due to having made certain assumptions about N and C balance of these. Too much POC and PON from phyto.

NIVAFjord

Vertical wind mixing using B-V. See page 45 in report 4.
Expand All @@ -94,11 +90,6 @@
Sulfate reduction?
Zooplankton?
Experiment with different phytoplankton formulation.

EasyChem

Shading effect on phyto light availability (light should be more like an average in the epilimnion taking into account attenuation).
But that would also be problematic since it is quite uneven but the resulting phyto conc isn't.

Connection system

Expand All @@ -117,6 +108,9 @@
graph connections

Regex
Maybe just remove this feature since it is too complex for the value we get out of it.
Just keep the necessary parts like checking what compartments are allowed and no cycles in case of no_cycles.

Finish regex check in the general case (cycles case)

Regex checking of fjord_horizontal regex is bugged for nivafjord_moss
Expand Down Expand Up @@ -180,6 +174,8 @@


*** Intermediate-pri ***

If an input series is provided indexed over "subcatchment", but should be indexed over "water body" and "subcatchment" is a union member of "water body", that should be made to work. (and all similar cases) Just re-map the index.

Store name of expected model file in data sets (?)
Done now as a comment, for documentation purposes only.
Expand Down Expand Up @@ -244,7 +240,11 @@
Multiple return values from functions (tuples). Needed if we want to implement MAGIC entirely in Mobius2 code for instance.
Only annoying thing is that we then have to introduce 'tuple(N)' to the type system and check against it everywhere it is not allowed.
A possible solution is to force it to be unpacked immediately at the call location of the function. I.e. if a function returns a tuple, you can only call it using something like
a, b := fun(),
a; b := fun(),
which de-sugars internally to
t := fun()
a := unpack(t, 0)
b := unpack(t, 1)

Position_Map
There can be problems if the max depth doesn't exactly match a boundary of two widths.
Expand Down
2 changes: 1 addition & 1 deletion models/example_data/NIVAFjord/glacier_lake_example.dat
Original file line number Diff line number Diff line change
Expand Up @@ -522,7 +522,7 @@ data_set {
par_group("Light") {

par_real("Diffuse attenuation coefficent (clear water)")
[ 0.15 ]
[ 0.4 ]

par_real("Shading factor")
[ 0.003 ]
Expand Down
19 changes: 19 additions & 0 deletions src/ast.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -706,6 +706,25 @@ parse_math_block(Token_Stream *stream) {
stream->expect_token(',');
local_var->exprs.push_back(expr);
block->exprs.push_back(local_var);
} else if (token.type == Token_Type::identifier && ((char)token2.type == ';')) {
auto unpack = new Unpack_Tuple_AST();
unpack->names.push_back(token);
stream->read_token(); stream->read_token();
while(true) {
token = stream->expect_token(Token_Type::identifier);
token2 = stream->read_token();
unpack->names.push_back(token);
if(token2.type == Token_Type::def)
break;
else if((char)token2.type != ';') {
token2.print_error_header();
fatal_error("Expected a ';' to continue the naming of tuple elements, or a ':=' for the assignment.");
}
}
auto expr = parse_math_expr(stream);
stream->expect_token(',');
unpack->exprs.push_back(expr);
block->exprs.push_back(unpack);
} else {
auto expr = parse_potential_if_expr(stream);
block->exprs.push_back(expr);
Expand Down
7 changes: 7 additions & 0 deletions src/ast.h
Original file line number Diff line number Diff line change
Expand Up @@ -190,6 +190,13 @@ Iterate_AST : Math_Expr_AST {
Iterate_AST() : Math_Expr_AST(Math_Expr_Type::iterate) {};
};

// TODO: should it be unified with Local_Var_AST ?
struct
Unpack_Tuple_AST : Math_Expr_AST {
std::vector<Token> names;
Unpack_Tuple_AST() : Math_Expr_AST(Math_Expr_Type::unpack_tuple) {};
};

struct
Regex_Body_AST : Body_AST {
Math_Expr_AST *expr;
Expand Down
3 changes: 2 additions & 1 deletion src/common_types.h
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ name(Decl_Type type) {

enum class
Value_Type : s32 {
unresolved = 0, none, iterate, real, integer, boolean,
unresolved = 0, none, iterate, tuple, real, integer, boolean,
};

inline bool is_value(Value_Type type) { return (s32)type >= (s32)Value_Type::real; }
Expand All @@ -134,6 +134,7 @@ name(Value_Type type) {
if(type == Value_Type::integer) return "integer";
if(type == Value_Type::boolean) return "boolean";
if(type == Value_Type::iterate) return "iterate";
if(type == Value_Type::tuple) return "tuple";
return "unresolved";
}

Expand Down
150 changes: 140 additions & 10 deletions src/function_tree.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ add_local_var(Math_Block_FT *scope, Math_Expr_FT *val) {
// NOTE: this should only be called on a block that is under construction.
auto local = new Local_Var_FT();
local->exprs.push_back(val);
local->value_type = val->value_type;
local->value_type = Value_Type::none;//val->value_type;
local->is_used = true;
s32 id = scope->n_locals;
local->id = id;
Expand Down Expand Up @@ -270,18 +270,83 @@ void fixup_intrinsic(Function_Call_FT *fun, Token *name) {
}
}

Tuple_FT *
find_tuple(Math_Expr_FT *tuple) {
if(tuple->expr_type == Math_Expr_Type::block)
return find_tuple(tuple->exprs.back());
else if(tuple->expr_type == Math_Expr_Type::tuple)
return static_cast<Tuple_FT *>(tuple);
tuple->source_loc.print_error_header();
fatal_error(Mobius_Error::internal, "Unable to find tuple referenced by value.");
return nullptr;
}

void
resolve_add_expr(Math_Expr_FT *parent, Math_Expr_FT *child, Standardized_Unit &unit, Function_Scope *scope, std::vector<Standardized_Unit> &units) {

// If we get an "unpack tuple" we desugar it to a local var holding the tuple and other local vars accessing the elements of that tuple.
// TODO: Move this to a separate function to make it cleaner?
if(child->expr_type == Math_Expr_Type::unpack_tuple) {
// TODO: Assert the types are correct?
auto unpack = static_cast<Unpack_Tuple_FT *>(child);
auto tuple = find_tuple(unpack->exprs[0]);

if(unpack->names.size() != tuple->exprs.size()) {
unpack->source_loc.print_error_header();
fatal_error("Incorrect number of elements in tuple unpacking. Expected ", tuple->exprs.size(), ".");
}

auto tuple_local = new Local_Var_FT();
tuple_local->name = "_tuple_"; // This should only be relevant for debug printing. Maybe make a better name??
tuple_local->source_loc = unpack->source_loc;
tuple_local->value_type = Value_Type::none;//Value_Type::tuple;
tuple_local->is_used = true; // Protect it from being removed.
tuple_local->exprs.push_back(unpack->exprs[0]);

Standardized_Unit no_unit = {};
resolve_add_expr(parent, tuple_local, no_unit, scope, units);
// The id of the tuple local should now have been resolved by the nested call to resolve_add_expr above.
Local_Var_Id tuple_id = { scope->block->unique_block_id, tuple_local->id };

for(int idx = 0; idx < tuple->exprs.size(); ++idx) {
auto local = new Local_Var_FT();
auto access = new Access_Tuple_Element_FT();

access->source_loc = unpack->source_loc;
access->value_type = tuple->exprs[idx]->value_type;
access->element_index = idx;
access->tuple_id = tuple_id;

local->source_loc = unpack->source_loc;
local->value_type = Value_Type::none;//access->value_type;
local->name = unpack->names[idx];
local->exprs.push_back(access);

resolve_add_expr(parent, local, tuple->element_units[idx], scope, units);
}

unpack->exprs.clear(); // So that the tuple itself is not deleted recursively.
delete unpack; // This one is not used in itself.

return;
}

parent->exprs.push_back(child);
if(child->expr_type == Math_Expr_Type::local_var) {
auto local = static_cast<Local_Var_FT *>(child);
local->id = scope->block->n_locals++;
scope->local_var_units[local->id] = unit;
}
units.push_back(unit);
}

void
resolve_arguments(Math_Expr_FT *ft, Math_Expr_AST *ast, Function_Resolve_Data *data, Function_Scope *scope, std::vector<Standardized_Unit> &units) {
//TODO allow error check on expected number of arguments
for(auto arg : ast->exprs) {
auto result = resolve_function_tree(arg, data, scope);
ft->exprs.push_back(result.fun);
if(result.fun->expr_type == Math_Expr_Type::local_var) {
auto local = static_cast<Local_Var_FT *>(result.fun);
local->id = scope->block->n_locals++;
scope->local_var_units[local->id] = result.unit;
}
units.push_back(std::move(result.unit));

resolve_add_expr(ft, result.fun, result.unit, scope, units);
}
}

Expand Down Expand Up @@ -578,7 +643,7 @@ arguments_must_be_values(Math_Expr_FT *expr, Function_Scope *scope) {
for(auto arg : expr->exprs) {
if(!is_value(arg->value_type)) {
arg->source_loc.print_error_header();
error_print("This expression argument must resolve to a value.");
error_print("This expression argument must resolve to a value, not '", name(arg->value_type), "'.");
fatal_error_trace(scope);
}
}
Expand Down Expand Up @@ -617,6 +682,7 @@ resolve_special_directive(Function_Call_AST *ast, Directive directive, Function_

std::vector<Standardized_Unit> arg_units;
resolve_arguments(new_fun, ast, data, scope, arg_units);

int allowed_arg_count = 1;
if(directive == Directive::in_flux || directive == Directive::out_flux)
allowed_arg_count = 2;
Expand Down Expand Up @@ -727,6 +793,17 @@ resolve_special_directive(Function_Call_AST *ast, Directive directive, Function_
result.unit = std::move(arg_units[var_idx]);
}

void
resolve_tuple(Function_Call_AST *ast, Function_Resolve_Data *data, Function_Scope *scope, Function_Resolve_Result &result) {

auto new_tuple = new Tuple_FT();
new_tuple->source_loc = ast->source_loc;
new_tuple->value_type = Value_Type::tuple;
resolve_arguments(new_tuple, ast, data, scope, new_tuple->element_units);

result.fun = new_tuple;
}

void
resolve_function_call(Function_Call_AST *fun, Function_Resolve_Data *data, Function_Scope *scope, Function_Resolve_Result &result) {

Expand Down Expand Up @@ -824,7 +901,7 @@ resolve_function_call(Function_Call_AST *fun, Function_Resolve_Data *data, Funct
auto inlined_arg = new Local_Var_FT();
inlined_arg->exprs.push_back(arg);
inlined_arg->name = fun_decl->args[argidx];
inlined_arg->value_type = arg->value_type;
inlined_arg->value_type = Value_Type::none;//arg->value_type;
inlined_fun->exprs[argidx] = inlined_arg;
inlined_arg->id = argidx;
new_scope.local_var_units[argidx] = arg_units[argidx];
Expand Down Expand Up @@ -1146,6 +1223,8 @@ resolve_function_tree(Math_Expr_AST *ast, Function_Resolve_Data *data, Function_
auto directive = get_special_directive(fun->name.string_value);
if(directive != Directive::none)
resolve_special_directive(fun, directive, data, scope, result);
else if(fun->name.string_value == "tuple")
resolve_tuple(fun, data, scope, result);
else
resolve_function_call(fun, data, scope, result);

Expand Down Expand Up @@ -1245,11 +1324,18 @@ resolve_function_tree(Math_Expr_AST *ast, Function_Resolve_Data *data, Function_

if(new_type == Value_Type::real) value_type = Value_Type::real;
else if(new_type == Value_Type::integer && value_type == Value_Type::boolean) value_type = Value_Type::integer;

if(new_type == Value_Type::tuple) {
new_if->exprs[idx]->source_loc.print_error_header();
error_print("The values in an 'if' expression can't be tuples currently. Instead, create the elements using separate if expressions, then pack them into a tuple after.");
fatal_error_trace(scope);
}
}

if(value_type == Value_Type::iterate || value_type == Value_Type::none) {
ifexpr->source_loc.print_error_header();
error_print("At least one of the possible results of the 'if' expression must evaluate to a value.");
fatal_error_trace(scope);
}

// Cast all possible result values up to the same type
Expand Down Expand Up @@ -1413,6 +1499,26 @@ resolve_function_tree(Math_Expr_AST *ast, Function_Resolve_Data *data, Function_
result.unit = std::move(to_unit);
} break;

case Math_Expr_Type::unpack_tuple : {
auto unpack = static_cast<Unpack_Tuple_AST *>(ast);
auto new_unpack = new Unpack_Tuple_FT();
new_unpack->value_type = Value_Type::none;

std::vector<Standardized_Unit> arg_units;
resolve_arguments(new_unpack, ast, data, scope, arg_units);

if(new_unpack->exprs[0]->value_type != Value_Type::tuple) {
ast->source_loc.print_error_header();
fatal_error("Tried to unpack something that is not a tuple.");
}

for(auto &name : unpack->names)
new_unpack->names.push_back(name.string_value);

result.fun = new_unpack;

} break;

default : {
fatal_error(Mobius_Error::internal, "Unhandled math expr type in resolve_function_tree().");
} break;
Expand Down Expand Up @@ -1507,6 +1613,14 @@ copy(Math_Expr_FT *source) {
result = copy_one<Math_Expr_FT>(source);
} break;

case Math_Expr_Type::tuple : {
result = copy_one<Tuple_FT>(source);
} break;

case Math_Expr_Type::access_tuple_element : {
result = copy_one<Access_Tuple_Element_FT>(source);
} break;

default : {
fatal_error(Mobius_Error::internal, "Unhandled math expr type in copy().");
} break;
Expand Down Expand Up @@ -1797,6 +1911,22 @@ print_tree_helper(Math_Expr_FT *expr, Print_Tree_Context *context, Print_Scope *
//}
} break;

case Math_Expr_Type::tuple : {
os << "tuple(";
int idx = 0;
for(auto arg : expr->exprs) {
print_tree_helper(arg, context, scope, block_tabs);
if (idx++ != expr->exprs.size()-1) os << ", ";
}
os << ")";
} break;

case Math_Expr_Type::access_tuple_element : {
auto access = static_cast<Access_Tuple_Element_FT *>(expr);
os << find_local_var(scope, access->tuple_id);
os << "[" << access->element_index << "]";
} break;

case Math_Expr_Type::no_op : {
os << "(no-op)";
} break;
Expand Down
Loading

0 comments on commit 7b593cd

Please sign in to comment.