148 Denotation Environment

The denotational rules define the frame denoted by each path expression. Most of the work is delegated to frame operations such as domain, relabel, close∃, close∀, ⊓, ⊔, --, tupleMerge, bind∃, and bind∀.

The rough mental model is that a rule translates an expression relative to the current frame and returns a new frame. The environment stack is bookkeeping for the cases where more information is available: structural lookup through enclosing frames, let-bound names, and tuple components already constructed.

Examples use the display notation introduced in the frame calculus section:


result(γ) = { attr = Term:Type, ... }
free(γ) = { Var:Type from attr, ... }

The formal triples are still present: Attr = Term:Type is (Attr,Term,Type) in the result, and Var:Type from Attr is (Var,Type,Attr) in the free variables. Lowercase names such as s are Predica source-code variables supplied by the program; uppercase names such as S, P, SP, N, and C are generated logic variables.

Environment and denotation function

An environment stack is a stack of frames. The top frame is the current frame; lower frames are available for structural lookup, let-bound names, and tuple-local names.


Γ = [γ₀, γ₁, ..., γₙ]
top(Γ) = γₙ
init(Γ) = [γ₀, γ₁, ..., γₙ₋₁]

ℒ(Γ, P, E) = γ

The protected set P contains variables protected from closure introduced while translating E.


emptyFrame = ⟨∅ for ∅ where true⟩

topOrEmpty(Γ)
{
    if Γ = []
        return emptyFrame
    else
        return top(Γ)
}

currentFrame(Γ) = topOrEmpty(Γ)

currentDomain(Γ) = domain(currentFrame(Γ))

push(Γ, γ) = Γ with γ appended on top

Some helper rules use the following meta-level sentinels. They are not Predica expressions, not values, and not frames in the semantic domain:


noFrame  = absence of a receiver frame
noResult = absence of a produced result term

currentFrame is the top frame, including its result. currentDomain is the same frame viewed only through its free variables and formula. Scalar terms use currentDomain; operations such as this, splat, and attribute navigation use frames with their results from the environment.

Tuple construction needs a way to make earlier tuple components available to later tuple components without replacing the current frame:


insertBeforeTop(Γ, γ)
{
    if result(γ) = ∅
        return Γ
    elif Γ = []
        return [γ]
    else
        return init(Γ) ⊕ [γ] ⊕ [top(Γ)]
}