-
Notifications
You must be signed in to change notification settings - Fork 116
CS1729 Assignment 1
Your first assignment has three parts:
- Compile a core Pyret to JavaScript,
- Implement a portion of the Pyret runtime in JavaScript.
You can work in pairs or solo for CS1729, tell me by the end of the day Monday if you're planning on working alone or with a partner.
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.
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. See the end of the file for example tests. You can run this to re-generate your tests usingraco pyret --no-checks create-tests.arr
This is probably the most convenient place to add your own tests, but you're free to come up with other ways and extend this framework; it is functional but minimal.
-
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.
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.
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.