Programming Languages and Logics Lecture Notes Cornell Cs4110 Itebooks PDF Download
Programming Languages and Logics Lecture Notes Cornell Cs4110 Itebooks PDF Download
https://ebookbell.com/product/programming-languages-and-logics-
lecture-notes-cornell-cs4110-itebooks-23837668
https://ebookbell.com/product/foundations-of-probabilistic-logic-
programming-languages-semantics-inference-and-learning-2nd-edition-
fabrizio-riguzzi-50378238
https://ebookbell.com/product/foundations-of-probabilistic-logic-
programming-languages-semantics-inference-and-learning-fabrizio-
riguzzi-10467928
https://ebookbell.com/product/programming-languages-and-systems-in-
computational-economics-and-finance-2002th-edition-jaime-r-
marquez-54965484
Programming Languages And Systems 33rd European Symposium On
Programming Part Ii Stephanie Weirich
https://ebookbell.com/product/programming-languages-and-systems-33rd-
european-symposium-on-programming-part-ii-stephanie-weirich-56563906
https://ebookbell.com/product/programming-languages-and-systems-8th-
asian-symposium-aplas-2010-shanghai-china-
november-28-december-1-2010-proceedings-1st-edition-jiang-liu-2014234
https://ebookbell.com/product/programming-languages-and-systems-18th-
asian-symposium-aplas-2020-fukuoka-japan-
november-30-december-2-2020-proceedings-1st-ed-bruno-c-d-s-
oliveira-22498018
https://ebookbell.com/product/programming-languages-and-systems-1st-
edition-nobuko-yoshida-23892780
CS 4110 – Programming Languages and Logics
Lecture #2: Introduction to Semantics
What is the meaning of a program? When we write a program, we represent it using sequences of
characters. But these strings are just concrete syntax—they do not tell us what the program actually
means. It is tempting to define meaning by executing programs—either using an interpreter or
a compiler. But interpreters and compilers often have bugs! We could look in a specification
manual. But such manuals typically only offer an informal description of language constructs.
A better way to define meaning is to develop a formal, mathematical definition of the seman-
tics of the language. This approach is unambiguous, concise, and—most importantly—it makes
it possible to develop rigorous proofs about properties of interest. The main drawback is that the
semantics itself can be quite complicated, especially if one attempts to model all of the features of
a full-blown modern programming language.
There are three pedigreed ways of defining the meaning, or semantics, of a language:
• Operational semantics defines meaning in terms of execution on an abstract machine.
• Denotational semantics defines meaning in terms of mathematical objects such as functions.
• Axiomatic semantics defines meaning in terms of logical formulas satisfied during execution.
Each of these approaches has advantages and disadvantages in terms of how mathematically so-
phisticated they are, how easy they are to use in proofs, and how easy it is to use them to imple-
ment an interpreter or compiler. We will discuss these tradeoffs later in this course.
1 Arithmetic Expressions
To understand some of the key concepts of semantics, let us consider a very simple language
of integer arithmetic expressions with variable assignment. A program in this language is an
expression; executing a program means evaluating the expression to an integer. To describe the
syntactic structure of this language we will use variables that range over the following domains:
x, y, z ∈ Var
n, m ∈ Int
e ∈ Exp
Var is the set of program variables (e.g., foo, bar , baz , i , etc.). Int is the set of constant integers
(e.g., 42, 40, 7). Exp is the domain of expressions, which we specify using a BNF (Backus-Naur
Form) grammar:
e ::= x
|n
| e1 + e2
| e1 * e2
| x := e1 ; e2
1
Informally, the expression x := e1 ; e2 means that x is assigned the value of e1 before evaluating e2 .
The result of the entire expression is the value described by e2 .
This grammar specifies the syntax for the language. An immediate problem here is that the
grammar is ambiguous. Consider the expression 1 + 2 * 3. One can build two abstract syntax trees:
+ *
1 * + 3
2 3 1 2
There are several ways to deal with this problem. One is to rewrite the grammar for the same
language to make it unambiguous. But that makes the grammar more complex and harder to
understand. Another possibility is to extend the syntax to require parentheses around all addition
and multiplication expressions:
e ::= x
|n
| (e1 + e2 )
| (e1 * e2 )
| x := e1 ; e2
However, this also leads to unnecessary clutter and complexity. Instead, we separate the “con-
crete syntax” of the language (which specifies how to unambiguously parse a string into program
phrases) from its “abstract syntax” (which describes, possibly ambiguously, the structure of pro-
gram phrases). In this course we will assume that the abstract syntax tree is known. When writing
expressions, we will occasionally use parenthesis to indicate the structure of the abstract syntax
tree, but the parentheses are not part of the language itself. (For details on parsing, grammars,
and ambiguity elimination, see or take CS 4120.)
This closely matches the BNF grammar above. The abstract syntax tree (AST) of an expression
can be obtained by applying the datatype constructors in each case. For instance, the AST of
expression 2 * (foo + 1) is:
In OCaml, parentheses can be dropped when there is one single argument, so the above expression
can be written as:
2
Mul(Int 2, Add(Var "foo", Int 1))
We could express the same structure in a language like Java using a class hierarchy, although it
would be a little more complicated:
2 Operational semantics
We have an intuitive notion of what expressions mean. For example, the 7 + (4 * 2) evaluates to 15,
and i := 6 + 1 ; 2 * 3 * i evaluates to 42. In this section, we will formalize this intuition precisely.
An operational semantics describes how a program executes on an abstract machine. A small-step
operational semantics describes how such an execution proceeds in terms of successive reductions—
here, of an expression—until we reach a value that represents the result of the computation. The
state of the abstract machine is often referred to as a configuration. For our language a configuration
must include two pieces of information:
• a store (also known as environment or state), which maps integer values to variables. Dur-
ing program execution, we will refer to the store to determine the values associated with
variables, and also update the store to reect assignment of new values to variables,
• the expression to evaluate.
We will represent stores as partial functions from Var to Int and configurations as pairs of expres-
sions and stores:
Store ≜ Var ⇀ Int
Config ≜ Store × Exp
We will denote configurations using angle brackets. For instance, ⟨σ, (foo + 2) * (bar + 2)⟩ is a con-
figuration where σ is a store and (foo + 2) * (bar + 2) is an expression that uses two variables, foo
and bar . The small-step operational semantics for our language is a relation →⊆ Config × Config
that describes how one configuration transitions to a new configuration. That is, the relation →
shows us how to evaluate programs one step at a time. We use infix notation for the relation →.
That is, given any two configurations ⟨σ1 , e1 ⟩ and ⟨σ2 , e2 ⟩, if (⟨e1 , σ1 ⟩, ⟨e2 , σ2 ⟩) is in the relation →,
then we write ⟨σ1 , e1 ⟩ → ⟨σ2 , e2 ⟩. For example, we have ⟨σ, (4 + 2) * y⟩ → ⟨σ, 6 * y⟩. That is, we can
evaluate the configuration ⟨σ, (4 + 2) * y⟩ one step to get the configuration ⟨σ, 6 * y⟩.
Using this approach, defining the semantics of the language boils down to to defining the
relation → that describes the transitions between configurations.
One issue here is that the domain of integers is infinite, as is the domain of expressions. There-
fore, there is an infinite number of possible machine configurations, and an infinite number of
possible single-step transitions. We need a finite way of describing an infinite set of possible tran-
sitions. We can compactly describe → using inference rules:
3
n = σ(x)
VAR
⟨σ, x ⟩ → ⟨σ, n⟩
The meaning of an inference rule is that if the facts above the line holds, then the fact below the
line holds. The fact above the line are called premises; the fact below the line is called the conclusion.
The rules without premises are axioms; and the rules with premises are inductive rules. We use the
notation σ[x 7→ n] for the store that maps the variable x to integer n, and maps every other variable
to whatever σ maps it to. More explicitly, if f is the function σ[x 7→ n], then we have
{
n if y = x
f (y) =
σ(y) otherwise
Now we need to show that the premise actually holds and find out what e′1 is. We look for a rule
whose conclusion matches ⟨σ, foo + 2⟩ → ⟨e′1 , σ⟩. We find that LA DD is the only matching rule:
We repeat this reasoning for ⟨σ, foo⟩ → ⟨σ, e′′1 ⟩ and find that the only applicable rule is the axiom
VAR:
σ(foo) = 4
VAR
⟨σ, foo⟩ → ⟨σ, 4⟩
4
Since this is an axiom and has no premises, there is nothing left to prove. Hence, e′′1 = 4 and
e′1 = 4 + 2. We can put together the above pieces and build the following proof:
σ(foo) = 4
VAR
⟨σ, foo⟩ → ⟨σ, 4⟩
LA DD
⟨σ, foo + 2⟩ → ⟨σ, 4 + 2⟩
LM UL
⟨σ, (foo + 2) * (bar + 1)⟩ → ⟨σ, (4 + 2) * (bar + 1)⟩
This proves that, given our inference rules, the one-step transition
is derivable. The structure above is called a “proof tree” or “derivation”. It is important to keep in
mind that proof trees must be finite for the conclusion to be valid.
We can use a similar reasoning to find out the next evaluation step:
6=4+2
A DD
⟨σ, 4 + 2⟩ → ⟨σ, 6⟩
LM UL
⟨σ, (4 + 2) * (bar + 1)⟩ → ⟨σ, 6 * (bar + 1)⟩
And we can continue this process. At the end, we can put together all of these transitions, to get a
view of the entire computation:
The result of the computation is a number, 24. The machine configuration that contains the fi-
nal result is the point where the evaluation stops; they are called final configurations. For our
language of expressions, the final configurations are of the form ⟨σ, n⟩.
We write → ∗ for the reflexive and transitive closure of the relation →. That is, if ⟨σ, e⟩ →
∗⟨σ ′ , e′ ⟩ using zero or more steps, we can evaluate the configuration ⟨σ, e⟩ to ⟨σ ′ , e′ ⟩. Thus, we
have:
⟨σ, (foo + 2) * (bar + 1)⟩ → ∗⟨σ, 24⟩
5
CS 4110 – Programming Languages and Logics
Lecture #3: Inductive definitions and proofs
In this lecture, we will use the semantics of our simple language of arithmetic expressions,
e ::= x | n | e1 + e2 | e1 * e2 | x := e1 ; e2 ,
to express useful program properties, and we will prove these properties by induction.
1 Program Properties
There are a number of interesting questions about a language one can ask: Is it deterministic?
Are there non-terminating programs? What sorts of errors can arise during evaluation? Having a
formal semantics allows us to express these properties precisely.
fvs(x) ≜ {x}
fvs(n) ≜ {}
fvs(e1 + e2 ) ≜ fvs(e1 ) ∪ fvs(e2 )
fvs(e1 * e2 ) ≜ fvs(e1 ) ∪ fvs(e2 )
fvs(x := e1 ; e2 ) ≜ fvs(e1 ) ∪ (fvs(e2 ) \ {x})
Now we can formulate two properties that imply a variant of the soundness property above:
1
• Progress: For each expression e and store σ such that the free variables of e are contained in
the domain of σ, either e is an integer or there exists a possible transition for ⟨σ, e⟩,
∀e ∈ Exp. ∀σ ∈ Store.
fvs(e) ⊆ dom(σ) =⇒ e ∈ Int or (∃e′ ∈ Exp. ∃σ ′ ∈ Store. ⟨σ, e⟩ → ⟨σ ′ , e′ ⟩)
• Preservation: Evaluation preserves containment of free variables in the domain of the store,
The rest of this lecture shows how can we prove such properties using induction.
2 Inductive sets
Induction is an important concept in programming language theory. An inductively-defined set A is
one that is described using a finite collection of axioms and inductive (inference) rules. Axioms of
the form
a∈A
indicate that a is in the set A. Inductive rules
a1 ∈ A ... an ∈ A
a∈A
x ∈ Exp n ∈ Exp
These axioms and rules describe the same set of expressions as the grammar:
e ::= x | n | e1 + e2 | e1 * e2 | x := e1 ; e2
Example 2. The natural numbers (expressed here in unary notation) can be inductively defined:
n∈N
0∈N succ(n) ∈ N
2
Example 4. The multi-step evaluation relation can be inductively defined:
y ∈ fvs(e1 ) y ̸= x y ∈ fvs(e2 )
y ∈ fvs(x := e1 ; e2 ) y ∈ fvs(x := e1 ; e2 )
3 Inductive proofs
We can prove facts about elements of an inductive set using an inductive reasoning that follows
the structure of the set definition.
The proposition P (0) is the basis of the induction (also called the base case) while P (m) =⇒ P (m+1)
is called induction step (or the inductive case). While proving the induction step, the assumption that
P (m) holds is called the induction hypothesis.
3
Note that if the set A is the set of natural numbers from Example 2 above, then the requirements
for proving that P holds for all elements of A is equivalent to mathematical induction.
If A describes a syntactic set, then we refer to induction following the requirements above as
structural induction. If A is an operational semantics relation (such as the small-step operational se-
mantics relation →) then such an induction is called induction on derivations. We will see examples
of structural induction and induction on derivations throughout the course.
Progress: For each store σ and expression e such that the free variables of e are contained in the
domain of σ, either e is an integer or there exists a possible transition for ⟨σ, e⟩:
The idea is to build a proof that follows the inductive structure given by the grammar:
e ::= x | n | e1 + e2 | e1 * e2 | x := e1 ; e2
This technique is called “structural induction on e.” We analyze each case in the grammar and
show that P (e) holds for that case. Since the grammar productions e1 + e2 and e1 * e2 and x := e1 ; e2
are inductive, they are inductive steps in the proof; the cases for x and n are base cases. The proof
proceeds as follows.
by structural induction on e. We analyze several cases, one for each case in the grammar for
expressions:
Case e = x: Let σ be an arbitrary store, and assume that fvs(e) ⊆ dom(σ). By the definition of
fvs we have fvs(x) = {x}. By assumption we have {x} ⊆ dom(σ) and so x ∈ dom(σ). Let
n = σ(x). By the VAR axiom we have ⟨σ, x⟩ → ⟨σ, n⟩, which finishes the case.
Case e = n: We immediately have e ∈ Int, which finishes the case.
Case e = e1 + e2 : Let σ be an arbitrary store, and assume that fvs(e) ⊆ dom(σ). We will assume
that P (e1 ) and P (e2 ) hold and show that P (e) holds. Let’s expand these properties. We have
4
Subcase e1 = n1 and e2 = n2 : By rule A DD, we immediately have ⟨σ, n1 + n2 ⟩ → ⟨σ, p⟩, where
p = n1 + n2 .
Subcase e1 ̸∈ Int: By assumption and the definition of fvs we have
Hence, by the induction hypothesis P (e1 ) we also have ⟨σ, e1 ⟩ → ⟨σ ′ , e′ ⟩ for some e′ and
σ ′ . By rule LA DD we have ⟨σ, e1 + e2 ⟩ → ⟨σ ′ , e′ + e2 ⟩.
Subcase e1 = n1 and e2 ̸∈ Int: By assumption and the definition of fvs we have
Hence, by the induction hypothesis P (e2 ) we also have ⟨σ, e2 ⟩ → ⟨σ ′ , e′ ⟩ for some e′ and
σ ′ . By rule RA DD we have ⟨σ, e1 + e2 ⟩ → ⟨σ ′ , e1 + e′ ⟩, which finishes the case.
Case e = e1 * e2 : . Analogous to the previous case.
Case e = x := e1 ; e2 : . Let σ be an arbitrary store, and assume that fvs(e) ⊆ dom(σ). As above, we
assume that P (e1 ) and P (e2 ) hold and show that P (e) holds. Let’s expand these properties.
We have
5
CS 4110 – Programming Languages and Logics
Lecture #4: Large-step semantics
We write ⟨σ, e⟩ ⇓ ⟨σ ′ , n⟩ to indicate that ((σ, e), (σ ′ , n)) ∈⇓. In other words, the expression e with
store σ evaluates in one big step to the final store σ ′ and integer n.
We define the relation ⇓ inductively, using inference rules:
n = σ(x)
I NT VAR
⟨σ, n⟩ ⇓ ⟨σ, n⟩ ⟨σ, x⟩ ⇓ ⟨σ, n⟩
⟨σ, e1 ⟩ ⇓ ⟨σ ′ , n1 ⟩ ⟨σ ′ , e2 ⟩ ⇓ ⟨σ ′′ , n2 ⟩ n = n1 + n2
′′ A DD
⟨σ, e1 + e2 ⟩ ⇓ ⟨σ , n⟩
⟨σ, e1 ⟩ ⇓ ⟨σ ′ , n1 ⟩ ⟨σ ′ , e2 ⟩ ⇓ ⟨σ ′′ , n2 ⟩ n = n1 × n2
M UL
⟨σ, e1 * e2 ⟩ ⇓ ⟨σ ′′ , n⟩
⟨σ, e1 ⟩ ⇓ ⟨σ ′ , n1 ⟩ ⟨σ ′ [x 7→ n1 ], e2 ⟩ ⇓ ⟨σ ′′ , n2 ⟩
A SSGN
⟨σ, x := e1 ; e2 ⟩ ⇓ ⟨σ ′′ , n2 ⟩
To illustrate the use of these rules, consider the following proof tree, which shows that evaluating
⟨σ, foo := 3 ; foo * bar ⟩ using a store σ such that σ(bar ) = 7 yields σ ′ = σ[foo 7→ 3] and 21 as a result:
VAR VAR
⟨σ ′ , foo⟩ ⇓ ⟨σ ′ , 3⟩ ⟨σ ′ , bar ⟩ ⇓ ⟨σ ′ , 7⟩
I NT M UL
⟨σ, 3⟩ ⇓ ⟨σ, 3⟩ ⟨σ ′ , foo * bar ⟩ ⇓ ⟨σ ′ , 21⟩
A SSGN
⟨σ, foo := 3 ; foo * bar ⟩ ⇓ ⟨σ ′ , 21⟩
A closer look to this structure reveals the relation between small step and large-step evaluation:
a depth-first traversal of the large-step proof tree yields the sequence of one-step transitions in
small-step evaluation.
1
2 Equivalence of semantics
A natural question to ask is whether the small-step and large-step semantics are equivalent. The
next theorem answers this question affirmatively.
Theorem (Equivalence of semantics). For all expressions e, stores σ and σ ′ , and integers n we have:
To streamline the proof, we will work with the following definition of the multi-step relation:
R EFL
⟨σ, e⟩ → ∗⟨σ, e⟩
=⇒: We want to prove that the following property P holds for all expressions e ∈ Exp:
Assume that P (e1 ) and P (e2 ) hold. Also assume that there exist σ, σ ′ and n such that
⟨σ, e1 + e2 ⟩ ⇓ ⟨σ ′ , n⟩. We need to show that ⟨σ, e1 + e2 ⟩ → ∗⟨σ ′ , n⟩.
We assumed that ⟨σ, e1 + e2 ⟩ ⇓ ⟨σ ′ , n⟩. This means that there is some derivation whose
conclusion is ⟨σ, e1 + e2 ⟩ ⇓ ⟨σ ′ , n⟩. By inspection, we see that only one rule has a conclu-
sion of this form: the A DD rule. Thus, the last rule used in the derivation was A DD and
it must be the case that ⟨σ, e1 ⟩ ⇓ ⟨σ ′′ , n1 ⟩ and ⟨σ ′′ , e2 ⟩ ⇓ ⟨σ ′ , n2 ⟩ hold for some n1 and n2
with n = n1 + n2 .
2
Other documents randomly have
different content
(Äänettömyys.)
(Sade lakkaa.)
KAIN. Mikä?
KAIN. Oi, oi! Tää huipun huippu on! — Jos vaan mahdollista.
LUSIFER (merkitsevästi). Ol' kerran vaimo, joka ensin uskoi, sitte
epäili, ja sen epäilyksen vuoksi vielä tänään maa ja luonto huokailee.
LUSIFER. Jaa?
KAIN. Ei mulla.
KAIN. Ei kai!
KAIN. Niin niin, niin se on! Nyt elon ongelma jo mulle selviää.
Ihminen näin itse ohjaa onnensa. Minne tahtoo, sinne menee: vettä,
maata, ilmaa myöten; sataa, jyrisee ja salamoi ja päivänsilmän
korkeuteen kohottaa. Kuin sanoisin, kuin nimittäisin: ihminen on…
LUSIFER. Ihminen on maailman…
LUSIFER. Amen!
LUSIFER. — että…?
KAIN. Se?
LUSIFER. Ehkei — sen sitte näkee. Vaan jos sentään — ja jos eivät
tyydy siihenkään, jos suorastansa vastaan iskevät?
KAIN. Kenen?
LUSIFER. Niin.
KAIN. Piru!
KAIN. Oikein, oikein! Juuri niin. Voitto, voitto! Tulen lipun valta on!
(Näky katoo.)
LUSIFER. Amen!
(Äänettömyys.)
Soittopa soimaan,
Nyt karkeloimaan!
Neito sorja,
Hemmen orja
Sankarin iloksi tanssimaan!
Taistoa elo tää —
Juhlina kukkapää!
Kas, kas, kun kimmeltää
Nestehet maljassa;
Helminä päilyy,
Kutsuen häilyy
Siskoset sarjassa.
Nauttios sankari!
Lyhyt on elosi!
Voittajan malja nyt juokaa!
(Tarjoovat maljat sankarille ja muille uroille, tanssivat sivuille.)
Nauttios sankari!
Lyhyt on elosi!
Voittajan malja nyt juokaa!
Kaihopa salaa
Rinnassa palaa;
Impi sorja,
Lemmen orja
Elonsa tanssihin kiiruhtaa!
Kevät on kerran vaan,
Siksipä riemuitaan!
Kas kuinka silmät nää
Tähtinä välkähtää;
Marjana poski,
Poikako koski?
Älä kysy enempää.
Lempiös, lempeä saa!
Hukkukoon taivas, maa!
Maljanne pohjahan juokaa!
(Juovat.)
LUSIFER. Amen!
Esirippu.
KOLMAS NÄYTÖS.
ENSIMÄINEN TOIMINTO.
ELOUHRI.
KAIN. Puhun! Minä, näet, en saa mitään ottamatta. Niin että jos
kerran kiittää pitää, tekisi mieleni kiittää (tekee voimakkaan liikkeen
nyrkkiin puristetuilla käsillä) — näitä kahta!
AABEL. Kain!
KAIN. Ne suitsuttavat uhria joka päivä. Se on minun uhrini — ja se
pitäisi riittää!
AABEL. Kain! Sinä et voi noin ajatella, minä tiedän sen. Se on vain
hetkellistä ajatuksen harhailua.
AABEL. Sepä juuri osottaa että todella kaipaat uhria. Uhri tuo aina
siunausta, aina mieltämme tyynnyttää ja kohottaa. Valitse alttari!
KAIN. Anna minun mennä, Aabel! Tai salli ainakin olla itse uhria
toimittamatta. Voinhan katsella, kun sinä uhraat, ja vaikkapa sitte
yhtyäkkin — jos voin.
KAIN (arvellen). Ei, ei! — No, jos välttämättä täytyy, niin valitse
sinä minun puolestani.
AABEL. Sinun puolestasi? Ei, Kain! Sitä en voisi koskaan tehdä —
se on sinun esikoisoikeutesi.
(Menee.)
(Äänettömyys.)
(Äänettömyys.)
(Menee syrjään.)
LUSIFER. Niin, tuo vanha laulu muistaos: kaikki, kaikki lahjaa on!
MIKAEL. Kain!
(Äänettömyys.)
AABEL. Et tarvitse —?
LUSIFER. Välähdä!
AABEL. Niinkö —?
(Äänettömyys.)
(Menee.)
MIKAEL. Jätä uhri tällä kertaa — olet liian kiihtynyt. Tai jos teet, se
nöyrin mielin tee —
KAIN. Min' en tätä kestä — en, en! Jätän kaikki ja juoksen pois.
KAIN. — Jos, niinkuin meille kerrottu on, sinä olet kaiken luoja,
niin sinä olet totisesti suuri, sillä sinä olet maan ja taivaan
suurenmoisesti suunnitellut ja sinun käsialasi ovat moninaiset ja
ihmeelliset —
KAIN. — Minä kiitän sinua, kiitän siitä ettet tehnyt itse kaikkea
valmiiksi, vaan jätit jotain ihmisellekin. Minä kiitän sinua että annoit
hänelle älynlahjan, jolla hän voi luomakunnan valtansa alle alistaa, ja
minä kiitän niistä —
(Vaipuu rukoukseen.)
KAIN. Minä tiesin sen, minä tiesin sen! Ne eivät sovi yhteen — ja
hän ei päästä irti. Mutta minä tahdon! Nyt — juuri — tällä hetkellä —
AABEL (nousten). Kain, Kain! Mitä sinä teet? Älä koske alttariin, se
on Herralle pyhitetty!
LUSIFER. Oikein!
MIKAEL. Kain!
AABEL (käy Kainiin päin ojennetuin käsin, aikoen häntä hillitä). Älä
jatka! Jehovan nimessä asetun tätä pyhän häväistystä vastaan!
AABEL. En askeltakaan!
Väliverho.
TOINEN TOIMINTA.
UKKOS-ILMA.
ebookbell.com