diff --git a/.github/workflows/push.yml b/.github/workflows/push.yml index f1c3377e..6a89bfed 100644 --- a/.github/workflows/push.yml +++ b/.github/workflows/push.yml @@ -27,7 +27,6 @@ jobs: run: | curl -Ls https://github.com/jgm/pandoc/releases/download/2.11.2/pandoc-2.11.2-1-amd64.deb -o pandoc.deb sudo dpkg -i pandoc.deb - sudo apt-get install nasm sudo apt-get install fonts-stix sudo apt-get install libunistring-dev - name: Install Racket @@ -39,9 +38,9 @@ jobs: version: '8.18' - name: Install a86, langs, and assignments run: | - git clone https://github.com/cmsc430/a86.git - git clone https://github.com/cmsc430/langs.git - git clone https://github.com/cmsc430/assignments.git + git clone --branch "${{ github.ref_name }}" --single-branch https://github.com/cmsc430/a86.git + git clone --branch "${{ github.ref_name }}" --single-branch https://github.com/cmsc430/langs.git + git clone --branch "${{ github.ref_name }}" --single-branch https://github.com/cmsc430/assignments.git raco pkg install --auto a86/ raco pkg install --auto langs/ raco pkg install --auto assignments/ diff --git a/README.md b/README.md index 24e888ea..75c84bdd 100644 --- a/README.md +++ b/README.md @@ -6,7 +6,7 @@ University of Maryland, College Park. The current instance of this course is: -* http://www.cs.umd.edu/class/fall2025/cmsc430/ +* http://www.cs.umd.edu/class/summer2026/cmsc430/ Copyright © David Van Horn and José Manuel Calderón Trilla and Leonidas Lampropoulos diff --git a/www/Makefile b/www/Makefile index bb089dce..4ad83e01 100644 --- a/www/Makefile +++ b/www/Makefile @@ -38,7 +38,7 @@ scribble: $(course).scrbl push: - rsync -rvzp main/ dvanhorn@junkfood.cs.umd.edu:/fs/www/class/fall2025/cmsc430/ + rsync -rvzp main/ dvanhorn@junkfood.cs.umd.edu:/fs/www/class/summer2026/cmsc430/ clean: rm -rf $(course) diff --git a/www/assignments/5.scrbl b/www/assignments/5.scrbl index 9042c47e..b5d7eab6 100644 --- a/www/assignments/5.scrbl +++ b/www/assignments/5.scrbl @@ -6,7 +6,8 @@ @(require (for-label a86/ast (except-in racket ...))) -@(ev '(require fraud-plus)) +@(ev '(require fraud-plus fraud-plus/compiler/compile fraud-plus/executor/run)) +@(ev '(define (exec e) (run (compile e)))) @bold{Due: @assign-deadline[5]} diff --git a/www/assignments/8.scrbl b/www/assignments/8.scrbl index 8f733d11..7762ccbb 100644 --- a/www/assignments/8.scrbl +++ b/www/assignments/8.scrbl @@ -95,7 +95,7 @@ respective pattern. [(vector (vector x) (vector 2)) x]) ] -@section[#:tag-prefix "a8-" #:style 'unnumbered #:tag "vector"]{Predicate patterns} +@section[#:tag-prefix "a8-" #:style 'unnumbered #:tag "predicate"]{Predicate patterns} The @racket[(? _f)] pattern matches any value for which the predicate @racket[_f] returns a true value (any value other than @racket[#f]) diff --git a/www/colophon.scrbl b/www/colophon.scrbl index d4d13bec..ecf89a57 100644 --- a/www/colophon.scrbl +++ b/www/colophon.scrbl @@ -18,6 +18,4 @@ System information: @itemlist[ (i "uname -srm") (i "racket --version") - (i "raco pkg show -lu a86 langs") - (i "nasm --version") - (i "gcc --version")] + (i "raco pkg show -lu a86 langs")] diff --git a/www/defns.rkt b/www/defns.rkt index f4ffa82c..89be1755 100644 --- a/www/defns.rkt +++ b/www/defns.rkt @@ -12,79 +12,53 @@ (define prof1-email "dvanhorn@cs.umd.edu") (define prof1-initials "DVH") -(define semester "fall") -(define year "2025") +(define semester "summer") +(define year "2026") (define courseno "CMSC 430") -(define lecture-dates "" #;"May 30 -- July 7, 2023") +(define lecture-dates "June 1 -- July 10, 2026") (define IRB "IRB") (define AVW "AVW") (define KEY "KEY") -(define office-hour-location (elem AVW " " "4122")) +(define office-hour-location "TBD") -(define start-date "September 2") -(define m1-date "October 16") -(define m2-date "November 13") +(define start-date "June 1") +(define m1-date "June 16") +(define m2-date "July 1") (define midterm-hours "24") -(define final-date "December 18") -(define final-end-time "12:30pm") -(define elms-url "https://umd.instructure.com/courses/1388468") +(define final-date "July 10") +(define final-end-time "11:59pm") +(define elms-url "https://umd.instructure.com/courses/1406982") (define racket-version "8.18") (define staff - (list (list "Pierce Darragh" "pdarragh@umd.edu") - (list "Benjamin Quiring" "bquiring@umd.edu") - (list "Kalyan Bhetwal" "bhetwal@umd.edu") - (list "Abhi Senthilkumar" "asenthil@terpmail.umd.edu") - (list "Zhongqi Wang" "zqwang@umd.edu") - (list "Kazi Tasnim Zinat" "kzintas@umd.edu"))) + (list (list "Pierce Darragh" "pdarragh@umd.edu"))) -(define lecture-schedule1 "TTh, 2:00-3:15pm") +(define lecture-schedule1 "Weekdays, 10:00-11:15am") -(define classroom1 "LEF 2205") +(define classroom1 + (link "https://umd.zoom.us/j/96210969737?pwd=K6Iz8IZNchyWEcV3UERCi6KPkawRDw.1" "Online")) ;(define discord "TBD") -(define piazza "https://piazza.com/umd/fall2025/cmsc430/home") -(define gradescope "https://www.gradescope.com/courses/1098215/") +(define piazza "https://piazza.com/umd/summer2026/cmsc430/home") +(define gradescope "https://www.gradescope.com/") (define feedback "https://forms.gle/99yTz7HVfopCaDMz9") (define (assign-deadline i) - (list-ref '("Thursday, September 11, 11:59PM" - "Thursday, September 18, 11:59PM" - "Thursday, September 25, 11:59PM" - "Thursday, October 2, 11:59PM" - "Thursday, October 23, 11:59PM" - "Tuesday, November 4, 11:59PM" - "Tuesday, November 11, 11:59PM" - "Tuesday, December 9, 11:59PM" - #;"Thursday, December 4, 11:59PM") + (list-ref '("Thursday, June 4, 11:59PM" + "Friday, June 5, 11:59PM" + "Friday, June 12, 11:59PM" + "Monday, June 22, 11:59PM" + "Monday, June 29, 11:59PM" + "Monday, July 6, 11:59PM" + "Wednesday, July 8, 11:59PM" + "Thursday, July 9, 11:59PM") (sub1 i))) (define office-hours - (itemlist - #:style 'compact - (item "Monday" - (itemlist - (item "9:00am–12:00pm — Zhonqi") - (item "12:00pm–3:00pm — Ben") - (item "3:00pm–6:00pm — Abhi"))) - (item "Tuesday" - (itemlist - (item "11:00am–2:00pm — Abhi"))) - (item "Wednesday" - (itemlist - (item "9:00am–12:00pm — Zhonqi") - (item "12:00pm–3:00pm — Ben") - (item "3:30pm–6:30pm — Kalyan"))) - (item "Thursday" - (itemlist - (item "10:00am–1:00pm — Zinat"))) - (item "Friday" - (itemlist - (item "11:00am–2:00pm — Zinat") - (item "4:00pm–7:00pm — Kalyan"))))) + "TBD") diff --git a/www/notes.scrbl b/www/notes.scrbl index 48a9efc7..2e5cd8fa 100644 --- a/www/notes.scrbl +++ b/www/notes.scrbl @@ -5,8 +5,7 @@ These are the course notes for CMSC 430. They can be a bit rough around the edges in places, but please keep in mind this is a @emph{living} document. If you spot errors, confusing prose, missing parts, or generally have -suggestions for improving the material, @bold{please}, -@bold{please}, @bold{please} submit an +suggestions for improving the material, submit an @link["https://github.com/cmsc430/www"]{issue on Github}. @local-table-of-contents[#:style 'immediate-only] @@ -36,3 +35,4 @@ suggestions for improving the material, @bold{please}, @include-section{notes/neerdowell.scrbl} @include-section{notes/outlaw.scrbl} @;include-section{notes/shakedown.scrbl} +@index-section[] \ No newline at end of file diff --git a/www/notes/1/what-is-a-compiler.scrbl b/www/notes/1/what-is-a-compiler.scrbl index ec6aed1d..68f46756 100644 --- a/www/notes/1/what-is-a-compiler.scrbl +++ b/www/notes/1/what-is-a-compiler.scrbl @@ -50,22 +50,22 @@ characterize a language in a theory of computation course. If we wanted to rigorously define this set, we could provide a formal recognizer such as a finite automaton or even a Turing-machine that given a string, determines membership in the set of programs. This -aspect of a language we call its @bold{syntax}: it concerns the rules -governing the formation of phrases in the language. While important, -syntax is not the end-all of a programming language, in fact it really -just the start of where the rubber meets the road. For example, when -writing a C program we mostly don't care about whether the thing we -are writing is or is not in the set of C programs --- it being a C -program is a prerequisite to thing we want to do, which is to -@emph{compute}. We write programs so that we run them. Sure, you -might make a syntax error, forget to put a semicolon here or mispell -an identifier there, but the thing you really care about comes after -those issues have been resolved. So another aspect of what a +aspect of a language we call its @index["syntax"]{@bold{syntax}}: it +concerns the rules governing the formation of phrases in the language. +While important, syntax is not the end-all of a programming language, +in fact it really just the start of where the rubber meets the road. +For example, when writing a C program we mostly don't care about +whether the thing we are writing is or is not in the set of C programs +--- it being a C program is a prerequisite to thing we want to do, +which is to @emph{compute}. We write programs so that we run them. +Sure, you might make a syntax error, forget to put a semicolon here or +mispell an identifier there, but the thing you really care about comes +after those issues have been resolved. So another aspect of what a programming language "is" concerns the @emph{meaning} of the sentences in that language. Every language has meaning. In the context of programming language, the meaning is a computation. This aspect of a -langauge we its @bold{semantics}: it concerns the computational -content of programs in the language. +langauge we call its @index["semantics"]{@bold{semantics}}: it +concerns the computational content of programs in the language. There are many ways we might define the meaning of a program, and therby define the semantics of the language. We could give examples, @@ -77,6 +77,15 @@ could write a program (perhaps in a different language) that @emph{interprets} expressions and computes their meaning. Typically some combination of all of these approaches are used. +Putting it all together, we can concisely define a programming +language as follows: + +@inset-flow{ +A programming language is a formal language for expressing +computations, with rules for what programs look like (syntax), and +rules for what those programs mean or do when run (semantics).} + + But all of this @emph{defines} what a programming language is, which is not the same as @emph{realizing} that definition. For that we need to implement the language, meaning we need to construct a universal @@ -227,7 +236,7 @@ descendent of the Scheme programming language, and a close cousin of OCaml, which you've previously studied. This is largely an arbitrary choice. We could have picked any programming language to build a compiler for. In choosing Racket, we get a mature, well-designed -language with a minimal, easy to parse semantics and a well-understood +language with a minimal, easy to parse syntax and a well-understood and clear semantics that has features representative of many modern high-level programming languages such as OCaml, Java, Python, JavaScript, Haskell, etc. If you can write a compiler for Racket, you diff --git a/www/notes/a86.scrbl b/www/notes/a86.scrbl index ff04edc6..028cf8d5 100644 --- a/www/notes/a86.scrbl +++ b/www/notes/a86.scrbl @@ -437,4 +437,4 @@ comes time to test the compilers we write. There is more to a86, which you can find documented in the @secref["a86_Reference"], although we try to introduce features of a86 -as we encounter them. \ No newline at end of file +as we encounter them. diff --git a/www/notes/abscond.scrbl b/www/notes/abscond.scrbl index b8ab85b6..83c307fe 100644 --- a/www/notes/abscond.scrbl +++ b/www/notes/abscond.scrbl @@ -13,7 +13,8 @@ @(define codeblock-include (make-codeblock-include #'here)) -@(ev '(require rackunit a86 abscond abscond/correct)) +@(ev '(require rackunit a86 abscond abscond/correct abscond/compiler/compile abscond/executor/run)) +@(ev '(define (exec-expr e) (run (compile e)))) @(define (shellbox . s) (parameterize ([current-directory (build-path langs "abscond")]) @@ -162,13 +163,13 @@ For each category, we will have an appropriate constructor. In the case of Absc all expressions are integers, so we have a single constructor, @racket[Lit]. A datatype for representing expressions can be defined as: -@codeblock-include["abscond/ast.rkt"] +@codeblock-include["abscond/syntax/ast.rkt"] The parser for Abscond checks that a given s-expression is an integer and constructs an instance of the AST datatype if it is, otherwise it signals an error: -@codeblock-include["abscond/parse.rkt"] +@codeblock-include["abscond/syntax/parse.rkt"] @ex[ (parse 5) @@ -184,7 +185,7 @@ The meaning of an Abscond program is simply the number itself. So We can write an ``interpreter'' that consumes an expression and produces it's meaning: -@codeblock-include["abscond/interp.rkt"] +@codeblock-include["abscond/interpreter/interp.rkt"] @#reader scribble/comment-reader (examples #:eval ev @@ -203,7 +204,7 @@ but this will change as the langauge grows. We can add a command line wrapper program for interpreting Abscond programs from stdin: -@codeblock-include["abscond/interp-stdin.rkt"] +@codeblock-include["abscond/interpreter/interp-stdin.rkt"] The details here aren't important (and you won't be asked to write this kind of code), but this program @racket[read]s the contents of a @@ -310,14 +311,14 @@ So our compiler will emit x86 assembly code. To make our lives a bit easier, we will write the run-time system in C. Let's start with the Abscond runtime: -@filebox-include[fancy-c abscond "main.c"] +@filebox-include[fancy-c abscond "runtime/main.c"] This C program provides the main entry point for running an Abscond program. It relies upon a function @tt{print_result} which is defined as follows: -@filebox-include[fancy-c abscond "print.h"] -@filebox-include[fancy-c abscond "print.c"] +@filebox-include[fancy-c abscond "runtime/print.h"] +@filebox-include[fancy-c abscond "runtime/print.c"] Separating out @tt{print_result}, which at this point is just a simple @tt{printf} statement, seems like overkill, but it will be useful in @@ -433,7 +434,7 @@ So the AST representation of our example is: Writing the @racket[compile] function is easy: -@codeblock-include["abscond/compile.rkt"] +@codeblock-include["abscond/compiler/compile.rkt"] @#reader scribble/comment-reader (examples #:eval ev @@ -456,7 +457,7 @@ Putting it all together, we can write a command line compiler much like the command line interpreter before, except now we emit assembly code: -@codeblock-include["abscond/compile-stdin.rkt"] +@codeblock-include["abscond/compiler/compile-stdin.rkt"] Example: @@ -518,15 +519,16 @@ What does that even mean, to be correct? First, let's formulate an alternative implementation of @racket[interp] that composes our compiler and a86 interpreter to define -a (hopefully!) equivalent function to @racket[interp]: +a lower-level execution wrapper for assembled programs: -@codeblock-include["abscond/exec.rkt"] +@codeblock-include["abscond/executor/exec.rkt"] -This function can be used as a drop-in replacement to @racket[interp]: +For source expressions, we can recover the old behavior by composing +@racket[compile] with @racket[run]: @ex[ -(exec (Lit 42)) -(exec (Lit 19))] +(exec-expr (Lit 42)) +(exec-expr (Lit 19))] It captures the idea of a phase-distinction in that you can first compile a program into a program in another language---in this case @@ -535,7 +537,7 @@ If the compiler is correct, the result should be the same: @bold{Compiler Correctness}: @emph{For all @racket[e] @math{∈} @tt{Expr} and @racket[i] @math{∈} @tt{Integer}, if @racket[(interp e)] -equals @racket[i], then @racket[(exec e)] equals +equals @racket[i], then @racket[(exec-expr e)] equals @racket[i].} One thing that is nice about specifying our language with an @@ -549,9 +551,9 @@ compilation within Racket: @ex[ -(exec (Lit 42)) -(exec (Lit 37)) -(exec (Lit -8))] +(exec-expr (Lit 42)) +(exec-expr (Lit 37)) +(exec-expr (Lit -8))] This of course agrees with what we will get from the interpreter: @@ -605,5 +607,3 @@ of a valid input (i.e. some integer) that might refute the correctness claim? Think on it. In the meantime, let's move on. - - diff --git a/www/notes/blackmail-semantics.rkt b/www/notes/blackmail-semantics.rkt new file mode 100644 index 00000000..619ee121 --- /dev/null +++ b/www/notes/blackmail-semantics.rkt @@ -0,0 +1,27 @@ +#lang racket + +(provide B-concrete B 𝑩) + +(require redex/reduction-semantics) + +(define-language B-concrete + (e ::= integer (add1 e) (sub1 e))) + +(define-language B + (e ::= (Int i) (Prim1 p1 e)) + (i ::= integer) + (p1 ::= 'add1 'sub1)) + +(define-judgment-form B + #:mode (𝑩 I O) + #:contract (𝑩 e i) + [---------- + (𝑩 (Int i) i)] + + [(𝑩 e_0 i_0) (where i_1 ,(+ (term i_0) 1)) + ----------- + (𝑩 (Prim1 'add1 e_0) i_1)] + + [(𝑩 e_0 i_0) (where i_1 ,(- (term i_0) 1)) + ----------- + (𝑩 (Prim1 'sub1 e_0) i_1)]) diff --git a/www/notes/blackmail.scrbl b/www/notes/blackmail.scrbl index 5cd8d4df..b19366d1 100644 --- a/www/notes/blackmail.scrbl +++ b/www/notes/blackmail.scrbl @@ -5,15 +5,17 @@ redex/pict "../fancyverb.rkt" "../utils.rkt" - blackmail/semantics + "blackmail-semantics.rkt" "utils.rkt" "ev.rkt") @(define codeblock-include (make-codeblock-include #'here)) @(ev '(require rackunit a86)) +@(ev '(require blackmail/compiler/compile blackmail/executor/run)) @(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "blackmail" f)))))) - '("main.rkt" "random.rkt" "correct.rkt")) + '("main.rkt" "syntax/random.rkt" "correct.rkt")) +@(ev '(define (exec e) (run (compile e)))) @(define (shellbox . s) (parameterize ([current-directory (build-path langs "blackmail")]) @@ -79,7 +81,7 @@ An example concrete program: The datatype for abstractly representing expressions can be defined as: -@codeblock-include["blackmail/ast.rkt"] +@codeblock-include["blackmail/syntax/ast.rkt"] So, @racket[(Lit 0)], @racket[(Lit 120)], and @racket[(Lit -42)] are Blackmail AST expressions, but so are @@ -90,7 +92,7 @@ So, @racket[(Lit 0)], @racket[(Lit 120)], and The parser is more involved than Abscond, but still straightforward: -@codeblock-include["blackmail/parse.rkt"] +@codeblock-include["blackmail/syntax/parse.rkt"] @ex[ (parse '42) @@ -162,14 +164,14 @@ an expression @math{e}, computes an integer @math{i}, such that To compute the meaning of an expression, the @racket[interp] function does a case analysis of the expression: -@codeblock-include["blackmail/interp.rkt"] +@codeblock-include["blackmail/interpreter/interp.rkt"] In the case of a @racket[(Prim1 p e)] expression, the interpreter first recursively computes the meaning of the subexpression @racket[e] and then defers to a helper function @racket[interp-prim1] which interprets the meaning of a given unary operation and value: -@codeblock-include["blackmail/interp-prim.rkt"] +@codeblock-include["blackmail/interpreter/interp-prim.rkt"] If the given operation is @racket['add1], the function adds 1; if it's @racket['sub1], it subtracts 1. @@ -267,7 +269,7 @@ functions: the first, which is given a program, emits the entry point and return instructions, invoking another function to compile the expression: -@codeblock-include["blackmail/compile.rkt"] +@codeblock-include["blackmail/compiler/compile.rkt"] Notice that @racket[compile-e] is defined by structural recursion, much like the interpreter. @@ -279,7 +281,7 @@ instructions that, when executed, will place @racket[e]'s value in the compiler emits instructions for carrying out the operation @racket[p], defering to a helper function @racket[compile-op1]: -@codeblock-include["blackmail/compile-ops.rkt"] +@codeblock-include["blackmail/compiler/compile-ops.rkt"] This function either emits an @racket[Add] or @racket[Sub] instruction, depending upon @racket[p]. @@ -310,7 +312,7 @@ what they each produce: Based on this, it's useful to define an @racket[exec] function that (should) behave like @racket[interp], just as we did for Abscond: -@codeblock-include["blackmail/exec.rkt"] +@codeblock-include["blackmail/executor/exec.rkt"] @ex[ (exec (parse '(add1 (add1 40)))) @@ -364,7 +366,7 @@ which you can use, without needing the understand how it was implemented. @ex[ -(eval:alts (require "random.rkt") (void)) +(eval:alts (require "syntax/random.rkt") (void)) (random-expr) (random-expr) (random-expr) diff --git a/www/notes/con-semantics.rkt b/www/notes/con-semantics.rkt new file mode 100644 index 00000000..5262c872 --- /dev/null +++ b/www/notes/con-semantics.rkt @@ -0,0 +1,26 @@ +#lang racket + +(provide C-concrete C 𝑪) + +(require redex/reduction-semantics + (only-in "blackmail-semantics.rkt" B B-concrete 𝑩)) + +(define-extended-language C-concrete B-concrete + (e ::= .... (if (zero? e) e e))) + +(define-extended-language C B + (e ::= .... (IfZero e e e))) + +(define-extended-judgment-form C 𝑩 + #:mode (𝑪 I O) + #:contract (𝑪 e i) + [(𝑪 e_0 i_0) (side-condition ,(= (term i_0) 0)) (𝑪 e_1 i_1) + -------- + (𝑪 (IfZero e_0 e_1 e_2) i_1)] + + [(𝑪 e_0 i_0) (side-condition ,(!= (term i_0) 0)) (𝑪 e_2 i_2) + -------- + (𝑪 (IfZero e_0 e_1 e_2) i_2)]) + +(define (!= n1 n2) + (not (= n1 n2))) diff --git a/www/notes/con.scrbl b/www/notes/con.scrbl index bb65e94a..8854b467 100644 --- a/www/notes/con.scrbl +++ b/www/notes/con.scrbl @@ -8,7 +8,7 @@ #;(prefix-in sem: (only-in "con/semantics.rkt" ext lookup)) "utils.rkt" "ev.rkt" - con/semantics + "con-semantics.rkt" "../utils.rkt") @@ -17,8 +17,10 @@ @(ev '(require rackunit a86)) +@(ev '(require con/compiler/compile con/executor/run)) @(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "con" f)))))) - '("main.rkt" "random.rkt" "correct.rkt")) + '("main.rkt" "syntax/random.rkt" "correct.rkt")) +@(ev '(define (exec e) (run (compile e)))) @title[#:tag "Con"]{Con: branching with conditionals} @@ -44,7 +46,7 @@ This leads to the following grammar for concrete Con: And abstract grammar: -@codeblock-include["con/ast.rkt"] +@codeblock-include["con/syntax/ast.rkt"] @;{ We will also need a predicate for well-formed Con expressions, but @@ -53,7 +55,7 @@ let's return to this after considering the semantics and interpreter. The parser is similar to what we've seen before: -@codeblock-include["con/parse.rkt"] +@codeblock-include["con/syntax/parse.rkt"] @ex[ (parse '(if (zero? 42) 1 2)) @@ -167,7 +169,7 @@ The semantics is defined by extending the interpreter to add a case for if-expressions, which recursively evaluates the test expression and branches based on its value. -@codeblock-include["con/interp.rkt"] +@codeblock-include["con/interpreter/interp.rkt"] We can confirm the interpreter computes the right result for the examples given earlier (using @racket[parse] to state the examples @@ -257,7 +259,7 @@ instructions. The complete compiler code is: -@codeblock-include["con/compile.rkt"] +@codeblock-include["con/compiler/compile.rkt"] Let's take a look at a few examples: @ex[ @@ -295,7 +297,7 @@ programs, and are provided in a @link["con/random.rkt"]{random.rkt} module. @ex[ -(eval:alts (require "random.rkt") (void)) +(eval:alts (require "syntax/random.rkt") (void)) (random-expr) (random-expr) (random-expr) @@ -306,4 +308,4 @@ module. This compiler has continues to have the issues identified in @secref{broken}, but appears correct in its implementation of -conditional expressions. \ No newline at end of file +conditional expressions. diff --git a/www/notes/dodger.scrbl b/www/notes/dodger.scrbl index 608bf583..0778bd92 100644 --- a/www/notes/dodger.scrbl +++ b/www/notes/dodger.scrbl @@ -14,8 +14,10 @@ @(define codeblock-include (make-codeblock-include #'h)) @(ev '(require rackunit a86)) +@(ev '(require dodger/compiler/compile dodger/executor/run)) @(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "dodger" f)))))) - '("main.rkt" "random.rkt" "correct.rkt")) + '("main.rkt" "syntax/random.rkt" "correct.rkt")) +@(ev '(define (exec e) (run (compile e)))) @title[#:tag "Dodger"]{Dodger: addressing a lack of character} @@ -49,11 +51,11 @@ We will also add the following operations: Abstract syntax is modelled with the following datatype definition: -@codeblock-include["dodger/ast.rkt"] +@codeblock-include["dodger/syntax/ast.rkt"] The s-expression parser is defined as follows: -@codeblock-include["dodger/parse.rkt"] +@codeblock-include["dodger/syntax/parse.rkt"] @ex[ (parse #\a) @@ -106,11 +108,11 @@ system, described below, takes care of printing them. The interpeter is much like that of Dupe, except we have a new base case: -@codeblock-include["dodger/interp.rkt"] +@codeblock-include["dodger/interpreter/interp.rkt"] And the interpretation of primitives is extended: -@codeblock-include["dodger/interp-prim.rkt"] +@codeblock-include["dodger/interpreter/interp-prim.rkt"] The meaning of characters and their operations are just lifted from Racket. @@ -160,7 +162,7 @@ Notice that each kind of value is disjoint. We can write down functions for encoding into and decoding out of bits: -@codeblock-include["dodger/types.rkt"] +@codeblock-include["dodger/runtime/types.rkt"] @section{A Compiler for Dodger} @@ -169,13 +171,13 @@ representation of values described earlier and uses logical operations to implement the bit manipulating operations. Most of the work happens in the compilation of primitives: -@codeblock-include["dodger/compile-ops.rkt"] +@codeblock-include["dodger/compiler/compile-ops.rkt"] In fact the @racket[compile] is identical to its Dodger predecessor, since all the new work is done in @racket[value->bits] and @racket[compile-op1]: -@codeblock-include["dodger/compile.rkt"] +@codeblock-include["dodger/compiler/compile.rkt"] We can take a look at a few examples: @@ -201,16 +203,16 @@ need to add run-time support for printing character literals. We extend the bit-encoding of values following the pattern we've already seen: -@filebox-include[fancy-c dodger "types.h"] +@filebox-include[fancy-c dodger "runtime/types.h"] And update the interface for values in the runtime system: -@filebox-include[fancy-c dodger "values.h"] -@filebox-include[fancy-c dodger "values.c"] +@filebox-include[fancy-c dodger "runtime/values.h"] +@filebox-include[fancy-c dodger "runtime/values.c"] The only other change is that @tt{print_result} is updated to handle the case of printing characters: -@filebox-include[fancy-c dodger "print.c"] +@filebox-include[fancy-c dodger "runtime/print.c"] -@;{FIXME: examples should be creating executable at the command-line, not exec.} \ No newline at end of file +@;{FIXME: examples should be creating executable at the command-line, not exec.} diff --git a/www/notes/dupe-semantics.rkt b/www/notes/dupe-semantics.rkt new file mode 100644 index 00000000..165a6030 --- /dev/null +++ b/www/notes/dupe-semantics.rkt @@ -0,0 +1,58 @@ +#lang racket + +(provide D-concrete D 𝑫 𝑫-𝒑𝒓𝒊𝒎 is-true is-false) + +(require redex/reduction-semantics + (only-in "con-semantics.rkt" C C-concrete)) + +(define-extended-language D-concrete C-concrete + (e ::= .... boolean (if e e e) (zero? e))) + +(define-extended-language D C + (e ::= (Int i) (Bool b) (Prim1 p1 e) (If e e e)) + (p1 ::= .... 'zero?) + (v ::= i b) + (b ::= #t #f)) + +(define-judgment-form D + #:mode (𝑫 I O) + #:contract (𝑫 e v) + [-------- + (𝑫 (Int i) i)] + + [-------- + (𝑫 (Bool b) b)] + + [(𝑫 e_0 v_0) (where v_1 (𝑫-𝒑𝒓𝒊𝒎 p1 v_0)) + ----------- + (𝑫 (Prim1 p1 e_0) v_1)] + + [(𝑫 e_0 v_0) (is-true v_0) (𝑫 e_1 v_1) + -------- + (𝑫 (If e_0 e_1 e_2) v_1)] + + [(𝑫 e_0 v_0) (is-false v_0) (𝑫 e_2 v_2) + -------- + (𝑫 (If e_0 e_1 e_2) v_2)]) + +(define-metafunction D + 𝑫-𝒑𝒓𝒊𝒎 : p1 v -> v or ⊥ + [(𝑫-𝒑𝒓𝒊𝒎 'add1 i) ,(+ (term i) (term 1))] + [(𝑫-𝒑𝒓𝒊𝒎 'sub1 i) ,(- (term i) (term 1))] + [(𝑫-𝒑𝒓𝒊𝒎 'zero? 0) #t] + [(𝑫-𝒑𝒓𝒊𝒎 'zero? i) #f] + [(𝑫-𝒑𝒓𝒊𝒎 _ _) ⊥]) + +(define-judgment-form D + #:mode (is-true I) + #:contract (is-true v) + [----------- + (is-true #t)] + [---------- + (is-true i)]) + +(define-judgment-form D + #:mode (is-false I) + #:contract (is-false v) + [----------- + (is-false #f)]) diff --git a/www/notes/dupe.scrbl b/www/notes/dupe.scrbl index cd8bb9ce..e3787123 100644 --- a/www/notes/dupe.scrbl +++ b/www/notes/dupe.scrbl @@ -8,8 +8,9 @@ "../fancyverb.rkt" "utils.rkt" "ev.rkt" - dupe/semantics - dupe/types + "dupe-semantics.rkt" + dupe/executor/decode + dupe/runtime/types "../utils.rkt") @@ -17,8 +18,10 @@ @(define codeblock-include (make-codeblock-include #'h)) @(ev '(require rackunit a86)) +@(ev '(require dupe/compiler/compile dupe/executor/decode dupe/executor/run)) @(for-each (λ (f) (ev `(require (file ,(path->string (build-path langs "dupe" f)))))) - '("main.rkt" "random.rkt" "correct.rkt")) + '("main.rkt" "syntax/random.rkt" "correct.rkt")) +@(ev '(define (exec e) (run (compile e)))) @title[#:tag "Dupe"]{Dupe: a duplicity of types} @@ -50,11 +53,11 @@ Together this leads to the following grammar for concrete Dupe. And abstract Dupe: -@codeblock-include["dupe/ast.rkt"] +@codeblock-include["dupe/syntax/ast.rkt"] The s-expression parser is defined as follows: -@codeblock-include["dupe/parse.rkt"] +@codeblock-include["dupe/syntax/parse.rkt"] @ex[ (parse '#t) @@ -182,12 +185,12 @@ The interpreter follows a similar pattern to what we've done so far, although notice that the result of interpretation is now a @tt{Value}: either a boolean or an integer: -@codeblock-include["dupe/interp.rkt"] +@codeblock-include["dupe/interpreter/interp.rkt"] The interpretation of primitives is extended to account for the new @racket[zero?] primitive: -@codeblock-include["dupe/interp-prim.rkt"] +@codeblock-include["dupe/interpreter/interp-prim.rkt"] We can confirm the interpreter computes the right result for the @@ -398,7 +401,7 @@ reprepresent booleans. Even numbers represent integers. Here are some functions to check our understanding of the encoding: -@codeblock-include["dupe/types.rkt"] +@codeblock-include["dupe/runtime/types.rkt"] @#reader scribble/comment-reader (ex @@ -989,12 +992,12 @@ the time the assemble code executes (i.e. run-time) the @racket[value->bits] function (and indeed all Racket functions) no longer exists. -@codeblock-include["dupe/compile.rkt"] +@codeblock-include["dupe/compiler/compile.rkt"] The compilation of primitives, including the new @racket[zero?] primitive, can be accomplished with: -@codeblock-include["dupe/compile-ops.rkt"] +@codeblock-include["dupe/compiler/compile-ops.rkt"] We can try out the compiler with the help of @racket[asm-interp], but you'll notice the results are a bit surprising: @@ -1022,7 +1025,7 @@ values: Which leads us to the following definition of @racket[exec]: -@codeblock-include["dupe/exec.rkt"] +@codeblock-include["dupe/executor/exec.rkt"] @ex[ (exec (parse #t)) @@ -1050,7 +1053,7 @@ Dupe. We define the bit representations in a header file corresponding to the definitions given in @tt{types.rkt}: -@filebox-include[fancy-c dupe "types.h"] +@filebox-include[fancy-c dupe "runtime/types.h"] It uses an idiom of ``masking'' in order to examine on particular bits of a value. So for example if we want to @@ -1066,18 +1069,18 @@ true. We use the following interface for values in the runtime system: -@filebox-include[fancy-c dupe "values.h"] -@filebox-include[fancy-c dupe "values.c"] +@filebox-include[fancy-c dupe "runtime/values.h"] +@filebox-include[fancy-c dupe "runtime/values.c"] The @tt{main} function remains largely the same although now we use @tt{val_t} in place of @tt{int64_t}: -@filebox-include[fancy-c dupe "main.c"] +@filebox-include[fancy-c dupe "runtime/main.c"] And finally, @tt{print_result} is updated to do a case analysis on the type of the result and print accordingly: -@filebox-include[fancy-c dupe "print.c"] +@filebox-include[fancy-c dupe "runtime/print.c"] @section{Correctness and testing} @@ -1179,7 +1182,7 @@ have type errors in them, but with our revised version how well this is actually testing the compiler). @ex[ -(eval:alts (require "random.rkt") (void)) +(eval:alts (require "syntax/random.rkt") (void)) (random-expr) (random-expr) (random-expr) @@ -1189,4 +1192,3 @@ how well this is actually testing the compiler). (for ([i (in-range 10)]) (check-compiler (random-expr))) ] - diff --git a/www/notes/evildoer.scrbl b/www/notes/evildoer.scrbl index d7cfb960..c8b12501 100644 --- a/www/notes/evildoer.scrbl +++ b/www/notes/evildoer.scrbl @@ -4,7 +4,7 @@ @(require redex/pict racket/runtime-path scribble/examples - evildoer/types + evildoer/runtime/types "../fancyverb.rkt" "utils.rkt" "ev.rkt" @@ -18,8 +18,10 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 evildoer evildoer/correct evildoer/compile-ops)) -@;{This is needed for the example that uses current-objs} +@(ev '(require rackunit a86 evildoer evildoer/correct evildoer/compiler/compile-ops evildoer/compiler/compile evildoer/executor/run)) +@(ev '(define (exec e) (run (compile e)))) +@(ev '(define (exec/io e i) (run/io (compile e) i))) +@;{This is needed for the example that uses current-objects} @(ev `(current-directory ,(path->string (build-path langs "evildoer")))) @(require (for-syntax racket/base)) @@ -146,11 +148,11 @@ Since we now have primitive operations that take 0 arguments, we split the @racket[Prim] constructor into @racket[Prim0] and @racket[Prim1]. -@codeblock-include["evildoer/ast.rkt"] +@codeblock-include["evildoer/syntax/ast.rkt"] The s-expression parser is defined as follows: -@codeblock-include["evildoer/parse.rkt"] +@codeblock-include["evildoer/syntax/parse.rkt"] @ex[ (parse 'eof) @@ -307,14 +309,14 @@ can then use to assert the expected behavior: Here's an interpreter for Evildoer: -@codeblock-include["evildoer/interp.rkt"] +@codeblock-include["evildoer/interpreter/interp.rkt"] The interpretation of primitives relies on the underlying implementations @racket[read-byte], @racket[write-byte], etc. from Racket (just like it does for all the other operations): -@codeblock-include["evildoer/interp-prim.rkt"] +@codeblock-include["evildoer/interpreter/interp-prim.rkt"] Interpreting a program that reads and writes will itself read and write: @@ -330,7 +332,7 @@ Using @racket[with-input-from-string] and @racket[with-output-to-string], we can also build a useful utility for interpreting programs with strings representing stdin and stdout: -@codeblock-include["evildoer/interp-io.rkt"] +@codeblock-include["evildoer/interpreter/interp-io.rkt"] @ex[ (interp/io (parse '(write-byte 104)) "") @@ -376,7 +378,7 @@ new bit encodings. So we add new encodings for @racket[eof] and @binary[(value->bits eof)] and @binary[(value->bits (void))], respectively. -@codeblock-include["evildoer/types.rkt"] +@codeblock-include["evildoer/runtime/types.rkt"] @section[#:tag "calling-c"]{Detour: Calling external functions} @@ -428,13 +430,13 @@ common divisor of two numbers. We could of course write such a function in assembly, but it's convenient to be able to write it in a higher-level language like C: -@filebox-include[fancy-c evildoer "gcd.c"] +@filebox-include[fancy-c evildoer "runtime/gcd.c"] We can compile this into an object file: @(define format (if (eq? (system-type 'os) 'macosx) "macho64" "elf64")) -@shellbox["gcc -c gcd.c -o gcd.o"] +@shellbox["gcc -c runtime/gcd.c -o gcd.o"] Now, how can we call @tt{gcd} from aseembly code? Just as there is a convention that a return value is communicated through @racket[rax], @@ -499,7 +501,7 @@ there is a mechanism for linking in object files to the assembly interprer: @ex[ -(current-objs '("gcd.o")) +(current-objects '("gcd.o")) (bits->value (asm-interp p))] We also could create an executable using the run-time system. @@ -515,7 +517,7 @@ Now we can assemble it into an object file, link the objects together to make an executable, and then run it: @shellbox[(string-append "nasm -f " format " p.s -o p.o") - "gcc runtime.o gcd.o p.o -o p.run" + "gcc runtime/runtime.o gcd.o p.o -o p.run" "./p.run"] So now we've seen the essence of how to call functions from assembly @@ -528,18 +530,18 @@ the compiled code. With new values comes the need to add new bit encodings. So we add new encodings for @racket[eof] and @racket[void]: -@filebox-include[fancy-c evildoer "types.h"] +@filebox-include[fancy-c evildoer "runtime/types.h"] The interface for the run-time system is extended to include file pointers for the input and output ports: -@filebox-include[fancy-c evildoer "runtime.h"] +@filebox-include[fancy-c evildoer "runtime/runtime.h"] The main entry point for the run-time sets up the input and output pointers to point to @tt{stdin} and @tt{stdout} and is updated to handle the proper printing of a void result: -@filebox-include[fancy-c evildoer "main.c"] +@filebox-include[fancy-c evildoer "runtime/main.c"] But the real novelty of the Evildoer run-time is that there will be new functions that implement @racket[read-byte], @@ -547,7 +549,7 @@ will be new functions that implement @racket[read-byte], functions called @racket[read_byte], @racket[peek_byte] and @racket[write_byte]: -@filebox-include[fancy-c evildoer "io.c"] +@filebox-include[fancy-c evildoer "runtime/io.c"] This functionality is implemented in terms of standard C library functions @tt{getc}, @tt{ungetc}, @tt{putc} and the run-time system's @@ -640,11 +642,11 @@ code. The top-level compiler: -@codeblock-include["evildoer/compile.rkt"] +@codeblock-include["evildoer/compiler/compile.rkt"] The primitive operation compiler: -@codeblock-include["evildoer/compile-ops.rkt"] +@codeblock-include["evildoer/compiler/compile-ops.rkt"] Notice how expressions like @racket[(read-byte)] and @racket[(write-byte)] @@ -668,7 +670,7 @@ The first is that the @racket[asm-interp] utility doesn't know anything about the Evildoer run-time. Hence we need to tell @racket[asm-interp] to link it in when running an example; otherwise labels like @tt{byte_write} will be undefined. We saw how to do this -in @secref["calling-c"] using the @racket[current-objs] parameter to +in @secref["calling-c"] using the @racket[current-objects] parameter to link in object files to @racket[asm-interp]. This time, the object file we want to link in is the Evildoer run-time. @@ -681,8 +683,8 @@ linked objects define an @tt{in} and @tt{out} symbol, it will set these appropriately to read input from a given string and collect output into a string. -@ex[ -(current-objs '("runtime.o")) +@racketblock[ +(current-objects '("runtime/runtime.o")) (asm-interp/io (compile (parse '(write-byte (read-byte)))) "a")] Notice though, that @racket[asm-interp/io] gives back a pair @@ -690,22 +692,22 @@ consisting of the @emph{bits} and the output string. To match the return type of @racket[interp/io] we need to convert the bits to a value: -@ex[ +@racketblock[ (match (asm-interp/io (compile (parse '(write-byte (read-byte)))) "a") [(cons b o) (cons (bits->value b) o)])] Using these pieces, we can write a function that matches the type signature of @racket[interp/io]: -@codeblock-include["evildoer/exec.rkt"] +@codeblock-include["evildoer/executor/exec.rkt"] -@ex[ +@racketblock[ (exec/io (parse '(write-byte (read-byte))) "z")] Note that we still provide an @racket[exec] function, but it assumes there is no input and it prints all output: -@ex[ +@racketblock[ (exec (parse '(eof-object? (read-byte)))) (exec (parse '(write-byte 97)))] @@ -723,7 +725,7 @@ stream: @codeblock-include["evildoer/correct.rkt"] -@ex[ +@racketblock[ (check-compiler (parse '(void)) "") (check-compiler (parse '(read-byte)) "a") (check-compiler (parse '(write-byte 97)) "")] @@ -734,8 +736,8 @@ guaranteed to be well-defined, as usual. Additionally, the @racket[random-input] function produces a random string that can be used as the input. -@ex[ -(require "random.rkt") +@racketblock[ +(require "syntax/random.rkt") (random-expr) (random-well-defined-expr) (random-input)] @@ -743,9 +745,9 @@ used as the input. Together, these can be used to randomly test the correctness of the compiler: -@ex[ +@racketblock[ (for ((i 100)) (check-compiler (random-expr) (random-input))) (for ((i 100)) (check-compiler (random-well-defined-expr) (random-input)))] - \ No newline at end of file + diff --git a/www/notes/extort-semantics.rkt b/www/notes/extort-semantics.rkt new file mode 100644 index 00000000..f615663c --- /dev/null +++ b/www/notes/extort-semantics.rkt @@ -0,0 +1,45 @@ +#lang racket + +(provide E-concrete E 𝑬) + +(require redex/reduction-semantics + (only-in "dupe-semantics.rkt" D-concrete D 𝑫)) + +(define-extended-language E-concrete D-concrete + (e ::= ....) + (a ::= v err)) + +(define-extended-language E D + (e ::= ....) + (a ::= v err)) + +(define-extended-judgment-form E 𝑫 + #:mode (𝑬 I O) + #:contract (𝑬 e a) + [(𝑬 e b) + -------- + (𝑬 (Prim1 'add1 e) err)] + + [(𝑬 e b) + ----------- + (𝑬 (Prim1 'sub1 e) err)] + + [(𝑬 e b) + ----------- + (𝑬 (Prim1 'zero? e) err)] + + [(𝑬 e err) + ----------- + (𝑬 (Prim1 'zero? e) err)] + + [(𝑬 e err) + ----------- + (𝑬 (Prim1 'add1 e) err)] + + [(𝑬 e err) + ----------- + (𝑬 (Prim1 'sub1 e) err)] + + [(𝑬 e err) + ----------- + (𝑬 (If e e_0 e_1) err)]) diff --git a/www/notes/extort.scrbl b/www/notes/extort.scrbl index 5957ba96..73a631a5 100644 --- a/www/notes/extort.scrbl +++ b/www/notes/extort.scrbl @@ -7,19 +7,20 @@ "../fancyverb.rkt" "utils.rkt" "ev.rkt" - extort/types - extort/semantics + extort/runtime/types + "extort-semantics.rkt" "../utils.rkt") @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 extort extort/compile-ops extort/correct)) +@(ev '(require rackunit a86 extort extort/compiler/compile-ops extort/correct extort/compiler/compile extort/executor/run)) +@(ev '(define (exec e) (run (compile e)))) @;{Hack to get un-provided functions from compile-ops} @(ev '(require (only-in rackunit require/expose))) -@(ev '(require/expose extort/compile-ops [assert-integer assert-char assert-byte assert-codepoint])) +@(ev '(require/expose extort/compiler/compile-ops [assert-integer assert-char assert-byte assert-codepoint])) @(define this-lang "Extort") @@ -140,7 +141,7 @@ shortcircuiting the remaining evaluation since as soon as we encounter an error, we know this is going to be the answer for the whole program. -@codeblock-include["extort/interp-prim.rkt"] +@codeblock-include["extort/interpreter/interp-prim.rkt"] Notice that the signature for these functions indicate that you get a value, or they raise an exception. The functions for interpreting @@ -159,7 +160,7 @@ this exception and returns the @racket['err] answer. The @racket[interp-e] that does the work of recursively interpreting the meaning of an expression: -@codeblock-include["extort/interp.rkt"] +@codeblock-include["extort/interpreter/interp.rkt"] We can confirm the interpreter computes the right result for the examples given earlier: @@ -184,7 +185,7 @@ This interpreter implicitly relies on the state of the input and output port, but we can define a pure interpreter like before, which we take as the specification of our language: -@codeblock-include["extort/interp-io.rkt"] +@codeblock-include["extort/interpreter/interp-io.rkt"] An important property of this semantics is that it provides a meaning for all possible expressions; in other words, @racket[interp/io] is a @@ -348,12 +349,12 @@ value is not of the asserted type: @item{@racket[assert-codepoint] @tt{: Register -> Asm} produces code to check that the value in the given register is an integer and either in the range [0,55295] or [57344, 1114111].} ] -@codeblock-include["extort/assert.rkt"] +@codeblock-include["extort/compiler/assert.rkt"] The compiler for primitive operations is updated to include appropriate type assertions: -@codeblock-include["extort/compile-ops.rkt"] +@codeblock-include["extort/compiler/compile-ops.rkt"] @ex[ (compile-op1 'add1) @@ -366,7 +367,7 @@ external label @racket['raise_error] that will be defined by the run-time system and defines a label called @racket['err] that calls @tt{raise_error}: -@codeblock-include["extort/compile.rkt"] +@codeblock-include["extort/compiler/compile.rkt"] @ex[ (compile-e (parse '(add1 #f))) @@ -386,13 +387,13 @@ code that prints and exits in @tt{error_exit}; this is done so that the testing framework can intercede and replace the error function, but it can be ignored.} -@filebox-include[fancy-c extort "main.c"] +@filebox-include[fancy-c extort "runtime/main.c"] Linking in the run-time allows us to define the @racket[exec] and @racket[exec/io] functions: -@codeblock-include["extort/exec.rkt"] +@codeblock-include["extort/executor/exec.rkt"] We can run examples: @@ -418,6 +419,6 @@ totality of the semantics: And again, we can randomly test the compiler by generating programs and inputs: @ex[ -(require extort/random) +(require extort/syntax/random) (for ((i 100)) (check-compiler (random-expr) (random-input)))] diff --git a/www/notes/fraud.scrbl b/www/notes/fraud.scrbl index d21d166a..fe385a45 100644 --- a/www/notes/fraud.scrbl +++ b/www/notes/fraud.scrbl @@ -21,7 +21,8 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 fraud fraud/translate)) +@(ev '(require rackunit a86 fraud fraud/syntax/translate fraud/compiler/compile fraud/executor/run)) +@(ev '(define (exec e) (run (compile e)))) @(define this-lang "Fraud") @(define prefix (string-append this-lang "-")) @@ -214,7 +215,7 @@ add it to the set of free variables in the result. When binding a variable, we add it to the set of bound variables before parsing the relevant part of the input where the variable is bound: -@codeblock-include["fraud/parse.rkt"] +@codeblock-include["fraud/syntax/parse.rkt"] @section[#:tag-prefix prefix]{Meaning of @this-lang programs} @@ -341,7 +342,7 @@ It is defined by structural recursion on the expression. Environments are represented as lists of associations between variables and values. There are two helper functions for @racket[ext] and @racket[lookup]: -@codeblock-include["fraud/interp.rkt"] +@codeblock-include["fraud/interpreter/interp.rkt"] We can confirm the interpreter computes the right result for the examples given earlier: @@ -486,7 +487,7 @@ address of a variable occurrence is count of variable names that occur before it in the list. When a variable is bound (via-@racket[let]) the list grows: -@codeblock-include["fraud/translate.rkt"] +@codeblock-include["fraud/syntax/translate.rkt"] Notice that @racket[translate] is a kind of mini-compiler that compiles @tt{Expr}s to @tt{IExpr}s. It's only job is to eliminate @@ -509,7 +510,7 @@ lexical address of (what used to be a) variable indicates the position in this list. When a value is bound by a @racket[let], the list grows: -@codeblock-include["fraud/interp-lexical.rkt"] +@codeblock-include["fraud/interpreter/interp.rkt"] Try to convince yourself that the two version of @racket[interp] compute the same function. @@ -823,7 +824,7 @@ bother with @racket[unpad-stack] because there's no coming back. Here is the compiler for primitives that incorporates all of these stack-alignment issues, but is otherwise the same as before: -@filebox-include[codeblock fraud "compile-ops.rkt"] +@filebox-include[codeblock fraud "compiler/compile-ops.rkt"] @section[#:tag-prefix prefix]{Complete @this-lang compiler} @@ -832,7 +833,7 @@ the compile-time environment which is weaved through out the @racket[compile-e] function and its subsidiaries, which is critical in @racket[compile-variable] and extended in @racket[compile-let]. -@filebox-include[codeblock fraud "compile.rkt"] +@filebox-include[codeblock fraud "compiler/compile.rkt"] Notice that the @racket[lookup] function computes a lexical address from an identifier and compile-time environment, @@ -908,4 +909,3 @@ The check for correctness is the same as before, although the check should only to elements of @tt{ClosedExpr}: @filebox-include[codeblock fraud "correct.rkt"] - diff --git a/www/notes/hoax.scrbl b/www/notes/hoax.scrbl index 2b2b7961..6055b854 100644 --- a/www/notes/hoax.scrbl +++ b/www/notes/hoax.scrbl @@ -4,7 +4,7 @@ @(require redex/pict racket/runtime-path scribble/examples - hoax/types + hoax/runtime/types ; (except-in "../../langs/hustle/semantics.rkt" ext lookup) ; (prefix-in sem: (only-in "../../langs/hustle/semantics.rkt" ext lookup)) "../fancyverb.rkt" @@ -15,7 +15,7 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 hoax hoax/compile-ops hoax/assert)) +@(ev '(require rackunit a86 hoax hoax/compiler/compile-ops hoax/compiler/assert)) @(define this-lang "Hoax") @@ -111,7 +111,7 @@ The @this-lang interpreter is essentially the same as for Hustle, although with the addition of ternary primitives, plus an extension of the @racket[interp-prim] module: -@codeblock-include["hoax/interp-prim.rkt"] +@codeblock-include["hoax/interpreter/interp-prim.rkt"] Vectors are easy to model in the interpreter because we can rely on vectors in the meta-level of Racket. @@ -1148,7 +1148,7 @@ Using this idea we can formulate a compiler for string literals: Most of the work for the @this-lang compiler is done in the compilation of the new operations: -@codeblock-include["hoax/compile-ops.rkt"] +@codeblock-include["hoax/compiler/compile-ops.rkt"] We can now confirm that the compiler generates code similar to what we wrote by hand above: @@ -1168,15 +1168,15 @@ wrote by hand above: First, we extend the value interface to include vectors: -@filebox-include[fancy-c hoax "values.h"] +@filebox-include[fancy-c hoax "runtime/values.h"] The implementation of @tt{val_typeof} is extended to handle another pointer type: -@filebox-include[fancy-c hoax "values.c"] +@filebox-include[fancy-c hoax "runtime/values.c"] Printing is updated to handle vectors and strings. Note that printing of strings seems complicated by this code is actually auto-generated from the Unicode specification. -@filebox-include[fancy-c hoax "print.c"] +@filebox-include[fancy-c hoax "runtime/print.c"] diff --git a/www/notes/hustle.scrbl b/www/notes/hustle.scrbl index c8841a12..bec03b0d 100644 --- a/www/notes/hustle.scrbl +++ b/www/notes/hustle.scrbl @@ -9,11 +9,20 @@ "ev.rkt" "../utils.rkt" "diagrams.rkt" - hustle/types) + hustle/runtime/types) @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 hustle hustle/unload hustle/heap hustle/interp-prims-heap hustle/compile-ops)) +@(ev '(require rackunit + a86 + hustle + hustle/interpreter/unload + hustle/interpreter/heap + hustle/interpreter/interp-prims-heap + hustle/compiler/compile-ops + hustle/compiler/compile + hustle/executor/run)) +@(ev '(define (exec e) (run (compile e)))) @(define this-lang "Hustle") @(define prefix (string-append this-lang "-")) @@ -173,7 +182,7 @@ syntax in Hustle: ] -@codeblock-include["hustle/parse.rkt"] +@codeblock-include["hustle/syntax/parse.rkt"] @section[#:tag-prefix prefix]{Meaning of @this-lang programs, implicitly} @@ -193,7 +202,7 @@ primitives to account for our new primitives such as @racket[cons], @racket[car], etc. And how should these primitives be interpreted? Using their Racket counterparts of course! -@codeblock-include["hustle/interp-prim.rkt"] +@codeblock-include["hustle/interpreter/interp-prim.rkt"] We can try it out: @@ -353,11 +362,11 @@ same thing: In fact, we can reconstruct a @tt{Value} from a @tt{Value*} and @tt{Heap}: -@codeblock-include["hustle/unload.rkt"] +@codeblock-include["hustle/interpreter/unload.rkt"] Which relies on our interface for heaps: -@codeblock-include["hustle/heap.rkt"] +@codeblock-include["hustle/interpreter/heap.rkt"] Try it out: @@ -407,7 +416,7 @@ pointed to by the given @racket[cons-ptr] value. Much of the work is handled in the new @tt{interp-prims-heap} module: -@codeblock-include["hustle/interp-prims-heap.rkt"] +@codeblock-include["hustle/interpreter/interp-prims-heap.rkt"] @ex[ @@ -427,7 +436,7 @@ function that modelled memory implicitly, calls @racket[interp-env-heap] with an initially empty heap and the unloads the final answer from the result: -@codeblock-include["hustle/interp-heap.rkt"] +@codeblock-include["hustle/interpreter/interp-heap.rkt"] @@ -1146,7 +1155,7 @@ the corresponding Racket value. In other words, extending @racket[bits->value] to work in the presence of tagged pointer values involves just the kind of thing we've written: -@codeblock-include["hustle/types.rkt"] +@codeblock-include["hustle/runtime/types.rkt"] You'll notice that instead of the @racket[mem-ref] we wrote, it uses Racket's own ``unsafe'' operations. The only difference is that this @@ -1156,7 +1165,7 @@ With @racket[bits->value] in place, we can now build up some utilities for running programs with the run-time system linked in and using @racket[bits->value] to construct the result value: -@codeblock-include["hustle/run.rkt"] +@codeblock-include["hustle/executor/run.rkt"] Let's make the list @racket['(1 2 3)]. Remember that @racket['(1 2 3)] is just @racket[(cons 1 (cons 2 (cons 3 '())))]. @@ -1307,7 +1316,7 @@ Putting it all together we get the compiler for the new primitives: @racket[car], @racket[car], @racket[cdr], @racket[cons?], and @racket[empty?]: -@codeblock-include["hustle/compile-ops.rkt"] +@codeblock-include["hustle/compiler/compile-ops.rkt"] @@ -1357,7 +1366,7 @@ this we're going to use @racket[rbx] to store our heap pointer. You can see that we do this in the compiler with @racket[(Mov rbx rdi)] as part of our entry code. -@filebox-include[fancy-c hustle "main.c"] +@filebox-include[fancy-c hustle "runtime/main.c"] @subsection{Updating the run-time system's notion of Values} @@ -1365,12 +1374,12 @@ part of our entry code. We extend our runtime system's view of values to include pointers and use C @tt{struct} to represent them: -@filebox-include[fancy-c hustle "values.h"] +@filebox-include[fancy-c hustle "runtime/values.h"] The implementation of @tt{val_typeof} is extended to handle pointer types: -@filebox-include[fancy-c hustle "values.c"] +@filebox-include[fancy-c hustle "runtime/values.c"] @subsection{Printing Values} @@ -1381,7 +1390,7 @@ analogous to how @racket[bits->value] had to recursively construct values, too). It also must account for the wrinkle of how the printing of proper and improper lists is different: -@filebox-include[fancy-c hustle "print.c"] +@filebox-include[fancy-c hustle "runtime/print.c"] @section[#:tag-prefix prefix]{Correctness} diff --git a/www/notes/iniquity.scrbl b/www/notes/iniquity.scrbl index 47ffc7fe..0e4e9698 100644 --- a/www/notes/iniquity.scrbl +++ b/www/notes/iniquity.scrbl @@ -11,7 +11,13 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86 iniquity)) +@(ev '(require rackunit a86 iniquity iniquity/compiler/compile iniquity/executor/run)) +@(ev '(define (exec e) (run (compile e)))) +@(ev '(define (Offset a b) + (cond + [(symbol? a) (Mem a b)] + [(symbol? b) (Mem b a)] + [else (error 'Offset "expected register and offset; given ~e ~e" a b)]))) @(define (shellbox . s) (parameterize ([current-directory (build-path langs "iniquity")]) @@ -95,7 +101,7 @@ An example concrete @this-lang program is: To represent these kinds of programs, we extend the definition of ASTs as follows: -@codeblock-include["iniquity/ast.rkt"] +@codeblock-include["iniquity/syntax/ast.rkt"] The parser will need to be updated to parse programs, not just expressions. Since a program is a sequence of forms, we will assume @@ -105,14 +111,14 @@ of s-expressions. There is also a new parse for function definitions, @racket[parse-definition]. The parser for expressions @racket[parse-e] is updated to include function applications. -@codeblock-include["iniquity/parse.rkt"] +@codeblock-include["iniquity/syntax/parse.rkt"] Because of the change from a program being a single expression to a sequence, we have to update the utilities that read program files, i.e. @tt{interp-stdin.rkt} and @tt{compile-stdin.rkt}: -@codeblock-include["iniquity/interp-stdin.rkt"] -@codeblock-include["iniquity/compile-stdin.rkt"] +@codeblock-include["iniquity/interpreter/interp-stdin.rkt"] +@codeblock-include["iniquity/compiler/compile-stdin.rkt"] @@ -133,7 +139,7 @@ number of parameters as there are arguments in the call, the body of the function is interpreted in an enviorment that maps each parameter to to the corresponding argument. That's it. -@codeblock-include["iniquity/interp.rkt"] +@codeblock-include["iniquity/interpreter/interp.rkt"] A couple of things to note: @@ -245,7 +251,7 @@ That means that the argument will be in @racket[(Offset 'rsp 8)]. So we can touch-up the example as follows and it will work: -@(void (ev '(current-objs '()))) +@(void (ev '(current-objects '()))) @#reader scribble/comment-reader (ex @@ -678,4 +684,4 @@ the interpreter: The complete compiler code: -@codeblock-include["iniquity/compile.rkt"] +@codeblock-include["iniquity/compiler/compile.rkt"] diff --git a/www/notes/jig.scrbl b/www/notes/jig.scrbl index 7f91e65a..1c0cf4d2 100644 --- a/www/notes/jig.scrbl +++ b/www/notes/jig.scrbl @@ -14,7 +14,11 @@ @(ev `(current-directory ,(path->string (build-path langs "jig")))) @(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Jig") @@ -171,7 +175,7 @@ Before addressing the issue of compiling proper tail calls, let's first think about the interpreter, starting from the interpreter we wrote for Iniquity: -@codeblock-include["iniquity/interp.rkt"] +@codeblock-include["iniquity/interpreter/interp.rkt"] What needs to be done to make it implement proper tail calls? @@ -205,7 +209,7 @@ all about and how we can make them work. Here's what this code will compile to, roughly: -@(void (ev '(current-objs '()))) +@(void (ev '(current-objects '()))) @#reader scribble/comment-reader (ex @@ -735,4 +739,4 @@ There are two important places where @racket[t?] is seeded to @racket[#t]: The complete compiler: -@codeblock-include["jig/compile.rkt"] +@codeblock-include["jig/compiler/compile.rkt"] diff --git a/www/notes/knock.scrbl b/www/notes/knock.scrbl index 1f3f904f..e5db5484 100644 --- a/www/notes/knock.scrbl +++ b/www/notes/knock.scrbl @@ -19,10 +19,14 @@ @(ev '(require rackunit a86)) @(ev `(current-directory ,(path->string (build-path langs "knock")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(ev '(current-objs '("runtime.o"))) +@(void (ev '(with-output-to-string (thunk (system "make -C runtime runtime.o"))))) +@(ev '(current-objects '("runtime/runtime.o"))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Knock") @@ -66,7 +70,7 @@ matching parts of @racket[_e]'s value. If no patterns match The syntax is extended as follows: -@codeblock-include["knock/ast.rkt"] +@codeblock-include["knock/syntax/ast.rkt"] @section[#:tag-prefix "knock"]{Match by Example} @@ -330,7 +334,7 @@ It's fairly straightforward: The complete interpreter: -@codeblock-include["knock/interp.rkt"] +@codeblock-include["knock/interpreter/interp.rkt"] We can now see it in action: @@ -677,7 +681,7 @@ expression: We can check that the compiler works for a complete example: -@ex[ +@racketblock[ (define (run . p) (bits->value (asm-interp (compile (apply parse p))))) @@ -693,4 +697,4 @@ We can check that the compiler works for a complete example: With these pieces in place, here's the complete compiler: -@codeblock-include["knock/compile.rkt"] +@codeblock-include["knock/compiler/compile.rkt"] diff --git a/www/notes/loot.scrbl b/www/notes/loot.scrbl index 2e55f894..26b89996 100644 --- a/www/notes/loot.scrbl +++ b/www/notes/loot.scrbl @@ -12,10 +12,14 @@ @(ev '(require rackunit a86)) @(ev `(current-directory ,(path->string (build-path langs "loot")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(void (ev '(current-objs '("runtime.o")))) +@(void (ev '(with-output-to-string (thunk (system "make -C runtime runtime.o"))))) +@(void (ev '(current-objects '("runtime/runtime.o")))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Loot") @@ -413,7 +417,7 @@ functions, given in @racket[ds]. The complete interpreter is: -@codeblock-include["loot/interp.rkt"] +@codeblock-include["loot/interpreter/interp.rkt"] We now have the full power of @racket[λ] expressions in our language. We can write recursive functions, using only anonymous functions, via @@ -566,7 +570,7 @@ to be in the (Racket) function: We can give it a try: -@(ev `(require (file ,(path->string (build-path langs "loot" "interp-defun.rkt"))))) +@(ev `(require (file ,(path->string (build-path langs "loot" "interpreter" "interp-defun.rkt"))))) @ex[ (define (run . p) (interp (parse p))) @@ -772,7 +776,7 @@ as the first argument on stack before calling the function's code. To implement this, we will need to compute the free variables, which we do with the following function: -@codeblock-include["loot/fv.rkt"] +@codeblock-include["loot/syntax/fv.rkt"] We can now write the function that compiles a labelled @racket[λ]-expression into a function in assembly: @@ -844,7 +848,7 @@ The compiler will need to generate one such function for each extracting all the @racket[λ]-expressions: -@codeblock-include["loot/lambdas.rkt"] +@codeblock-include["loot/syntax/lambdas.rkt"] And another for compiling each of them: @@ -1235,4 +1239,4 @@ of all the closures and adjusts @racket['rbx] appropriately: Putting all the pieces together, we have the complete compile for Loot: -@codeblock-include["loot/compile.rkt"] +@codeblock-include["loot/compiler/compile.rkt"] diff --git a/www/notes/modules.scrbl b/www/notes/modules.scrbl index 45e65f13..e7d58ce8 100644 --- a/www/notes/modules.scrbl +++ b/www/notes/modules.scrbl @@ -220,7 +220,7 @@ work from our compiler by simply linking them against this new runtime: @#reader scribble/comment-reader (ex -(current-objs '("runtime-plus-length.o")) +(current-objects '("runtime-plus-length.o")) (unload/free (asm-interp (seq (Extern (symbol->label 'length)) @@ -250,4 +250,3 @@ Before addressing these issues, let's instead turn to another, closely related issue: supporting the compilation of multi-module programs. @section[#:tag-prefix "knock"]{Modules} - diff --git a/www/notes/mountebank-simple-interp.rkt b/www/notes/mountebank-simple-interp.rkt new file mode 100644 index 00000000..91d447ba --- /dev/null +++ b/www/notes/mountebank-simple-interp.rkt @@ -0,0 +1,59 @@ +#lang racket + +;; type Expr = Number +;; | Boolean +;; | (list Op1 Expr) +;; | (list Op2 Expr) +;; | (list 'if Expr Expr Expr) +;; | (list Expr Expr) +;; | (list 'λ (list Id) Expr) +;; | Id + +;; type Id = Symbol +;; type Op1 = 'sub1 | 'zero? +;; type Op2 = '+ + +;; type Value = Number +;; | Boolean +;; | (Value -> Value) + +;; Expr Env -> Value +(define (interp e r) + (match e + [(list '+ e1 e2) + (+ (interp e1 r) (interp e2 r))] + [(list 'sub1 e1) + (sub1 (interp e1 r))] + [(list 'zero? e1) + (zero? (interp e1 r))] + [(list 'if e1 e2 e3) + (if (interp e1 r) + (interp e2 r) + (interp e3 r))] + [(list 'λ (list x) e1) + (λ (v) (interp e1 (cons (cons x v) r)))] + [(list e1 e2) + ((interp e1 r) (interp e2 r))] + [_ + (if (symbol? e) + (lookup e r) + e)])) + +;; Id Env -> Value +(define (lookup x r) + (match r + [(cons (cons y v) r) + (if (eq? x y) + v + (lookup x r))])) + +(interp '(((λ (t) + ((λ (f) (t (λ (z) ((f f) z)))) + (λ (f) (t (λ (z) ((f f) z)))))) + (λ (tri) + (λ (n) + (if (zero? n) + 0 + (+ n (tri (sub1 n))))))) + 36) + '()) diff --git a/www/notes/mountebank.scrbl b/www/notes/mountebank.scrbl index fc06a805..f099e371 100644 --- a/www/notes/mountebank.scrbl +++ b/www/notes/mountebank.scrbl @@ -3,20 +3,30 @@ @(require (for-label (except-in racket compile ...) a86/ast)) @(require redex/pict racket/runtime-path + racket/file + racket/string scribble/examples + mountebank/executor/decode "utils.rkt" "ev.rkt" "../fancyverb.rkt" "../utils.rkt") +@(define-runtime-path simple-interp-file "mountebank-simple-interp.rkt") @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86)) +@(ev '(require rackunit a86 mountebank/executor/decode)) @(ev `(current-directory ,(path->string (build-path langs "mountebank")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(void (ev '(current-objs '("runtime.o")))) +@(void (ev '(with-output-to-string (thunk (system "make -C runtime runtime.o"))))) +@(void (ev '(current-objects '("runtime/runtime.o")))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "compile-expr.rkt" "compile-literals.rkt" "compile-datum.rkt" "utils.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "compiler/compile-literals.rkt" + "compiler/compile-datum.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Mountebank") @@ -128,13 +138,13 @@ string are redundant, we remove all of the literal constructors. Here is the new AST definition: -@filebox-include[codeblock mountebank "ast.rkt"] +@filebox-include[codeblock mountebank "syntax/ast.rkt"] The parser is updated to parse things like booleans, numbers, etc. as @racket[Quote] nodes now and also to support the ability to write arbitrary datum value under a quote: -@filebox-include[codeblock mountebank "parse.rkt"] +@filebox-include[codeblock mountebank "syntax/parse.rkt"] @section[#:tag-prefix "mountebank"]{Quotes are constants} @@ -185,7 +195,7 @@ that appear in @racket[quote]d datums are interned as usual: Interpreting a quoted datum is trivial---it evaluates to the datum itself: -@filebox-include[codeblock mountebank "interp.rkt"] +@filebox-include[codeblock mountebank "interpreter/interp.rkt"] The proper treatment of datums as constants is inherited from Racket, so our interpreter does the right thing on these examples: @@ -218,7 +228,7 @@ The latter is achieved by extending the @racket[literals] function from Mug to traverse the datum in a @racket[quote] to extract any string or symbol occurrences. -@filebox-include[codeblock mountebank "compile-literals.rkt"] +@filebox-include[codeblock mountebank "compiler/compile-literals.rkt"] The static allocation of compound datums is achieved use the same static memory allocation mechanism we saw when allocating the string @@ -241,7 +251,7 @@ contain its data and a tagged address of that memory.} Let's see some examples: -@ex[ +@racketblock[ (compile-datum 0) @@ -267,7 +277,7 @@ Datums can be built up arbitrarily large, so in order to compound datums, we need to recursive traverse their structure to emit the static data section of their construction. Here's a larger example: -@ex[ +@racketblock[ (compile-datum '((3) fred #(x y z) (("wilma")))) ] @@ -278,7 +288,7 @@ appropriately tagged, to those labels. Here is a simple example of a nested datum: a box containing a box containing zero. -@ex[ +@racketblock[ (compile-datum '#&#&0) ] @@ -290,13 +300,13 @@ address of the outer box into @racket['rax]. Here is the complete code for @racket[compile-datum]: -@filebox-include[codeblock mountebank "compile-datum.rkt"] +@filebox-include[codeblock mountebank "compiler/compile-datum.rkt"] Now we've succsefully implemented @racket[quote] and can confirm are examples behave as expected: -@ex[ -(current-objs '("runtime.o")) +@racketblock[ +(current-objects '("runtime/runtime.o")) (define (run . p) (bits->value (asm-interp (compile (parse p))))) @@ -324,36 +334,39 @@ kind of data in a way that may seem familiar. For example, here's a program that interprets a little language that has elements of the ones we've been building: -@filebox-include[codeblock mountebank "simple-interp.rkt"] +@(filebox (link "code/mountebank/simple-interp.rkt" (tt "mountebank/simple-interp.rkt")) + (codeblock (file->string simple-interp-file))) Now of course this is a Racket program, which we can run. Running it will run the interpreter we defined on the input program, computing the 36th triangular number: -@(define (shellbox . s) - (parameterize ([current-directory (build-path langs "mountebank")]) - (filebox (emph "shell") - (fancyverbatim "fish" (apply shell s))))) +@(define (shellbox-static . ss) + (filebox (emph "shell") + (fancyverbatim "fish" (string-join ss "\n")))) -@shellbox[ -"racket simple-interp.rkt" -] +@shellbox-static{ +racket simple-interp.rkt +666 +} But of course, this is also a Mountebank program! So we can interpret it with our Mountenank interpreter: -@shellbox[ -"racket -t interp-file.rkt -m simple-interp.rkt" -] +@shellbox-static{ +racket -t interp-file.rkt -m simple-interp.rkt +666 +} And since it's a Mountebank program, we can also compile it and then running the resulting executable: -@shellbox[ -"make simple-interp.run" -"./simple-interp.run" -] +@shellbox-static{ +make simple-interp.run +./simple-interp.run +666 +} We are moving ever closer to the point where our compiler can compile -the source code of itself. \ No newline at end of file +the source code of itself. diff --git a/www/notes/mug.scrbl b/www/notes/mug.scrbl index 4f1bb8c0..cfc96be3 100644 --- a/www/notes/mug.scrbl +++ b/www/notes/mug.scrbl @@ -4,6 +4,7 @@ @(require redex/pict racket/runtime-path scribble/examples + mug/executor/decode "utils.rkt" "ev.rkt" "../fancyverb.rkt" @@ -11,12 +12,18 @@ @(define codeblock-include (make-codeblock-include #'h)) -@(ev '(require rackunit a86)) +@(ev '(require rackunit a86 mug/compiler/compile-literals mug/syntax/literals mug/executor/decode)) @(ev `(current-directory ,(path->string (build-path langs "mug")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(void (ev '(current-objs '("runtime.o")))) +@(void (ev '(with-output-to-string (thunk (system "make -C runtime runtime.o"))))) +@(void (ev '(current-objects '("runtime/runtime.o")))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "compile-expr.rkt" "compile-literals.rkt" "utils.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "compiler/compile-literals.rkt" + "compiler/compile-ops.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Mug") @@ -70,7 +77,7 @@ Let's consider how strings were previously compiled. Here's an assembly program that returns @racket["Hello!"]: @;{ -@ex[ +@racketblock[ (require loot/compile) (seq (Label 'entry) (Mov 'rbx 'rdi) @@ -80,7 +87,7 @@ that returns @racket["Hello!"]: We can run it just to make sure: -@ex[ +@racketblock[ (bits->value (asm-interp (seq (Global 'entry) @@ -143,7 +150,7 @@ So to write a similar program that returns @racket["Hello!"] but @emph{statically} allocates the memory for the string, we could do the following: -@ex[ +@racketblock[ (bits->value (asm-interp (seq (Global 'entry) @@ -193,7 +200,7 @@ Here is a version of the same program that avoids the @racket[Or] instruction, instead computing that type tagging at link time: -@ex[ +@racketblock[ (bits->value (asm-interp (seq (Global 'entry) @@ -217,7 +224,7 @@ reduces the run-time memory that is allocated and makes is more efficient to evaluate string literals. We could replace the old @racket[compile-string] function with the following: -@ex[ +@racketblock[ (define (compile-string s) (let ((l (gensym 'string))) (seq (Data) @@ -226,7 +233,9 @@ efficient to evaluate string literals. We could replace the old (map Dd (map char->integer (string->list s))) (Text) (Lea 'rax (|@| (+ l type-str)))))) +] +@racketblock[ (compile-string "Hello!") (bits->value @@ -355,7 +364,7 @@ association needs to be maintained explicity. So here's how an occurrence of @racket["Hello!"] is compiled: -@ex[ +@racketblock[ (compile-string "Hello!") ] @@ -393,10 +402,10 @@ returned. Using @racket[literals], we can write a function that compiles all of the string literals into static data as follows: -@(ev '(require mug/compile-literals)) +@(ev '(require mug/compiler/compile-literals)) @#reader scribble/comment-reader -(ex +(racketblock ;; Prog -> Asm (define (compile-literals p) (append-map compile-literal (literals p))) @@ -465,9 +474,9 @@ So now we have accomplished our goal: string literals are statically allocated and different occurrence of the same string literal are considered @racket[eq?] to each other: -@(ev '(require mug/compile)) +@(ev '(require mug/compiler/compile)) -@ex[ +@racketblock[ (seq (compile-string "Hello!") (compile-string "Hello!") (compile-literal 'Hello!)) @@ -476,7 +485,7 @@ considered @racket[eq?] to each other: We can try it out to confirm some examples. -@ex[ +@racketblock[ (define (run . p) (bits->value (asm-interp (compile (parse p))))) @@ -495,7 +504,7 @@ It's still worth noting that only string literals are interned. Dynamically created strings are not pointer-equal to structurally equal string literals: -@ex[ +@racketblock[ (run '(eq? "fff" (make-string 3 #\f))) ] @@ -580,7 +589,7 @@ The key additions are a function for compiling symbol occurrences: Which works as follows: -@ex[ +@racketblock[ (compile-symbol 'Hello!) ] @@ -606,7 +615,7 @@ chunk of memory is allocated to hold the character data @tt{H}, symbol, while the string @racket["Hello"] is represent as the same pointer, but tagged as a string. So this program compiles to: -@ex[ +@racketblock[ (seq (compile-string "Hello!") (compile-symbol 'Hello!) (compile-literal 'Hello!)) @@ -671,7 +680,7 @@ for alphabetic ordering. If an entry is found, it returns the previously seen symbol, otherwise it adds the symbol to the table and returns it. -@filebox-include[fancy-c mug "symbol.c"] +@filebox-include[fancy-c mug "runtime/symbol.c"] The idea will be that every time a symbol is constructed, we call @tt{intern_symbol} to intern it. @@ -691,7 +700,7 @@ To accomplish this, we'll design a function: Here's what it will produce for some example programs: -@ex[ +@racketblock[ (init-symbol-table (parse '['Hello!])) (init-symbol-table (parse '[(begin 'Hello! 'Hello!)])) (init-symbol-table (parse '["Hello!"])) @@ -744,14 +753,14 @@ being a symbol. We can now confirm that dynamically created symbols are still pointer-equal to symbols that statically appear in the program: -@ex[ +@racketblock[ (run '(eq? 'fff (string->symbol (make-string 3 #\f)))) ] Even creating two symbols dynamically will result in the same pointer so long as they are spelled the same: -@ex[ +@racketblock[ (run '(eq? (string->symbol (make-string 3 #\a)) (string->symbol (make-string 3 #\a)))) ] @@ -799,13 +808,13 @@ heap pointer is incremented by that number of copied bytes. We can see that this works: -@ex[ +@racketblock[ (run '(symbol->string 'foo)) ] To observe the copying behavior, notice: -@ex[ +@racketblock[ (run '(eq? (symbol->string 'foo) (symbol->string 'foo))) ] @@ -855,7 +864,7 @@ avoid calling @tt{intern_symbol}: We can confirm this works as expected: -@ex[ +@racketblock[ (run '(string->uninterned-symbol "foo")) (run '(eq? 'foo (string->uninterned-symbol "foo"))) (run '(eq? (string->uninterned-symbol "foo") @@ -937,7 +946,7 @@ the same, so it works just as well to compare strings.) We can confirm some examples: -@ex[ +@racketblock[ (run '(match 'foo ['foo 1] ["foo" 2])) @@ -963,9 +972,9 @@ expressions to its own module: @code-link{mug/compile-expr.rkt}. The top-level compiler is now: -@filebox-include[codeblock mug "compile.rkt"] +@filebox-include[codeblock mug "compiler/compile.rkt"] The work of compiling literals and emitting calls to initialize the symbol table is contained in its own module: -@filebox-include[codeblock mug "compile-literals.rkt"] +@filebox-include[codeblock mug "compiler/compile-literals.rkt"] diff --git a/www/notes/neerdowell.scrbl b/www/notes/neerdowell.scrbl index b71dab49..0ad673d4 100644 --- a/www/notes/neerdowell.scrbl +++ b/www/notes/neerdowell.scrbl @@ -13,10 +13,18 @@ @(ev '(require rackunit a86)) @(ev `(current-directory ,(path->string (build-path langs "neerdowell")))) -@(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(void (ev '(current-objs '("runtime.o")))) +@(void (ev '(with-output-to-string (thunk (system "make -C runtime runtime.o"))))) +@(void (ev '(current-objects '("runtime/runtime.o")))) @(for-each (λ (f) (ev `(require (file ,f)))) - '("interp.rkt" "compile.rkt" "compile-expr.rkt" "compile-literals.rkt" "compile-datum.rkt" "utils.rkt" "ast.rkt" "parse.rkt" "types.rkt")) + '("interpreter/interp.rkt" + "compiler/compile.rkt" + "compiler/compile-expr.rkt" + "compiler/compile-literals.rkt" + "compiler/compile-datum.rkt" + "compiler/utils.rkt" + "syntax/ast.rkt" + "syntax/parse.rkt" + "runtime/types.rkt")) @(define this-lang "Neerdowell") @@ -394,7 +402,7 @@ The @racket[compile-make-struct] function is defined as follows: We can now see structures in action: -@ex[ +@racketblock[ (define (run . p) (bits->value (asm-interp (compile (parse p))))) @@ -416,4 +424,4 @@ We can now see structures in action: '(count (node 8 (node 3 (leaf) (leaf)) (leaf)))) -] \ No newline at end of file +] diff --git a/www/notes/outlaw.scrbl b/www/notes/outlaw.scrbl index d3c14acc..023e1ebb 100644 --- a/www/notes/outlaw.scrbl +++ b/www/notes/outlaw.scrbl @@ -30,7 +30,7 @@ @(ev '(require rackunit a86)) @(ev `(current-directory ,(path->string (build-path langs "outlaw")))) @(void (ev '(with-output-to-string (thunk (system "make runtime.o"))))) -@(void (ev '(current-objs '("runtime.o")))) +@(void (ev '(current-objects '("runtime.o")))) @(for-each (λ (f) (ev `(require (file ,f)))) '(#;"interp.rkt" "compile.rkt" "compile-expr.rkt" "compile-literals.rkt" "compile-datum.rkt" "utils.rkt" "ast.rkt" "parse.rkt" "types.rkt")) @@ -264,14 +264,14 @@ which would emit the following code: We can play around an make sure this assembly code is actually computing the length of the list in @racket['rax]: -@(void (ev '(current-objs '()))) +@(void (ev '(current-objects '()))) @#reader scribble/comment-reader (ex -(require neerdowell/parse - neerdowell/compile-datum - neerdowell/compile-ops - neerdowell/types) +(require neerdowell/syntax/parse + neerdowell/compiler/compile-datum + neerdowell/compiler/compile-ops + neerdowell/runtime/types) (require a86) ;; Datum -> Natural diff --git a/www/notes/sugar.scrbl b/www/notes/sugar.scrbl index c94b4ed5..f10ca09b 100644 --- a/www/notes/sugar.scrbl +++ b/www/notes/sugar.scrbl @@ -11,7 +11,7 @@ @(define codeblock-include (make-codeblock-include #'h)) @(for-each (λ (f) (ev `(require (file ,(path->string (build-path notes "mug" f)))))) - '("interp.rkt" "interp-env.rkt" #;"compile.rkt" "syntax.rkt" "pat.rkt" #;"asm/interp.rkt" #;"asm/printer.rkt")) + '("interpreter/interp.rkt" "interpreter/env.rkt" #;"compile.rkt" "syntax.rkt" "pat.rkt" #;"asm/interp.rkt" #;"asm/printer.rkt")) @title[#:tag "Mug"]{Mug: matching, throwing, quoting} @@ -44,7 +44,7 @@ extensions considered in the various assignments up through @seclink["Assignment 7"]{Assignment 7}. -@codeblock-include["mug/interp-env.rkt"] +@codeblock-include["mug/interpreter/env.rkt"] @section[#:tag-prefix "mug"]{A bit more sugar} diff --git a/www/notes/utils.rkt b/www/notes/utils.rkt index 444e3d68..6b42cf51 100644 --- a/www/notes/utils.rkt +++ b/www/notes/utils.rkt @@ -1,8 +1,16 @@ #lang racket (provide (all-defined-out)) -(require (for-syntax racket/runtime-path racket/base racket/file pkg/lib) +(require (for-syntax racket/base + racket/file + racket/path + racket/runtime-path + pkg/lib) (for-meta 2 racket/base pkg/lib)) -(require scribble/manual racket/runtime-path pkg/lib) +(require racket/file + racket/path + scribble/manual + racket/runtime-path + pkg/lib) (require (for-label (except-in racket compile) a86)) (require images/icons/file) @@ -18,15 +26,17 @@ (syntax-case stx (a86) [(_ form lang fn) (parameterize () - (let ((s (file->string (build-path (syntax-case #'lang (a86) - [a86 a86] - [_ (build-path langs (symbol->string (syntax->datum #'lang)))]) - (syntax->datum #'fn))))) + (let* ((root (syntax-case #'lang (a86) + [a86 a86] + [_ (build-path langs (symbol->string (syntax->datum #'lang)))])) + (s (file->string + (build-path root (syntax->datum #'fn))))) #`(filebox (link (string-append "code/" fn) (tt fn)) (form #,(datum->syntax #'form s)))))])) (define ((make-codeblock-include ctxt) fn) (filebox (link (string-append "code/" fn) (tt fn)) - (typeset-code #:context ctxt (file->string (build-path langs fn))))) + (typeset-code #:context ctxt + (file->string (build-path langs fn))))) (define-syntax (filebox-include-fake stx) (syntax-case stx () diff --git a/www/schedule.scrbl b/www/schedule.scrbl index 484569bc..115f6747 100644 --- a/www/schedule.scrbl +++ b/www/schedule.scrbl @@ -12,235 +12,41 @@ @(define (day s) @elem[s]) -@;{ Fall } -@tabular[#:style 'boxed - #:sep @hspace[1] - #:row-properties '(bottom-border) - (list (list @bold{Week} - @;bold{Due} - @bold{Tuesday} - @bold{Thursday}) - - (list @wk{9/2} - #;"" - @elem{No class} - @itemlist[@item{@secref["Intro"]} - @item{@secref["OCaml to Racket"]}]) - - - (list @wk{9/9} - @;seclink["Assignment 1"]{A1} - @elem{@secref["a86"]} - @elem{@secref["a86"]}) - - (list @wk{9/16} - @;seclink["Assignment 2"]{A2} - @itemlist[@item{@secref["Abscond"]} - @item{@secref["Blackmail"]}] - @itemlist[@item{@secref["Con"]} - @item{@secref["Dupe"]}]) - - (list @wk{9/23} - @;"" - @itemlist[@item{@secref["Dodger"]} - @item{@secref["Evildoer"]}] - @secref["Extort"]) - - (list @wk{9/30} - @;elem{A3} - @;elem{@seclink["Assignment 2"]{A2}} - @secref["Fraud"] - @secref["Fraud"]) - - (list @wk{10/7} - @;"" - @secref{Hustle} - @secref{Hustle}) - - (list @wk{10/14} - @;elem{A4} - @elem{No class: Fall Break} - @secref["Midterm_1"]) - - (list @wk{10/21} - @;"" - @secref{Hoax} - @secref{Hoax}) - - (list @wk{10/28} - @;elem{A5} - @;elem{@seclink["Assignment 4"]{A4}} - @secref{Iniquity} - @secref{Iniquity}) - - (list @wk{11/4} - @;"" - @secref{Knock} - @secref{Jig}) - - - (list @wk{11/11} - @;elem{A6} - @secref{Loot} - @secref["Midterm_2"]) - - - (list @wk{11/18} - @;"" - @secref{Loot} - @secref{Mug}) - - (list @wk{11/25} - @;elem{A7} - @;elem{@seclink["Assignment 5"]{A5}} - @secref{Neerdowell} - @elem{No class: Thanksgiving}) - - (list @wk{12/2} - @;"" - @secref{Outlaw} - @elem{Outlaw}) - - (list @wk{12/9} - @;"" - @elem{Slack} - @elem{Slack}) - -)] - -@;{ Spring } -@;{ -@tabular[#:style 'boxed - #:sep @hspace[1] - #:row-properties '(bottom-border) - (list (list @bold{Week} - @bold{Due} - @bold{Monday} - @bold{Wednesday}) - - (list @wk{1/22} - "" - "No class" - @secref["Intro"]) - - - (list @wk{1/29} - "" - @elem{@secref["OCaml to Racket"]} - @elem{@secref["OCaml to Racket"]}) - - (list @wk{2/5} - @seclink["Assignment 1"]{A1} - @elem{@secref["a86"]} - @elem{@secref["a86"]}) - - (list @wk{2/12} - @seclink["Assignment 2"]{A2} - @itemlist[@item{@secref["Abscond"]} - @item{@secref["Blackmail"]}] - @itemlist[@item{@secref["Con"]} - @item{@secref["Dupe"]}]) - - (list @wk{2/19} - @elem{@seclink["Assignment 2"]{A3}} - @itemlist[@item{@secref["Dodger"]} - @item{@secref["Evildoer"]}] - @secref["Evildoer"]) - - (list @wk{2/26} - "" - @secref{Extort} - @secref{Fraud}) - - (list @wk{3/4} - "" - @secref{Fraud} - @secref["Midterm_1"]) - - (list @wk{3/11} - "" - @secref{Fraud} - @secref{Hustle}) - (list @wk{3/18} - "" - @elem{Spring Break} - @elem{Spring Break}) - (list @wk{3/25} - @elem{@seclink["Assignment 4"]{A4} P1} - @secref{Hustle} - @secref{Hustle}) - - (list @wk{4/1} - @elem{@seclink["Assignment 4"]{A4} P2} - @secref{Hoax} - @secref{Iniquity}) - - (list @wk{4/8} - "" - @secref{Iniquity} - @secref{Iniquity}) - - - (list @wk{4/15} - "" - @secref{Knock} - @secref["Midterm_2"]) - - - (list @wk{4/22} - "" - @secref{Jig} - @secref{Loot}) - - (list @wk{4/29} - @elem{@seclink["Assignment 5"]{A5}} - @secref{Loot} - @secref{Mug}) - - (list @wk{5/6} - "" - @secref{Neerdowell} - @secref{Outlaw}) - -)] -} - -@;{ @tabular[#:style 'boxed #:sep @hspace[1] #:row-properties '(bottom-border) (list (list @bold{Date} @bold{Topic} @bold{Due}) -(list @day{5/30} @secref["Intro"] "") -(list @day{5/31} @secref["OCaml to Racket"] "") -(list @day{6/1} @secref["a86"] "") -(list @day{6/2} @secref["Abscond"] @seclink["Assignment 1"]{A1}) -(list @day{6/5} @itemlist[@item{@secref["Blackmail"]} @item{@secref["Con"]}] @seclink["Assignment 2"]{A2}) -(list @day{6/6} @itemlist[@item{@secref["Dupe"]} @item{@secref{Dodger}}] "") -(list @day{6/7} @secref["Evildoer"] "") -(list @day{6/8} @secref["Extort"] "") -(list @day{6/9} @secref["Fraud"] "") +(list @day{6/1} @secref["Intro"] "") +(list @day{6/2} @secref["OCaml to Racket"] "") +(list @day{6/3} @secref["a86"] "") +(list @day{6/4} @secref["Abscond"] @seclink["Assignment 1"]{A1}) +(list @day{6/5} @itemlist[@item{@secref["Blackmail"]} @item{@secref["Con"]}] @seclink["Assignment 2"]{A2}) +(list @day{6/8} @itemlist[@item{@secref["Dupe"]} @item{@secref{Dodger}}] "") +(list @day{6/9} @secref["Evildoer"] "") +(list @day{6/10} @secref["Extort"] "") +(list @day{6/11} @secref["Fraud"] "") (list @day{6/12} @secref["Hustle"] @seclink["Assignment 3"]{A3}) -(list @day{6/13} @secref["Hoax"] "") -(list @day{6/14} "Midterm 1" @secref["Midterm_1"]) -(list @day{6/15} @secref["Iniquity"] "") -(list @day{6/16} @elem{@secref["Iniquity"], cont.} "") +(list @day{6/15} @secref["Hoax"] "") +(list @day{6/16} "Midterm 1" @secref["Midterm_1"]) +(list @day{6/17} @secref["Iniquity"] "") +(list @day{6/18} @elem{@secref["Iniquity"], cont.} "") (list @day{6/19} @elem{Juneteenth Holiday} "") -(list @day{6/20} @secref["Jig"] @seclink["Assignment 4"]{A4}) -(list @day{6/21} @secref["Knock"] "") -(list @day{6/22} @elem{@secref["Knock"], cont.} "") -(list @day{6/23} @secref["Loot"] "") +(list @day{6/22} @secref["Jig"] @seclink["Assignment 4"]{A4}) +(list @day{6/23} @secref["Knock"] "") +(list @day{6/24} @elem{@secref["Knock"], cont.} "") +(list @day{6/25} @secref["Loot"] "") (list @day{6/26} @elem{@secref["Loot"], cont.} "") -(list @day{6/27} @elem{GC} @seclink["Assignment 5"]{A5}) -(list @day{6/28} @secref["Mug"] "") -(list @day{6/29} "Midterm 2" @secref["Midterm_2"]) -(list @day{6/30} @secref["Mountebank"] "") -(list @day{7/3} @secref["Neerdowell"] @seclink["Assignment 6"]{A6}) -(list @day{7/4} "Independence Day Holiday" "") -(list @day{7/5} @secref["Outlaw"] "") -(list @day{7/6} @elem{@secref["Outlaw"], cont.} "") -(list @day{7/7} "Slack" @secref{Project}) +(list @day{6/29} @elem{GC} @seclink["Assignment 5"]{A5}) +(list @day{6/30} @secref["Mug"] "") +(list @day{7/1} "Midterm 2" @secref["Midterm_2"]) +(list @day{7/2} @secref["Mountebank"] "") +(list @day{7/3} "Independence Day Holiday" "") +(list @day{7/6} @secref["Neerdowell"] @seclink["Assignment 6"]{A6}) +(list @day{7/7} @secref["Outlaw"] "") +(list @day{7/8} @elem{@secref["Outlaw"], cont.} "") +(list @day{7/9} "Slack" "") +(list @day{7/10} "Slack" @secref{Project}) ) ] -} @bold{Final project assessment: @|final-date|.} diff --git a/www/slides.scrbl b/www/slides.scrbl index 4a7469ea..62cba813 100644 --- a/www/slides.scrbl +++ b/www/slides.scrbl @@ -3,4 +3,4 @@ @title[#:style '(unnumbered)]{Slides} Slides are updated after every class: -@link["slides/cmsc430-fall-2025.pdf"]{cmsc430-fall-2025.pdf} +@link["slides/cmsc430-summer-2026.pdf"]{cmsc430-summer-2026.pdf} diff --git a/www/software.scrbl b/www/software.scrbl index d6edec32..54a12e67 100644 --- a/www/software.scrbl +++ b/www/software.scrbl @@ -50,9 +50,6 @@ This course will make use of the following software: @item{Racket @tt{langs} package: a package containing utilities for this course.} - @item{NASM: the Netwide Assembler, which we will use to - assemble x86 programs.} - @item{GCC: the GNU compiler collection or a GCC-compatible system such as clang.} ] @@ -66,9 +63,7 @@ If you have an ARM-based machine, you will need to use For x86-based Linux machines, you will need to @seclink["install-racket"]{install Racket} and the -@seclink["langs-package"]{langs package}. Finally, install @tt{nasm}. -You can use your favorite package manager; they should all have -@tt{nasm}. +@seclink["langs-package"]{langs package}. @section[#:tag "mac"]{Using macOS} @@ -83,10 +78,7 @@ a chip name containing either Intel or Apple. Intel-based Macs are fairly straightforward to set up. You will need to @seclink["install-racket"]{install Racket} and the -@seclink["langs-package"]{langs package}. You will also need to -install @tt{nasm}. It's probably easiest to use a package manager -such as @link["https://brew.sh/"]{Homebrew} to install with @tt{brew -install nasm}. +@seclink["langs-package"]{langs package}. You will also want to make sure your Racket installation is visible from your @tt{PATH} environment variable. Assuming Racket was @@ -124,10 +116,10 @@ Otherwise, follow the steps given above. For Windows users, using WSL for testing is highly recommended. Beyond the first few assignments, the projects will require generating and -executing assembly code using the nasm package. Students in the past -have had trouble trying to configure this in the Windows environment, -so an easier workaround is simply to enable WSL and run your tests through -some Linux Distribution. Here is a breakdown of the steps: +executing assembly code. Students in the past have had trouble trying +to configure this in the Windows environment, so an easier workaround +is simply to enable WSL and run your tests through some Linux +Distribution. Here is a breakdown of the steps: @itemlist[ #:style 'ordered @@ -143,8 +135,7 @@ some Linux Distribution. Here is a breakdown of the steps: etc.). Run @tt{sudo apt update} and follow with @tt{sudo apt upgrade}. These two may take some time. } - @item{Run @tt{sudo apt install racket} and @tt{ - sudo apt install nasm}. These two should cover the necessary + @item{Run @tt{sudo apt install racket}. This should cover the necessary installations for this course.} @item{Here is where to determine which IDE you would like to @@ -215,22 +206,21 @@ The @tt{-Y} command line option sets up X11 forwarding, which lets you run GUI applications from GRACE. If you leave this off, programs like DrRacket will fail to launch when started. -Racket and @tt{nasm} are already installed, but you will +Racket is already installed, but you will need to modify your @tt{PATH} environment variable so that you can execute them from the command-line. You can do this with the following commands: @verbatim|{ # CMSC 430 set up - set path = ( /cell_root/software/racket/8.4/sys/bin $path ) - set path = ( /cell_root/software/nasm/2.15.05/sys/bin/ $path )}| + set path = ( /cell_root/software/racket/8.4/sys/bin $path )}| If you add these lines to the @tt{.path} file in your home directory, then you won't have to run this command manually every time you login; it will happen automatically. -Once set, you should be able to run commands such as @tt{racket}, -@tt{raco}, and @tt{nasm}. Other tools such as @tt{gcc} are already +Once set, you should be able to run commands such as @tt{racket} +and @tt{raco}. Other tools such as @tt{gcc} are already available. Finally, you will need to install @secref{langs-package}. @@ -301,137 +291,3 @@ If you'd like to use Emacs, there's a good @link["https://www.racket-mode.com/"]{Racket mode}, but we recommend using DrRacket for a while before switching to Emacs. Using any other editor is fine, too. - -@;{ -@section{Detailed compatiblity list} - -The course software has been successfully tested with the -following: - -@itemlist[ - @item{Operating systems: - @itemlist[@item{Ubuntu 20.04} - @item{Ubuntu 18.04} - @item{Red Hat Enterprise Linux 7.7} - @item{macOS 11.0 (Big Sur)} - @item{macOS 10.15 (Catalina)}]} - - @item{Racket: - @itemlist[@item{Racket 8.1 [cs]} - @item{Racket 8.1 [bc]} - @item{Racket 8.0 [cs]} - @item{Racket 8.0 [bc]} - @item{Racket 7.9 [cs]} - @item{Racket 7.9 [bc]} - @item{Racket 7.8 [cs]} - @item{Racket 7.8 [bc]}]} - - @item{NASM: - @itemlist[@item{NASM version 2.13.02} - @item{NASM version 2.15.05}]} - - @item{GCC: - @itemlist[@item{gcc 9.3.0} - @item{gcc 7.5.0} - @item{Clang/LLVM 12.0.0}]}] - -@; DVH: I'm not sure this is useful. The OCaml to Racket notes are better. -@;{ - -@section{Grammar} - -A program is a sequence of definitions or expressions. - -@(define unquote "whatever") @;{Needed to make redex happy with unquote in grammar} -@(define-language R0 - (d ::= (define x e) (define (x x ...) e)) - (e ::= (e e ...) (δ e ...) sv x (λ (x ...) e) (quasiquote qq) (match e [p e] ...)) - (qq ::= (qq ...) sv x (unquote e)) - (sv ::= b n s) - (p ::= (quasiquote r) b n x s (cons p p)) - (r ::= b n x s (unquote p)) - (s ::= string) - (b ::= #t #f) - (n ::= integer) - (x ::= variable) - (δ ::= add1 sub1 = * + - list cons)) - -The grammar for the subset of Racket we will use is: - -@(with-unquote-rewriter - (lambda (lw) - (build-lw (list (build-lw "(" (lw-line lw) (lw-line-span lw) (lw-column lw) 1) - (build-lw 'unquote (lw-line lw) (lw-line-span lw) (+ 1 (lw-column lw)) 7) - (build-lw " " (lw-line lw) (lw-line-span lw) (+ 2 (lw-column lw)) 1) - (build-lw (lw-e lw) (lw-line lw) (lw-line-span lw) (+ 3 (lw-column lw)) (lw-column-span lw)) - (build-lw ")" (lw-line lw) (lw-line-span lw) (lw-column lw) 1)) - (lw-line lw) - (lw-line-span lw) - (lw-column lw) - (+ 8 (lw-column-span lw)))) - - - (render-grammar R0)) - -@section{Built-In Datatypes} - -We will use: - -@itemize[ -@item{Booleans} -@item{Numbers} -@item{Strings} -@item{Symbols} -@item{Pairs and Lists} -] - -We will make extensive use of @link["https://docs.racket-lang.org/guide/quote.html"]{@tt{quote}}. - -@section{Definitions} - -A definition takes the form: - -@render-grammar/nts[R0 '(d)] - -A definition @render-term[R0 (define x e)] defines @render-term[R0 x] -to stand for the value produced by evaluating @render-term[R0 e]. - -The @render-term[R0 (define (x_0 x_1 ...) e)] form is shorthand for -@render-term[R0 (define x_0 (λ (x_1 ...) e))]. - -@;{ -@section{Style} - -TODO: write style guidelines. -} - -@section{Examples} - -Here are some examples of writing various functions in our subset of Racket. - -@#reader scribble/comment-reader -(ex - -;; compute the product of a list of numbers -(define (prod xs) - (match xs - ['() 1] - [(cons x xs) (* x (prod xs))])) - -(prod '(1 2 3 4 5)) - -;; reverse a list -(define (rev xs) - (rev/acc xs '())) - -(define (rev/acc xs a) - (match xs - ['() a] - [(cons x xs) (rev/acc xs (cons x a))])) - -(rev '(1 2 3 4 5)) -) - -} - -} \ No newline at end of file