Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
19 changes: 17 additions & 2 deletions .github/workflows/push.yml
Original file line number Diff line number Diff line change
@@ -1,12 +1,22 @@
name: Build and Deploy
on:
- push

name: Build and Deploy
- repository_dispatch:
types: [slides-updated]

jobs:
build:
runs-on: ubuntu-latest
env:
GH_TOKEN: ${{ github.token }}
steps:
# Show payload only for repository_dispatch
- name: Show dispatch payload
if: ${{ github.event_name == 'repository_dispatch' }}
run: |
echo "Event type: ${{ github.event.action || 'N/A' }}"
echo "Client payload (JSON):"
echo '${{ toJson(github.event.client_payload) }}'
- name: Set timezone
uses: szenius/set-timezone@v2.0
with:
Expand Down Expand Up @@ -41,6 +51,11 @@ jobs:
# raco pkg install --auto www/
raco make www/main.scrbl
make -C www main
- name: Download slides
run: |
gh api repos/cmsc430/slides/zipball/pdfs > pdfs.zip
mkdir -p www/main/slides
unzip -jq pdfs.zip '*.pdf' -d www/main/slides/
- name: Upload www for GitHub Pages
uses: actions/upload-pages-artifact@v3
with:
Expand Down
20 changes: 15 additions & 5 deletions www/Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -9,14 +9,24 @@ test: clean

$(course): scribble zips


langs = abscond blackmail con dupe dodger evildoer extort fraud hustle hoax iniquity iniquity-gc jig knock loot mug mountebank neerdowell outlaw
assigns = hoax-plus

zips:
mkdir -p $(course)/code/
cd `racket -e '(require pkg/lib) (display (path->string (pkg-directory "langs")))'` ; \
tar -c `git ls-files a86 abscond blackmail con dupe dodger evildoer extort fraud hustle hoax iniquity iniquity-gc jig knock loot mug mountebank neerdowell outlaw` | \
(cd ../www/main/code ; tar -x ; \
for f in abscond blackmail con dupe dodger evildoer extort fraud hustle hoax iniquity iniquity-gc jig knock loot mug mountebank neerdowell outlaw ; do \
(cd `racket -e '(require pkg/lib) (display (path->string (pkg-directory "langs")))'` ; \
tar -c `git ls-files $(langs)`) | \
(cd $(course)/code/ ; tar -x ; \
for f in $(langs) ; do \
zip $${f}.zip -r $${f}/ ; \
done)
(cd `racket -e '(require pkg/lib) (display (path->string (pkg-directory "assignments")))'` ; \
tar -c `git ls-files $(assigns)`) | \
(cd $(course)/code/ ; tar -x ; \
for f in $(assigns) ; do \
zip $${f}.zip -r $${f}/ ; \
done )
done)

scribble:
raco scribble --htmls \
Expand Down
5 changes: 4 additions & 1 deletion www/assignments/6.scrbl
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
#lang scribble/manual
@(require "../defns.rkt"
"../notes/ev.rkt")
"../notes/ev.rkt"
"../notes/utils.rkt")
@title[#:tag "Assignment 6" #:style 'unnumbered]{Assignment 6: List and vector primitives}

@src-code["hoax-plus"]

@(require (for-label a86 (except-in racket ...)))

@(require racket/port)
Expand Down
266 changes: 263 additions & 3 deletions www/assignments/7.scrbl
Original file line number Diff line number Diff line change
@@ -1,9 +1,269 @@
#lang scribble/manual
@(require "../defns.rkt")
@title[#:tag "Assignment 7" #:style 'unnumbered]{Assignment 7: Binding sequentially, n-ary prmimitives}
@(require "../defns.rkt"
"../notes/ev.rkt")

@title[#:tag "Assignment 7" #:style 'unnumbered]{Assignment 7:
Variable-arity and arity-overloaded functions}

@(require (for-label a86 (except-in racket ...)))

@bold{Due: @assign-deadline[7]}

Details of this assignment will be released later in the semester.
@(ev '(require iniquity-plus))

The goal of this assignment is to implement variable arity functions.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Overview}

For this assignment, you are given a @tt{iniquity-plus.zip} file on ELMS
with a starter compiler similar to the @seclink["Iniquity"]{Iniquity}
language we studied in class.

@section[#:tag-prefix "a7-" #:style 'unnumbered #:tag "rest"]{Rest
arguments}

Many languages including JavaScript, C, C++, Java, Ruby, Go, PHP, and
Racket provide facilities for defining functions that take a ``rest
argument'' which allows the function to be called with more arguments
than expected and these additional arguments will be bound to a single
value that collects all of these arguments. In Iniquity, as in
Racket, the obvious way of collecting these arguments into a single
value is to use a list.

Here are some examples:

@itemlist[

@item{@racket[(define (f . xs) ...)]: this function takes @emph{any} number
of arguments and binds @racket[xs] to a list containing all of them,}

@item{@racket[(define (f x . xs) ...)]: this function takes @emph{at
least} one argument and binds @racket[x] to the first argument and
@racket[xs] to a list containing the rest. It's an error to call this function
with zero arguments.}

@item{@racket[(define (f x y z . xs) ...)]: this function takes
@emph{at least} three arguments and binds @racket[x], @racket[y], and
@racket[z] to the first three arguments and @racket[xs] to a list
containing the rest. It's an error to call this function with 0, 1,
or 2 arguments.}
]

Here are some examples in Racket to get a sense of the behavior:

@ex[
(define (f . xs) (list xs))
(f)
(f 1)
(f 1 2)
(f 1 2 3)
(f 1 2 3 4)
(define (f x . xs) (list x xs))
(eval:error (f))
(f 1)
(f 1 2)
(f 1 2 3)
(f 1 2 3 4)
(define (f x y z . xs) (list x y z xs))
(eval:error (f))
(eval:error (f 1))
(eval:error (f 1 2))
(f 1 2 3)
(f 1 2 4)
]

The code generated for a function call should not change: it should
pass all of the arguments on the stack along with information about
the number of arguments.

The compilation of function definitions that use a rest argument
should generate code that checks that the given number of arguments is
acceptable and should generate code to pop all ``extra'' arguments off
the stack and construct a list which is then bound to the rest
parameter.

It is worth remembering that arguments are pushed on the stack in such
a way that the last argument is the element most recently pushed on
the stack. This has the benefit of making it easy to pop off the
extra arguments and to construct a list with the elements in the
proper order.

HINT: the function definition knows the number of ``required''
arguments, i.e. the minimum number of arguments the function can be
called with---call this @math{m}---and the caller communicates how
many actual arguments have been supplied---call this @math{n}. The
compiler needs to generate a loop that pops @math{n-m} times,
constructing a list with with popped elements, and then finally pushes
this list in order to bind it to the rest parameter.


@section[#:tag-prefix "a7-" #:style 'unnumbered #:tag "case-lambda"]{Arity dispatch}

Some languages such as Java, Haskell, and Racket make it possible to
overload a single function name with multiple definitions where the
dispatch between these different definitions is performed based on the
number (or kind) of arguments given at a function call.

In Racket, this is accomplished with the @racket[case-lambda] form for
constructing multiple-arity functions.

Here is an example:

@ex[
(define f
(case-lambda
[(x) "got one!"]
[(p q) "got two!"]))

(f #t)
(f #t #f)
(eval:error (f #t #f 0))
]

This function can accept @emph{either} one or two arguments. If given
one argument, it evaluates the right-hand-side of the first clause
with @racket[x] bound to that argument. If given two arguments, it
evaluates the right-hand-side of the second clause with @racket[p] and
@racket[q] bound to the arguments. If given any other number of
arguments, it signals an error.

A @racket[case-lambda] form can have any number of clauses (including
zero!) and the first clause for which the number of arguments is
acceptable is taken when the function is called.

Note that @racket[case-lambda] can be combined with rest arguments too.
A clause that accepts any number of arguments is written by simply
listing a parameter name (no parentheses). A clause that accepts some
non-zero minimum number of parameters is written with a dotted
parameter list.

For example:

@ex[
(define f
(case-lambda
[(x y z . r) (length r)]
[(x) "just one!"]))

(f 1 2 3 4 5 6)
(f #t)
(eval:error (f))
(eval:error (f 1 2))]

This function takes three or more arguments @emph{or} one argument. Any
other number of arguments (i.e. zero or two) results in an error.

@ex[
(define f
(case-lambda
[(x y z) "three!"]
[xs (length xs)]))

(f)
(f 1 2)
(f 1 2 3)
(f 1 2 3 4 5 6)
]

This function takes any number of arguments, but when given three, it
produces @racket["three!"]; in all other cases it produces the number
of arguments.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Representing the
syntax of function definitions}

The @seclink["Iniquity"]{Iniquity} language has a single function
definition form: @racket[(define (_f _x ...) _e)] which is represented
with the following AST type:

@#reader scribble/comment-reader
(racketblock
;; type Defn = (Defn Id (Listof Id) Expr)
(struct Defn (f xs e) #:prefab)
)

Because there are three different forms of function definition in
Iniquity+, we use the following AST representation:

@#reader scribble/comment-reader
(racketblock
;; type Defn = (Defn Id Fun)
(struct Defn (f fun) #:prefab)

;; type Fun = (FunPlain [Listof Id] Expr)
;; | (FunRest [Listof Id] Id Expr)
;; | (FunCase [Listof FunCaseClause])
;; type FunCaseClause = (FunPlain [Listof Id] Expr)
;; | (FunRest [Listof Id] Id Expr)
(struct FunPlain (xs e) #:prefab)
(struct FunRest (xs x e) #:prefab)
(struct FunCase (cs) #:prefab)
)

What used to be represented as @racket[(Defn _f _xs _e)] is now
represented as @racket[(Defn _f (FunPlain _xs _e))].


The parser already works for these new forms of function definitions.
Here are some examples of how function definitions are parsed, but you
are encouraged to try out more to get a better sense:

@ex[
(parse-define '(define (f x) x))
(parse-define '(define (f . xs) xs))
(parse-define '(define (f x y z . q) q))
(parse-define
'(define f
(case-lambda
[(x y) 2]
[(z) 1]
[(a b c . d) "3+"]
[q "other"])))
]

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Starter code}

The compiler code given to you is just an implementation of Iniquity,
but updated to parse the new forms of function definitions and
re-organized slightly to match the new AST representation.

The interpreter code given to you works on the full Iniquity+
language, so you do not need to update @racket[interp.rkt] and can use
the interpreter to guide your implementation of the compiler.

@ex[
(interp
(parse '(define (f x) x)
'(f 1)))
(interp
(parse '(define (f . x) x)
'(f 1)))
(interp
(parse '(define (f . x) x)
'(f)))
(interp
(parse '(define (f . x) x)
'(f 1 2 3 4 5)))
(interp
(parse '(define f
(case-lambda
[(x y) 2]
[(z) 1]
[(a b c . d) "3+"]
[q "other"]))
'(cons (f 7)
(cons (f 3 4)
(cons (f)
(cons (f 7 8 9 10 11)
'()))))))
]


Thus, you should only need to modify @racket[compile.rkt].

A small number of test cases are given as usual.

@section[#:tag-prefix "a7-" #:style 'unnumbered]{Submitting}

To submit, use @tt{make} from within the @tt{iniquity-plus} directory
to create a zip file containing your work and submit it to Gradescope.
1 change: 1 addition & 0 deletions www/main.scrbl
Original file line number Diff line number Diff line change
Expand Up @@ -59,6 +59,7 @@ staff using this @link[feedback]{form}.
@include-section{texts.scrbl}
@include-section{schedule.scrbl}
@include-section{notes.scrbl}
@include-section{slides.scrbl}
@include-section{assignments.scrbl}
@include-section{midterms.scrbl}
@include-section{project.scrbl}
Expand Down
6 changes: 6 additions & 0 deletions www/slides.scrbl
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
#lang scribble/manual

@title[#:style '(unnumbered)]{Slides}

Slides are updated after every class:
@link["slides/cmsc430-fall-2025.pdf"]{cmsc430-fall-2025.pdf}