Assigned: | 10/2 |
Due: |
|
git apply path/to/m2_cmake_gov_patch.diff
. You can then add and commit the changes. Note it prints nothing if it applies successufully and can only be applied once.In this milestone you will extend PlotScript with some additional features and add support for the graphics primitives Point, Line, and Text. You will also prototype the interface to the graphical notebook application using Qt.
You should complete milestone 0 and 1 before attempting this one.
We will need the ability to give our plots titles, label axes, etc. For this we need a simple string type. Our strings will always be constant string literals denoted by surrounding "
(decimal ASCII character 34) and can include any printable character (including spaces) other than "
. Extend the interpreter code to support such strings as the type String
which are entered directly with quotes. Strings are printed as expressions, including the surrounding quotes to distinguish them from symbols. For now, we are not defining any procedures that operate on strings, but they should be able to be used within PlotScript; i.e. symbols can be defined as strings and lists can hold strings, strings can be used as procedure arguments, etc. Some examples:
plotscript> ("foo")
("foo")
plotscript> ("a string with spaces")
("a string with spaces")
plotscript> (define x ("foo"))
("foo")
plotscript> (x)
("foo")
plotscript> (define mylist (list "eggs" "bread" "milk" "bacon"))
(("eggs") ("bread") ("milk") ("bacon"))
plotscript> (first mylist)
("eggs")
Note: Implementing the String
type will require modifications to the Token and Parse modules, in addition to the classes you have modified in earlier milestones.
Now that we have added several features to plotscript, it has become quite useful as a scripting language. The last major piece we will need is a way to tag certain expression with properties. While these might have many uses, we will apply them in the next task to implement geometric primitives within PlotScript itself.
In the C++ code, add the ability to store a property list, consisting of unordered key-value pairs (entries in the property list), associated with the head of an expression. The key must be an expression of PlotScript type String
. The value can be any Expression. Properties get copied and assigned with the expression, but do not affect comparisons.
Add two built-in procedures, set-property
and get-property
, to set and retrieve a property.
set-property
is a tertiary procedure taking a String
expression as it's first argument (the key), an arbitrary expression as it's second argument (the value), and an Expression as the third argument. The procedure should add or reset an entry to the third argument's property list, after evaluating it, with a key equal to the first argument and value that results from evaluating the second argument. The resulting expression, with the modified property list, is returned. The property-list has no effect on how the expression is printed. It is a semantic error if the arguments are not of the correct type. Note the value argument to set-property is evaluated before adding it to the property list, but there are no side effects to the global environment (similar to lambdas). REPL examples:
plotscript> (set-property "number" "three" (3))
(3)
plotscript> (set-property "number" (3) "three")
("three")
plotscript> (set-property "number" (+ 1 2) "three")
("three")
plotscript> (set-property "label" "foo" (lambda (x) (* 3 (+ 2 x))))
(((x)) (* (3) (+ (2) (x))))
plotscript> (set-property (+ 1 2) "number" "three")
Error: first argument to set-property not a string
get-property
is a binary procedure taking a String
expression as it's first argument (the key) and an arbitrary expression as the second argument. The procedure returns the expression associated with the first argument of the expression in the second argument, or returns an Expression of type None
if they key does not exist in the property list. It is a semantic error if the arguments are not of the correct type. REPL examples:
plotscript> (define a (+ 1 I))
(1,1)
plotscript> (define b (set-property "note" "a complex number" a))
(1,1)
plotscript> (get-property "note" b)
("a complex number")
plotscript> (get-property "foo" b)
NONE
Note: In the starter code, Expressions of type None where printed as ()
but now that we have empty lists, this needs to be adjusted. You should modify the way these expressions are printed to match the example above, that is print NONE
for PlotScript Expressions of type None.
We will need some simple graphical primitives to build our plots from. These should be implemented in plotscript itself as defined lambda expressions, using the previously developed features, and saved as a plain-text file in the source code repository (give it the extension .pls). These pre-defined lambda functions should be parsed and evaluated by plotscript before interpreting any user code (from a file the -e flag, or the REPL). If there is a parsing or semantic error in the start-up script, it should be reported as an warning by the REPL prior to printing results or prompt.
Points: A Point
graphic consists of a PlotScript List of two numbers representing a point in a two-dimensional plane, where the first number is the horizontal coordinate and the second number is the vertical coordinate. The list is tagged with the property "object-name"
with a value of "point"
.
Point graphics have the properties:
The following user-defined procedure should be developed in Plotscript and placed in the start-up file.
Construct a point with coordinates x y, where x and y are any expressions producing a Number.
(make-point x y)
Line: A Line
graphic consists of a PlotScript List with two Point
entries indicating the start and end-points of the line respectively.
Lines graphics have the properties:
The following user-defined procedure should be developed in Plotscript and placed in the start-up file.
Construct a line with points p1 and p2, where p1 and p2 are any expressions producing a Point.
(make-line p1 p2)
Text: A Text
graphic consists simply of a PlotScript String.
Text graphics have the properties:
The following user-defined procedure should be developed in Plotscript and placed in the start-up file.
Construct text with the contents str, where str could be any expression producing a String.
(make-text str)
Note that there is no precondition checking or error checking within the PlotScript procedures.
Our code will need to locate the start-up script file at run-time. To do this add a text file named startup_config.hpp.in
to the source code repository with the following contents
#ifndef STARTUP_CONFIG_HPP
#define STARTUP_CONFIG_HPP
#include <string>
const std::string STARTUP_FILE = "@STARTUP_FILE@";
#endif
We need to configure cmake to use this file to create a header startup_config.hpp
in the build directory with the @STARTUP_FILE@
string above replaced by the actual location of the startup file in the source code repository. To do this add the following to the CMakeLists.txt file (replace startup.pls
with whatever you named your file)
set(STARTUP_FILE ${CMAKE_SOURCE_DIR}/startup.pls)
configure_file(${CMAKE_SOURCE_DIR}/startup_config.hpp.in ${CMAKE_BINARY_DIR}/startup_config.hpp)
include_directories(${CMAKE_BINARY_DIR})
Now when you run configure using cmake it will write the startup_config.hpp
into the build directory, which we can then include and use to locate the file.
Important Note: In order for testing and grading to work properly the type and object names specified below must be adhered to.
The initial notebook interface consists of two widgets, of type InputWidget
and OutputWidget
, arranged vertically within a main widget NotebookApp
. The NotebookApp widget should be used in the notebook.cpp
file to instantiate and show the NotebookApp widget, and enter the Qt event loop only (no other work should be done). The NotebookApp module filename should be notebook_app.hpp/.cpp
.
InputWidget
must derive from QPlainTextEdit
(using dynamic polymorphism) and have a Qt object name of "input". The InputWidget module filename should be input_widget.hpp/.cpp
. The widget should allow the user to type in PlotScript code within the widget. If the user enters Shift-Enter (hold a shift key down while pressing the return/enter key) the current text is evaluated as a plotscript expression. The result, one of a parsing error, an expression, or an exception, is displayed by the OutputWidget
.
OutputWidget
must internally use QGraphicsView
, QGraphicsScene
, and related types using composition, and have a Qt object name of "output". The OnputWidget module filename should be output_widget.hpp/.cpp
. The widget should display the output from the previous expression evaluation using the following formatting rules.
Formatting Rules:
QGraphicsTextItem
with the default font staring at position (0,0).QGraphicsTextItem
with the default font starting at position (0,0).QGraphicsTextItem
with the default font starting at position (0,0).QGraphicsEllipseItem
) should be displayed, centered at the Point's coordinates with a diameter equal to the size property. If present in the property list, it is an error if this property is not a positive Number.QGraphicsLineItem
) should be displayed, using the Line's coordinates with a thickness equal to the thickness property. If present in the property list, it is an error if this property is not a positive Number.QGraphicsTextItem
) should be displayed using the default font starting at the position indicated by it's position property. If present in the property list, it is an error if this property is not a Point.The display is cleared before displaying the next results. For this milestone we will use the default QGraphicsView
coordinate system. Below is a video demonstrating the expected behavior. Note the widgets on your host may appear slightly different. You can pause the video to see study specific input/output behavior. Note the example with the user-defined procedure looks strange, but that is the expected behavior for this milestone.
You should write functional tests for your NotebookApp widget within the provided notebook_test.cpp
file. These should locate the widgets defined above (see lecture 14) and used them to inject user input and verify the proper response. There is no explicit coverage requirement for these tests, however you should have tests that demonstrate the all the expected behaviors defined above.
To submit your milestone:
Tag the git commit that you wish to be considered for grading as "milestone2".
git tag milestone2
Push this change to GitHub
git push origin milestone2
If you need to tag a different version of your code simply create and push a new tag appending a monotonically increasing number to milestone0 using '-', e.g. milestone2-2, milestone2-3, etc.
Be sure you have committed all the changes you intend to. You should re-clone your repository into a separate directory and double check it is what you intend to submit. Failure to complete these steps by the due date will result in a failed submission.
There are 6 course percentage points allocated to this milestone. You will receive a detailed feedback report on your submission via Canvas within two weeks of the due date.
Code compiles in the reference environment | 0.5 points |
Correctness Tests | 4 points |
Testing | 0.5 points |
Code Quality | 0.5 point |
Good Development Practices | 0.5 point |
Grading Notes: