Class: Heist::Runtime::Frame
- Inherits:
-
Object
- Object
- Heist::Runtime::Frame
- Defined in:
- lib/heist/runtime/frame.rb
Overview
Frame
is used by the Stack
class to encapsulate a single Expression
bound to a Scope
. Frame
objects evaluate expressions one sub-expression at a time, providing fine-grained opportunity for inspecting intermediate results and supporting the Scheme notion of continuations.
The state of a Frame
is held in five variables: @expression
, @scope
, @values
, @current
and @complete
. @expression
is the Expression
object the Frame
is evaluating, and @scope
is a Scope
object in which to evaluate it. @values
is a copy of @expression
that is used to store the values returned by the subexpressions; it represents the partially evaluated state of the expression for use when saving a Stack
when creating a Continuation
. @current
is a pointer to the subexpression that is to be evaluated next, and @complete
is a boolean that indicates whether all the required subexpressions have been evaluated.
See Stack
for a fuller explanation of how expressions are evaluated.
Direct Known Subclasses
Instance Attribute Summary collapse
-
#expression ⇒ Object
readonly
Returns the value of attribute expression.
-
#scope ⇒ Object
readonly
Returns the value of attribute scope.
Instance Method Summary collapse
-
#clone ⇒ Object
Returns a copy of the
Frame
. -
#complete? ⇒ Boolean
Returns
true
iff theFrame
has completed evaluating its expression. -
#fill!(subexpr, value) ⇒ Object
Fills the hole corresponding to
subexpr
in the receivingFrame
withvalue
. -
#initialize(expression, scope) ⇒ Frame
constructor
A
Frame
is initialized using anExpression
and aScope
, which are simply stored as instance variables. -
#process! ⇒ Object
Processes a single subexpression from the
Frame
and returns the result. -
#replaces(expression) ⇒ Object
Sets the replacement target for the receiving
Frame
to the givenexpression
. -
#target ⇒ Object
Returns the
Expression
that is the replacement target for the previousFrame
on theStack
.
Constructor Details
#initialize(expression, scope) ⇒ Frame
A Frame
is initialized using an Expression
and a Scope
, which are simply stored as instance variables. No evaluation takes place upon initialization.
28 29 30 31 |
# File 'lib/heist/runtime/frame.rb', line 28 def initialize(expression, scope) reset!(expression) @scope = scope end |
Instance Attribute Details
#expression ⇒ Object (readonly)
Returns the value of attribute expression.
23 24 25 |
# File 'lib/heist/runtime/frame.rb', line 23 def expression @expression end |
#scope ⇒ Object (readonly)
Returns the value of attribute scope.
23 24 25 |
# File 'lib/heist/runtime/frame.rb', line 23 def scope @scope end |
Instance Method Details
#clone ⇒ Object
Returns a copy of the Frame
. The @values
variable is also copied by this method so that state changes do not transfer from the copy to the original.
88 89 90 91 92 |
# File 'lib/heist/runtime/frame.rb', line 88 def clone copy, values = super, @values.clone copy.instance_eval { @values = values } copy end |
#complete? ⇒ Boolean
Returns true
iff the Frame
has completed evaluating its expression.
34 35 36 |
# File 'lib/heist/runtime/frame.rb', line 34 def complete? @complete end |
#fill!(subexpr, value) ⇒ Object
Fills the hole corresponding to subexpr
in the receiving Frame
with value
. When a hole is filled, the @current
pointer is moved to the next subexpression that needs evaluating.
97 98 99 100 101 102 103 104 105 106 107 108 109 |
# File 'lib/heist/runtime/frame.rb', line 97 def fill!(subexpr, value) value = value.value if Value === value and @values and not (Syntax === @values.car) epair, vpair = @expression, @values while Cons === epair and not epair.null? if epair.car.equal?(subexpr) vpair.car = value @current = epair.cdr end epair, vpair = epair.cdr, vpair.cdr end end |
#process! ⇒ Object
Processes a single subexpression from the Frame
and returns the result. If all the required subexpressions have been evaluated, we call the resulting function with the arguments and return the result of that call.
42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 |
# File 'lib/heist/runtime/frame.rb', line 42 def process! case @expression when Cons then # If we have evaluated all the subexpressions, or we have a # +Syntax+ value first in the list, we can make a function call # and return its result. function = @values.car if Syntax === function or @current.null? @complete = true raise SyntaxError.new("Invalid expression: #{@expression}") unless Function === function result = function.call(@scope, @values.cdr) # If the result of the call is a macro expansion, inline it # and set its expression as the new expression for the frame return result unless Macro::Expansion === result return reset!(result.expression, true) end # Otherwise, we still have work to do on the subexpressions, so # we evaluate the next one in the list. stack = @scope.runtime.stack stack << Frame.new(@current.car, @scope) # If it's a Vector (it will be unquoted if it has arrived here, copy # it and return it. We cannot freeze it for fear of breaking macros, # and we must copy it so that vector-set! cannot modify the parse tree. when Vector then @complete = true @expression.dup # If the expression is an +Identifier+, just look it up. when Identifier then @complete = true @scope[@expression] # Otherwise, the expression is just data so return it. else @complete = true Heist.evaluate(@expression, @scope) end end |
#replaces(expression) ⇒ Object
Sets the replacement target for the receiving Frame
to the given expression
. When the receiving frame returns, it will fill the hole corresponding to the expression
in the previous Frame
on the Stack
.
115 116 117 |
# File 'lib/heist/runtime/frame.rb', line 115 def replaces(expression) @target = expression end |
#target ⇒ Object
Returns the Expression
that is the replacement target for the previous Frame
on the Stack
. When the receiving Frame
completes, the returned value will replace the return value of this method in the previous Frame
. The target
of a Frame
is its @expression
by default, but tail calls require us to change the target as, when a tail call returns, the Frame
that originated it will no longer by on the stack so replaces
is used to change the replacement target for the tail call.
127 128 129 |
# File 'lib/heist/runtime/frame.rb', line 127 def target @target || @expression end |