Skip to main content

Stack System

The stack (Stack) is the core execution mechanism of the Alia language, used to manage the execution environments and contexts of code. Stacks are created as literals or via new Stack expressions, held as Stack objects, and provide execution, context, and state operations through their members.

1. Defining Stacks​

Syntax​

{} ~ uses the stack of the original context
*{} ~ creates a stack with a new context
mut {} ~ a mutable stack of the original context (supports this context operations)
mut *{} ~ a mutable stack with a new context (supports this context operations)
new Stack <String content> ~ creates a new stack (Eval)

{}, *{}, mut {}, and mut *{} evaluate to Stack objects as expressions; *{} and mut *{} create an independent context at runtime. When a stack literal appears as a standalone statement, it only constructs a Stack object without executing the stack body; execution is performed via call <Stack>.run.

new Stack accepts a source code string or an array of source code, parsing and constructing an equivalent stack at runtime:

const stack = new Stack 'let y = 20
print y'
call stack.run ~ prints 20

const stack2 = new Stack ['let x = 5
let y = 3
x * y']
print call stack2.run ~ prints 15

Type-prefixed stacks​

Using a type name before the stack body (a built-in type, or a custom type declared with type/typify/class) declares the stack's return value:

run Number { 42 } ~ runs a stack that returns a Number in parallel
mut String { 'hi' } ~ a type-prefixed mutable stack
const typed = Number { 9 } ~ a type-prefixed stack
enter Number { 3 } ~ enter target
alias f -> Number { 42 } ~ Alias implementation body
  • return / return-val inside the stack must match the declared type; a mismatch reports TYPE_MISMATCH at check time
  • The inferred type of a stack node is Stack<T>, and the inferred result of await <Stack> is T
  • Stack literals, parallel stacks, and standard library async Stacks all have a typeOf of 'Stack', and Ok/as work as expected
  • An undefined type name used as a prefix reports TYPE_INVALID_OPERATION at check time

2. Stack Operations​

OperationDescriptionApplies to
call <Stack>.runExecutes the stack content, returning the value at the end of the stack or the break valueAll
<Stack>.define <String name> <Value val>Defines a variable in the contextMutable stacks
<Stack>.set <String name> <Value val>Sets a variable in the contextMutable stacks
<Stack>.resolve <String name>Looks up a variable in the contextMutable stacks
<Stack>.has <String name>Checks whether a variable exists in the contextMutable stacks
<Stack>.context <Object context>Injects object members into the contextMutable stacks
compileOverrides the compilation functions of subnodes provided within the contextCompile time only

run, result, join, and detach are zero-argument members, invoked with call. define, set, resolve, and has receive positional arguments, using syntax-style calls. context receives a single object: when the object is provided as a variable, write <Stack>.context <Object var>; when provided as a literal, write call <Stack>.context *[ ... ].

const stack = *{
let x = 10
x + 5
}

const result = call stack.run
print result ~ prints 15

print call stack.run ~ still prints 15

3. Operations Inside a Stack​

The execution context of a mutable stack contains this, pointing to the mutable context object. this has the same members as the stack:

  • call this.run: recursively executes the current stack
  • this.define <String name> <Value val>: defines a variable in the context
  • this.set <String name> <Value val>: sets a variable in the context
  • this.resolve <String name>: looks up a variable in the context
  • this.has <String name>: checks whether a variable exists in the context
  • this.context <Object context>: injects object members into the context
const stack = mut *{
this.define 'name' 'Alice'
this.set 'name' 'Bob'
this.resolve 'name'
}
print call stack.run ~ prints 'Bob'
const stack = mut *{
print name
print age
}
call stack.context *[ name: 'Bob', age: 25 ]
call stack.run ~ prints Bob and 25
const stack = mut *{
call this.context *[ name: 'Alice' ]
name
}
print call stack.run ~ prints 'Alice'

4. Ways to Create Stacks​

Method 1: literal creation​

const newContextStack = *{
let x = 1
x
}

const sameContextStack = {
let y = 2
y
}

Method 2: using new Stack​

const stack = new Stack 'let x = 10
print x'
call stack.run ~ prints 10

5. Stack Return Values​

A stack's return value defaults to the last item of the stack body; break <Value return> makes the stack return the value it carries.

const stack1 = *{
let x = 1
x + 1
}
print call stack1.run ~ prints 2

const stack2 = *{
break 'custom return value'
}
print call stack2.run ~ prints 'custom return value'

break directly returns from the enclosing stack inside if / if else branches; inside while/for/repeat loop bodies it terminates the current loop.

6. Stacks and Scope​

*{} creates an independent context, while {} reuses the original context. Both must be executed via call <Stack>.run.

let x = 0
call {
let x = 10
x + 5
}.run
print x ~ prints 10

let y = 0
call *{
let y = 10
y + 5
}.run
print y ~ prints 0

7. Example Code​

~ 1. Basic stack operations
const stack = *{
let x = 10
let y = 20
x + y
}
const result = call stack.run
print 'Stack return value: ' .. result

~ 2. Stack nesting
const outerStack = *{
let outerVar = 'outer'
const innerStack = *{
let innerVar = 'inner'
outerVar .. ' ' .. innerVar
}
call innerStack.run
}
print call outerStack.run

~ 3. Stack context operations
const contextStack = mut *{
this.define 'name' 'Alice'
this.define 'age' 30
print 'Name: ' .. name
print 'Age: ' .. age
this.set 'age' 31
print 'Updated age: ' .. age
'Context updated successfully'
}
print call contextStack.run

~ 4. Looping execution of a stack
let count = 0
const loopStack = {
count = count + 1
if count >= 5 {
break
}
call this.run
}
call loopStack.run
print count

~ 5. Using new Stack
const evalStack = new Stack 'let x = 5
let y = 3
x * y'
print 'Eval result: ' .. call evalStack.run

8. Mutable Stack Features​

Mutable stacks (mut {} or mut *{}) support Has and enter within their contexts and can preserve state across multiple executions.

The Has keyword​

Has checks whether the specified variable exists in a mutable stack's context and returns a Boolean.

const stack = mut *{
if Has 'var' {
var
} else {
let var = 10
var
}
}
print call stack.run ~ prints 10
stack.set 'var' 20
print call stack.run ~ prints 20

Type narrowing​

When Has is used in an if condition, the type checker narrows the variable's type from <Null> to <Value>:

const stack = mut *{
if Has 'name' {
name
} else {
let name = 'x'
name
}
}

The enter keyword​

enter enters a stack's context. enter <Stack> executes the target stack; enter <Stack> <Stack body> executes the body in the target context, with modifications persisted to the original stack.

const stack = mut *{
let val = 100
val
}
call stack.run

enter (stack) {
if Has 'val' {
print val ~ prints 100
}
}

When the target stack is provided as a variable, the enter operand takes parentheses (enter (stack) { ... }) so that it is not parsed as a type-prefixed stack.

const stack = mut *{
let count = 0
count
}
call stack.run

enter (stack) {
this.set 'count' 5
}

print stack.resolve 'count' ~ prints 5

9. The Purpose of Stacks​

  1. Context isolation: create independent execution environments via stacks
  2. Code organization: wrap code blocks into stacks to improve maintainability
  3. Control flow management: control the execution flow of code through stack operations
  4. State preservation: mutable stacks can preserve state across multiple executions
  5. Recursive execution: recursively call a stack via call this.run

Next Steps​