-
Notifications
You must be signed in to change notification settings - Fork 112
CS1729 Assignment 1
- Due date: 11:59 PM, October 16
- To hand in: Email me ([email protected]) a link to the latest commit you want to be considered part of your submission. For example: https://github.com/brownplt/pyret-lang/commit/64e50914bd246f39b1cb8f32e026e5ab4dd41a15. (This link should be to your fork of Pyret, so it should have your username instead of "brownplt").
Your first assignment has two parts:
- Compile a core Pyret to JavaScript,
- Implement a portion of the Pyret runtime in JavaScript.
Your grade will be based on the proportion of a selected test suite that you pass (as of October 5, it's not complete, but the test suite you need to pass for this assignment will be finalized by 11:59 pm October 7). Part of your grade for the whole semester will be based on keeping up-to-date with a growing set of test cases for the language, so it's in your best interest (and the best interest of a solid implementation) to be testing early and often beyond the suite that we pick for grading any particular assignment. We'll share and build up tests as the semester goes on to harden all the implementations.
You should fork the pyret-lang
repo on Github and do all your work on your own fork. If we have contributions from one another that we want to share, we'll incorporate them into the main Pyret repo and then distribute them from there.
Important: We are going to do our work on the nwo
branch (the "New World Order" for Pyret). So when you fork and clone Pyret, before doing any work, you should run:
git checkout nwo
In order to allow us the freedom to add features that would interfere with the version of Pyret that's being used in courses, we're segregating our work to this branch. Joe will be responsible for merging into nwo
any updates from the master branch, and updating the test suite accordingly. You'll be responsible for keeping your implementation up to date with these changes, which we'll try to keep to a minimum.
The Pyret repository has a directory called js/
that contains the files and support code you'll need for this assignment (and CS1729 in general):
-
runtime.js
--- Your JavaScript implementation of Pyret's runtime. It corresponds to the filesrc/lang/runtime.rkt
, and will hold your representation of Pyret values, and built-in functions and methods for accessing fields, updating objects, doing primitive arithmetic and string manipulation, etc. It is seeded with a naive representation of numbers, methods, and functions to show one simple approach to the problem. -
pyret-to-js.arr
--- Your Pyret implementation of a Pyret-to-JavaScript compiler. The main entry point isprogram-to-js(ast :: Program) -> String
, which converts a parsed Pyret program to a string of JavaScript code. You will also be completing this implementation, which starts with a naive compilation for numbers, blocks, and application.You will need to refer to the definition of the Pyret AST in
src/lang/racket-ffi/ast.arr
. This is the complete Pyret surface grammar. For now, you only need to support a smaller subset of this (the post-desugaring subset of the language), which is documented below. -
create-tests.arr
--- A script that generates test files to run unit tests on your compiler. It outputs the tests intotest-runner/tests.js
as a JavaScript program that creates aTESTS
datastructure. You can run this to re-generate your tests usingraco pyret --no-checks create-tests.arr
The
js/tests/
directory contains Pyret files. The way test creation works is by taking these files, running them and capturing their output, and saving that output. Then, theTESTS
datastructure on the client gets the JavaScript-compiled versions of the program, runs them in your runtime, and compares the output. This ensures that your implementation has exactly the same observable behavior as current Pyret.There is a special identifier bound in both your runtime and the evaluation of these tests, called
test-print
, which is simply a print statement that stores its output in a string that we can read off later. This lets us test values in the middle of computations as well as the final result. In addition to checking the output oftest-print
, we also call the built-intorepr
(short for to-representation) on the final value of the program and compare that output. The providedruntime.js
has a simpletorepr
implementation that is a fragment of the real implementation. All this means is that for programs that evaluate to simple values liketrue
or5
, we don't need a verbosetest-print
statement added to test them; they'll print out a string representation of their value by default and we can test that.The test suite will grow over the course of the semester, from both your contributions and programs that we add as we go along (say, interpreters from CS173). This will ensure that all the implementations are able to handle the same Pyret that everyone is using for their assignments. For this assignment, however, we'll fix the test suite on Monday night to give everyone a concrete target.
-
test-runner/test-support.js
--- This contains JavaScript support for running tests. This is where you can write JavaScript functions to use as predicates on the result of running compiled Pyret programs. Two are defined ---pyretEquals
andisNumber
--- you will probably want to write more predicates. -
test-runner/test.html
--- You shouldn't need to edit this file, it contains the test runner that usestests.js
andtest-support.js
to run your unit tests. You can open this file up in a browser to run the tests.
Pyret goes through a desugaring phase before reaching core Pyret, which is the language you must compile to JavaScript. As a result, the set of forms you need to handle is much smaller than the entire data definition in ast.arr
. You must compile these forms:
-
s_block(l :: Loc, stmts :: List<Expr>)
--- A block of expressions as statements that get their own scope -
s_user_block(l :: Loc, body :: Expr)
--- Compiles identically tos_block
, indicates that a user used theblock:
form (rather than an implicit block for a function body, etc.) -
s_var(l :: Loc, name :: Bind, value :: Expr)
--- Note that the desugaring process also checks well-formedness, so you can assume that only variables are used ins_assign
expressions. s_let(l :: Loc, name :: Bind, value :: Expr)
s_assign(l :: Loc, id :: String, value :: Expr)
s_if_else(l :: Loc, branches :: List<IfBranch>, _else :: Expr)
-
s_try(l :: Loc, body :: Expr, id :: Bind, _except :: Expr)
--- Pyret's semantics for try/catch is modelled nicely by JavaScript's try/catch. -
s_lam(l :: Loc, params :: List<String>, args :: List<Bind>, ann :: Ann, doc :: String, body :: Expr, check :: Expr)
--- You don't need to worry about anything to do with types/annotations or check blocks. These are completely handled by desugaring and annotation checking before arriving at core Pyret. s_method(l :: Loc, args :: List<Bind>, ann :: Ann, doc :: String, body :: Expr, check :: Expr)
-
s_extend(l :: Loc, super :: Expr, fields :: List<Member>)
--- This is the expression form forobj.{field1: val1, ...}
s_obj(l :: Loc, fields :: List<Member>)
s_app(l :: Loc, _fun :: Expr, args :: List<Expr>)
-
s_id(l :: Loc, id :: String)
--- You should think carefully about your representation of identifiers that come from your JavaScript implemented runtime. s_num(l :: Loc, n :: Number)
s_bool(l :: Loc, b :: Bool)
s_str(l :: Loc, s :: String)
-
s_bracket(l :: Loc, obj :: Expr, field :: Expr)
--- Important: You can assume for now thatfield
is always ans_str
. We'll get to hash tables and computed-string lookup later on. -
s_colon_bracket(l :: Loc, obj :: Expr, field :: Expr)
--- Important: You can assume for now thatfield
is always ans_str
. We'll get to hash tables and computed-string lookup later on. -
s_get_bang(l :: Loc, obj :: Expr, field :: String)
--- Corresponds toobj!x
expressions -
s_update(l :: Loc, super :: Expr, fields :: List<Member>)
--- This is the expression form forobj!{field1: val1, ...}
Documentation for these forms is at http://cs.brown.edu/~joe/public/lang/Language_Constructs.html; it is not perfect, and you should ask questions about anything you don't understand. It's the documentation's job to be complete and clear, and we should strive to improve it and make it clear enough to all work from. For ground-truth reference, the implementation of compile.rkt
is definitionally correct (since it is the current and only implementation of Pyret), but it's only one implementation and it certainly requires additional explanation.
Exceptions: You may avoid things that are noted as "deprecated" or "planned to be deprecated" in the documentation. This includes the o.[x]
expression where x
is a non-string, and object literals like {[expr]: val}
where expr
is a computed string.
The file src/lang/runtime.rkt
defines a number of Pyret's built-in helpers and data structures. The runtime, in particular, defines the following structs:
;; p-base: (SetOf Symbol) StringMap (Value ... -> Value) -> p-base
(struct p-base (brands dict app method) #:transparent)
(struct p-nothing p-base () #:transparent)
(struct p-object p-base () #:transparent)
;; p-num : p-base Number -> p-num
(struct p-num p-base (n) #:transparent)
;; p-bool : p-base Boolean -> p-bool
(struct p-bool p-base (b) #:transparent)
;; p-str : p-base String -> p-str
(struct p-str p-base (s) #:transparent)
;; p-fun : p-base (Loc -> Proc) -> p-fun
(struct p-fun p-base () #:transparent)
;; p-method: p-base Proc -> p-method
(struct p-method p-base () #:transparent)
;; p-mutable p-base Box (Listof (Value -> Value)) (Listof (Value -> Value))
(struct p-mutable p-base (b read-wrappers write-wrappers) #:transparent)
;; p-placeholder p-base Box (Listof (Value -> Value))
(struct p-placeholder p-base (b wrappers) #:mutable)
You need to define a JavaScript representation of each of these structures, and JavaScript equivalents of the functions that operate over them from runtime.rkt
. Some examples of more or less one-to-one translations are in the provided runtime.js
file. You do not have to replicate the same kind of representation as we have in the Racket implementation. Feel free to experiment, as we will be building up a large enough test suite over the time in the course to check correspondence of all our implementations.
Most of these values have relatively straightforward implementations in the Racket functions of runtime.rkt
, which defines the built-in methods on numbers and strings, for example. Mutables and Placeholders are especially Pyret-specific; they have special documentation at http://cs.brown.edu/~joe/public/lang/s_mutables.html and http://cs.brown.edu/~joe/public/lang/s_placeholders.html.