![]() |
![]() |
|
Over the course of several homeworks, we'll implement a “tower” of languages (B0, B1, …, B6) each language incorporating more features than the previous. B0 is provided for you.
Op ::= … | # Interpretation: “modulo” (see A.iii for details) Expr ::= … | IfGT IfGT ::= obi Expr Expr wan Expr Expr Interpretation: if third Expr is > than fourth, result is the first Expr else the second. |
update eval to handle these new operators.
the remainder after dividing x by y, where the result is always between 0 (inclusive) and y (exclusive)1In particular, the result should never be positive if y<0. Notice that this is slightly different behavior than either Java's built-in % (which behaves differently on negative numbers), and from Racket's built-in modulo (which only accepts integers). In both racket and Java, you can calculate this as y * (x/y - ⌊x/y⌋), where ⌊r⌋ means the the
Note that you are already provided sufficient test cases for #s, in the comments of the B0 test-case files.
update eval to handle this new type of expression. The semantics of obi Expr3 Expr4 wan Expr1 Expr2 is:
first evaluate just Expr3 and Expr4 to get values v3; and v4; if v3 is greater v4 then evaluate Expr1 and return its value; otherwise evaluate Expr2 and return that value.For example, obi 7.5 44 wan 3.4 2.1 evaluates to 2.1.
You must make your own test cases for IfGTs; include at least two as-simple-as-possible tests, and two tests with more deeply nested Exprs. I suggest including one where the IfGT is not the top-level expression (e.g., a / expression which contains a IfGT as one of its operands).
(50pts) Implement B2 in either racket or Java (your choice).
B2 adds identifiers to B1:
Expr ::= … | Id | LetExpr LetExpr ::= hrrm Expr in Expr, Id is2 |
obiso you do not need to bother checking for that, and (b) for now any nested LetExpr expressions will use different Ids. We'll handle shadowing in B3, later.)
Update your three methods parse, toString (a.k.a. expr->string), eval.
eval'ing an identifier simply throws an error.
You don't need test cases for evaling Ids. Though if you want to be spiffy, you can use check-error in racket, or assertThrows in JUnit5. In JUnit4, the hack-ish approach is to put “ExpectedException.none().expect(RunTimeException.class)” on the line before the one that should trigger an error.
<will be read as one token, and
-as second (both racket and Java).
In order to write eval, we need to define the semantics of hrrm Expr0 in Expr1, Id is:
- Evaluate Expr0; let's call the result v0.
- Then, substitute v0 for all occurrences of Id inside the tree Expr1; name the result of the substitution E′.
- Evaluate E′, and return that result.
For example: hrrm 3 in ^x 2/, x is ⇒ ^3 2/ ⇒ 54. Be sure to write test cases for your substitution function before you write its code; include several trivial and easy tests, along with a couple of more complicated nestings and one deeply nested expression.
Observe that when evaluating a (legal) B2 program, eval will never actually encounter an Id — that Id will have been substituted out before we ever recur down to it.
Hint: Substituting a variable with a value in an syntax-tree is essentially the same as replacing every occurrence of one name with another in an anc-tree. (The only difference is that an anc-tree had only two cond-branches, while Expr has around five, though the code for most of those are very similar.)
(Note: you must do substitution in the parse tree; no credit given for string-substitution 6.)
Hint: Your code will correspond almost word-for-word to the semantics given above.
ML-like: | let x = 2+3 in x*9 end; |
lisp-like: | (let {[x (+ 2 3)]} (* x 9)) |
lisp-like, simplified: | (let x (+ 2 3) (* x 9)) |
C#-like: | using (var x = 2+3) { return x*9; } |
javascript-like: | var x = 2+3; return x*9; |
Java-like: | { int x = 2+3; return x*9; } |
Haskell-like: | * x 9 \n where x = + 2 3 \n |
Note that you can (and should) test and write a “substitute” function w/o worrying about the exact syntax of a LetExpr. Substituting one thing in a tree for another is its own independent task, de-coupled from eval’ing a local-binding statement.
↩eval(parse!("hrrm 3 in ^x 2/, x is")) = eval(parse!("^3 2/")) = eval(parse!("5")) |
This page licensed CC-BY 4.0 Ian Barland Page last generated | Please mail any suggestions (incl. typos, broken links) to ibarland ![]() |