PARSER-LIKE FUNCTIONS: 1) Develop the list-of-length function, that accepts a list and a number, and returns true when the list is of length exactly n. Don't use the "length" function in defining this one. Why might you want to use this function instead of the "length" function? 2) Develop a parser that accepts this grammar: ::= grub | {ding grub } | {ding } | {drib {id *} {expr *}} ... and returns the corresponding element of this tree language: (define-type Expr [grub] [ding-3 (fst Expr?) (snd Expr?)] [ding-2 (fst Expr?) (snd Expr?)] [drib (lob (list-of Binding?))]) with the standard (define-type Binding [binding (id symbol?) (rhs Expr?)]) Note that you have to be careful about distinguishing the second case from the third one; this is why it's nice to have a parser, to get rid of the ambiguity up front. EXPR-TAKING FUNCTIONS 3) Develop the grubToDing function that accepts an Expr and replaces every 'grub' with (the parsed representation of) {ding grub grub grub}. 4) Develop the all-names function that accepts an Expr and returns a list of all of the ids that appear in all of the {drib ...} expressions. The order is not important.