Starting from:
$35

$29

MP4 Forth Solution




Logistics

---------




- revision: 1.0




Objectives

----------




The objective of this MP is to implement a stack-based language. Such languages

are often useful for embedded systems. (Postscript, the printer language, is an

example.) The language we will implement is a simplified version of stack based

programming language called Forth.




Goals

-----




- Simulate a stack using recursive function calls

- Create a function lookup table

- Distinguish between an interpreter and a compiler

- See a new language paradigm (stack based) and have fun with it.




Readings

--------




To get a rough idea about Forth, you can start from following links:

- [Starting Forth](http://www.forth.com/starting-forth) is the classic

tutorial to Forth programmming language.

- [Gforth](https://www.gnu.org/software/gforth/) is the GNU project that

implements Forth interpreter in C. You can dowload and try it locally.

**This MP will mainly follow Gforth for output and error messages.**

- [Easy Forth](https://skilldrick.github.io/easyforth/) provides a short

tutorial and a simplified Forth interpreter (still more complicated than

this MP) in JavaScript so that you can try it online.

- [Forth Tutorials](http://www.forth.org/tutorials.html) provides a list of

references to know more about Forth.




Note that we are implementing a greatly reduced version of the Forth language.

The behavior of our interpreter may not exactly match Easy Forth or Gforth.




Getting Started

===============




How Forth Works

---------------




Forth is a stack-based language. All user-input is split on white-space into a

sequence of *words*. The words are checked one at a time, and stack is modified

accordingly; they are interpreted according to the dictionary. Built-in

operators are supported by initializing the dictionary with predefined entries.




The Forth interpreter/compiler follows the work-flow below.




Interpreter mode:




- First, Forth checks if the word is in the dictionary of special symbols.




* If the word is the ":" operator, Forth enters compiler mode immediately to

add the new definition to the dictionary. After compiler mode is finished, it

evaluates the rest of the words

* Otherwise, it loads the definition and runs it.




- If the word is not in the dictionary, Forth checks if the word is an integer;

in that case the integer is pushed to the integer stack.

- If the word is neither an integer nor a dictionary word (or one of a few special

operators), the integer stack is emptied, the input stream is flushed, and an

error message is printed.




``` {.forth}

2 5 furbitz

Undefined symbol: 'furbitz'

: furbitz 1 + ;

ok

1 furbitz

ok

bye

Bye!

```




Compiler mode:




- Forth takes the word immediately following ":" as the identifier for the new definition.

- Next Forth steps through the remaining words, chaining them together to form a single instruction...

- Until either:

* Forth finds a ";" word signalling the termination of compiler mode, or

* Forth reaches the end of line and reports an error




``` {.forth}

```




Note: For compiler mode, the definition should be compiled to machine instructions

and therefore varies from one hardware architecture to another. In this MP, we

benefit from our meta-language Haskell, so we *compile* the definition to a

continuation that encapsulates a sequence of stack operations and state

transitions. We will describe the compiler in detail in Problem section.




Relevant Files

--------------




In the directory `src` you'll find `Lib.hs` with all the relevant code. In this

file you will find all of the data definitions, the primitive function maps,

some stubbed-out simple parsers for special inputs, stubbed out evaluation

functions, and the REPL itself.




Running Code

------------




To run your code, start GHCi with `stack ghci` (make sure to load the `Main`

module if `stack ghci` doesn't automatically). From here, you can test

individual functions, or you can run the REPL by calling `main`. Note that the

initial `$` and `` are prompts.




``` {.sh}

$ stack ghci

... Some Output ...

*Main main

Welcome to your forth interpreter!

10 32 + .

42

ok

2 3 + 5 6 + + .

16

ok

bye

Bye!

```




To run the REPL directly, build the executable with `stack build` and run it

with `stack exec main`.




Testing Your Code

-----------------




You will be able to run the test-suite with `stack test`:




``` {.sh}

$ stack test

```




It will tell you which test-suites you pass, fail, and have exceptions on. To

see an individual test-suite (so you can run the tests yourself by hand to see

where the failure happens), look in the file `test/Spec.hs`.




You can run individual test-sets by running `stack test --ta "-t TEST-PATTERN"`

where TEST-PATTERN specifies the pattern to search for test properties or test

groups. All tests within matching groups or properties will be executed.

``` {.sh}

$ stack test --ta "-t dup"

mp4-forth-0.1.0.0: test (suite: test, args: -t dup)




=G= Dictionary for primitive operators:

=P= IStack Manipulations `dup`: [OK, passed 100 tests]




Properties Total

Passed 1 1

Failed 0 0

Total 1 1




$ stack test --ta "-t Operators"

mp4-forth-0.1.0.0: test (suite: test, args: -t Operators)




=G= Dictionary for primitive operators:

=P= Arithmetic Operators: [OK, passed 100 tests]

=P= Comparison Operators: [Failed]

*** Failed! (after 1 test):

Exception:

Prelude.undefined

CallStack (from HasCallStack):

error, called at libraries/base/GHC/Err.hs:79:14 in base:GHC.Err

undefined, called at src/Lib.hs:107:14 in

mp4-forth-0.1.0.0-f6OEElXAAq28J3aJyjkjH:Lib

Given operator: "<"

Given IStack (Top at left):

[1,-3]

(used seed 536919711409488064)




Properties Total

Passed 1 1

Failed 1 1

Total 2 2

```




Look in the file `test/Spec.hs` to see which test properties were tested.




Given Library Code

==================




The setup code is concerned with importing the modules we will need and

declaring the types we will use.




``` {.haskell}

-- for stack underflow errors

msgUnderflow :: String

msgUnderflow = "Stack underflow"




-- ... Other predefined error messages




-- for stack underflow errors

underflow :: a

underflow = error msgUnderflow

```




The Types

---------




The Forth machine will need to keep track of its state (`ForthState`). It will

have an integer stack for intermediate results (`IStack`), a dictionary for

primitive and defined functions (`Dictionary`), and a place to keep track of

output messages (`Output`). State to state functions are defined as transitions

(`Transition`).




``` {.haskell}

type ForthState = (IStack, Dictionary, Output)

type Transition = (ForthState - ForthState)

type IStack = [Integer]

type Dictionary = [(String, Value)]

type Output = [String]




type CStack = [(String, Transition)]

```




The dictionary will store `(key, value)` pairs where a key is a `String` that

can be primitive operators, reserved keywords, or user defined function names.

The mapped values (`Value`), which can be either primitive state transitions

(`Prim :: Transition - Value`) or commands only used in compiler mode

(`Compile :: (CStack - Maybe CStack) - Value`).




We also have data-constructors for numbers (`Num :: Integer - Value`) and

unknowns (`Unknown :: String - Value`). This is so that the dictionary lookup

function `dlookup` can signal the `eval` and `compile` function that the lookup

failed, and the input is not a number.




We also provide a `Show` instance for `Value`, which allows us to pretty-print

things of type `Value`.




``` {.haskell}

data Value = Prim Transition

| Define

| EndDef

| Compile (CStack - Maybe CStack)

| Num Integer

| Unknown String




instance Show Value where

show (Prim f) = "Prim"

show (Compile _) = msgCompileOnly

show Define = "Define"

show EndDef = msgCompileOnly

show (Num i) = show i

show (Unknown s) = msgUndefinedSym s

```




Dictionary Access

-----------------




Lookups




When `eval` uses `dlookup` and the lookup succeeds (the entry is present), the

most recent definition in the dictionary will be used (closer to the head). If

the lookup fails, then `dlookup` will try to convert the input to an `Integer`

using `reads`. If that fails, it will signal `eval` that nothing worked by using

the data-constructor `Unknown`.




``` {.haskell}

-- handle input lookups (and integers)

dlookup :: String - Dictionary - Value

dlookup word dict

= case lookup word dict of

Just x - x

_ - case reads word of

[(i,"")] - Num i

_ - Unknown word

```




Insert




On inserting, we will simply add the new definition (Value) to the front of the

dictionary. In this way, you preserve past definitions in case you ever extend

our language with the ability to revert to previous definitions. Our language

will not have that extension yet, but Forth language includes the concept of

**marker** to store current state and discard definitions newer than a given

marker. You can think about how to add it.




``` {.haskell}

-- handle inserting things into the dictionary

dinsert :: String - Value - Dictionary - Dictionary

dinsert key val dict = (key, val):dict

```




Given Executable Code

=====================




[Initial State](#initial-state), [Read-Eval-Print Loop](#read-eval-print-loop),

and the `main` function are implemented in `app/Main.hs`. These code provides

the interactive interpreter described in [Running Code](#running-code). You

wouldn't need to modify or refer to them in order to finish this MP.




Initial State

-------------




The initial state will have an empty integer stack (`initialIStack`), a

dictionary with initial function definitions (`initialDictionary`), and an

empty output stack (`initialOutput`).




The `initialDictionary` is defined later in the Problems section because you

must complete it as part of the assignment.




``` {.haskell}

import System.IO (hFlush, stdout)

import Lib (IStack, ForthState, initialDictionary, eval)




-- initial integer stack

initialIStack :: IStack

initialIStack = []




-- initial output

initialOutput :: [String]

initialOutput = []




-- initial ForthState

initialForthState :: ForthState

initialForthState = (initialIStack, initialDictionary, initialOutput)

```




Read-Eval-Print Loop

--------------------




The read-eval-print loop handles all the action. It puts a prompt on the screen,

reads some input, spits the input into words, feeds the words to the evaluator,

and keeps track of the updated state. It also outputs anything that `eval` says

should be output. Notice how the output is reversed before printing it because

it is built in reverse order when processing the input recursively.




``` {.haskell}

repl :: ForthState - IO ()

repl state

= do putStr " "

input <- getLine

hFlush stdout

if input == "bye"

then do putStrLn "Bye!"

return ()

else let (is, d, output) = eval (words input) state

in do mapM_ putStrLn (reverse output)

repl (is, d, [])




main = do putStrLn "Welcome to your Forth interpreter!"

repl initialForthState

```




Problems

========




Lifters

-------




Since a large portion of the built-in operators in Forth is modifying only the

`IStack` instead of the whole `ForthState`. We provide the following lifter

function to lift a stack operation to a Forth state transition.




Notice that we invoke `underflow` error when the lifted function returns

`Nothing` because the stack operation failed.




``` {.haskell}

liftIStackOp :: (IStack - Maybe IStack) - ForthState - ForthState

liftIStackOp op (i, d, o)

= case op i of

Just i' - (i', d, o)

Nothing - underflow

```




`liftIntOp`




To make our lives easier, we will use a function `liftIntOp` that takes a

Haskell function and converts it into one that will work on the integer stack.

Essentially, the function pops out the top two elements in current `IStack`,

calculates the result with given `op`, and push the result back to `IStack`.

This will be used to create primitive operators, which will be stored in the

`initialDictionary` (see next problem for example).




Note the order that the arguments are given to the operator `op` in. Also note

that we return `Nothing` if there are not enough entries on the input stack.




``` {.haskell}

liftIntOp :: (Integer - Integer - Integer) - IStack - Maybe IStack

liftIntOp op (x:y:xs) = Just $ (y `op` x) : xs

liftIntOp _ _ = Nothing

```




`liftCompOp`




You need to define `liftCompOp` so that we can have comparison operators between

integers in our Forth language. In Forth, `0` is false and anything else is

true, so you must return `0` if the result is `False`. You can return anything

else otherwise (traditionally `-1` is used for `True`).




``` {.haskell}

liftCompOp :: (Integer - Integer - Bool) - IStack - Maybe IStack

liftCompOp = undefined

```




The Dictionary

--------------




As mentioned in previous sections, the interpreter supports built-in operators

via a dictionary initialized with predefined entries. Here we are defining these

entries for `initialDictionary`. To help you define other built-in operators,

we provided the first entry for `+` in the `initialDictionary`. You must finish

the rest of the definitions. Notice how we use `liftIntOp` to turn the Haskell

function `(+)` into one that works on our Forth integer stack. The result type

`IStack - Maybe IStack` indicates that the function should return `Nothing`

when underflow occurs. We further lift the function using `liftIStackOp`, so it

could be applied on `ForthState` and throw `underflow` when receiving `Nothing`.

Then we wrap the function in a `Prim :: (ForthState - ForthState) - Value` so

that we can hold it as a value in a `Dictionary`.




``` {.haskell}

initialDictionary :: Dictionary

initialDictionary = initArith ++ initComp ++ initIStackOp ++ initPrintOp

++ initCompileOp

```




Arithmetic Operators




Provide the definition of the arithmetic operators subtraction (`"-"`),

multiplication (`"*"`), and integer division (`"/"`). You will find the function

`liftIntOp` useful here. We have provided addition (`"+"`) for you.




Note: You are encouraged to define division that generates a Forth error rather

than a Haskell error, but it will not affect your grade.




Comparison Operators




Provide the definition of the comparison operators less-than (`"<"`),

greater-than (`""`), less-than-or-equal-to (`"<="`), greater-than-or-equal-to

(`"="`), equal-to (`"="`), and not-equal-to (`"!="`). You will find the

function `liftCompOp` useful here.




Note: equal-to (`"="`) and not-equal-to (`"!="`) operators in Forth are

different from Haskell.




Stack Manipulations




Define `swap` (which swaps the top two elements), `drop` (which pops the top element

without printing), and `rot` (which pops the third element and pushes it to the top

of the IStack).




We have provided the definition of `dup` which duplicates the top of stack.




Make sure you handle the cases when stack underflow happens.




``` {.forth}

2 3 4 .S

2 3 4

ok

dup .S

2 3 4 4

ok

drop .S

2 3 4

ok

swap .S

2 4 3

ok

rot .S

4 3 2

ok

3 dup rot .S

4 3 3 3 2

ok

```




Popping the Stack




For printing and supporting more advanced operators, we now have to modify more

than just the `IStack` inside a `ForthState`.




Here, we handle one Forth word for you, the `.` operator. This operator consumes

one element of the integer stack and outputs it. Notice once again how we handle

the underflow case by generating the `underflow` error in `printPop` function.

The mapping from `.` to `printPop`is then added into `initialDictionary`.




``` {.haskell}

printPop :: ForthState - ForthState

printPop (i:istack, dict, out) =

(istack, dict, show i : out)

printPop _ = underflow

```




Printing the Stack




Define `.S` which prints the entire stack. It does not consume the stack,

however. It should print from bottom of stack to top. You may find the built-in

Haskell function `unwords` useful here.




``` {.haskell}

2 3 4 .S

2 3 4

ok



```




Evaluator

---------




Next is the evaluator. It takes a list of strings as the next tokens of input, a

Forth state, and returns a Forth state. We have handled the implementation for

you. Be sure you understand the code!




``` {.haskell}

eval :: [String] - ForthState - ForthState

```




If the input is empty, we are done processing input and should return the

current Forth state with `ok` indicating we successfully interpret the given

words.




``` {.haskell}

-- empty input - return current state and output "ok"

eval [] (istack, dict, out) = (istack, dict, "ok":out)

```




Lookup in dictionary




Otherwise, we check if the word matches one of the defined words in dictionary.

`eval` will use `dlookup` to determine what to do with it. It could be that we

get a number. If so, push it onto the stack. It could be that we get a built-in

or user defined primitive function. If so, modify the state by feeding it to the

function. It could also get `Define` indicating the beginning of a user

definition, so we invoke `compileDef` to compile and update the dictionary.




If instead `dlookup` says that its an `Unknown` or `Complie` other than `Colon`,

we then empty the `IStack`, flush the input stream, and output that error

message accordingly.




``` {.haskell}

-- otherwise it should be handled by `dlookup` to see if it's a `Num`, `Prim`,

-- `Define`, or `Unknown`

eval (w:ws) state@(istack, dict, out)

= case dlookup w dict of

Prim f - eval ws (f state)

Num i - eval ws (i:istack, dict, out)

Define -

case compileDef ws dict of

Right (rest, dict') - eval rest (istack, dict', out)

Left msg - ([], dict, msg:out)

otherwise - -- reset IStack and add error message

([], dict, (show otherwise):out)

```




Compiler

--------




Consider the operators we defined for evaluation so far, all these operations

directly modifies `IStack` or `Output`; therefore they can be interpreted

right away. However, we would also like some common language features in other

languages, such as user defined functions/procedures for code reuse and

modularity, conditional decisions to alter program flow, and loops for

repetition. These features are achieved more easily by *compiling* the

computations instead of interpreting them right away, and *executing* the

computations later when used. This concept is very similar to **continuation**

in functional programming. In fact, we will ask you to "compile" a user

definition in Forth to a continuation in Haskell for this MP.




In this MP, we follow Gforth to implement user definitions. A user definition

starts from a colon `:` and ends with a semicolon `;`. Also a set of reserved

words, namely `for`, `next`, `if`, `else`, `then`, `begin`, and `until`, is specified as

compile-only words in `initialDictionary`. These reserved words are only allowed

within user definitions; therefore they must be enclosed with in a pair of `:`

and `;`. `eval` should output error messages when these words are used, and we

will now implement functions to compile these reserved words as well as other

words we've defined for [Evaluator](#evaluator).




User definitions




Now we are ready to add something interesting. Add the ability to define new

words. The syntax is




``` {.forth}

: <name <definition... ;

```




The definition will be compiled as a continuation `k` with type `Transition`, in

other words, `ForthState - ForthState`. Then `(<name, Prim k)` will be added

into the dictionary of the Forth state. In the future when the symbol `<name`

is encountered, the definition will be looked up in the dictionary, so `eval`

can find `k` and apply `k` on current `ForthState` just as other primitive

operators. See below for how this is handled.




For example, to make the square function:




``` {.forth}

: square dup * ;

ok

4 square .

16

ok

```




We get the name `square` and the definition body `dup * ;`. To compile the body,

we first start from the initial continuation `id` that given a `ForthState` it

returns the same state. Then we lookup the dictionary and find that `dup` maps

to `Prim (liftIStackOp istackDup)`, so we can construct a new continuation `k1`

by `\state - (liftIStackOp istackDup) (id state)` or equivalently

`(liftIStackOp istackDup) . id` to accumulate computations.




Similarly, we lookup `*` and find it maps to a value `Prim f2`. We then can

construct the continuation `k2` with `\state - f2 (k1 state)` or equivalently

`f2 . k1` meaning that we apply `k1` then `f2` on any given state. Finally, we

reach the end of definition `;`, so we update the dictionary with the entry

`("square", Prim k2)`.




To handle well-nested control structure for compilation, we introduce `CStack`

and `Compile` values. Notice in the code below, continuation of primitive

operators (`Prim f`) are accumulated on the current top of `CStack` via

`updateTop`. To deal with keywords for compile-only, we lookup from dictionary

to get `cf` to update `cstack`. We will discuss how `cf` works by giving an

example of handle `for ... next` loop in next section.




The implementation is given below. Be sure you understand how the code works!




``` {.haskell}

-- | Return rest of words after compilation and a dictionary w/ new defintion

-- Assuming ':' is already striped away

compileDef :: [String] - Dictionary - Either ErrorMsg ([String], Dictionary)

compileDef [] _ = Left msgZeroLenDef

compileDef (name:ws) dict

= case compile ws dict [("", id)] of

Right (rest, f) - Right (rest, dinsert name (Prim f) dict)

Left msg - Left msg




compile :: [String] - Dictionary - CStack

- Either ErrorMsg ([String], Transition)

compile [] _ _ = Left "The definition does not end"




compile (w:ws) dict cstack

= case dlookup w dict of

Prim f - compile ws dict (updateTop f cstack)

Num i - compile ws dict (updateTop f cstack)

where f = (liftIStackOp (\is - Just (i:is)))

Compile cf- case cf cstack of

Just cstack' - compile ws dict cstack'

Nothing - Left $ msgUnstructured w

Define - Left "Nested definition is not allowed"

EndDef - case cstack of

[("", k)] - Right (ws, k)

otherwise - Left $ msgUnstructured w

otherwise - Left $ show otherwise




updateTop :: Transition - CStack - CStack

updateTop k ((c, kold):cs) = (c, k . kold) : cs

updateTop _ [] = underflow

```




Definite Loops




To help you understand how to use `CStack` to deal with well-nested language

constructs, we give the implementation for one of the simplest loop structure

in Forth as follows:




``` {.forth}

for <loop-body next

```




The semantics of this language construct is, when `for` is encountered, it pops

the top of `IStack` and get a number `i`; `<loop-body` is then executed exactly

`i+1` times (i.e., from `0` to `i`) if `i` is non-negative. Otherwise when `i`

is negative, we simply don't execute `<loop-body` in this MP.




``` {.forth}

: incTwice 1 for 1 + next ;

ok

40 incTwice .

42

ok

: incN+1 for 1 + next ;

ok

31 10 incN+1 .

42

ok

```




To compile `for ... next` to a `Transition`, we need the computation of

`<loop-body` alone so that we can repeat it. Therefore, in `cstackFor`, we push

`("for", id)` into `CStack` to accumulate continuations only for `<loop-body`.

Recall `updateTop` function, any succeeding primitive function will now be

composed in this new top element. Also any computation before `for` is still

in `CStack`.




Up until encountering `next`, `cstackNext` is called by looking up dictionary.

We know that the top element in `CStack` should match `("for", kloop)`, or else

the loop is unstructured. In addition, the second element for top, `(c, kold)`

should preserve computation right before `for`. Here, we design an auxiliary

function `transForLoop` to help compose a new `Transition` from `kloop`. It is

obvious that `transForLoop` checks if the top `i` of `IStack` in a given

`ForthState` is negative and return the state with `i` popped. If `i` is

non-negative then it compose `kloop` with itself `i` times via `aux` function

and apply it on the state. Finally, the return result from `transForLoop` is

composed with `kold` to update the current top of `CStack`.




``` {.haskell}

cstackFor :: CStack - Maybe CStack

cstackFor cstack = Just $ ("for", id):cstack




cstackNext :: CStack - Maybe CStack

cstackNext (("for", kloop):(c, kold):cstack) =

Just ((c, knew):cstack) where knew = (transForLoop kloop) . kold

cstackNext _ = Nothing




transForLoop :: Transition - (ForthState - ForthState)

transForLoop kloop (i:is, d, o) =

if i < 0 then

(is, d, o)

else

(aux kloop i) (is, d, o)

where aux k 0 = k

aux k n = k . (aux k (n-1))

transForLoop _ _ = underflow

```







Conditionals




Add conditionals. The syntax for conditions is




``` {.forth}

if <if-branch else <else-branch then

```




The `else <else-branch` is optional.




The keyword order looks a bit different than in the languages you have been

using. When you read an `if`, the top element is popped from `IStack` and

compared with 0. If the element is not equal to 0, then `<if-branch` is taken,

or else `<else-branch` is taken.




Notice how nested `if ... else ... then` statements should be handled properly

in the examples below.




``` {.forth}

: f 3 4 < if 10 else 20 then . ; f

10

ok

: f 3 4 if 10 else 20 then . ; f

20

ok

: f 3 4 if 10 then . ; f

main: Stack underflow

... More error messages.




: f 3 4 < if 10 then . ; f

10

ok

: f 3 4 < if 3 4 < if 10 else 20 then else 30 then . ; f

10

ok

: f 3 4 < if 3 4 if 10 else 20 then else 30 then . ; f

20

ok

: f 3 4 if 3 4 < if 10 else 20 then else 30 then . ; f

30

ok

```




In this problem, you have to implement how to modify `CStack` for `if`, `else`,

and `then`. The actual flow for compiling `if ... else ... then` is first

compiling the continuation `kif` of `<if-branch` after you see `if`, and

compiling `kelse` of `<else-barnch` separately after you see `else`.

Eventually, when you see `then`, the final continuation is constructed using

`kif`, `kelse`, and the continuation `kold` that represents any continuation

before this branch.




``` {.haskell}

cstackIf :: CStack - Maybe CStack

cstackIf cstack = undefined




cstackElse :: CStack - Maybe CStack

cstackElse cstack@(("if", _):_) = undefined

cstackElse _ = Nothing




cstackThen :: CStack - Maybe CStack

cstackThen (("else", kelse):("if", kif):(c, kold):cstack) = undefined

cstackThen (("if", kif):(c, kold):cstack) = undefined

cstackThen _ = Nothing

```







Indefinite Loops




Another loop structure in Forth is as follows:




``` {.forth}

begin <loop-body until

```




The part between `begin` and `until` is executed repeatedly until the top

element consumed by `until` is True (not equal to 0). For example, `noLoop`

will always stop at the first iteration because `until` will consume `-1`.

`infLoop` will loop forever because `until` will consume `0` every time.

`toZero` will repeat until the top element is less than or equal to 0.




``` {.forth}

: noLoop begin -1 until ;

ok

noLoop

ok

: infLoop begin 0 until ;

ok

infLoop

... It won't stop. Press Ctrl+C to interrupt.




: toZero begin .S 1 - dup 0 <= until ;

ok

4 toZero

4

3

2

1

ok

```




Again you use `CStack` to handle loops. When you hit a `begin`, you should start

accumulate continuation for `<loop-body` on top of `CStack`. Once you hit

`until`, you will have the continuation `kloop` for `<loop-body` and `kold`

that represent the continuation before the loop. Use `kloop` and `kold` to

construct the final continuation.




``` {.haskell}

cstackBegin :: CStack - Maybe CStack

cstackBegin cstack = undefined




cstackUntil :: CStack - Maybe CStack

cstackUntil (("begin", kloop):(c, kold):cstack) = undefined

cstackUntil _ = Nothing

```

More products