Skip to content

Commit f962af9

Browse files
gh-175: Fix PARALLEL.
1 parent 92ca0ea commit f962af9

1 file changed

Lines changed: 144 additions & 3 deletions

File tree

src/builtins.c

Lines changed: 144 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8581,16 +8581,157 @@ typedef struct {
85818581
int* err_cols;
85828582
} ParallelStart;
85838583

8584+
// Local copy of interpreter helper to map ValueType -> DeclType
8585+
static DeclType value_type_to_decl(ValueType vt) {
8586+
switch (vt) {
8587+
case VAL_BOOL: return TYPE_BOOL;
8588+
case VAL_INT: return TYPE_INT;
8589+
case VAL_FLT: return TYPE_FLT;
8590+
case VAL_STR: return TYPE_STR;
8591+
case VAL_TNS: return TYPE_TNS;
8592+
case VAL_MAP: return TYPE_MAP;
8593+
case VAL_FUNC: return TYPE_FUNC;
8594+
case VAL_THR: return TYPE_THR;
8595+
default: return TYPE_UNKNOWN;
8596+
}
8597+
}
8598+
8599+
// Local coercion helper (mirrors interpreter.c behavior but is available
8600+
// in this translation unit). Returns true on success and sets *out_value.
8601+
static bool coerce_value_to_decl_type(Interpreter* interp,
8602+
Value input,
8603+
DeclType target,
8604+
Env* env,
8605+
int line,
8606+
int col,
8607+
Value* out_value) {
8608+
if (!out_value) return false;
8609+
*out_value = value_null();
8610+
8611+
if (value_type_to_decl(input.type) == target) {
8612+
*out_value = value_copy(input);
8613+
return true;
8614+
}
8615+
8616+
const char* builtin_name = NULL;
8617+
switch (target) {
8618+
case TYPE_BOOL: builtin_name = "BOOL"; break;
8619+
case TYPE_INT: builtin_name = "INT"; break;
8620+
case TYPE_FLT: builtin_name = "FLT"; break;
8621+
case TYPE_STR: builtin_name = "STR"; break;
8622+
case TYPE_TNS: builtin_name = "TNS"; break;
8623+
default:
8624+
return false;
8625+
}
8626+
8627+
BuiltinFunction* builtin = builtin_lookup(builtin_name);
8628+
if (!builtin || !builtin->impl) return false;
8629+
8630+
Value args[1];
8631+
args[0] = input;
8632+
Value converted = builtin->impl(interp, args, 1, NULL, env, line, col);
8633+
if (interp->error) {
8634+
return false;
8635+
}
8636+
if (value_type_to_decl(converted.type) != target) {
8637+
value_free(converted);
8638+
return false;
8639+
}
8640+
8641+
*out_value = converted;
8642+
return true;
8643+
}
8644+
85848645
static int parallel_worker(void* arg) {
85858646
ParallelStart* ps = (ParallelStart*)arg;
85868647
// Create a per-worker interpreter state similar to PARFOR
85878648
Interpreter* thr_interp = ps->interp;
85888649

8589-
// Prepare a call environment from the function's closure
8650+
// Create a call environment from the function's closure and
8651+
// perform normal function parameter binding (no-arg call).
85908652
Env* call_env = env_create(ps->func->closure);
85918653

8592-
// Execute the function body as if it were a function call so RETURN
8593-
// statements inside the function body are allowed.
8654+
// Bind parameters as the interpreter would for a user-call with
8655+
// zero positional/keyword arguments. This ensures missing
8656+
// required parameters and default-evaluation errors become
8657+
// runtime errors (as mandated by the spec).
8658+
for (size_t i = 0; i < ps->func->params.count; i++) {
8659+
Param* param = &ps->func->params.items[i];
8660+
Value arg_val = value_null();
8661+
bool provided = false;
8662+
8663+
if (param->default_value) {
8664+
arg_val = eval_expr(thr_interp, param->default_value, call_env);
8665+
if (thr_interp->error) {
8666+
// transfer ownership of interpreter error into shared slot
8667+
ps->errors[ps->index] = thr_interp->error;
8668+
if (ps->err_lines) ps->err_lines[ps->index] = thr_interp->error_line;
8669+
if (ps->err_cols) ps->err_cols[ps->index] = thr_interp->error_col;
8670+
thr_interp->error = NULL;
8671+
// cleanup
8672+
env_free(call_env);
8673+
free(thr_interp);
8674+
free(ps);
8675+
return 0;
8676+
}
8677+
provided = true;
8678+
} else {
8679+
char buf[128];
8680+
snprintf(buf, sizeof(buf), "Missing argument for parameter '%s'", param->name);
8681+
ps->errors[ps->index] = strdup(buf);
8682+
if (ps->err_lines) ps->err_lines[ps->index] = 0;
8683+
if (ps->err_cols) ps->err_cols[ps->index] = 0;
8684+
env_free(call_env);
8685+
free(thr_interp);
8686+
free(ps);
8687+
return 0;
8688+
}
8689+
8690+
Value bind_val = arg_val;
8691+
bool used_coercion = false;
8692+
if (value_type_to_decl(bind_val.type) != param->type && param->coerced) {
8693+
Value coerced = value_null();
8694+
if (coerce_value_to_decl_type(thr_interp, arg_val, param->type, call_env, 0, 0, &coerced)) {
8695+
bind_val = coerced;
8696+
used_coercion = true;
8697+
}
8698+
}
8699+
8700+
if (value_type_to_decl(bind_val.type) != param->type) {
8701+
char buf[128];
8702+
snprintf(buf, sizeof(buf), "Type mismatch for parameter '%s'", param->name);
8703+
ps->errors[ps->index] = strdup(buf);
8704+
if (ps->err_lines) ps->err_lines[ps->index] = 0;
8705+
if (ps->err_cols) ps->err_cols[ps->index] = 0;
8706+
if (used_coercion) value_free(bind_val);
8707+
value_free(arg_val);
8708+
env_free(call_env);
8709+
free(thr_interp);
8710+
free(ps);
8711+
return 0;
8712+
}
8713+
8714+
env_define(call_env, param->name, param->type);
8715+
if (!env_assign(call_env, param->name, bind_val, param->type, true)) {
8716+
char buf[256];
8717+
snprintf(buf, sizeof(buf), "Cannot assign to frozen identifier '%s'", param->name);
8718+
ps->errors[ps->index] = strdup(buf);
8719+
if (ps->err_lines) ps->err_lines[ps->index] = 0;
8720+
if (ps->err_cols) ps->err_cols[ps->index] = 0;
8721+
if (used_coercion) value_free(bind_val);
8722+
value_free(arg_val);
8723+
env_free(call_env);
8724+
free(thr_interp);
8725+
free(ps);
8726+
return 0;
8727+
}
8728+
8729+
// Release temporaries (env_assign copied value)
8730+
value_free(arg_val);
8731+
if (used_coercion) value_free(bind_val);
8732+
}
8733+
8734+
// Execute the function body as a proper call frame so RETURN is allowed
85948735
ExecResult res = exec_program_in_env_as_function(thr_interp, ps->func->body, call_env, ps->func->name);
85958736

85968737
if (res.status == EXEC_ERROR && res.error) {

0 commit comments

Comments
 (0)