crosslang/octive-lean/OctiveLean/Env.lean
Maximus Gorog 6592cd058d Add 'octive-lean/' from commit '4b6fcec565a170d7029d4ccba21be2ecd0512d13'
git-subtree-dir: octive-lean
git-subtree-mainline: fd3d42ae33
git-subtree-split: 4b6fcec565
2026-05-12 02:59:14 -06:00

114 lines
4.5 KiB
Text
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import OctiveLean.Value
namespace OctiveLean
/-! Scope and environment management -/
/-- A single scope frame (function call frame or top-level) -/
structure Scope where
vars : Array (String × Value) -- local variables
globals : Array String -- names declared `global` in this scope
persist : Array String -- names declared `persistent`
retVals : Array String -- expected return variable names
deriving Inhabited
namespace Scope
def empty : Scope := { vars := #[], globals := #[], persist := #[], retVals := #[] }
def get (s : Scope) (name : String) : Option Value :=
s.vars.findSome? fun (k, v) => if k == name then some v else none
def set (s : Scope) (name : String) (val : Value) : Scope :=
let idx := s.vars.findIdx? fun (k, _) => k == name
match idx with
| some i => { s with vars := s.vars.set! i (name, val) }
| none => { s with vars := s.vars.push (name, val) }
def del (s : Scope) (name : String) : Scope :=
{ s with vars := s.vars.filter fun (k, _) => k != name }
end Scope
/-- The interpreter environment: a call stack of scopes + global frame -/
structure Env where
stack : Array Scope -- call stack; last = current frame
globals : Array (String × Value) -- global workspace
builtinRegistry : Array (String × (Array Value → IO (Array Value)))
deriving Inhabited
namespace Env
def empty : Env := { stack := #[Scope.empty], globals := #[], builtinRegistry := #[] }
/-- Current (innermost) scope -/
def currentScope (env : Env) : Scope :=
if env.stack.isEmpty then Scope.empty
else env.stack.back!
/-- Update the current scope -/
def updateScope (env : Env) (f : Scope → Scope) : Env :=
if env.stack.isEmpty then env
else { env with stack := env.stack.set! (env.stack.size - 1) (f env.currentScope) }
/-- Look up a variable: current scope, then globals -/
def get (env : Env) (name : String) : Option Value :=
let scope := env.currentScope
-- if declared global in this scope, redirect to global frame
if scope.globals.contains name then
env.globals.findSome? fun (k, v) => if k == name then some v else none
else
match scope.get name with
| some v => some v
| none =>
-- also check global frame for top-level variables
if env.stack.size == 1 then
env.globals.findSome? fun (k, v) => if k == name then some v else none
else
-- inside a function: functions from top-level workspace are accessible
let globalVal := env.stack[0]?.bind (·.get name)
match globalVal with
| some v => match v with
| .fn _ => some v
| _ => env.globals.findSome? fun (k, gv) => if k == name then some gv else none
| none => env.globals.findSome? fun (k, v) => if k == name then some v else none
/-- Set a variable in the current scope -/
def set (env : Env) (name : String) (val : Value) : Env :=
let scope := env.currentScope
if scope.globals.contains name then
-- write to global frame
let idx := env.globals.findIdx? fun (k, _) => k == name
match idx with
| some i => { env with globals := env.globals.set! i (name, val) }
| none => { env with globals := env.globals.push (name, val) }
else
env.updateScope (·.set name val)
/-- Declare a name as global in the current scope -/
def declareGlobal (env : Env) (name : String) : Env :=
env.updateScope fun s => { s with globals := s.globals.push name }
/-- Push a new call frame -/
def pushFrame (env : Env) (retVals : Array String) : Env :=
{ env with stack := env.stack.push { Scope.empty with retVals } }
/-- Pop the current call frame; return (env without frame, frame's return values) -/
def popFrame (env : Env) : Env × Scope :=
if env.stack.size <= 1 then (env, Scope.empty)
else
let frame := env.stack.back!
({ env with stack := env.stack.pop }, frame)
/-- Register a builtin function -/
def registerBuiltin (env : Env) (name : String)
(fn : Array Value → IO (Array Value)) : Env :=
let idx := env.builtinRegistry.findIdx? fun (k, _) => k == name
match idx with
| some i => { env with builtinRegistry := env.builtinRegistry.set! i (name, fn) }
| none => { env with builtinRegistry := env.builtinRegistry.push (name, fn) }
/-- Look up a builtin -/
def getBuiltin (env : Env) (name : String)
: Option (Array Value → IO (Array Value)) :=
env.builtinRegistry.findSome? fun (k, v) => if k == name then some v else none
end Env
end OctiveLean