Difference between revisions of "Itty Bitty Programming Language Interpreter Assignment"

From CSE425S Wiki
Jump to navigation Jump to search
 
(12 intermediate revisions by the same user not shown)
Line 36: Line 36:
 
</syntaxhighlight>
 
</syntaxhighlight>
  
==Utility==
+
==Environment Support==
 
===entry===
 
===entry===
 
Environments in IBPL will be stored as a list of entries.  An entry is comprised of a name of type string and a value which is a value.
 
Environments in IBPL will be stored as a list of entries.  An entry is comprised of a name of type string and a value which is a value.
Line 45: Line 45:
 
<syntaxhighlight lang="racket">
 
<syntaxhighlight lang="racket">
 
(struct entry (name value) #:transparent)
 
(struct entry (name value) #:transparent)
 +
</syntaxhighlight>
 +
 +
==Runtime Type Error Support==
 +
===raise-ib-type-error===
 +
When you encounter a runtime type error in evaluate, you should raise an exception which is more descriptive than, for example, what the [https://docs.racket-lang.org/reference/generic-numbers.html#%28def._%28%28quote._~23~25kernel%29._%2B%29%29 + function] might report.  We have provided the function <code>raise-ib-type-error</code> which accepts a <code>message</code> and a <code>value</code> as well as an optional list of whatever other details you would like to provide.
 +
 +
<syntaxhighlight lang="racket">
 +
(define (raise-ib-type-error message value . details)
 +
  ; implementation can be found in ast.rkt
 +
</syntaxhighlight>
 +
 +
===example use in evaluate===
 +
For example, if in handling an AddExp, you had defined local variables for right-exp and right-value and detected that right-value was not an exact-integer, then you could raise an exception something like the following:
 +
 +
<syntaxhighlight lang="racket">
 +
(raise-ib-type-error "AddExp-right_exp not an exact-integer" right-value right-exp)
 
</syntaxhighlight>
 
</syntaxhighlight>
  
Line 60: Line 76:
 
         (error 'not-yet-implemented))  
 
         (error 'not-yet-implemented))  
 
</syntaxhighlight>
 
</syntaxhighlight>
 +
 +
Here we build a utility function to be used in <code>evaluate</code>.  If <code>name</code> is a [https://docs.racket-lang.org/reference/strings.html#%28def._%28%28quote._~23~25kernel%29._string~3f%29%29 string?], <code>value</code> is a value?, and <code>env</code> is a [https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._list~3f%29%29 list?] we will [https://docs.racket-lang.org/reference/pairs.html#%28def._%28%28quote._~23~25kernel%29._cons%29%29 cons] an entry with the name and value onto the environment.  If any of these conditions are not met, you should [https://docs.racket-lang.org/reference/exns.html#%28def._%28%28quote._~23~25kernel%29._raise-argument-error%29%29 raise-argument-error] with the appropriate information.
  
 
==evaluate==
 
==evaluate==
Line 68: Line 86:
 
This is the definition of IBPL’s syntax:
 
This is the definition of IBPL’s syntax:
  
* If name is a Racket string, then <code>(IdentifierExp name)</code> is a IBPL expression (a variable use).
+
* If <code>name</code> is a Racket string, then <code>(IdentifierExp name)</code> is a IBPL expression (a variable use).
* If value is a Racket integer, then <code>(IntExp value)</code> is a IBPL expression (a constant).
+
* If <code>value</code> is a Racket integer, then <code>(IntExp value)</code> is a IBPL expression (a constant).
* If e1 and e2 are IBPL expressions, then <code>(AddExp e1 e2)</code> is a IBPL expression (an addition).
+
* If <code>left_exp</code> and <code>right_exp</code> are IBPL expressions, then <code>(AddExp left_exp right_exp)</code> is a IBPL expression (an addition).
* If s1 and s2 are Racket strings and e is a IBPL expression, then <code>(FunctionExp s1 s2 e)</code> is a IBPL expression (a function). In e, s1 is bound to the function itself (for recursion) and s2 is bound to the (one) argument. Also, <code>(fun #f s2 e)</code> is allowed for anonymous nonrecursive functions.
+
* If <code>name_option</code> and <code>parameter_name</code> are Racket strings and <code>body_exp</code> is a IBPL expression, then <code>(FunctionExp name_option parameter_name body_exp)</code> is a IBPL expression (a function). In body_exp, name_option is bound to the function itself (for recursion) and parameter_name is bound to the (one) argument. Also, <code>(FunctionExp #f parameter_name body_exp)</code> is allowed for anonymous nonrecursive functions.
* If e1, e2, and e3, and e4 are IBPL expressions, then <code>(IfGreaterExp e1 e2 e3 e4)</code> is a IBPL expression. It is a conditional where the result is e3 if e1 is strictly greater than e2 else the result is e4. Only one of e3 and e4 is evaluated.
+
* If <code>left_exp</code>, <code>right_exp</code>, <code>then_body_exp</code>, and <code>else_body_exp</code> are IBPL expressions, then <code>(IfGreaterExp left_exp right_exp then_body_exp else_body_exp)</code> is a IBPL expression. It is a conditional where the result is then_body_exp if left_exp is strictly greater than right_exp else the result is else_body_exp. Only one of then_body_exp and else_body_exp is evaluated.
* If e1 and e2 are IBPL expressions, then <code>(CallExp e1 e2)</code> is a IBPL expression (a function call).
+
* If <code>function_exp</code> and <code>argument_exp</code> are IBPL expressions, then <code>(CallExp function_exp argument_exp)</code> is a IBPL expression (a function call).
* If s is a Racket string and e1 and e2 are IBPL expressions, then <code>(LetExp s e1 e2)</code> is a IBPL expression (a let expression where the value resulting e1 is bound to s in the evaluation of e2).
+
* If <code>binding_name</code> is a Racket string and <code>binding_exp</code> and <code>body_exp</code> are IBPL expressions, then <code>(LetExp binding_name binding_exp body_exp)</code> is a IBPL expression (a let expression where the value resulting binding_exp is bound to binding_name in the evaluation of body_exp).
* If e1 and e2 are IBPL expressions, then <code>(ConsExp e1 e2)</code> is a IBPL expression (a pair-creator).
+
* If <code>car_exp</code> and <code>cdr_exp</code> are IBPL expressions, then <code>(ConsExp car_exp cdr_exp)</code> is a IBPL expression (a pair-creator).
* If e1 is a IBPL expression, then <code>(CarOfConsExp e1)</code> is a IBPL expression (getting the first part of a pair).
+
* If <code>cons_exp</code> is a IBPL expression, then <code>(CarOfConsExp cons_exp)</code> is a IBPL expression (getting the first part of a pair).
* If e1 is a IBPL expression, then <code>(CdrOfConsExp e1)</code> is a IBPL expression (getting the second part of a pair).
+
* If <code>cons_exp</code> is a IBPL expression, then <code>(CdrOfConsExp cons_exp)</code> is a IBPL expression (getting the second part of a pair).
 
* <code>(NilExp)</code> is a IBPL expression. Notice <code>(NilExp)</code> is a IBPL expression, but <code>NilExp</code> is not.
 
* <code>(NilExp)</code> is a IBPL expression. Notice <code>(NilExp)</code> is a IBPL expression, but <code>NilExp</code> is not.
* If e1 is a IBPL expression, then <code>(IsNilExp e1)</code> is a IBPL expression (testing for (NilExp)).
+
* If <code>exp</code> is a IBPL expression, then <code>(IsNilExp e1)</code> is a IBPL expression (testing for (NilExp)).
  
You should assume MUPL programs are syntactically correct (e.g., do not worry about wrong things like <code>(IntExp "hi")</code> or <code>(IntExp (IntExp 37))</code>. But do not assume IBPL programs are free of type errors like <code>(AddExp (NilExp) (IntExp 7)) or (CarOfConExp (IntExp 7))</code>.
+
You should assume IBPL programs are syntactically correct (e.g., do not worry about wrong things like <code>(IntExp "hi")</code> or <code>(IntExp (IntExp 37))</code>. But do not assume IBPL programs are free of type errors like <code>(AddExp (NilExp) (IntExp 7)) or (CarOfConsExp (IntExp 7))</code>.
  
  
Line 100: Line 118:
 
* A CarOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CarOfConsExp expression is the car field in the pair.
 
* A CarOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CarOfConsExp expression is the car field in the pair.
 
* A CdrOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CdrOfConsExp expression is the cdr field in the pair.
 
* A CdrOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CdrOfConsExp expression is the cdr field in the pair.
* An <code>IsNilExp</code> expression evaluates its subexpression. If the result is an NilExp expression, then the result for the IsNilExp expression is the value <code>1</code>, else the result is the value <code>0</code>.
+
* An <code>IsNilExp</code> expression evaluates its subexpression. If the result is a null?, then the result for the IsNilExp expression is the value <code>1</code>, else the result is the value <code>0</code>.
  
 
Hint: The call case is the most complicated. In the sample solution, no case is more than 12 lines and several are 1 line.
 
Hint: The call case is the most complicated. In the sample solution, no case is more than 12 lines and several are 1 line.

Latest revision as of 18:31, 12 November 2024

In a series of assignments, we will build the Itty Bitty Programming Language (IBPL). The inspiration for these assignments is MUPL by Dan Grossman and his team at UW.

One can build an IBPL Abstract Syntax Tree (AST) with the following expressions:

Expressions

(struct IdentifierExp (name)                                           #:transparent)
(struct IntExp        (value)                                          #:transparent)
(struct AddExp        (left_exp right_exp)                             #:transparent)
(struct IfGreaterExp  (left_exp right_exp then_body_exp else_body_exp) #:transparent)
(struct NilExp        ()                                               #:transparent)
(struct IsNilExp      (exp)                                            #:transparent)
(struct ConsExp       (car_exp cdr_exp)                                #:transparent)
(struct CarOfConsExp  (cons_exp)                                       #:transparent)
(struct CdrOfConsExp  (cons_exp)                                       #:transparent)
(struct LetExp        (binding_name binding_exp body_exp)              #:transparent)
(struct FunctionExp   (name_option parameter_name body_exp)            #:transparent)
(struct CallExp       (function_exp argument_exp)                      #:transparent)

Values

int

the empty list

the mighty cons cell

closure

(struct closure (env function) #:transparent)

Environment Support

entry

Environments in IBPL will be stored as a list of entries. An entry is comprised of a name of type string and a value which is a value.

(struct entry (name value) #:transparent)

Runtime Type Error Support

raise-ib-type-error

When you encounter a runtime type error in evaluate, you should raise an exception which is more descriptive than, for example, what the + function might report. We have provided the function raise-ib-type-error which accepts a message and a value as well as an optional list of whatever other details you would like to provide.

(define (raise-ib-type-error message value . details)
  ; implementation can be found in ast.rkt

example use in evaluate

For example, if in handling an AddExp, you had defined local variables for right-exp and right-value and detected that right-value was not an exact-integer, then you could raise an exception something like the following:

(raise-ib-type-error "AddExp-right_exp not an exact-integer" right-value right-exp)

Code To Implement

file: src/main/racket/ibpl/interpreter/interpreter.rkt Racket-logo.svg
functions: value?
expand-environment
evaluate

value?

(define (value? v)
        (error 'not-yet-implemented))

expand-environment

(define (expand-environment name value env)
        (error 'not-yet-implemented))

Here we build a utility function to be used in evaluate. If name is a string?, value is a value?, and env is a list? we will cons an entry with the name and value onto the environment. If any of these conditions are not met, you should raise-argument-error with the appropriate information.

evaluate

Note: the syntaxdescription below is adapted from Prof. Grossman's MUPL syntax description.

IBPL programs are written directly in Racket by using the constructors defined by the structs defined in ast.rkt.

This is the definition of IBPL’s syntax:

  • If name is a Racket string, then (IdentifierExp name) is a IBPL expression (a variable use).
  • If value is a Racket integer, then (IntExp value) is a IBPL expression (a constant).
  • If left_exp and right_exp are IBPL expressions, then (AddExp left_exp right_exp) is a IBPL expression (an addition).
  • If name_option and parameter_name are Racket strings and body_exp is a IBPL expression, then (FunctionExp name_option parameter_name body_exp) is a IBPL expression (a function). In body_exp, name_option is bound to the function itself (for recursion) and parameter_name is bound to the (one) argument. Also, (FunctionExp #f parameter_name body_exp) is allowed for anonymous nonrecursive functions.
  • If left_exp, right_exp, then_body_exp, and else_body_exp are IBPL expressions, then (IfGreaterExp left_exp right_exp then_body_exp else_body_exp) is a IBPL expression. It is a conditional where the result is then_body_exp if left_exp is strictly greater than right_exp else the result is else_body_exp. Only one of then_body_exp and else_body_exp is evaluated.
  • If function_exp and argument_exp are IBPL expressions, then (CallExp function_exp argument_exp) is a IBPL expression (a function call).
  • If binding_name is a Racket string and binding_exp and body_exp are IBPL expressions, then (LetExp binding_name binding_exp body_exp) is a IBPL expression (a let expression where the value resulting binding_exp is bound to binding_name in the evaluation of body_exp).
  • If car_exp and cdr_exp are IBPL expressions, then (ConsExp car_exp cdr_exp) is a IBPL expression (a pair-creator).
  • If cons_exp is a IBPL expression, then (CarOfConsExp cons_exp) is a IBPL expression (getting the first part of a pair).
  • If cons_exp is a IBPL expression, then (CdrOfConsExp cons_exp) is a IBPL expression (getting the second part of a pair).
  • (NilExp) is a IBPL expression. Notice (NilExp) is a IBPL expression, but NilExp is not.
  • If exp is a IBPL expression, then (IsNilExp e1) is a IBPL expression (testing for (NilExp)).

You should assume IBPL programs are syntactically correct (e.g., do not worry about wrong things like (IntExp "hi") or (IntExp (IntExp 37)). But do not assume IBPL programs are free of type errors like (AddExp (NilExp) (IntExp 7)) or (CarOfConsExp (IntExp 7)).


Write a IBPL interpreter, i.e., a Racket function evalulate that takes an IBPL expression e and either returns the IBPL value that e evaluates to under the empty environment or calls Racket’s error if evaluation encounters a run-time IBPL type error or unbound IBPL variable.

A IBPL expression is evaluated under an environment (for evaluating variables, as usual). In your interpreter, use a Racket list of entries to represent this environment) so that you can use without modification the provided lookup-value-in-environment function. Here is a description of the semantics of IBPL expressions:

  • An identifier evaluates to the value associated with it in the environment.
  • An addition evaluates its subexpressions and assuming they both produce integers, produces the integer that is their sum. (Note this case is done for you to get you pointed in the right direction.)
  • Functions are lexically scoped: A function evaluates to a closure holding the function and the current environment.
  • An IfGreaterExp evaluates its first two subexpressions to values v1 and v2 respectively. If both values are integers, it evaluates its third subexpression if v1 is a strictly greater integer than v2 else it evaluates its fourth subexpression.
  • A LetExp expression evaluates its first expression to a value v. Then it evaluates the second expression to a value, in an environment extended to map the name in the LetExp expression to v.
  • A call evaluates its first and second subexpressions to values. If the first is not a closure, it is an error. Else, it evaluates the closure’s function’s body in the closure’s environment extended to map the function’s name to the closure (unless the name field is #f) and the function’s argument-name (i.e., the parameter name) to the result of the second subexpression.
  • A cons expression evaluates its two subexpressions and produces a (new) pair holding the results.
  • A CarOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CarOfConsExp expression is the car field in the pair.
  • A CdrOfCons expression evaluates its subexpression. If the result for the subexpression is a pair, then the result for the CdrOfConsExp expression is the cdr field in the pair.
  • An IsNilExp expression evaluates its subexpression. If the result is a null?, then the result for the IsNilExp expression is the value 1, else the result is the value 0.

Hint: The call case is the most complicated. In the sample solution, no case is more than 12 lines and several are 1 line.

(define (evaluate exp env)
  (if (expression? exp)
      (if (list? env)
          (ensure-value? 
                 (error 'not-yet-implemented))
          (raise-argument-error 'env "list?" env))
      (raise-argument-error 'exp "expression?" exp)))

Test

file: test_interpreter.rkt Racket-logo.svg Test
source folder: src/test/racket/ibpl/interpreter

note: ensure that you have removed all printing to receive credit for any assignment.