“If \(B\) is a subclass of \(A\), then an object of \(B\) can be used wherever \(A\) is expected.”
To do this, we will lay out:
the pointer *
points to a method table
Other Semantics than structural operational semantics
Runtime Errors
We have to catch some runtime errors:
- calling on voids
- method dispatch problems
What to track in context?
- environment: where in memory a variable is stored?
Map<Symbol, void *>
- store: what is in memory?
Map<void *, Value>
This means that a judgment is:
\begin{equation} \text{self}, E, S \vdash e: V, S' \end{equation}
that is, “given environment \(E\), store \(S\), we have that \(e\) evaluates to \(V\), and \(S\) are side effects.”
The reason why environments doesn’t change is because we can’t pass globals up, so scoping always travels down.
assignments
As you would expect:
\begin{equation} \frac{so,E, S \vdash e : v, S_1 | E\qty(id) = I_{id} | S_2=S_1[v / I_{id}]}{so, E, S \vdash id \leftarrow e: v, S_2} \end{equation}
Notice! we have side effects that are CHAINED; if evaluating \(e\) has side effects, we carry them into our evaluation in the side effects \(S \to S_1 \to S_2\).
loops
We use recursion!
\begin{equation} \frac{\begin{align}&so, E, S \vdash e_1: true, S_1 \\ &so, E, S_1, \vdash S_2 : v, S_2 \\ &so, E, S_2 \vdash \text{ while } e_1 \text{ loop } e_2 \text{ pool } : void, S_3\end{align}}{so, E, S \vdash \text{ while } e_1 \text{ loop } e_2 \text{ pool } : void, S_3} \end{equation}
how do we allocate new spaces?
We can use the syntax
\begin{align} &l_{new} = \text{newloc}\qty(S_1) \\ &so, E[l_{new} / id], S_1[v_1 / l_{new}] \vdash \dots \end{align}
how do we allocate new objects?
…what about self
?
\begin{equation} \frac{}{\text{so}, E, S \vdash \text{self}: \text{so}, S} \end{equation}
errors to check
- dispatch on void
- division by zero
- substring out of range
- heap overflow
generally we need to exit gracefully