Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
087998b
25476: Begins implementing scope stack break
howsohazard May 10, 2026
fe06e48
25476: More implementation
howsohazard May 11, 2026
a8291b5
25467: More implementation, fixes bugs
howsohazard May 11, 2026
5aaac84
25476: Fixes stack bug, adds unit test, introduces new potential bug
howsohazard May 12, 2026
38f291a
25476: Fixes bug
howsohazard May 12, 2026
f824cec
Automated docs rebuild
May 13, 2026
8e2ea17
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 14, 2026
51e632f
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 16, 2026
f03b643
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 19, 2026
9d246ba
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 19, 2026
9cf2a60
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 20, 2026
322c5e5
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 21, 2026
e8353d8
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 21, 2026
53d1047
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 22, 2026
885a1db
Automated docs rebuild
May 22, 2026
0701452
Merge branch 'main' into 25476-scope-stack-break
howsohazard May 26, 2026
7c5957e
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 1, 2026
b990db6
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 2, 2026
76d6ab7
Automated docs rebuild
Jun 2, 2026
e635e7a
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 2, 2026
43e57c1
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 4, 2026
da17d76
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 5, 2026
652b83d
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 12, 2026
a5dd913
Merge branch '25476-scope-stack-break' of https://github.com/howsoai/…
howsohazard Jun 14, 2026
3e65f6f
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 14, 2026
f96a084
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 19, 2026
83566ef
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 19, 2026
760b551
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 21, 2026
7769f6c
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 24, 2026
a1dd95c
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 25, 2026
4490c68
Automated docs rebuild
Jun 25, 2026
42234da
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 25, 2026
8f4bb0d
Automated docs rebuild
Jun 25, 2026
c633794
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 25, 2026
94dd5e6
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 25, 2026
d26a6ef
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 30, 2026
014e06d
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 30, 2026
0356e52
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jun 30, 2026
8367939
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 2, 2026
c28867f
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 2, 2026
a0aa6a4
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 6, 2026
10f068b
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 11, 2026
d3243e4
Automated docs rebuild
Jul 11, 2026
ad38ff5
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 16, 2026
e5edc37
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 16, 2026
7e74a74
Automated docs rebuild
Jul 16, 2026
681efa4
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 17, 2026
8538796
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 18, 2026
d3fb2db
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 21, 2026
d70f3b9
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 28, 2026
a3b4d75
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 29, 2026
e10a459
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 30, 2026
2be77bb
Merge branch 'main' into 25476-scope-stack-break
howsohazard Jul 30, 2026
a1893e3
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 2, 2026
c2273a2
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 3, 2026
7fc38df
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 5, 2026
e4b236f
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 6, 2026
05f3649
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 12, 2026
1e31f61
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 13, 2026
18d6bb2
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 17, 2026
57d46c5
Merge branch 'main' into 25476-scope-stack-break
howsohazard Aug 18, 2026
2d42713
Merge branch 'main' into 25476-scope-stack-break
howsohazard Sep 11, 2026
83989d0
Automated docs rebuild
Sep 11, 2026
a8032ad
Merge branch 'main' into 25476-scope-stack-break
howsohazard Sep 11, 2026
e03d7a4
Merge branch '25476-scope-stack-break' of https://github.com/howsoai/…
howsohazard Sep 11, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions debug-visualizers/AmalgamInternalTypes.natvis
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@
<Item Name="FREEABLE">(attributes &amp; 16) != 0</Item>
<Item Name="FREEABLE_TOP_NODE">(attributes &amp; 32) != 0</Item>
<Item Name="KNOWN_TO_BE_IN_USE">(attributes &amp; 64) != 0</Item>
<Item Name="SCOPE_BREAK">(attributes &amp; 128) != 0</Item>
<Item Name="attributes">attributes</Item>
</Expand>
</Type>
Expand Down
8 changes: 4 additions & 4 deletions docs/basic_math.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ parent: Opcodes
nav_order: 6
---

### Opcode: `+` {#opcode-add}
### Opcode: `+`
#### Parameters
`[number x1] [number x2] ...`
#### Returns
Expand All @@ -31,7 +31,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `-` {#opcode-subtract}
### Opcode: `-`
#### Parameters
`[number x1] [number x2] ...`
#### Returns
Expand Down Expand Up @@ -65,7 +65,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `*` {#opcode-multiply}
### Opcode: `*`
#### Parameters
`[number x1] [number x2] ...`
#### Returns
Expand All @@ -91,7 +91,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `/` {#opcode-divide}
### Opcode: `/`
#### Parameters
`[number x1] [number x2] ...`
#### Returns
Expand Down
2 changes: 1 addition & 1 deletion docs/control_flow.md
Original file line number Diff line number Diff line change
Expand Up @@ -141,7 +141,7 @@ Output:
#### Returns
`any`
#### Description
Evaluates `function` after pushing the `params` assoc onto the scope stack. If `constraints` is specified and not false or null, it will constrain execution. If `constraints` is true or an assoc, it will default all constraints to be on at reasonable values for small execution without access to any data beyond `params`. They optional key-value combinations for `constraints` are as follows. If "max_node_operations" is specified, it represents the number of operations that are allowed to be performed. If "max_node_operations" is 0, then an infinite of operations will be allotted, up to the limits of the current calling context. If "max_node_allocations" is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If "max_node_allocations" is 0 and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if "max_node_allocations" is specified while in a multithreaded environment, if the collective memory from all the executing threads exceeds the average memory specified by "max_node_allocations", that may trigger a memory limit for the call. If "max_operation_depth" is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise "max_operation_depth" limits how deep nested opcodes will be called. If `return_warnings` is true (default is false), the result will be a tuple of the form [value, warnings, performance_constraint_violation], where warnings is a list of all warnings, and perf_constraint_violation is a string denoting the performance constraint exceeded (or .null if none)). The keys "read_access" and "write_access" are boolean and control whether the execution can read from or write to entities and access their relevant permissions (e.g., to load files, make system calls). If the parameter `return_warnings` is false, just the value will be returned.
Evaluates `function` after pushing the `params` assoc onto the scope stack. If `params` is missing or null, then it will allow access to the scope stack of the caller. If `constraints` is specified and not false or null, it will constrain execution. If `constraints` is true or an assoc, it will default all constraints to be on at reasonable values for small execution without access to any data beyond `params`. They optional key-value combinations for `constraints` are as follows. If "max_node_operations" is specified, it represents the number of operations that are allowed to be performed. If "max_node_operations" is 0, then an infinite of operations will be allotted, up to the limits of the current calling context. If "max_node_allocations" is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If "max_node_allocations" is 0 and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if "max_node_allocations" is specified while in a multithreaded environment, if the collective memory from all the executing threads exceeds the average memory specified by "max_node_allocations", that may trigger a memory limit for the call. If "max_operation_depth" is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise "max_operation_depth" limits how deep nested opcodes will be called. If `return_warnings` is true (default is false), the result will be a tuple of the form [value, warnings, performance_constraint_violation], where warnings is a list of all warnings, and perf_constraint_violation is a string denoting the performance constraint exceeded (or .null if none)). The keys "read_access" and "write_access" are boolean and control whether the execution can read from or write to entities and access their relevant permissions (e.g., to load files, make system calls). If the parameter `return_warnings` is false, just the value will be returned.
#### Details
- Permissions required: none
- Allows concurrency: false
Expand Down
16 changes: 8 additions & 8 deletions docs/logic_and_comparison.md
Original file line number Diff line number Diff line change
Expand Up @@ -197,7 +197,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `=` {#opcode-equal}
### Opcode: `=`
#### Parameters
`[any node1] [any node2] ...`
#### Returns
Expand Down Expand Up @@ -266,7 +266,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `!=` {#opcode-not_equal}
### Opcode: `!=`
#### Parameters
`[any node1] [any node2] ...`
#### Returns
Expand Down Expand Up @@ -348,7 +348,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `<` {#opcode-less_than}
### Opcode: `<`
#### Parameters
`[number|string node1] [number|string node2] ...`
#### Returns
Expand Down Expand Up @@ -398,7 +398,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `<=` {#opcode-less_than_or_equal}
### Opcode: `<=`
#### Parameters
`[number|string node1] [number|string node2] ...`
#### Returns
Expand Down Expand Up @@ -464,7 +464,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `>` {#opcode-greater_than}
### Opcode: `>`
#### Parameters
`[number|string node1] [number|string node2] ...`
#### Returns
Expand Down Expand Up @@ -514,7 +514,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `>=` {#opcode-greater_than_or_equal}
### Opcode: `>=`
#### Parameters
`[number|string node1] [number|string node2] ...`
#### Returns
Expand Down Expand Up @@ -580,7 +580,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `~` {#opcode-type_equal}
### Opcode: `~`
#### Parameters
`[any node1] [any node2] ...`
#### Returns
Expand Down Expand Up @@ -614,7 +614,7 @@ Output:

[Amalgam Opcodes](./opcodes.md)

### Opcode: `!~` {#opcode-type_not_equal}
### Opcode: `!~`
#### Parameters
`[any node1] [any node2] ...`
#### Returns
Expand Down
24 changes: 12 additions & 12 deletions docs/opcodes.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,19 +45,19 @@
- [or](./logic_and_comparison.md#opcode-or)
- [xor](./logic_and_comparison.md#opcode-xor)
- [not](./logic_and_comparison.md#opcode-not)
- [=](./logic_and_comparison.md#opcode-equal)
- [!=](./logic_and_comparison.md#opcode-not_equal)
- [<](./logic_and_comparison.md#opcode-less_than)
- [<=](./logic_and_comparison.md#opcode-less_than_or_equal)
- [>](./logic_and_comparison.md#opcode-greater_than)
- [>=](./logic_and_comparison.md#opcode-greater_than_or_equal)
- [~](./logic_and_comparison.md#opcode-type_equal)
- [!~](./logic_and_comparison.md#opcode-type_not_equal)
- [=](./logic_and_comparison.md#opcode-=)
- [!=](./logic_and_comparison.md#opcode-!=)
- [<](./logic_and_comparison.md#opcode-<)
- [<=](./logic_and_comparison.md#opcode-<=)
- [>](./logic_and_comparison.md#opcode->)
- [>=](./logic_and_comparison.md#opcode->=)
- [~](./logic_and_comparison.md#opcode-~)
- [!~](./logic_and_comparison.md#opcode-!~)
### Basic Math
- [+](./basic_math.md#opcode-add)
- [-](./basic_math.md#opcode-subtract)
- [\*](./basic_math.md#opcode-multiply)
- [/](./basic_math.md#opcode-divide)
- [+](./basic_math.md#opcode-+)
- [-](./basic_math.md#opcode--)
- [\*](./basic_math.md#opcode-*)
- [/](./basic_math.md#opcode-/)
- [mod](./basic_math.md#opcode-mod)
- [get_digits](./basic_math.md#opcode-get_digits)
- [set_digits](./basic_math.md#opcode-set_digits)
Expand Down
2 changes: 0 additions & 2 deletions docs/variable_definition_and_modification.md
Original file line number Diff line number Diff line change
Expand Up @@ -718,8 +718,6 @@ Example:
Output:
```amalgam
[
{}
{x 1}
{a 1}
]
```
Expand Down
18 changes: 16 additions & 2 deletions src/Amalgam/evaluablenode/EvaluableNode.h
Original file line number Diff line number Diff line change
Expand Up @@ -66,8 +66,10 @@ class EvaluableNode
FREEABLE_TOP_NODE = 1 << 5,
//if true, then known to be in use with regard to garbage collection
KNOWN_TO_BE_IN_USE = 1 << 6,
ALL = HAS_EXTENDED_VALUE | NEED_CYCLE_CHECK | IDEMPOTENT | CONCURRENT
| FREEABLE | FREEABLE_TOP_NODE | KNOWN_TO_BE_IN_USE
//if true, then the scope stops at this node
SCOPE_BREAK = 1 << 7,
ALL = HAS_EXTENDED_VALUE | NEED_CYCLE_CHECK | IDEMPOTENT | CONCURRENT | FREEABLE | FREEABLE_TOP_NODE
| KNOWN_TO_BE_IN_USE | SCOPE_BREAK
};

//constructors
Expand Down Expand Up @@ -696,6 +698,18 @@ class EvaluableNode
}
#endif

//if true, then the scope stops at this node
__forceinline bool IsScopeBreak()
{
return HasAttribute(Attribute::SCOPE_BREAK);
}

//sets whether the scope stops at this node
__forceinline void SetScopeBreak(bool scope_break)
{
SetAttribute(Attribute::SCOPE_BREAK, scope_break);
}

//returns true if value contains an extended type
__forceinline bool HasExtendedValue()
{
Expand Down
35 changes: 30 additions & 5 deletions src/Amalgam/interpreter/Interpreter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -79,7 +79,7 @@ EvaluableNodeReference Interpreter::ExecuteNode(EvaluableNode *en,
return retval;
}

void Interpreter::InterpretAndPushNewScopeStackNode(EvaluableNode *new_scope_node, bool interpret_with_new_scope)
void Interpreter::InterpretAndPushNewScopeStackNode(EvaluableNode *new_scope_node, bool interpret_with_new_scope, bool scope_break)
{
EvaluableNodeReference new_scope = EvaluableNodeReference::Null();
bool need_to_interpret_new_scope = false;
Expand Down Expand Up @@ -107,6 +107,7 @@ void Interpreter::InterpretAndPushNewScopeStackNode(EvaluableNode *new_scope_nod
//just in case a variable is added which needs cycle checks
new_scope->SetNeedCycleCheck(true);

new_scope->SetScopeBreak(scope_break);
scopeStack.push_back(new_scope);

return;
Expand All @@ -119,6 +120,7 @@ void Interpreter::InterpretAndPushNewScopeStackNode(EvaluableNode *new_scope_nod
//just in case a variable is added which needs cycle checks
new_scope->SetNeedCycleCheck(true);

new_scope->SetScopeBreak(scope_break);
scopeStack.push_back(new_scope);

auto new_scope_mcn = new_scope->GetMappedChildNodesViewOnAssoc();
Expand Down Expand Up @@ -159,6 +161,8 @@ void Interpreter::InterpretAndPushNewScopeStackNode(EvaluableNode *new_scope_nod
//just in case a variable is added which needs cycle checks
new_scope_on_stack->SetNeedCycleCheck(true);

new_scope_on_stack->SetScopeBreak(scope_break);

if(interpret_with_new_scope)
scopeStack.push_back(new_scope_on_stack);

Expand Down Expand Up @@ -301,11 +305,21 @@ EvaluableNode *Interpreter::GetScopeStackGivenDepth(size_t depth
EvaluableNode *Interpreter::MakeCopyOfScopeStack()
{
EvaluableNode stack_top_holder(ENT_LIST);
stack_top_holder.SetOrderedChildNodes(begin(scopeStack), end(scopeStack), true, false);

//set flags conservatively before copy
stack_top_holder.SetNeedCycleCheck(true);
stack_top_holder.SetIsIdempotent(false);

//find scope break and erase before that
size_t scope_start_index = scopeStack.size();
for(; scope_start_index > 0; scope_start_index--)
{
if(scopeStack[scope_start_index - 1]->IsScopeBreak())
break;
}
stack_top_holder.GetOrderedChildNodesReference()
= EvaluableNode::OrderedType(begin(scopeStack) + scope_start_index, end(scopeStack));

EvaluableNodeReference copied_stack = evaluableNodeManager->DeepAllocCopy(&stack_top_holder);

#ifdef MULTITHREAD_SUPPORT
Expand All @@ -315,9 +329,20 @@ EvaluableNode *Interpreter::MakeCopyOfScopeStack()
auto &stack_nodes_ocn = copied_stack->GetOrderedChildNodesReference();
for(Interpreter *interp = callingInterpreter; interp != nullptr; interp = interp->callingInterpreter)
{
stack_nodes_ocn.insert(begin(stack_nodes_ocn), scopeStack.size(), nullptr);
for(size_t i = 0; i < scopeStack.size(); i++)
stack_nodes_ocn[i] = evaluableNodeManager->DeepAllocCopy(scopeStack[i]);
size_t parent_scope_start_index = interp->scopeStack.size();
for(; parent_scope_start_index > 0; parent_scope_start_index--)
{
if(interp->scopeStack[parent_scope_start_index - 1]->IsScopeBreak())
break;
}

stack_nodes_ocn.insert(begin(stack_nodes_ocn), interp->scopeStack.size() - parent_scope_start_index, nullptr);

for(size_t i = 0; i < interp->scopeStack.size() - parent_scope_start_index; i++)
{
stack_nodes_ocn[i]
= evaluableNodeManager->DeepAllocCopy(interp->scopeStack[i + parent_scope_start_index]);
}

if(interp->bottomOfScopeStack)
break;
Expand Down
16 changes: 14 additions & 2 deletions src/Amalgam/interpreter/Interpreter.h
Original file line number Diff line number Diff line change
Expand Up @@ -90,7 +90,8 @@ class Interpreter
//but if not, it will attempt to put an appropriate unique associative array on scopeStack
//if interpret_with_new_scope will push the new scope before interpreting if needed (e.g., ENT_LET),
// otherwise it will push after interpreting (e.g., ENT_CALL)
void InterpretAndPushNewScopeStackNode(EvaluableNode *new_context_node, bool interpret_with_new_scope);
//if scope_break is true, then it will break the scope from all below it and not give access below
void InterpretAndPushNewScopeStackNode(EvaluableNode *new_context_node, bool interpret_with_new_scope, bool scope_break);

//pops the top context off the stack
//if returning_unique_value, then can potentially free the whole scope
Expand Down Expand Up @@ -351,6 +352,9 @@ class Interpreter
#endif
)
{
#ifdef MULTITHREAD_SUPPORT
bool hit_scope_break = false;
#endif
//find appropriate context for symbol by walking up the stack
for(auto it = rbegin(scopeStack); it != rend(scopeStack); ++it)
{
Expand Down Expand Up @@ -396,11 +400,19 @@ class Interpreter
return ScopeStackSymbolLocation{
&found->second, mcn, it == rbegin(scopeStack), is_freeable, is_freeable_top_node};
}

if((*it)->IsScopeBreak())
{
#ifdef MULTITHREAD_SUPPORT
hit_scope_break = true;
#endif
break;
}
}

#ifdef MULTITHREAD_SUPPORT
//need to search further down the stack if appropriate
if(!bottomOfScopeStack && callingInterpreter != nullptr)
if(!hit_scope_break && !bottomOfScopeStack && callingInterpreter != nullptr)
{
bool top_is_next_stack = (scopeStack.size() == 0);
auto symbol_location = callingInterpreter->GetScopeStackSymbolLocation(
Expand Down
7 changes: 5 additions & 2 deletions src/Amalgam/interpreter/OpcodesControlFlow.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -155,7 +155,7 @@ static OpcodeInitializer _ENT_CALL(ENT_CALL, &Interpreter::InterpretNode_ENT_CAL
OpcodeDetails::ParameterGroup({"return_warnings", OpcodeDetails::DataType::BOOL, true})
};
d.returns = OpcodeDetails::DataType::ANY_BASIC;
d.description = R"(Evaluates `function` after pushing the `params` assoc onto the scope stack. If `constraints` is specified and not false or null, it will constrain execution. If `constraints` is true or an assoc, it will default all constraints to be on at reasonable values for small execution without access to any data beyond `params`. They optional key-value combinations for `constraints` are as follows. If "max_node_operations" is specified, it represents the number of operations that are allowed to be performed. If "max_node_operations" is 0, then an infinite of operations will be allotted, up to the limits of the current calling context. If "max_node_allocations" is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If "max_node_allocations" is 0 and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if "max_node_allocations" is specified while in a multithreaded environment, if the collective memory from all the executing threads exceeds the average memory specified by "max_node_allocations", that may trigger a memory limit for the call. If "max_operation_depth" is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise "max_operation_depth" limits how deep nested opcodes will be called. If `return_warnings` is true (default is false), the result will be a tuple of the form [value, warnings, performance_constraint_violation], where warnings is a list of all warnings, and perf_constraint_violation is a string denoting the performance constraint exceeded (or .null if none)). The keys "read_access" and "write_access" are boolean and control whether the execution can read from or write to entities and access their relevant permissions (e.g., to load files, make system calls). If the parameter `return_warnings` is false, just the value will be returned.)";
d.description = R"(Evaluates `function` after pushing the `params` assoc onto the scope stack. If `params` is missing or null, then it will allow access to the scope stack of the caller. If `constraints` is specified and not false or null, it will constrain execution. If `constraints` is true or an assoc, it will default all constraints to be on at reasonable values for small execution without access to any data beyond `params`. They optional key-value combinations for `constraints` are as follows. If "max_node_operations" is specified, it represents the number of operations that are allowed to be performed. If "max_node_operations" is 0, then an infinite of operations will be allotted, up to the limits of the current calling context. If "max_node_allocations" is specified, it represents the maximum number of nodes that are allowed to be allocated, limiting the total memory, up to the current calling context's limit. If "max_node_allocations" is 0 and the caller also has no limit, then there is no limit to the number of nodes to be allotted as long as the machine has sufficient memory. Note that if "max_node_allocations" is specified while in a multithreaded environment, if the collective memory from all the executing threads exceeds the average memory specified by "max_node_allocations", that may trigger a memory limit for the call. If "max_operation_depth" is 0 or infinite and the caller also has no limit, then there is no limit to the depth that opcodes can execute, otherwise "max_operation_depth" limits how deep nested opcodes will be called. If `return_warnings` is true (default is false), the result will be a tuple of the form [value, warnings, performance_constraint_violation], where warnings is a list of all warnings, and perf_constraint_violation is a string denoting the performance constraint exceeded (or .null if none)). The keys "read_access" and "write_access" are boolean and control whether the execution can read from or write to entities and access their relevant permissions (e.g., to load files, make system calls). If the parameter `return_warnings` is false, just the value will be returned.)";
d.examples = MakeAmalgamExamples({
{R"&((let
{
Expand Down Expand Up @@ -313,7 +313,10 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_CALL(EvaluableNode *en, Ev
profiling_call = true;
}

InterpretAndPushNewScopeStackNode(ocn.size() > 1 ? ocn[1] : nullptr, false);
if(ocn.size() > 1 && !EvaluableNode::IsNull(ocn[1]))
InterpretAndPushNewScopeStackNode(ocn.size() > 1 ? ocn[1] : nullptr, false, true);
else
InterpretAndPushNewScopeStackNode(ocn.size() > 1 ? ocn[1] : nullptr, false, false);

//call the code
auto result = InterpretNode(function, immediate_result);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -104,7 +104,7 @@ EvaluableNodeReference Interpreter::InterpretNode_ENT_LET(EvaluableNode *en, Eva
if(ocn_size == 0) [[unlikely]]
return EvaluableNodeReference::Null();

InterpretAndPushNewScopeStackNode(ocn[0], true);
InterpretAndPushNewScopeStackNode(ocn[0], true, false);

//run code
EvaluableNodeReference result = EvaluableNodeReference::Null();
Expand Down Expand Up @@ -1359,8 +1359,6 @@ static OpcodeInitializer _ENT_STACK(ENT_STACK, &Interpreter::InterpretNode_ENT_S
)
{x 1}
))&", R"([
{}
{x 1}
{a 1}
])"}
});
Expand Down