|
62 | 62 | (struct Var (x) #:prefab) |
63 | 63 | (struct App (f es) #:prefab) |
64 | 64 | (struct Apply (f es e) #:prefab) |
| 65 | + |
| 66 | +;; Prog -> Void |
| 67 | +(define (check-syntax p) |
| 68 | + (match p |
| 69 | + [(Prog ds e) |
| 70 | + (let ((dr (defined-ids ds))) |
| 71 | + (check-syntax-unique-defines ds) |
| 72 | + (check-syntax-defines ds dr) |
| 73 | + (check-syntax-e e dr '()))])) |
| 74 | + |
| 75 | +;; [Listof Defn] -> [Listof Id] |
| 76 | +(define (defined-ids ds) |
| 77 | + (map (λ (d) (match d [(Defn f _) f])) |
| 78 | + ds)) |
| 79 | + |
| 80 | +;; [Listof Defn] -> Void |
| 81 | +(define (check-syntax-unique-defines ds) |
| 82 | + (unless (= (length ds) |
| 83 | + (length (remove-duplicates ds #:key Defn-f))) |
| 84 | + (error "duplicate definition for function"))) |
| 85 | + |
| 86 | +;; [Listof Defn] [Listof Id] -> Void |
| 87 | +(define (check-syntax-defines ds r) |
| 88 | + (for-each (λ (d) (check-syntax-define d r)) ds)) |
| 89 | + |
| 90 | +;; Defn [Listof Id] -> Void |
| 91 | +(define (check-syntax-define d dr) |
| 92 | + (match d |
| 93 | + [(Defn f (FunPlain xs e)) |
| 94 | + (check-unique (cons f xs)) |
| 95 | + (check-syntax-e e dr xs)] |
| 96 | + [(Defn f (FunRest xs x e)) |
| 97 | + (check-unique (cons f (cons x xs))) |
| 98 | + (check-syntax-e e dr (cons x xs))] |
| 99 | + [(Defn f (FunCase '())) |
| 100 | + (void)] |
| 101 | + [(Defn f (FunCase (cons c cs))) |
| 102 | + (check-syntax-define (Defn f c) dr) |
| 103 | + (check-syntax-define (Defn f (FunCase cs)) dr)])) |
| 104 | + |
| 105 | +;; [Listof Id] -> Void |
| 106 | +(define (check-unique xs) |
| 107 | + (unless (= (length xs) (length (remove-duplicates xs))) |
| 108 | + (error "duplicate identifier"))) |
| 109 | + |
| 110 | +;; Expr [Listof Id] [Listof Id] -> Void |
| 111 | +(define (check-syntax-e e dr r) |
| 112 | + (match e |
| 113 | + [(Eof) (void)] |
| 114 | + [(Empty) (void)] |
| 115 | + [(Int i) (void)] |
| 116 | + [(Bool b) (void)] |
| 117 | + [(Char c) (void)] |
| 118 | + [(Str s) (void)] |
| 119 | + [(Prim0 p) (void)] |
| 120 | + [(Prim1 p e) (check-syntax-e e dr r)] |
| 121 | + [(Prim2 p e1 e2) |
| 122 | + (check-syntax-e e1 dr r) |
| 123 | + (check-syntax-e e2 dr r)] |
| 124 | + [(Prim3 p e1 e2 e3) |
| 125 | + (check-syntax-e e1 dr r) |
| 126 | + (check-syntax-e e2 dr r) |
| 127 | + (check-syntax-e e3 dr r)] |
| 128 | + [(If e1 e2 e3) |
| 129 | + (check-syntax-e e1 dr r) |
| 130 | + (check-syntax-e e2 dr r) |
| 131 | + (check-syntax-e e3 dr r)] |
| 132 | + [(Begin e1 e2) |
| 133 | + (check-syntax-e e1 dr r) |
| 134 | + (check-syntax-e e2 dr r)] |
| 135 | + [(Let x e1 e2) |
| 136 | + (check-syntax-e e1 dr r) |
| 137 | + (check-syntax-e e2 dr (cons x r))] |
| 138 | + [(Var x) |
| 139 | + (unless (member x r) |
| 140 | + (error "unbound variable"))] |
| 141 | + [(App f es) |
| 142 | + (unless (member f dr) |
| 143 | + (error "undefined function")) |
| 144 | + (for-each (λ (e) (check-syntax-e e dr r)) es)] |
| 145 | + [(Apply f es e) |
| 146 | + (unless (member f dr) |
| 147 | + (error "undefined function")) |
| 148 | + (check-syntax-e e dr r) |
| 149 | + (for-each (λ (e) (check-syntax-e e dr r)) es)])) |
| 150 | + |
| 151 | +(module+ test |
| 152 | + (require rackunit) |
| 153 | + (check-exn exn:fail? (λ () (check-syntax-e (Var 'x) '() '()))) |
| 154 | + (check-exn exn:fail? (λ () (check-syntax-e (Var 'x) '(x) '()))) |
| 155 | + (check-not-exn (λ () (check-syntax-e (Var 'x) '() '(x)))) |
| 156 | + (check-not-exn (λ () (check-syntax-e (Let 'x (Int 1) (Var 'x)) '() '()))) |
| 157 | + (check-not-exn (λ () (check-syntax-e (Let 'x (Int 1) (Let 'y (Int 2) (Var 'x))) '() '()))) |
| 158 | + (check-not-exn (λ () (check-syntax-e (Let 'x (Int 1) (Let 'x (Int 2) (Var 'x))) '() '()))) |
| 159 | + (check-not-exn (λ () (check-syntax-e (Let 'x (Int 1) (Let 'y (Int 2) (Var 'y))) '() '()))) |
| 160 | + (check-exn exn:fail? (λ () (check-syntax (Prog (list (Defn 'f (FunPlain '() (Int 1)))) (Var 'f))))) |
| 161 | + (check-exn exn:fail? (λ () (check-syntax (Prog (list (Defn 'f (FunPlain '(f) (Int 1)))) (Int 1))))) |
| 162 | + (check-exn exn:fail? (λ () (check-syntax (Prog (list (Defn 'f (FunRest '(f) 'x (Int 1)))) (Int 1))))) |
| 163 | + (check-exn exn:fail? (λ () (check-syntax (Prog (list (Defn 'f (FunRest '() 'f (Int 1)))) (Int 1))))) |
| 164 | + (check-exn exn:fail? (λ () (check-syntax (Prog (list (Defn 'f (FunPlain '(x x) (Int 1)))) (Int 1))))) |
| 165 | + (check-exn exn:fail? |
| 166 | + (λ () (check-syntax |
| 167 | + (Prog (list (Defn 'f (FunPlain '(x) (Int 1))) |
| 168 | + (Defn 'f (FunPlain '(y) (Int 2)))) |
| 169 | + (Int 1))))) |
| 170 | + (check-exn exn:fail? (λ () (check-syntax (Prog '() (App 'f '()))))) |
| 171 | + (check-exn exn:fail? (λ () (check-syntax (Prog '() (Apply 'f '() (Int 1)))))) |
| 172 | + (check-not-exn (λ () (check-syntax (Prog (list (Defn 'f (FunPlain '() (Int 1)))) (App 'f '()))))) |
| 173 | + (check-not-exn (λ () (check-syntax (Prog (list (Defn 'f (FunPlain '() (Int 1)))) (Apply 'f '() (Int 1))))))) |
0 commit comments