Skip to content
Snippets Groups Projects
Commit b7aaf063 authored by James R. Wilcox's avatar James R. Wilcox
Browse files

intitial handout repo

parents
Branches master
No related tags found
No related merge requests found
Makefile 0 → 100644
JC = javac -source 11 -g
# Windows uses ; as a path separator while Linux and Mac use :
SEP = :
ifeq ($(OS),Windows_NT)
SEP = ;
endif
OUT = java/out
DEPS = java/deps
JAVA_SRC = $(shell find java/hw/*/src -name "*.java")
JAVA_TST = $(shell find java/hw/*/tst -name "*.java")
.PHONY: all clean run test
all: $(OUT)/src/ $(OUT)/tst/
$(OUT)/src/: $(JAVA_SRC)
@ echo "[javac] $@"
@ mkdir -p $@
@ touch $@
$(JC) -d $@ -cp "$(DEPS)/*" $(JAVA_SRC)
$(OUT)/tst/: $(JAVA_TST) $(OUT)/src/
@ echo "[javac] $@"
@ mkdir -p $@
@ touch $@
$(JC) -d $@ -cp "$(DEPS)/*$(SEP)$(OUT)/src" $(JAVA_TST)
run: $(OUT)/src/
java -cp "$(DEPS)/*$(SEP)$(OUT)/src" Trefoil
test: $(OUT)/tst/
java -cp "$(DEPS)/*$(SEP)$(OUT)/src$(SEP)$(OUT)/tst" org.junit.runner.JUnitCore TrefoilTest
clean:
@ echo "[clean]"
@ rm -rf $(OUT)
# CSE 341 homework repository
See `java/hw/hw1/README.md` for HW1.
# Compiled Java
out/
# IntelliJ
.idea/workspace.xml
.idea/tasks.xml
.idea-out
# Common OS/Editor files
*~
._*
.DS_Store
.DS_Store?
Thumbs.db
ehthumbs.db
Desktop.ini
# analytics
.usage-database.db
# Default ignored files
/shelf/
/workspace.xml
trefoil
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="CompilerConfiguration">
<annotationProcessing>
<profile default="true" name="Default" enabled="true" />
</annotationProcessing>
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectRootManager" version="2" languageLevel="JDK_11" default="true" project-jdk-name="11 (2)" project-jdk-type="JavaSDK">
<output url="file://$PROJECT_DIR$/.idea-out" />
</component>
</project>
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/trefoil.iml" filepath="$PROJECT_DIR$/trefoil.iml" />
</modules>
</component>
</project>
\ No newline at end of file
File added
# HW1 Feedback questions
Approximately how many hours did you spend on this homework?
Any feedback about the lecture material corresponding to this homework?
Any feedback about the homework itself?
Anything about your submission that is extra cool that we should be sure to check out?
# A more formal language description of Trefoil v1
Start by reading the informal description in the README if you haven't already.
The definition below describes the syntax and semantic of Trefoil v1 programs.
"Syntax" describes how programs are written down, similar to how one would
describe "spelling and grammar" rules for a human language. "Semantics"
describes what programs mean, similar to how one would describe the meaning of
words, sentences, and paragraphs in a human language.
In the more formal description below, *italicized words* are technical terms being defined.
## Syntax
The syntax has three levels: characters, words, and tokens.
### Character level
At the character level, a Trefoil v1 program is a sequence of characters. For
now, you may assume that the characters are "reasonable" and representable as a
Java string.
### Word level
We use the word *sequence* to mean a (possibly empty) list. We say "non-empty
sequence" if we mean that the sequence will always have at least one element.
At the word level, the sequence of characters in a Trefoil v1 program is
split into a sequence of words separated by whitespace. Between each pair
of adjacent words, the amount of whitespace must be non-empty. Before the first
word and after the last word there can be a possibly-empty amount of whitespace.
For this week, you are free to interpret *whitespace* however is most convenient for you.
Just leaving Java's `Scanner` in its default configuration is fine.
A *word* consists of a non-empty sequence of non-whitespace characters.
### Token level
At the token level, each word in the program is checked to correspond to a token.
A *token* is either a comment, an integer literal, or an operator.
A *comment* is any word that starts with a semicolon.
For this week, you are free to interpret *integer literal* however is most
convenient for you. Just leaving Java's `Scanner` in its default configuration
and calling `hasNextInt`/`nextInt` is fine.
There are four *operators* in Trefoil v1: `+`, `-`, `*`, and `.` (a period).
If an input word is not a token, Trefoil should gracefully report an error to
the Trefoil programmer and exit with status 1. The error message can be anything
you think is useful (the more useful, the better!), but should *not* contain a
Java exception stack trace.
## Semantics
The meaning of a Trefoil v1 program is a *stack transformer*: the
program takes a stack as input, operates on that stack, and returns a stack as a
result. A program can also output integers, and it can also signal an error.
We introduce a notation for stack transformers. We write
```
Syntax Semantics
f ( a b -- c )
```
to mean that `f` is a stack transformer that expects at least two elements on
the stack, `a` and `b`, pops them, and pushes one element `c` on the stack.
In other words, everything before the `--` is the stack before `f` is evaluated,
and everything after the `--` is the stack after `f` is evaluated.
**The top of the stack is written to the right.** So in the example above,
`b` is on the top of the stack, and `a` is right below it on the stack.
This notation also brings up another important point about our stack
transformers: each transformer looks only at some number of elements on the top
of the stack; it does not care what is underneath it. So in our example of `f`
above, even if the stack had 10 elements, `f` would just look at the top two
(`a` under `b` on top) and transform them into one new element `c`. If there
were 10 elements before, `f` popped two and pushed one, so after there would be
9 elements. To emphasize, the notation does *not* mean that `f` would fail if
there were more than two elements on the stack, but rather means that `f`
ignores any additional elements and leaves them as is.
Remember that a program is a sequence of tokens.
The meaning of a program is to process the tokens in left-to-right order
and execute each one's stack transformer.
**At the end of the sequence, the program outputs the entire remaining stack.**
The meaning of an individual token is also a stack transformer that can output and fail.
Remember there are three kinds of tokens: comments, integer literals,
and operators.
A comment has no effect on the stack, performs no output, and never fails.
In our notation,
```
Syntax Semantics
;comment ( -- )
```
An integer literal pushes itself onto the stack, performs no output, and never fails.
```
Syntax Semantics
n ( -- n )
```
where `n` stands for any integer literal.
The operators `+`, `-`, and `*` each pop two elements off the input stack. If
the input stack contains fewer than two elements, these operations signal an error.
Otherwise, they compute the sum, difference, or product of the two popped elements,
and push the result back onto the stack. These operators perform no output.
```
Syntax Semantics
+ ( a b -- a+b ) and signal error if <2 elements available
- ( a b -- a-b ) and signal error if <2 elements available
* ( a b -- a*b ) and signal error if <2 elements available
```
The operators `+` and `*` are commutative (it doesn't matter what order their
arguments are given in), but the operator `-` is not. Look carefully at the
notation giving the meaning for `-`. The subtraction happens in the same order
that we write the stack from left to right: bottom to top. So the *top* of the
stack is subtracted from the *second* element. Everybody messes this up at least
a few times, because it's very confusing! But eventually it will help to
remember that subtraction happen in the order written in the stack transformer
notation.
Finally, the operator `.` pops one element off of the input stack and prints it.
```
Syntax Semantics
. ( a -- ) and output a but signal error if <1 elements available
```
Throughout the semantics, whenever Trefoil signals an error, it should do so
gracefully (no Java exception stack trace) and then immediately exit with status 1.
# HW1: Trefoil v1
In this homework, you will implement an interpreter for version 1 of the *T*h*re*e
*f*orty *o*ne *i*nstructional *l*anguage (Trefoil). The language is not much more than a
pocket calculator at this point, but we will extend it throughout the quarter.
## Informal description
A Trefoil v1 program is a space-separated sequence of integer literals and operators.
Programs are evaluated left to right and in the context of a stack of integers.
When encountering an integer literal in the program, it is pushed on the stack.
The four operators are `+`, `-`, `*`, and `.` (a period). All manipulate items
near the top of the stack. The first three pop two integers off the top of the
stack and combine them according to the operators usual arithmetic meaning; the
result is pushed back onto the stack. For the `-` operator, the order of
operations is to subtract the top of the stack *from* the second item on the
stack. (Read that sentence carefully!!) The `.` operator pops one element from
the top of the stack and prints it.
We recommend you try to take a first cut at implementing this language and its
interpreter based on the description in this file, before returning to the full
description in `LANGUAGE.md`. There are many fiddly details to get right
eventually, but it's more fun to get something mostly working quickly first
before worrying about corner cases.
## A tour of the starter code
We recommend you use IntelliJ for the Java projects. Open the folder `java/` at
the root of this repository (two levels above this file) as a project in
IntelliJ. (We have included a settings directory that should work
automatically.)
There are two starter files: `java/hw/hw1/src/Trefoil.java` and
`java/hw/hw1/tst/TrefoilTest.java`. You should not need to create any other
files for this homework.
We recommend reading both files before starting to make changes.
### `Trefoil.java`
We have a provided a nearly complete `main` method for you that will either read
input from `System.in` or from a file, based on arguments passed on the command
line. These two possibilities both channel into the `interpret(Scanner)` method.
At the bottom of `main` we call `println` on the `Trefoil` instance to print the
stack.
You will implement three public methods on `Trefoil`: `interpret(Scanner)`,
`pop()` and `toString()`.
Unlike the demo code in lecture, you should store the state of the interpreter
(the stack) as a field or fields on the `Trefoil` class. The `interpret` and
`pop` methods will manipulate this stack.
We have placed `TODO`s in this file everywhere we think you will need to change.
That said, you are free to do whatever you want as long as you don't change the
change the public interface to this class, nor change the command-line behavior.
For example, you could add features to the `main` method to implement a `--help`
flag, or whatever else you think would be fun.
### `TrefoilTest.java`
This file contains just a handful of unit tests for the interpreter to get you
started. Even if you are not familiar with JUnit, hopefully the tests should be
relatively self explanatory. All tests start by constructing a `Trefoil` object.
They then send that object some input and make some assertions or expectations
about the resulting behavior. The first four starter tests are "normal case"
tests, i.e., about programs that do not contain errors. The fifth test is an
"error case" test, i.e., about a program that contains an error.
The tests should help you understand how to fill out the `TODO`s in `Trefoil.java`.
There are also two `TODO`s in the test file. You need to add tests that cover features
not covered by the starter tests.
## Suggested workflow
You can do anything in whatever order you want, but here is one suggested path.
- Read both starter files.
- Press the "Build" button in IntelliJ and ensure there are no errors.
- Run the tests in `TrefoilTest.java` and ensure that they all fail.
- Decide how you will represent the interpreter's stack in Java. Fix the first
two `TODO`s in `Trefoil.java`. Feel free to follow our choice in from Lecture
1 or do your own thing.
- Implement `Trefoil.pop()`.
- Implement `Trefoil.toString()`. This will help you if you need to debug later.
- Implement integer literals in `Trefoil.interpret(Scanner)`.
- Ensure `TrefoilTest.testOne` passes
- Ensure `TrefoilTest.toString_` passes
- Implement the `+` operator in `Trefoil.interpret(Scanner)`.
- Ensure `TrefoilTest.interpretAdd` and `TrefoilTest.interpretAddSplit` pass.
- Implement graceful error handling if the stack is too short for the `+` operator.
- Ensure `TrefoilTest.stackUnderflow` passes.
- Write normal case tests for `-`, `*`, and `.`.
- Be careful to get the order of subtraction right in your test! (See lecture 1.)
- Implement `-`, `*`, and `.`.
- Ensure your normal case tests pass.
- Somewhere around here, take a look at `LANGUAGE.md`.
- Write error case tests for `-`, `*`, and `.` and ensure they pass.
- Write normal case tests for comments.
- Implement comments and ensure your normal case tests pass.
- (No need to write error case tests since comments never signal an error.)
- Write an error case test for a program with a word that is not a token.
- Fix the `TODO` on the `TrefoilError` class by editing `main()` to catch this
exception, print a nice error message, and exit with status 1.
- Run `Trefoil` from the command line and play around with the interpreter
interactively.
- Make sure that your tool really does gracefully handle erroneous inputs.
## Working from the command line or using something other than IntelliJ
We have provided a Makefile with the following targets:
- `make` or `make all` will compile your project
- `make run` will run your interpreter interactively
- `make test` will run the unit tests
## Grading
This homework will be graded relatively leniently. We expect most people who
attempt it will get full credit.
- 75 points for the `Trefoil.interpret(Scanner)` method
- 75 points for normal case and error case tests
- 25 points for `Trefoil.toString()`
- 10 points for `Trefoil.pop()`
- 10 points for error handling in `main()`
- 25 points for filling out `FEEDBACK.md`
## Submitting your work
Commit and push your work to Gitlab. We will open the Gradescope for this
assignment and give further instructions soon. You will be able to submit on
Gradescope through Gitlab.
import java.io.*;
import java.util.Scanner;
/**
* Interpreter for the Trefoil v1 language.
*
* The interpreter's state is a stack of integers.
*
* The interpreter also implements a main method to accept input from the keyboard or a file.a
*/
public class Trefoil {
// TODO: probably declare one or more fields to track the state of the interpreter.
// TODO: either initialize your fields directly or create an explicit zero-argument constructor to do so.
public static void main(String[] args) {
Trefoil trefoil = new Trefoil();
if (args.length == 0) {
trefoil.interpret(new Scanner(System.in));
} else if (args.length == 1) {
try {
trefoil.interpret(new Scanner(new File(args[0])));
} catch (FileNotFoundException e) {
e.printStackTrace(); // TODO: wrong; print nice message instead
System.exit(0); // TODO: wrong; exit indicating error instead
}
} else {
System.err.println("Expected 0 or 1 arguments but got " + args.length);
System.exit(1);
}
// print the stack
System.out.println(trefoil);
}
/**
* Interpret the program given by the scanner.
*/
public void interpret(Scanner scanner) {
// TODO: your interpreter here. feel free to adapt the demo code from lecture 1
}
/**
* Convenience method to interpret the given string. Useful for unit tests.
*/
public void interpret(String input) {
// Don't change this method unless you know what you're doing.
interpret(new Scanner(input));
}
/**
* Pop a value off the stack and return it. Useful for unit tests.
*
* @throws TrefoilError if there are no elements on the stack.
*/
public int pop() {
// TODO: implement this
return 0;
}
@Override
public String toString() {
// TODO: change this to print the stack
// (don't print it using Stack.toString like we did it in Lecture 1;
// read the spec and check the tests)
return super.toString();
}
/**
* Throw this error whenever your interpreter detects a problem.
*
* TODO: Catch this error in main and print a nice message and exit in a way that indicates an error.
*/
public static class TrefoilError extends RuntimeException {
public TrefoilError(String message) {
super(message);
}
}
}
import org.junit.Test;
import static junit.framework.TestCase.*;
public class TrefoilTest {
@Test
public void interpretOne() {
Trefoil trefoil = new Trefoil();
trefoil.interpret("1");
assertEquals(trefoil.pop(), 1);
}
@Test
public void interpretAdd() {
Trefoil trefoil = new Trefoil();
trefoil.interpret("1 2 +");
assertEquals(trefoil.pop(), 3);
}
@Test
public void interpretAddSplit() {
Trefoil trefoil = new Trefoil();
trefoil.interpret("1 2");
// the interpreter should track the stack across multiple calls to interpret()
trefoil.interpret("+");
assertEquals(trefoil.pop(), 3);
}
@Test
public void toString_() {
Trefoil trefoil = new Trefoil();
trefoil.interpret("1 2 3");
assertEquals(trefoil.toString(), "1 2 3");
}
// TODO: add unit tests here to cover all features in the language (don't forget to test comments!)
@Test(expected = Trefoil.TrefoilError.class)
public void stackUnderflow() {
Trefoil trefoil = new Trefoil();
trefoil.interpret("1 +");
}
// TODO: add unit tests for malformed programs that the user might accidentally input
// you can use the @Test(expected = Trefoil.TrefoilError.class) notation above
// to write a test that fails if the exception is *not* thrown. Add at least
// one test for each operator that can signal an error, plus at least one test
// containing malformed input (a word that is not a token).
}
\ No newline at end of file
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$">
<sourceFolder url="file://$MODULE_DIR$/hw/hw1/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/hw/hw1/tst" isTestSource="true" />
<sourceFolder url="file://$MODULE_DIR$/hw/hw3/src" isTestSource="false" />
<sourceFolder url="file://$MODULE_DIR$/hw/hw3/tst" isTestSource="true" />
</content>
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="module-library">
<library>
<CLASSES>
<root url="file://$MODULE_DIR$/deps" />
</CLASSES>
<JAVADOC />
<SOURCES />
<jarDirectory url="file://$MODULE_DIR$/deps" recursive="false" />
</library>
</orderEntry>
</component>
</module>
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment