Tag(sp22)
Make sure you have Java installed, and that it's on your path (can you run java
from a terminal?) If Java isn't installed, Forge run
commands can't start the solver and so Sterling won't open.
Try running racket
directly on your .frg
file in a terminal (from outside VSCode). E.g., I might type racket feb14.frg
.
If you still can't run Racket from your terminal, make sure you have Racket installed. If you have Racket installed, find where the installation is on your computer, and use the absolute path to the racket
(MacOS/Linux) or Racket.exe
(Windows) executable.
/Applications/
folder, and the executable is inside a bin
subdirectory of the Racket installation.Racket
or DrRacket
in your start menu, you can right click and go to its location. Run Racket.exe
and give the full path, e.g., C:\Program Files\Racket\Racket.exe feb14.frg
.If the absolute path has spaces in it, you may need to escape them. For example, on Windows, If I open cmd
and type C:\Users\Tim>C:\Program Files\Racket\Racket.exe
, I get an error saying that C:\Program
is not recognized as an internal or external command, operable program or batch file. That's because of the space. Instead (on Windows) wrap the whole thing in double-quotes. On MacOS, you can also use a backslash before the space.
If raco pkg update forge
gives you "raco pkg update: cannot update linked packages; except with a replacement package source" error, the problem is often that the installation raco
knows about was built from Git, not Racket's package system. If you see follow-up info like
package name: forge
package source: /Users/Tim/repos/Forge/forge
or some other source path with a double forge
in it, it's likely that raco
is still pointing to a Git-installed version. You have two options:
raco pkg update
, instead cd
to the repository and git pull
; you should see new commits. Then raco setup forge
. Crucially, do not run raco pkg install forge
(since the added forge
at the end invokes the package system).raco pkg remove forge
, and then reinstall with raco pkg install forge
.If these don't work, you can get additional information to share via raco pkg show forge
.
Make sure the first line in your model file is a #lang
annotation. If you're running a model for a homework or lab, try to use the header you're given (and plug in your anonymous grading ID). E.g.,
#lang forge/bsl "forge2/river_crossing" "1234567890"
The problem label helps with our grading system. The anonymous ID helps us correlate error logging with your homework submission, so that we can spot and hopefully correct major issues. We use your anonymous ID here to help keep the grading system unbiased.
We've seen test expect
blocks. But Forge actually provides another way to test: concrete example
s. Where a test
is usually about checking satisfiability or unsatisfiability of some set of constraints, an example
is about whether a specific instance satisfies a given predicate.
Since Forge's essential function involves checking whether an instance satisfies constraints, this style of test can be extremely useful for checking that (e.g.) small helper predicates do what you expect.
Why use example
at all? A couple of reasons:
one sig
s or some
quantification for every object in the instance, provided you're trying to describe an instance rather than a property that defines a set of them.example
can often be much faster than a constraint-based approach.You may be wondering whether there's a way to leverage that same speedup in a run
command. Yes, there is! But for now, let's get used to the syntax just for writing examples. Here are some, well, examples:
-- Do not try to write, e.g., `State0.board = ...
example emptyBoardXturn is {some s: State | XTurn[s]} for {
State = `State0
no board
}
Here, we've said that there is one state in the instance, and its board
field has no entries. NOTE: we didn't write State0.board
on the left side of these statements; Forge's example syntax, for the moment, requires us to describe "one big field" for all states.
-- You need to define all the sigs that you'll use values from
example xMiddleOturn is {some s: State | OTurn[s]} for {
State = `State0
Player = `X0 + `O0
X = `X0
O = `O0
board = `State0 -> 1 -> 1 -> `X0
}
You can see the "one big field" notation above: there's one entry, but we have to say whose entry it is as part of the definition.
This syntax is admittedly strange, but it will turn out to be useful next week, when we start using more in Forge.
How can we prove that it's impossible for the system to reach a cheating
state without generating traces of ever-increasing length?
This might not be immediately obvious. After all, it's not as simple as asking Forge to run all s: State | not cheating[s]
. (Why not?)
This simple example illustrates a central challenge in software and hardware verification. Given a discrete-event model of a system, how can we check whether all reachable states satisfy some property? In your other courses, you might have heard properties like this called invariants, as in: "Does my LinkedList
class maintain the invariant that the last node's next
pointer is null?""
One way to solve the problem without the limitation of bounded-length traces goes something like this:
Consider what it means if both checks pass. We'd know that games of length
How do we write this in Forge?
test expect {
noCheatingAtStart: {
wellformed
some s: State | init[s] and cheating[s]
} is unsat
noCheatingTransitions: {
wellformed
some pre, post: State |
some row, col: Int, p: Player | {
not cheating[pre]
move[pre, row, col, p, post]
cheating[post]
}
} is unsat
}
If both of these pass, we've just shown that cheating states are impossible to reach via valid moves of the game.
Does this technique always work? Well, that's another lecture.