///|
/// Environment and container helpers.
///|
let list_storage_name = "$__list__"
///|
let dict_storage_name = "$__mpython_dict__"
///|
let int_storage_name = "$__int__"
///|
let tuple_storage_name = "$__tuple__"
///|
let set_storage_name = "$__mpython_set__"
///|
let firstlineno_capture_name = "$__mpython_firstlineno__"
///|
fn is_builtin_class_name(name : String) -> Bool {
match name {
"bool"
| "bytearray"
| "bytes"
| "classmethod"
| "complex"
| "dict"
| "float"
| "frozenset"
| "int"
| "list"
| "memoryview"
| "property"
| "range"
| "set"
| "staticmethod"
| "str"
| "tuple"
| "type" => true
_ => false
}
}
///|
fn builtin_class_from_name(
name : String,
builtins : Array[(String, Value)],
) -> ClassValue {
match get_named_value(builtins, name) {
Some(Value::Class(klass)) => return klass
_ => ()
}
let bases : Array[Value] = []
if name == "bool" {
match get_named_value(builtins, "int") {
Some(Value::Class(int_class)) => bases.push(Value::Class(int_class))
_ => ()
}
}
if bases.length() == 0 {
match get_named_value(builtins, "object") {
Some(Value::Class(object_class)) => bases.push(Value::Class(object_class))
_ => ()
}
}
let dict : Array[(String, Value)] = [
("__module__", Value::Str("builtins")),
("__name__", Value::Str(name)),
("__qualname__", Value::Str(name)),
]
if name == "bool" {
dict.push(
(
"__new__",
Value::Function(FunctionValue::{
name: "bool.__new__",
params: ["cls", "x"],
defaults: [Value::Bool(false)],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "slice" {
dict.push(
(
"__new__",
Value::Function(FunctionValue::{
name: "slice.__new__",
params: ["cls", "start", "stop", "step"],
defaults: [Value::None, Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "float" {
dict.push(
(
"__getformat__",
Value::Function(FunctionValue::{
name: "float.__getformat__",
params: ["typestr"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "bytes" {
dict.push(
(
"maketrans",
Value::Function(FunctionValue::{
name: "bytes.maketrans",
params: ["from", "to"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "staticmethod" {
dict.push(
(
"__get__",
Value::Function(FunctionValue::{
name: "staticmethod.__get__",
params: ["self", "obj", "owner"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "range" {
dict.push(
(
"__iter__",
Value::Function(FunctionValue::{
name: "range.__iter__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"count",
Value::Function(FunctionValue::{
name: "range.count",
params: ["self", "value"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"index",
Value::Function(FunctionValue::{
name: "range.index",
params: ["self", "value", "start", "stop"],
defaults: [Value::Int(0N), Value::Int(9223372036854775807N)],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__len__",
Value::Function(FunctionValue::{
name: "range.__len__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__bool__",
Value::Function(FunctionValue::{
name: "range.__bool__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__contains__",
Value::Function(FunctionValue::{
name: "range.__contains__",
params: ["self", "value"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__getitem__",
Value::Function(FunctionValue::{
name: "range.__getitem__",
params: ["self", "index"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "property" {
dict.push(
(
"__init__",
Value::Function(FunctionValue::{
name: "property.__init__",
params: ["self", "fget", "fset", "fdel", "doc"],
defaults: [Value::None, Value::None, Value::None, Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__get__",
Value::Function(FunctionValue::{
name: "property.__get__",
params: ["self", "obj", "owner"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"getter",
Value::Function(FunctionValue::{
name: "property.getter",
params: ["self", "fget"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"setter",
Value::Function(FunctionValue::{
name: "property.setter",
params: ["self", "fset"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"deleter",
Value::Function(FunctionValue::{
name: "property.deleter",
params: ["self", "fdel"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "type" {
dict.push(
(
"__new__",
Value::Function(FunctionValue::{
name: "type.__new__",
params: ["mcls", "name", "bases", "dict"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "dict" {
dict.push(
(
"fromkeys",
Value::Function(FunctionValue::{
name: "dict.fromkeys",
params: [],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"get",
Value::Function(FunctionValue::{
name: "dict.get",
params: ["self", "key", "default"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"pop",
Value::Function(FunctionValue::{
name: "dict.pop",
params: ["self", "key", "default"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"popitem",
Value::Function(FunctionValue::{
name: "dict.popitem",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"setdefault",
Value::Function(FunctionValue::{
name: "dict.setdefault",
params: ["self", "key", "default"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"keys",
Value::Function(FunctionValue::{
name: "dict.keys",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"values",
Value::Function(FunctionValue::{
name: "dict.values",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"items",
Value::Function(FunctionValue::{
name: "dict.items",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"update",
Value::Function(FunctionValue::{
name: "dict.update",
params: ["self", "other"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"clear",
Value::Function(FunctionValue::{
name: "dict.clear",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__contains__",
Value::Function(FunctionValue::{
name: "dict.__contains__",
params: ["self", "key"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__getitem__",
Value::Function(FunctionValue::{
name: "dict.__getitem__",
params: ["self", "key"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__setitem__",
Value::Function(FunctionValue::{
name: "dict.__setitem__",
params: ["self", "key", "value"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__delitem__",
Value::Function(FunctionValue::{
name: "dict.__delitem__",
params: ["self", "key"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__iter__",
Value::Function(FunctionValue::{
name: "dict.__iter__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__len__",
Value::Function(FunctionValue::{
name: "dict.__len__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "tuple" {
dict.push(
(
"__new__",
Value::Function(FunctionValue::{
name: "tuple.__new__",
params: ["cls", "iterable"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "int" {
dict.push(
(
"__new__",
Value::Function(FunctionValue::{
name: "int.__new__",
params: ["cls", "value"],
defaults: [Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__bool__",
Value::Function(FunctionValue::{
name: "int.__bool__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__index__",
Value::Function(FunctionValue::{
name: "int.__index__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"__repr__",
Value::Function(FunctionValue::{
name: "int.__repr__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "float" {
dict.push(
(
"__repr__",
Value::Function(FunctionValue::{
name: "float.__repr__",
params: ["self"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "str" {
dict.push(
(
"join",
Value::Function(FunctionValue::{
name: "str.join",
params: ["self", "iterable"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"maketrans",
Value::Function(FunctionValue::{
name: "str.maketrans",
params: [],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "classmethod" {
dict.push(
(
"__init__",
Value::Function(FunctionValue::{
name: "classmethod.__init__",
params: ["self", "func"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "staticmethod" {
dict.push(
(
"__init__",
Value::Function(FunctionValue::{
name: "staticmethod.__init__",
params: ["self", "func"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
if name == "property" {
dict.push(
(
"__init__",
Value::Function(FunctionValue::{
name: "property.__init__",
params: ["self", "fget=", "fset=", "fdel=", "doc="],
defaults: [Value::None, Value::None, Value::None, Value::None],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"getter",
Value::Function(FunctionValue::{
name: "property.getter",
params: ["self", "fget"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"setter",
Value::Function(FunctionValue::{
name: "property.setter",
params: ["self", "fset"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
dict.push(
(
"deleter",
Value::Function(FunctionValue::{
name: "property.deleter",
params: ["self", "fdel"],
defaults: [],
body: [],
is_generator: false,
is_async: false,
closure: [],
}),
),
)
}
ClassValue::{ name, bases, dict, }
}
///|
fn normalize_index(index : Int32, length : Int32) -> Int32 {
if index < 0 {
length + index
} else {
index
}
}
///|
fn index_from_value(
value : Value,
default : Int32,
) -> Result[Int32, RuntimeError] {
match value {
Value::None => Ok(default)
Value::Int(v) => bigint_to_int_checked(v)
Value::Bool(v) => Ok(if v { 1 } else { 0 })
Value::Float(v) => Ok(v.to_int())
_ =>
Err(
make_runtime_error(RuntimeErrorKind::Type, "invalid index".to_string()),
)
}
}
///|
fn maybe_index_from_value(value : Value) -> Result[Int32?, RuntimeError] {
match value {
Value::None => Ok(None)
_ =>
match index_from_value(value, 0) {
Ok(v) => Ok(Some(v))
Err(err) => Err(err)
}
}
}
///|
fn step_from_value(value : Value) -> Result[Int32, RuntimeError] {
match value {
Value::None => Ok(1)
_ => index_from_value(value, 1)
}
}
///|
fn slice_params_from_values(
length : Int32,
start_value : Value,
end_value : Value,
step_value : Value,
) -> Result[(Int32, Int32, Int32), RuntimeError] {
let step = match step_from_value(step_value) {
Ok(v) => v
Err(err) => return Err(err)
}
if step == 0 {
return Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"ValueError: slice step cannot be zero".to_string(),
),
)
}
let start_opt = match maybe_index_from_value(start_value) {
Ok(v) => v
Err(err) => return Err(err)
}
let end_opt = match maybe_index_from_value(end_value) {
Ok(v) => v
Err(err) => return Err(err)
}
if step > 0 {
let mut start = match start_opt {
Some(v) => v
None => 0
}
let mut end = match end_opt {
Some(v) => v
None => length
}
if start < 0 {
start += length
}
if start < 0 {
start = 0
}
if start > length {
start = length
}
if end < 0 {
end += length
}
if end < 0 {
end = 0
}
if end > length {
end = length
}
Ok((start, end, step))
} else {
let mut start = match start_opt {
Some(v) => v
None => length - 1
}
let mut end = match end_opt {
Some(v) => v
None => -1
}
if start < 0 {
start += length
}
if start < 0 {
start = -1
}
if start >= length {
start = length - 1
}
// For negative-step slices, CPython treats `stop=None` specially:
// `xs[::-1]` uses `stop=-1` (not normalized), but `xs[:-1:-1]` uses
// `stop=length-1` (because -1 was explicit).
if end_opt is Some(_) && end < 0 {
end += length
}
if end < 0 {
end = -1
}
if end >= length {
end = length - 1
}
Ok((start, end, step))
}
}
///|
fn slice_indices_from_values(
length : Int32,
start_value : Value,
end_value : Value,
step_value : Value,
) -> Result[Array[Int32], RuntimeError] {
if length <= 0 {
return Ok([])
}
let (start, end, step) = match
slice_params_from_values(length, start_value, end_value, step_value) {
Ok(v) => v
Err(err) => return Err(err)
}
let out : Array[Int32] = []
if step > 0 {
let mut i = start
while i < end {
out.push(i)
i += step
}
} else {
let mut i = start
while i > end {
out.push(i)
i += step
}
}
Ok(out)
}
///|
fn slice_values_by_indices(
values : Array[Value],
indices : Array[Int32],
) -> Array[Value] {
let out : Array[Value] = []
for idx in indices {
if idx >= 0 && idx < values.length() {
out.push(values[idx])
}
}
out
}
///|
fn slice_ints_by_indices(
values : Array[Int32],
indices : Array[Int32],
) -> Array[Int32] {
let out : Array[Int32] = []
for idx in indices {
if idx >= 0 && idx < values.length() {
out.push(values[idx])
}
}
out
}
///|
fn slice_string_by_indices(
chars : Array[Char],
indices : Array[Int32],
) -> String {
let buf = StringBuilder()
for idx in indices {
if idx >= 0 && idx < chars.length() {
buf.write_char(chars[idx])
}
}
buf.to_string()
}
///|
fn is_cell_value(value : Value) -> Bool {
match value {
Value::Instance(inst) => inst.class.name == "cell"
_ => false
}
}
///|
fn cell_new(value : Value) -> Value {
Value::Instance(InstanceValue::{
class: ClassValue::{ name: "cell", bases: [], dict: [], },
dict: [
("hashvalue", Value::Int(fresh_object_hashvalue())),
("value", value),
],
})
}
///|
fn cell_new_unbound() -> Value {
Value::Instance(InstanceValue::{
class: ClassValue::{ name: "cell", bases: [], dict: [], },
dict: [("hashvalue", Value::Int(fresh_object_hashvalue()))],
})
}
///|
fn cell_get_value(value : Value) -> Value? {
match value {
Value::Instance(inst) =>
if inst.class.name == "cell" {
get_named_value(inst.dict, "value")
} else {
None
}
_ => None
}
}
///|
fn cell_set_value(cell : Value, value : Value) -> Unit {
match cell {
Value::Instance(inst) =>
if inst.class.name == "cell" {
set_named_value(inst.dict, "value", value)
} else {
()
}
_ => ()
}
}
///|
fn cell_clear_value(cell : Value) -> Unit {
match cell {
Value::Instance(inst) =>
if inst.class.name == "cell" {
for idx = 0; idx < inst.dict.length(); idx = idx + 1 {
if inst.dict[idx].0 == "value" {
let _ = inst.dict.remove(idx)
break
}
}
} else {
()
}
_ => ()
}
}
///|
fn deref_cell_value(value : Value) -> Value {
match cell_get_value(value) {
Some(v) => v
None => value
}
}
///|
fn maybe_deref_cell_value(value : Value) -> Value? {
if is_cell_value(value) {
cell_get_value(value)
} else {
Some(value)
}
}
///|
fn ensure_local_cell(
locals : Array[(String, Value)],
name : String,
initial : Value,
) -> Unit {
for i = 0; i < locals.length(); i = i + 1 {
if locals[i].0 == name {
if is_cell_value(locals[i].1) {
return
}
locals[i] = (name, cell_new(initial))
return
}
}
locals.push((name, cell_new(initial)))
}
///|
fn shadow_local_cell(
locals : Array[(String, Value)],
name : String,
initial : Value,
) -> Unit {
for i = 0; i < locals.length(); i = i + 1 {
if locals[i].0 == name {
locals[i] = (name, cell_new(initial))
return
}
}
locals.push((name, cell_new(initial)))
}
///|
fn ensure_local_cell_unbound(
locals : Array[(String, Value)],
name : String,
) -> Unit {
for i = 0; i < locals.length(); i = i + 1 {
if locals[i].0 == name {
if is_cell_value(locals[i].1) {
return
}
locals[i] = (name, cell_new(locals[i].1))
return
}
}
locals.push((name, cell_new_unbound()))
}
///|
fn capture_cell_closure(
locals : Array[(String, Value)],
) -> Array[(String, Value)] {
let out : Array[(String, Value)] = []
for pair in locals {
if is_cell_value(pair.1) {
out.push(pair)
}
}
out
}
///|
let closure_env_stack : Ref[Array[Array[(String, Value)]]] = { val: [], }
///|
fn push_closure_env(env : Array[(String, Value)]) -> Unit {
closure_env_stack.val.push(env)
}
///|
fn pop_closure_env() -> Unit {
if closure_env_stack.val.length() > 0 {
let _ = closure_env_stack.val.pop()
}
}
///|
fn current_closure_env() -> Array[(String, Value)] {
if closure_env_stack.val.length() > 0 {
closure_env_stack.val[closure_env_stack.val.length() - 1]
} else {
[]
}
}
///|
fn has_active_closure_env() -> Bool {
closure_env_stack.val.length() > 0
}
///|
let active_config_stack : Ref[Array[Config]] = { val: [], }
///|
fn push_active_config(config : Config) -> Unit {
active_config_stack.val.push(config)
}
///|
fn pop_active_config() -> Unit {
if active_config_stack.val.length() == 0 {
return
}
let _ = active_config_stack.val.pop()
}
///|
fn current_config() -> Config {
if active_config_stack.val.length() == 0 {
Config::default()
} else {
active_config_stack.val[active_config_stack.val.length() - 1]
}
}
///|
let current_workdir_ref : Ref[String] = { val: ".", }
///|
// The working directory at interpreter start. We keep this stable so that
// sys.path entries like "Lib" continue to resolve even if user code calls
// os.chdir() (we track chdir virtually via `current_workdir_ref`).
let initial_workdir_ref : Ref[String] = { val: ".", }
///|
fn current_workdir() -> String {
current_workdir_ref.val
}
///|
fn initial_workdir() -> String {
initial_workdir_ref.val
}
///|
fn set_initial_workdir(path : String) -> Unit {
initial_workdir_ref.val = path
}
///|
fn set_current_workdir(path : String) -> Unit {
current_workdir_ref.val = path
}
///|
fn resolve_path_from_initial_cwd(path : String) -> String {
if path.has_prefix("/") {
return path
}
let cwd = initial_workdir()
if cwd.length() == 0 || cwd == "." {
path
} else {
@path.Path(cwd).join(@path.Path(path)).to_string()
}
}
///|
fn resolve_path_from_cwd(path : String) -> String {
if path.has_prefix("/") {
return path
}
let cwd = current_workdir()
if cwd.length() == 0 || cwd == "." {
path
} else {
@path.Path(cwd).join(@path.Path(path)).to_string()
}
}
///|
fn set_global_value(
globals : Array[(String, Value)],
name : String,
value : Value,
) -> Unit {
for i = 0; i < globals.length(); i = i + 1 {
if globals[i].0 == name {
globals[i] = (name, value)
return
}
}
globals.push((name, value))
}
///|
fn get_global_value(globals : Array[(String, Value)], name : String) -> Value? {
for pair in globals {
if pair.0 == name {
return Some(pair.1)
}
}
None
}
///|
fn delete_global_value(globals : Array[(String, Value)], name : String) -> Unit {
for idx = 0; idx < globals.length(); idx = idx + 1 {
if globals[idx].0 == name {
let _ = globals.remove(idx)
break
}
}
}
///|
fn clone_locals(locals : Array[(String, Value)]) -> Array[(String, Value)] {
let result : Array[(String, Value)] = []
for pair in locals {
result.push(pair)
}
result
}
///|
fn set_local_value(
locals : Array[(String, Value)],
name : String,
value : Value,
) -> Unit {
for i = 0; i < locals.length(); i = i + 1 {
if locals[i].0 == name {
if is_cell_value(locals[i].1) {
cell_set_value(locals[i].1, value)
} else {
locals[i] = (name, value)
}
return
}
}
locals.push((name, value))
}
///|
fn get_local_value(locals : Array[(String, Value)], name : String) -> Value? {
for pair in locals {
if pair.0 == name {
return Some(pair.1)
}
}
None
}
///|
fn delete_local_value(locals : Array[(String, Value)], name : String) -> Unit {
for idx = 0; idx < locals.length(); idx = idx + 1 {
if locals[idx].0 == name {
let _ = locals.remove(idx)
break
}
}
}
///|
/// Scope declarations (minimal): tracks `global` names in the active function.
priv struct ScopeDecls {
globals : Array[String]
nonlocals : Array[String]
}
///|
let scope_decls_stack : Ref[Array[ScopeDecls]] = { val: [], }
///|
fn push_scope_decls_with_nonlocals(
global_names : Array[String],
nonlocal_names : Array[String],
) -> Unit {
scope_decls_stack.val.push(ScopeDecls::{
globals: global_names,
nonlocals: nonlocal_names,
})
}
///|
fn pop_scope_decls() -> Unit {
if scope_decls_stack.val.length() > 0 {
let _ = scope_decls_stack.val.pop()
}
}
///|
fn in_function_scope() -> Bool {
scope_decls_stack.val.length() > 0
}
///|
fn scope_name_is_global(name : String) -> Bool {
if scope_decls_stack.val.length() == 0 {
return false
}
let decls = scope_decls_stack.val[scope_decls_stack.val.length() - 1]
for n in decls.globals {
if n == name {
return true
}
}
false
}
///|
fn scope_name_is_nonlocal(name : String) -> Bool {
if scope_decls_stack.val.length() == 0 {
return false
}
let decls = scope_decls_stack.val[scope_decls_stack.val.length() - 1]
for n in decls.nonlocals {
if n == name {
return true
}
}
false
}
///|
fn get_scoped_value(
locals : Array[(String, Value)],
globals : Array[(String, Value)],
name : String,
) -> Value? {
if scope_name_is_global(name) {
get_global_value(globals, name)
} else if scope_name_is_nonlocal(name) {
match lookup_closure_cell(name) {
Some(cell) => maybe_deref_cell_value(cell)
None => None
}
} else {
match get_local_value(locals, name) {
Some(v) => maybe_deref_cell_value(v)
None => None
}
}
}
///|
fn set_scoped_value(
locals : Array[(String, Value)],
globals : Array[(String, Value)],
name : String,
value : Value,
) -> Unit {
if scope_name_is_global(name) {
set_global_value(globals, name, value)
} else if scope_name_is_nonlocal(name) {
match lookup_closure_cell(name) {
Some(cell) => cell_set_value(cell, value)
None => ()
}
} else {
set_local_value(locals, name, value)
}
}
///|
fn delete_scoped_value(
locals : Array[(String, Value)],
globals : Array[(String, Value)],
name : String,
) -> Result[Bool, RuntimeError] {
if scope_name_is_global(name) {
if get_global_value(globals, name) is Some(_) {
delete_global_value(globals, name)
Ok(true)
} else {
Ok(false)
}
} else if scope_name_is_nonlocal(name) {
Ok(false)
} else {
match get_local_value(locals, name) {
Some(v) =>
if is_cell_value(v) {
if cell_get_value(v) is None {
Err(
make_runtime_error(
RuntimeErrorKind::Runtime,
"UnboundLocalError: local variable '" +
name +
"' referenced before assignment",
),
)
} else {
cell_clear_value(v)
Ok(true)
}
} else {
delete_local_value(locals, name)
Ok(true)
}
None => Ok(false)
}
}
}
///|
fn lookup_closure_cell(name : String) -> Value? {
if closure_env_stack.val.length() == 0 {
return None
}
let env = closure_env_stack.val[closure_env_stack.val.length() - 1]
for pair in env {
if pair.0 == name && is_cell_value(pair.1) {
return Some(pair.1)
}
}
None
}
///|
fn merge_cell_closures(
locals : Array[(String, Value)],
extra : Array[(String, Value)],
) -> Array[(String, Value)] {
let out = capture_cell_closure(locals)
for pair in extra {
let mut exists = false
for existing in out {
if existing.0 == pair.0 {
exists = true
break
}
}
if !exists {
out.push(pair)
}
}
out
}
///|
// Iteration variables belong to the comprehension, while free names and walrus
// assignments retain the enclosing declarations. Capture this for suspended
// async comprehensions as well as immediately evaluated comprehensions.
fn comprehension_scope_decls(clauses : Array[CompClause]) -> ScopeDecls {
let names : Array[String] = []
for clause in clauses {
names.append(clause.targets)
}
scope_decls_without_bindings(names)
}
///|
fn scope_decls_without_bindings(names : Array[String]) -> ScopeDecls {
if scope_decls_stack.val.is_empty() {
return ScopeDecls::{ globals: [], nonlocals: [], }
}
let enclosing = scope_decls_stack.val[scope_decls_stack.val.length() - 1]
ScopeDecls::{
globals: enclosing.globals.filter(fn(name) { !names.contains(name) }),
nonlocals: enclosing.nonlocals.filter(fn(name) { !names.contains(name) }),
}
}