CSCI 330 Lab 3: macros

The lab3 repo has now been pushed and is ready to be forked/cloned.

In this lab we'll be working with grades/requirements checking again (somewhat like lab 1) but with looser error checking and with the focus on the creation and use of lisp macros. You'll have three weeks to do the lab (including study week) and it will be due at noon on Mar. 4th.

Similar to previous labs, the exercise involves forking/cloning a lab3 git repository, editing the lab3.cl and selftest.cl files, and then submitting your updated repository.

The instructions to fork/clone/submit the lab are as per lab1, except that the repo this time is named lab3.


Lab 3 objectives and requirements

Lab 3's focus is primarily on the implementation and use of macros.

You'll be implementing the bodies of 8 macros (some of which will use each other) and one function that will use those macros extensively. The skeletons and comments for each of the macros and the function are provided in the lab3.cl file.

Each of these involves the checking/use of lists for grades (like lab1), where each list consists of 4 elements: a string for the area, then an integer for the course number, then an integer for the course credits, then a string for the lettergrade.

Error checking is a bit more relaxed in this lab: the course numbers and credits can be any integer (not restricted to a range), and the course area and lettergrade can be any strings (not restricted to specific lists).

Four of the macros are very simple ones, that perform no error checking at all (trusting they are used on valid grades), and simply return the relevant list field

The (okGradeForm G) macro is also fairly basic, generating code to check that G has a valid structure for a course grade: i.e. it is a list of four elements, the first and fourth being strings while the second and third are integers. (Essentially creating just an 'and' of a bunch of tests on G, using the macros above to grab the right components after ensuring G is a list of the correct size).

The (courseMatch C1 C2) macro is generating code to check that both courses pass okGradeForm and have identical fields for area, course number, and credits. (Again, this generates a glorified 'and' using the various macros above.)

The (letterRank G) macro generates code to find the rank of G's letter grade on a scale of 0-11 based on the order they appear in the constant Letters list: ("D" "C-" "C" "CR" "C+" "B-" "B" "TRF" "B+" "A-" "A" "A+"). Any letter grade not appearing in the list is assigned a value of nil.

We'll discuss the (meetsReq R A) function here, since it will be used by the reqPassed macro we discuss next. meetsReq is similar to the meetsReq from lab1, just with less strict error checking and making use of the various macros above. The logic for meetsReq is as follows:

The (reqPassed R A1 A2 ... AN) macro generates code to determine if at least one of the actual course grades A1..AN meets the requirement specified by R. The macro should generate code that will return true if R doesn't pass okGradeForm or if at least one of the Ai's meets requirement R (as checked using meetsReq).


Starting code/documentation

A copy of the initial lab3.cl file is provided below.

; Note that each of the macros returns either t or nil,
;    and none of them should produce any output
; the macros are for processing course grades stored in list form
;    (as per lab1), e.g. ("CSCI" 330 3 "B+")

; (grArea G)
; ----------
; trusts G has valid form, returns the area (first element) of G
(defmacro grArea (G)
   )

; (grNum G)
; ---------
; trusts G has valid form, returns the course number (second element) of G
(defmacro grNum (G)
   )

; (grCred G)
; ----------
; trusts G has valid form, returns the credits (third element) of G
(defmacro grCred (G)
   )

; (grLetter G)
; ------------
; trusts G has valid form, returns the lettergrade (final element) of G
(defmacro grLetter (G)
   )


; list of letter grades ordered by [fictional] level of qualification,
;    any grade that appears later in list is treated as better than
;    any grade that appears earlier in list
; anything that doesn't appear in the list is treated as unqualified
(defconstant Letters '("D" "C-" "C" "CR" "C+" "B-" "B" "TRF" "B+" "A-" "A" "A+"))

; (letterRank G)
; --------------
; return nil if G's letter grade doesn't appear in Letters,
; otherwise return the position of L in Letters (0 to 11)
(defmacro letterRank (G)
   )


; (okGradeForm G)
; ---------------
; checks that G is structurally valid for a grade,
;   i.e. 4 element list whose types are (string integer integer string)
; returns t if G's form is correct, nil otherwise
; (does not check the area or lettergrade against VALIDAREAS or VALIDLETGRADES,
;  and does not check the range of values for the course number or credits)
(defmacro okGradeForm (G)
   )


; (courseMatch C1 C2)
; -------------------
; if C1 and C2 both pass okGradeform and they have matching
;    areas, course numbers, and credits then t
; otherwise nil
(defmacro courseMatch (C1 C2)
   )


; (reqPassed R A1 A2 ... AN)
; --------------------------
; variadic macro: R is required, then zero or more A's
; true if R does not have okGradeForm
;   or if at least one actual course (the Ai's) meetsReq R
; false otherwise
(defmacro reqPassed (R &rest Actuals)
   )


; (meetsReq R A)
; --------------
; A meets requirement R if:
;    R does not pass okGradeForm or
;    R and A both pass okGradeForm, and they courseMatch,
;      and either R's letterRank is nil or is <= A's letterRank
(defun meetsReq (R A)
   )


selftest.cl: a test script for checking/debugging your functions

The selftest.cl script is executable. It loads the lab3.cl file, declares a variety of grades variables for testing, then executes a suite of tests.

As with previous labs, its intended use is as a testing (and debugging) mechanism for you to check that your lab3.cl functions actually work correctly.

All testing and debugging code, including test values, belongs in this script, NOT in lab3.cl. Marks will be deducted if you including testing/debugging content in your lab3.cl file.

A few sample data values and test calls are included in the initial version of selftest.cl, but these are nowhere near complete.

The initial content of selftest.cl is shown below, followed by the results of a sample run with a working lab3.cl. (Your macro expansions might not look exactly like the expansions in the sample solution of course.)

#! /usr/bin/sbcl --script

; ******************************************************
; THE TEST CASES/CALLS BELOW ARE MERELY A STARTING POINT
; FOR TESTING YOUR CODE, THEY ARE NOT ADEQUATE TO ENSURE
; YOUR CODE IS ANYWHERE CLOSE TO SUFFICIENT
; ******************************************************

; ensure the lab3 macros and functions are correctly loaded
(load "lab3.cl")
(format t "---~%")
(format t "lab3.cl macro and function loading complete~%")
(format t "---~%")

; some basic grades for use in testing
(defvar CS159D '("CSCI" 159 4 "D"))
(defvar CS159Cm '("CSCI" 159 4 "C-"))

(defvar CS265F '("CSCI" 265 3 "F"))
(defvar CS265B '("CSCI" 265 3 "B"))
(defvar CS265Ap '("CSCI" 265 3 "A+"))

(defvar MA123C '("MATH" 123 3 "C"))
(defvar MA123A '("MATH" 123 3 "A"))
(defvar MA265B '("MATH" 265 3 "B"))

(defvar notAList 99)
(defvar missField '("CSCI" 4 "C+"))
(defvar xtraField '("CSCI" 100 4 "C+" 1))
(defvar badArea '(100 123 3 "C-"))
(defvar badNum '("MATH" "123" 3 "C-"))
(defvar badCred '("MATH" 123 "3" "C-"))
(defvar badLetter '("MATH" 123 3 #\C))

(format t "~%Checking some macro expansions~%")
(format t "(grArea CS159D) expanded: ~A~%" (macroexpand-1 '(grArea CS159D)))
(format t "(grNum CS159D) expanded: ~A~%" (macroexpand-1 '(grNum CS159D)))
(format t "(grCred CS159D) expanded: ~A~%" (macroexpand-1 '(grCred CS159D)))
(format t "(grLetter CS159D) expanded: ~A~%" (macroexpand-1 '(grLetter CS159D)))
(format t "(letterRank CS159D) expanded: ~A~%" (macroexpand-1 '(letterRank CS159D)))
(format t "~%(okGradeForm CS159D) expanded: ~A~%" (macroexpand-1 '(okGradeForm CS159D)))
(format t "~%(courseMatch CS159D CS159Cm) expanded: ~A~%" (macroexpand-1 '(courseMatch CS159D CS159Cm)))
(format t "~%(reqPassed CS159D CS159Cm MA123C) expanded: ~A~%" (macroexpand-1 '(reqPassed CS159D CS159Cm MA123C)))
(format t "---~%")

(format t "~%Checking valid grades with okGradeForm, each should pass~%")
(format t "~A: result ~A~%" CS159D (okGradeForm CS159D))
(format t "~A: result ~A~%" CS159Cm (okGradeForm CS159Cm))
(format t "~A: result ~A~%" CS265F (okGradeForm CS265F))
(format t "~A: result ~A~%" CS265B (okGradeForm CS265B))
(format t "~A: result ~A~%" CS265Ap (okGradeForm CS265Ap))
(format t "~A: result ~A~%" MA123C (okGradeForm MA123C))
(format t "~A: result ~A~%" MA123A (okGradeForm MA123A))
(format t "---~%")

(format t "~%Checking invalid grades with okGradeForm, each should fail~%")
(format t "~A: result ~A~%" notAList (okGradeForm notAList))
(format t "~A: result ~A~%" missField (okGradeForm missField))
(format t "~A: result ~A~%" xtraField (okGradeForm xtraField))
(format t "~A: result ~A~%" badArea (okGradeForm badArea))
(format t "~A: result ~A~%" badNum (okGradeForm badNum))
(format t "~A: result ~A~%" badCred (okGradeForm badCred))
(format t "~A: result ~A~%" badLetter (okGradeForm badLetter))
(format t "---~%")

(format t "~%Checking course matches, each should pass~%")
(format t "Req and Act ~A (same var): result ~A~%" CS159Cm (courseMatch CS159Cm CS159Cm))
(format t "Req ~A, Act ~A (match): result ~A~%" MA123C MA123A (courseMatch MA123C MA123A))
(format t "Req ~A, Act ~A (match): result ~A~%" CS265Ap CS265B (courseMatch CS265Ap CS265B))
(format t "---~%")

(format t "~%Checking course matches, each should fail~%")
(format t "Req ~A (invalid), Act ~A: result ~A~%" notAList CS159D (courseMatch notAList CS159D))
(format t "Req ~A, Act ~A (invalid): result ~A~%" CS159D badNum (courseMatch CS159D badNum))
(format t "Req ~A, Act ~A (mismatch num): result ~A~%" CS159Cm CS265B (courseMatch CS159Cm CS265B))
(format t "Req ~A, Act ~A (mismatch area): result ~A~%" CS265B MA265B (courseMatch CS265B MA265B))
(format t "---~%")

(format t "~%Checking actual grades vs required, each should pass~%")
(format t "Req and Act ~A (exact match): result ~A~%" CS159Cm (meetsReq CS159Cm CS159Cm))
(format t "Req ~A, Act ~A (higher): result ~A~%" MA123C MA123A (meetsReq MA123C MA123A))
(format t "Req ~A (invalid), Act ~A: result ~A~%" badLetter CS159D (meetsReq badLetter CS159D))
(format t "---~%")

(format t "~%Checking actual grades vs required, each should fail~%")
(format t "Req ~A, Act ~A (mismatch): result ~A~%" CS159Cm CS265B (meetsReq CS159Cm CS265B))
(format t "Req ~A, Act ~A (mismatch): result ~A~%" CS265B MA265B (meetsReq CS265B MA265B))
(format t "Req ~A, Act ~A (too low): result ~A~%" CS265Ap CS265B (meetsReq CS265Ap CS265B))
(format t "Req ~A, Act ~A (invalid): result ~A~%" CS159D badCred (meetsReq CS159D badCred))
(format t "---~%")

(format t "~%Checking multiple actual grade args vs req using reqPassed, each should pass~%")
(format t "Req ~A, Act ~A (higher): result ~A~%" MA123C MA123A (reqPassed MA123C MA123A))
(format t "Req ~A Act ~A,~A,~A: result ~A~%" CS265B MA123A CS265F CS265Ap (reqPassed CS265B MA123A CS265F CS265Ap))
(format t "Req ~A (invalid), Act ~A, ~A: result ~A~%" xtraField CS159D CS265F (reqPassed xtraField CS159D CS265F))
(format t "Req ~A (invalid), no actual: result ~A~%" missField (reqPassed missField))
(format t "---~%")

(format t "~%Checking multiple actual grade args vs required, each should fail~%")
(format t "Req ~A, Act ~A, ~A, ~A (too low): result ~A~%" CS159Cm CS265B CS159D MA123C (reqPassed CS159Cm CS265B MA123C))
(format t "Req ~A, Act ~A (invalid): result ~A~%" CS159D badArea (reqPassed CS159D badArea))
(format t "Req ~A, no actuals: result ~A~%" MA123A (reqPassed MA123A))
(format t "---~%")


--- lab3.cl macro and function loading complete --- Checking some macro expansions (grArea CS159D) expanded: (NTH 0 CS159D) (grNum CS159D) expanded: (NTH 1 CS159D) (grCred CS159D) expanded: (NTH 2 CS159D) (grLetter CS159D) expanded: (NTH 3 CS159D) (letterRank CS159D) expanded: (POSITION (GRLETTER CS159D) LETTERS TEST #'STRING=) (okGradeForm CS159D) expanded: (AND (LISTP CS159D) (= 4 (LENGTH CS159D)) (STRINGP (GRAREA CS159D)) (INTEGERP (GRNUM CS159D)) (INTEGERP (GRCRED CS159D)) (STRINGP (GRLETTER CS159D))) (courseMatch CS159D CS159Cm) expanded: (AND (OKGRADEFORM CS159D) (OKGRADEFORM CS159CM) (STRING= (GRAREA CS159D) (GRAREA CS159CM)) (= (GRNUM CS159D) (GRNUM CS159CM)) (= (GRCRED CS159D) (GRCRED CS159CM))) (reqPassed CS159D CS159Cm MA123C) expanded: (IF (MEETSREQ CS159D CS159CM) T (REQPASSED CS159D MA123C)) --- Checking valid grades with okGradeForm, each should pass (CSCI 159 4 D): result T (CSCI 159 4 C-): result T (CSCI 265 3 F): result T (CSCI 265 3 B): result T (CSCI 265 3 A+): result T (MATH 123 3 C): result T (MATH 123 3 A): result T --- Checking invalid grades with okGradeForm, each should fail 99: result NIL (CSCI 4 C+): result NIL (CSCI 100 4 C+ 1): result NIL (100 123 3 C-): result NIL (MATH 123 3 C-): result NIL (MATH 123 3 C-): result NIL (MATH 123 3 C): result NIL --- Checking course matches, each should pass Req and Act (CSCI 159 4 C-) (same var): result T Req (MATH 123 3 C), Act (MATH 123 3 A) (match): result T Req (CSCI 265 3 A+), Act (CSCI 265 3 B) (match): result T --- Checking course matches, each should fail Req 99 (invalid), Act (CSCI 159 4 D): result NIL Req (CSCI 159 4 D), Act (MATH 123 3 C-) (invalid): result NIL Req (CSCI 159 4 C-), Act (CSCI 265 3 B) (mismatch num): result NIL Req (CSCI 265 3 B), Act (MATH 265 3 B) (mismatch area): result NIL --- Checking actual grades vs required, each should pass Req and Act (CSCI 159 4 C-) (exact match): result T Req (MATH 123 3 C), Act (MATH 123 3 A) (higher): result T Req (MATH 123 3 C) (invalid), Act (CSCI 159 4 D): result T --- Checking actual grades vs required, each should fail Req (CSCI 159 4 C-), Act (CSCI 265 3 B) (mismatch): result NIL Req (CSCI 265 3 B), Act (MATH 265 3 B) (mismatch): result NIL Req (CSCI 265 3 A+), Act (CSCI 265 3 B) (too low): result NIL Req (CSCI 159 4 D), Act (MATH 123 3 C-) (invalid): result NIL --- Checking multiple actual grade args vs req using reqPassed, each should pass Req (MATH 123 3 C), Act (MATH 123 3 A) (higher): result T Req (CSCI 265 3 B) Act (MATH 123 3 A),(CSCI 265 3 F),(CSCI 265 3 A+): result T Req (CSCI 100 4 C+ 1) (invalid), Act (CSCI 159 4 D), (CSCI 265 3 F): result T Req (CSCI 4 C+) (invalid), no actual: result T --- Checking multiple actual grade args vs required, each should fail Req (CSCI 159 4 C-), Act (CSCI 265 3 B), (CSCI 159 4 D), (MATH 123 3 C) (too low): result NIL Req (CSCI 159 4 D), Act (100 123 3 C-) (invalid): result NIL Req (MATH 123 3 A), no actuals: result NIL ---