User Tools

Site Tools


BYU CS330 - Concepts of Programming Languages - HP Int Lab


This lab is designed to transition our interpreter to a more powerful image processing engine by binding in new data types, high performance primitives, and automatic parallelization of interpretation.


This lab builds on the previous interpreters. In addition, to support the image processing primitives and text rendering primitives, you will need to install the Cairo and Images packages into Julia. This is done using the Julia package management system:




Note: Julia uses Git as the foundation of their package management system. This can create problems if your system can't access Git using a


protocol – this is manifest by Julia hanging when trying to install packages. To alleviate this, please check out the following links:

When I installed my Julia packages, it took a long time, but it worked. Be patient. :)


For this lab, you will expand the interpreter that we have already built in three ways: with new data types, new high performance primitives, and automatic parallelization.

The primary deliverable for this lab is a new Julia module. Your module should export parse, calc analyze functions, and three return types, NumVal, ClosureVal, and MatrixVal.

Your module should be able to do everything that the three previous interpreters could do. You are welcome to examine the code we developed in class. The CI6.jl module is available on LearningSuite, under “Content → Julia → High Performance Primitives”.

Please name your module HPInt.


For this lab, you will implement several new features:

  1. You must create a new MatrixVal data type.
  2. You must bind in several new primitives (listed in the grammar below)
  3. You must put in automatic parallelization statements.

Each is discussed in more detail in the following sections.

The grammar for our new language is the following:

<OWL> ::= number
          | (+ <OWL> <OWL> <OWL>*)    # all owls could be a MatrixVal or a NumVal
          | (- <OWL> <OWL>)
          | (* <OWL> <OWL>)
          | (/ <OWL> <OWL>)
          | (mod <OWL> <OWL>)
          | (- <OWL>) 
          | (collatz <OWL>)     # Only a NumVal version required for collatz     
          | id
          | (if0 <OWL> <OWL> <OWL>)
          | (with ( (id <OWL>)* ) <OWL>)
          | (lambda (id*) <OWL>)
          | (and <OWL> <OWL> <OWL>*)
          | (<OWL> <OWL>*)   
          # new primitives here
          | (simple_load <string>)
          | (simple_save <OWL> <string>)        # this owl should evaluate to a MatrixVal
          | (render_text <string> <OWL> <OWL>)  # these owls should evaluate to NumVals
          | (emboss <OWL>)                      # this owl should evaluate to a MatrixVal
          | (drop_shadow <OWL>)                 # this owl should evaluate to a MatrixVal
          | (inner_shadow <OWL>)                # this owl should evaluate to a MatrixVal
          | (min <OWL> <OWL>)                   # boths owls could be a MatrixVal or a NumVal
          | (max <OWL> <OWL>)

New functions:

The new productions in our language map directly to the primitive functions of the same name. For each, you will need to do the following:

  1. Create a new node in the AST
  2. Parse the parameter properly
  3. Recursively analyze any <OWL> subexpressions
  4. Implement a calc function for each AST node type

We've been through this process several times in class, so we won't belabor it here.

You must also ensure that the +, -, * and / operators work any combination of NumVal or MatrixVal. I showed you my solution to this problem in class, but the reference code does not contain that, because your implementation (using BinOp) is likely quite different.

A few notes:

  1. The min,max,+,-,/,* functions all must work with any combination of NumVal and MatrixVals.
  2. Remember that when multiplying a MatrixVal by a MatrixVal, we want to use the .* operation (element-wise matrix multiply) instead of * (matrix-matrix multiply).

Error handling:

The new primitives create new opportunities for error handling. You should bullet-proof them!

In particular, you should carefully check the number and type of all subexpressions and strings. For example, operations such as min or max should operate on NumVal or MatrixVal objects, but should throw an error if handed something like a ClosureVal.


We discussed several opportunities for parallelization in class. You must use the proper combination of @spawn and fetch to parallelize as many of the calc functions as possible by spawning for each sub-call to calc. You do not need to parallelize the analyze functions.


To verify that your matrix code is working properly, you can run the test cases described in cattestcases.pdf on cat_256.png

Once all is said and done, the following program:

(with ((base_img (render_text "Hello" 25 100))
    (swirl (simple_load "/Users/wingated/Desktop/swirl_256.png")))
    (with ((ds (drop_shadow base_img)))
        (with ((tmp4 (+ (* (+ (min ds base_img) (- 1 base_img)) base_img) (* (- 1 base_img) swirl) )))
            (with ((tmp5 (- 1 (emboss tmp4)))
                    (base_img2 (render_text "world!" 5 200)))
                (with ((is (inner_shadow base_img2)))
                    (with ((tmp6 (max base_img2 (* (- 1 base_img2) is) )))
                        (with ( (output (min tmp5 tmp6 )) )
                            (simple_save output "output.png")

should generate the image shown below, using the swirl_256.png image shown at the right:

cs330_f2016/labhppint.txt · Last modified: 2021/06/30 23:42 (external edit)