Static Analysis & Error Checking


                         Lecture 9



                                               Co...
Coming up
Lecture 8: Context-sensitive transformation
   ★ design 2
   ★ transformation with dynamic rewrite rules

Lectur...
Consistency Checking
Consistency Checking


Syntax definition
   ★ what are well-formed sentences?

Static analysis
   ★ not all ‘well-formednes...
Consistency Checking: Ingredients

Editor Interface
   ★ collecting and displaying errors, warnings

Error checking
   ★ c...
Consistency Checking: Generic Approach


Rename
   ★ make identifiers unique

Map
   ★ map identifiers to declarations

Proj...
Editor Interface

module nwl-Builders

imports nwl-Builders.generated

builders

  provider: include/nwl.ctree

  observer...
Editor Interface




editor-analyze:
  (ast, path, fullpath) -> (errors, warnings, notes)
  with
    errors   := <collect-...
Error Checking Rules




                 check :
                   context -> (target, error)
                   where a...
Error Checking: Binary Operators



check :
  e@BinOp(e1, op, e2) ->
    (e, $[operator [op] not defined for [<pp>t1] and ...
Pretty-Printing with String Interpolation


 pp : Entity(x, prop*) ->
      $[entity [x] {
           [<map(pp)>prop*]
   ...
Origin Tracking
check :
  e@BinOp(e1, op, e2) ->
    (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]])
  where ....
Error Checking: Control-Flow Statements

  check :
    While(e, b) -> (e, $[Expression should have type Bool])
    where t...
Type Analysis




type-of : e -> t




 compute type of expression
Type Analysis: Literals




type-of :
  StringLit(x) -> SimpleType("String")

type-of :
  IntLit(x) -> SimpleType("Int")
Type Analysis: Binary Operators


type-of :
  BinOp(e1, op, e2) -> t
  where t := <function-type>(op, [<type-of>e1, <type-...
Type Analysis



BinOp(
  IntLit("2")
, "+"
, BinOp(
    IntLit("3"),
    "+",
    IntLit("4")
  )
)
Type Analysis



BinOp(
  IntLit("2")
, "+"
, BinOp(
                        type-of
    IntLit("3"),                   Si...
Type Analysis



BinOp(
                       type-of
  IntLit("2")                        SimpleType("Int")
, "+"
      ...
Type Analysis



                   type-of
BinOp(                             SimpleType("Int")
  IntLit("2")
, "+"
, Bin...
Type Analysis: What is Type of Variable?


define page root(x : Int) {
  action exptest() {
    for(y : Int in {1,2,x}) {
...
Variables: Map


declare-all =
  alltd(declare)

declare :
  Param(x, t) -> Param(x, t)
  with rules(
         TypeOf : x ...
Scope




                    define page root(x : Int) {
                      action exptest() {
                       ...
Variables: Map + Rename
rename-all = alltd(rename)

rename :
  Param(x, t) -> Param(y, t)
  with y := <rename-var>(x, t)
 ...
Term Annotations




                   t{t1,...,tn}




add additional information to term without affecting signature
Variables: Check




check :
  e@Var(x) -> (e, $[Variable '[x]' not declared])
  where require(<type-of>e)
Variable Binding Constructs




 rename :
   For(x, t, e1, stat1*) -> For(y, t, e2, stat2*)
   with e2 := <rename-all> e1
...
Assignment

is-lvalue =
  ?Var(_) <+ ?PropertyAccess(_, _)

check :
  Assign(e1, e2) -> (e1, $[Left-hand side of assignmen...
Editor Interface with Analysis



editor-analyze:
  (ast, path, fullpath) -> (errors, warnings, notes)
  with
    ast2    ...
Rename, Map, Project, Check


Rename
   ★ make local variables unique

Map
   ★ variables to their type

Project
   ★ comp...
Data Model Consistency
Consistency of Data Model Declarations
entity Blog {
  url    : String (id)
  name   : String (name)
  posts : Set<Post>
 ...
Consistency Constraints for Data Models



Unique declarations
   ★ entity names unique in model
   ★ property names uniqu...
Rename; Map; Project; Check


Rename
   ★ not needed: top-level declarations have global scope

Map
   ★ map identifier to ...
Entity Declarations: Map & Project
declare-def:
  ent@Entity(x, prop*) -> Entity(x, prop*)       Map
  with rules( EntityD...
Entity Declarations: Check


check :
  ent@Entity(x, prop*) -> (x, $[Entity '[x]' defined more than once])
  where require...
Properties: Project


lookup-property(|x) =
  lookup-property(?Property(x,_,_))

lookup-property(s) :
  Entity(x, prop*) -...
Properties: Check



check:
  ent@Entity(x, prop*) -> errors
  where errors := <filter(check-property(|ent))> prop*
  wher...
Inverse Property: Check




check-property(|ent) :
  prop@Property(f, t, annos) -> (g, $[Inverse relation requires entity ...
Inverse Property: Check
check:
  ent@Entity(x, prop*) -> errors
  where errors := <filter(check-property(|ent)); not(?[])>...
Property References




type-of :
  PropertyAccess(e, f) -> <type-of; lookup-property(|f); type-of> e

check :
  e1@Proper...
Template Consistency
define page editpost(p : Post) {
Template Consistency                     action save() {
                                ...
define page editpost(p : Post) {
         Template AST                                                   action save() {
 ...
Template Definitions: Map + Rename

declare-def :
  def@TemplateDef(mod*, x, param*, elem*) -> def
  with sig := <signature...
Template Definitions: Project
is-page-def =
  ?TemplateDef([Page()],_,_,_)

param-types =
  is-list; map(?Param(_,<id>))

p...
Template Definitions: Check Uniqueness


check :
  def@TemplateDef(mod*, x, param*, elem*) ->
                             ...
Checking Template/Page/Function Calls



List of expressions consistent with list of types
   ★ zip

Multiple possible err...
Templates: Check Page References



check :
  PageRef(x, e*) -> (x, $[Navigation to non-existing page])
  where require(de...
Template Call: Project




signature-of :
  Call(x, e*, elem*) -> (x, <map(type-of)> e*)

call-of :
  Call(x, e*, elem*) -...
Templates: Check Template Calls

check :
  Call(x, e*, elem*) -> (x, $[Template '[x]' is not defined])
  where not(<is-pri...
Checking Call Arguments


check-args =
  !(<call-of>, <declaration-of>);
  (check-arg-types <+ check-args-arity)

check-ar...
Reference Resolution
Reference Resolution




Aiding program navigation
  ★ Hover-click on identifier to jump to declaration

Reuse name resolut...
Reference Resolution

module nwl-References

imports nwl-References.generated

references

  reference _ : editor-resolve
...
From Use to Declaration



editor-resolve:
  (SimpleType(type), position, ast, path, fullpath) -> target
  where
    Entit...
Schedule
Case 3
  ★ Syntax definition & term rewriting
  ★ Deadline: May 4

Design 2
  ★ Make a proposal (can be submitted ...
Model-Driven Software Development - Static Analysis & Error Checking
Model-Driven Software Development - Static Analysis & Error Checking
Upcoming SlideShare
Loading in …5
×

Model-Driven Software Development - Static Analysis & Error Checking

2,053 views

Published on

0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total views
2,053
On SlideShare
0
From Embeds
0
Number of Embeds
740
Actions
Shares
0
Downloads
48
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Model-Driven Software Development - Static Analysis & Error Checking

  1. 1. Static Analysis & Error Checking Lecture 9 Course IN4308 Eelco Visser Master Computer Science http://eelcovisser.org Delft University of Technology
  2. 2. Coming up Lecture 8: Context-sensitive transformation ★ design 2 ★ transformation with dynamic rewrite rules Lecture 9: Static analysis & error checking ★ name resolution, reference resolution ★ type analysis Lecture 10: Code generation ★ string templates, code generation by model transformation ★ concrete object syntax Lecture 11: Code generation strategies ★ customization of generated code
  3. 3. Consistency Checking
  4. 4. Consistency Checking Syntax definition ★ what are well-formed sentences? Static analysis ★ not all ‘well-formedness’ properties are context-free ★ consistency of compositions ★ consistency of expressions wrt declarations Error reporting ★ indicate errors in editor ★ use sensible error message
  5. 5. Consistency Checking: Ingredients Editor Interface ★ collecting and displaying errors, warnings Error checking ★ checking static constraints and reporting errors Type analysis ★ computing types of expressions Name resolution ★ disambiguation of names Reference resolving ★ linking identifiers to declarations
  6. 6. Consistency Checking: Generic Approach Rename ★ make identifiers unique Map ★ map identifiers to declarations Project ★ compute properties of declarations, expressions Check ★ check constraints
  7. 7. Editor Interface module nwl-Builders imports nwl-Builders.generated builders provider: include/nwl.ctree observer: editor-analyze editor/nwl-Builders.esv trans/static-analysis.str module static-analysis imports include/nwl imports entities imports utils rules // static analysis editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ...
  8. 8. Editor Interface editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with errors := <collect-all(check, conc)> ast; warnings := <collect-all(constraint-warning, conc)> ast; notes := <collect-all(constraint-note, conc)> ast
  9. 9. Error Checking Rules check : context -> (target, error) where assumption where require(constraint) require(s) = not(s) – Context: identifying points in the code to check – Assumptions: only report an error if certain assumptions hold (validating the context and avoiding spurious errors) – Constraints: checking for constraints at the context – Formulating an error message – Attribution of the error to a particular character range in the source text (usually, only part of the context
  10. 10. Error Checking: Binary Operators check : e@BinOp(e1, op, e2) -> (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]]) where t1 := <type-of> e1 where t2 := <type-of> e2 where require(<type-of> e)
  11. 11. Pretty-Printing with String Interpolation pp : Entity(x, prop*) -> $[entity [x] { [<map(pp)>prop*] }] pp : Property(x,t) -> $[[x] : [<pp>t] ] pp : SimpleType(x) -> x pp : SetType(t) -> $[Set<[<pp> t]>] pp : [] -> $[] pp : [t] -> <pp>t pp : [t1,t2|ts] -> $[[<pp>t1],[<pp>[t2|ts]]]
  12. 12. Origin Tracking check : e@BinOp(e1, op, e2) -> (e, $[operator [op] not defined for [<pp>t1] and [<pp>t2]]) where ... Assign( Var("x") , BinOp( IntLit("2") , "+" , BinOp(IntLit("3"), "+", StringLit("4")) ) ) Assign( Var("x") , Plus(IntLit("2"), Times(IntLit("3"), StringLit("4"))) )
  13. 13. Error Checking: Control-Flow Statements check : While(e, b) -> (e, $[Expression should have type Bool]) where t := <type-of> e where require(<eq>(t, SimpleType("Bool"))) check : If(e, b1, b2) -> (e, $[Expression should have type Bool]) where t := <type-of> e where require(<eq>(t,SimpleType("Bool"))) check : For(x, t, e, elem*) -> (e, $[[<pp>SetType(t)] expected]) where t2 := <type-of> e where require(<eq>(t2,SetType(t))) check rules follow the same pattern: type analysis + local consistency check
  14. 14. Type Analysis type-of : e -> t compute type of expression
  15. 15. Type Analysis: Literals type-of : StringLit(x) -> SimpleType("String") type-of : IntLit(x) -> SimpleType("Int")
  16. 16. Type Analysis: Binary Operators type-of : BinOp(e1, op, e2) -> t where t := <function-type>(op, [<type-of>e1, <type-of>e2]) function-type : ("+", [SimpleType("String"), SimpleType("String")]) -> SimpleType("String") function-type : ("+", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int") function-type : ("-", [SimpleType("Int"), SimpleType("Int")]) -> SimpleType("Int")
  17. 17. Type Analysis BinOp( IntLit("2") , "+" , BinOp( IntLit("3"), "+", IntLit("4") ) )
  18. 18. Type Analysis BinOp( IntLit("2") , "+" , BinOp( type-of IntLit("3"), SimpleType("Int") "+", type-of IntLit("4") SimpleType("Int") ) )
  19. 19. Type Analysis BinOp( type-of IntLit("2") SimpleType("Int") , "+" type-of , BinOp( SimpleType("Int") IntLit("3"), "+", IntLit("4") ) )
  20. 20. Type Analysis type-of BinOp( SimpleType("Int") IntLit("2") , "+" , BinOp( IntLit("3"), "+", IntLit("4") ) )
  21. 21. Type Analysis: What is Type of Variable? define page root(x : Int) { action exptest() { for(y : Int in {1,2,x}) { x := x + y; } } type-of : } Var(x) -> t where t := ??? Assign( Var("x") , BinOp(Var("x"), "+", Var("y")) ) type of variable not part of variable use
  22. 22. Variables: Map declare-all = alltd(declare) declare : Param(x, t) -> Param(x, t) with rules( TypeOf : x -> t ) type-of : Var(x) -> t where t := <TypeOf> x
  23. 23. Scope define page root(x : Int) { action exptest() { for(x : Int in {1,2,x}) { print(x); } } } multiple occurrences of same identifier corresponding to different declarations
  24. 24. Variables: Map + Rename rename-all = alltd(rename) rename : Param(x, t) -> Param(y, t) with y := <rename-var>(x, t) unique annotation rename-var : (x, t) -> y with y := x{<new>}; map variable to type rules( TypeOf : y -> t RenameId : x -> y ) rename occurrences rename : Var(x) -> Var(y) where y := <RenameId> x type-of : Var(x) -> t where t := <TypeOf> x
  25. 25. Term Annotations t{t1,...,tn} add additional information to term without affecting signature
  26. 26. Variables: Check check : e@Var(x) -> (e, $[Variable '[x]' not declared]) where require(<type-of>e)
  27. 27. Variable Binding Constructs rename : For(x, t, e1, stat1*) -> For(y, t, e2, stat2*) with e2 := <rename-all> e1 with {| RenameId : y := <rename-var>(x, t) ; stat2* := <rename-all> stat1* |} For defines local variable x in body stat1*’
  28. 28. Assignment is-lvalue = ?Var(_) <+ ?PropertyAccess(_, _) check : Assign(e1, e2) -> (e1, $[Left-hand side of assignment should be variable or property access]) where require(<is-lvalue> e1) check : Assign(e1, e2) -> (<id>, $[Type of lhs ('[<pp>t1]') does not match type of rhs ('[<pp>t2]')]) where t1 := <type-of>e1 where t2 := <type-of>e2 where require(<eq>(t1, t2))
  29. 29. Editor Interface with Analysis editor-analyze: (ast, path, fullpath) -> (errors, warnings, notes) with ast2 := <analyze> ast; errors := <collect-all(check, conc)> ast2; warnings := <collect-all(constraint-warning, conc)> ast2; notes := <collect-all(constraint-note, conc)> ast2 analyze = rename-all
  30. 30. Rename, Map, Project, Check Rename ★ make local variables unique Map ★ variables to their type Project ★ compute type of expressions Check ★ check constraints using types
  31. 31. Data Model Consistency
  32. 32. Consistency of Data Model Declarations entity Blog { url : String (id) name : String (name) posts : Set<Post> author : User } entity Post { url : String (id) title : String (name) text : WikiText blog : Blog (inverse:posts) author : User blog : Blog version : Int }
  33. 33. Consistency Constraints for Data Models Unique declarations ★ entity names unique in model ★ property names unique in entity Valid types ★ type is either primitive types (e.g. String) or declared entity type Inverse properties ★ should refer to existing entity with existing property
  34. 34. Rename; Map; Project; Check Rename ★ not needed: top-level declarations have global scope Map ★ map identifier to AST of declaration Project ★ lookup information in declaration Check ★ check consistency using map & project
  35. 35. Entity Declarations: Map & Project declare-def: ent@Entity(x, prop*) -> Entity(x, prop*) Map with rules( EntityDeclaration : x -> ent ) declaration-of : SimpleType(x) -> <EntityDeclaration> x carrier-type = try(?SetType(<id>)) is-entity-type = where(SimpleType(EntityDeclaration)) is-simple-type = is-primitive-type <+ is-entity-type name-of : Entity(x, prop*) -> x Project type-of : Entity(x, prop*) -> SimpleType(x)
  36. 36. Entity Declarations: Check check : ent@Entity(x, prop*) -> (x, $[Entity '[x]' defined more than once]) where require(<EntityDeclaration> x => ent) check : t@SimpleType(x) -> (x, $[Type '[x]' is not defined]) where require(<is-simple-type>t) check : t@SetType(type) -> (t, $[Set should have entity type as argument]) where <is-simple-type> type where require(<is-entity-type> type)
  37. 37. Properties: Project lookup-property(|x) = lookup-property(?Property(x,_,_)) lookup-property(s) : Entity(x, prop*) -> <fetch-elem(s)> prop* lookup-property(s) : SimpleType(x) -> <declaration-of; lookup-property(s)> type-of : Property(_, type, _) -> type inverse : Property(_, _, anno*) -> <fetch-elem(?Inverse(_))> anno*
  38. 38. Properties: Check check: ent@Entity(x, prop*) -> errors where errors := <filter(check-property(|ent))> prop* where require(<not(?[])> errors) check-property(|ent) : Property(name, type, annos) -> (name, $[Property '[name]' defined more than once]) where require(<type-of><lookup-property(|name)>ent => type)
  39. 39. Inverse Property: Check check-property(|ent) : prop@Property(f, t, annos) -> (g, $[Inverse relation requires entity type]) where Inverse(g) := <inverse>prop where tc := <carrier-type> t where <is-simple-type> tc where require(<is-entity-type> tc)
  40. 40. Inverse Property: Check check: ent@Entity(x, prop*) -> errors where errors := <filter(check-property(|ent)); not(?[])> prop* check-property(|ent) : Property(f, t, annos) -> (g, $[Inverse relation requires entity type]) where Inverse(g) := <inverse> where tc := <carrier-type> t where <is-simple-type> tc // non-existing type already produces error message where require(<is-entity-type> tc) check-property(|ent1) : Property(f, t, annos) -> (g, $[Entity '[<pp>tc]' has no property '[g]']) where Inverse(g) := <inverse> where tc := <carrier-type> t where <is-entity-type> tc where require(<lookup-property(|g)> tc) check-property(|ent) : Property(f, t, anno*) -> (g, $[Type of '[<pp>t1].[g]' should be [<pp>t3] or [<pp>SetType(t3)]]) where Inverse(g) := <inverse> where t1 := <carrier-type> t where t2 := <lookup-property(|g); type-of; carrier-type> t1 where t3 := <type-of>ent where require(<eq>(t2, t3)) multiple check rules necessary to check different cases
  41. 41. Property References type-of : PropertyAccess(e, f) -> <type-of; lookup-property(|f); type-of> e check : e1@PropertyAccess(e2, f) -> (f, $[[<pp>t] has no property '[f]]) where t := <type-of> e2 where require(<type-of>e1)
  42. 42. Template Consistency
  43. 43. define page editpost(p : Post) { Template Consistency action save() { p.version := p.version + 1; return post(p); } header{output(p.title)} form{ input(p.url) input(p.title) input(p.text) Template definitions submit save() { "Save" } } ★ should be unique } Template references ★ to existing definition ★ consistent with parameter declarations
  44. 44. define page editpost(p : Post) { Template AST action save() { p.version := p.version + 1; return post(p); } header{output(p.title)} TemplateDef( form{ [Page()] , "editpost" input(p.url) , [Param("p", SimpleType("Post"))] input(p.title) , [ Action( input(p.text) "save" , [] submit save() { "Save" } , [ Assign( } PropertyAccess(Var("p"), "version") } , Plus(PropertyAccess(Var("p"), "version"), IntLit("1")) ) , ReturnPage(PageRef("post", [Var("p")])) ] ) , CallElems( "header" , [CallArgs("output", [PropertyAccess(Var("p"), "title")])] ) , CallElems( "form" , [ CallArgs("input", [PropertyAccess(Var("p"), "url")]) , CallArgs("input", [PropertyAccess(Var("p"), "title")]) , CallArgs("input", [PropertyAccess(Var("p"), "text")]) , Submit("save", [], [String(""Save"")]) ] ) ] )
  45. 45. Template Definitions: Map + Rename declare-def : def@TemplateDef(mod*, x, param*, elem*) -> def with sig := <signature-of> def; rules( Template : x -> def Template : sig -> def ) rename : TemplateDef(mod*, x, param1*, elem1*) -> <declare-def> TemplateDef(mod*, x, param2*, elem3*) with {| RenameId, RenameAction : param2* := <rename-all> param1* ; elem2* := <alltd(rename-action)> elem1* ; elem3* := <rename-all> elem2* |} rename local variables in template definition
  46. 46. Template Definitions: Project is-page-def = ?TemplateDef([Page()],_,_,_) param-types = is-list; map(?Param(_,<id>)) param-types : TemplateDef(mod*, x, param*, elem*) -> <param-types> param* signature-of : TemplateDef(mod*, x, param*, elem*) -> (x, <param-types>param*) declaration-of : TemplateDef(mod*, x, param*, elem*) -> <signature-of; Template> declaration-of : Navigate(ref, elems) -> <declaration-of> ref declaration-of : PageRef(x, e*) -> <Template> x call-of : PageRef(x, e*) -> (x, e*)
  47. 47. Template Definitions: Check Uniqueness check : def@TemplateDef(mod*, x, param*, elem*) -> (x, $[Multiple definitions for page '[x]']) where <is-page-def> def where require(<Template> x => def) check : def@TemplateDef(mod*, x, param*, elem*) -> (x, $[Multiple definitions for template with signature [sig]]) where not(is-page-def) where require(<declaration-of> def => def) where sig := <signature-of;pp-sig> def
  48. 48. Checking Template/Page/Function Calls List of expressions consistent with list of types ★ zip Multiple possible error causes ★ call of non-existing definition ★ parameter arity mismatch ★ argument type mismatch Argument checking reusable
  49. 49. Templates: Check Page References check : PageRef(x, e*) -> (x, $[Navigation to non-existing page]) where require(declaration-of) check : PageRef(x, e*) -> [(x, $[Navigation to template '[x]' (not a page)])] where def := <declaration-of> where require(<is-page-def> def) check : PageRef(x, e*) -> <check-args>
  50. 50. Template Call: Project signature-of : Call(x, e*, elem*) -> (x, <map(type-of)> e*) call-of : Call(x, e*, elem*) -> (x, e*) declaration-of : Call(x, e*, elem*) -> <signature-of; Template> is-primitive-template = ?"input" <+ ?"output" <+ ?"form"
  51. 51. Templates: Check Template Calls check : Call(x, e*, elem*) -> (x, $[Template '[x]' is not defined]) where not(<is-primitive-template> x) where require(<Template> x) check : Call(x, e*, elem*) -> (x, $[No definition for template with signature '[x]([<map(type-of);pp> e*])']) where not(<is-primitive-template> x) where <Template> x where require(declaration-of) constraint-warning : Call(x, e*, elem*) -> [(x, $[Page definition is used as template])] where def := <declaration-of> where require(not(<is-page-def> def)) check : Call(x, e*, elem*) -> <check-args>
  52. 52. Checking Call Arguments check-args = !(<call-of>, <declaration-of>); (check-arg-types <+ check-args-arity) check-arg-types : ((f, e*), def) -> errors where errors := <zip; filter(check-arg); not(?[])> (e*, <param-types> def) check-arg : (e, t) -> (e, $[Argument of type '[<pp>t]' expected (not of type '[<pp>t2]')]) where t2 := <type-of> e where require(<eq>(t, t2)) check-args-arity : ((f, e*), def) -> [(f, $['[f]' expects [<int-to-string>l] arguments; [<int-to-string>k] provided])] with k := <length>e* with l := <param-types; length> def where require(<eq>(k, l))
  53. 53. Reference Resolution
  54. 54. Reference Resolution Aiding program navigation ★ Hover-click on identifier to jump to declaration Reuse name resolution infrastructure
  55. 55. Reference Resolution module nwl-References imports nwl-References.generated references reference _ : editor-resolve editor-resolve: (source, position, ast, path, fullpath) -> target where target := <compute-target> source
  56. 56. From Use to Declaration editor-resolve: (SimpleType(type), position, ast, path, fullpath) -> target where Entity(target,_) := <EntityDeclaration> type editor-resolve: (ref@PageRef(x,e*), position, ast, path, fullpath) -> target where TemplateDef(_,target,_,_) := <declaration-of> ref
  57. 57. Schedule Case 3 ★ Syntax definition & term rewriting ★ Deadline: May 4 Design 2 ★ Make a proposal (can be submitted separately) ★ Deadline: May 5 Lab this week ★ Finish Case 3 ★ Syntax for Design 2 Next ★ Lecture 10: code generation

×