dotpar final report team 3 - cs.columbia.eduaho/cs4115_spring-2012/lectures/12-05-09_team3... ·...

56
DotPar Final Report Team 3 Logan Donovan - [email protected] Justin Hines - [email protected] Andrew Hitti - [email protected] Nathan Hwang - [email protected] Sid Nair - [email protected] May 9, 2012 Contents I Introduction 6 1 Motivation 6 1.1 Locking ..................................... 6 1.2 Task-based Concurrency and the Actor Model ................ 6 1.3 Functional Programming ............................ 6 1.4 Implicit Parallelization ............................. 7 2 Introducing DotPar 7 2.1 Automatic Parallelization ........................... 7 2.2 Strong Typing ................................. 7 2.3 First-class Functions and Lexical Closures .................. 8 2.4 Robustness and Security ............................ 8 2.5 Memory Management and Garbage Collection ................ 8 II Language Tutorial 8 3 Overview 8 3.1 Hello, World ................................... 8 3.2 Types And Arithmetic Expressions ...................... 9 3.2.1 Numbers and Basic Arithmetic Operators .............. 10 3.2.2 Booleans and Operators ........................ 10 3.2.3 Char ................................... 11 3.3 Arrays and Control Flow ............................ 11 1

Upload: others

Post on 04-Jun-2020

1 views

Category:

Documents


0 download

TRANSCRIPT

Page 1: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

DotPar

Final Report

Team 3

Logan Donovan - [email protected] Hines - [email protected] Hitti - [email protected]

Nathan Hwang - [email protected] Sid Nair - [email protected]

May 9, 2012

Contents

I Introduction 6

1 Motivation 61.1 Locking . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.2 Task-based Concurrency and the Actor Model . . . . . . . . . . . . . . . . 61.3 Functional Programming . . . . . . . . . . . . . . . . . . . . . . . . . . . . 61.4 Implicit Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 7

2 Introducing DotPar 72.1 Automatic Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.2 Strong Typing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 72.3 First-class Functions and Lexical Closures . . . . . . . . . . . . . . . . . . 82.4 Robustness and Security . . . . . . . . . . . . . . . . . . . . . . . . . . . . 82.5 Memory Management and Garbage Collection . . . . . . . . . . . . . . . . 8

II Language Tutorial 8

3 Overview 83.1 Hello, World . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 83.2 Types And Arithmetic Expressions . . . . . . . . . . . . . . . . . . . . . . 9

3.2.1 Numbers and Basic Arithmetic Operators . . . . . . . . . . . . . . 103.2.2 Booleans and Operators . . . . . . . . . . . . . . . . . . . . . . . . 103.2.3 Char . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

3.3 Arrays and Control Flow . . . . . . . . . . . . . . . . . . . . . . . . . . . . 11

1

Page 2: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

3.3.1 Revisiting Hello, World . . . . . . . . . . . . . . . . . . . . . . . . 113.4 Array Iteration . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.4.1 the each method . . . . . . . . . . . . . . . . . . . . . . . . . . . . 123.4.2 the for loop . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 12

3.5 Control Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 133.6 List Comprehensions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 143.7 Working with Functions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.7.1 Declaring an Array . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.8 Idioms and Parallelism . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 15

3.8.1 Map . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 153.8.2 Reduce . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 163.8.3 Matrix Multiplication . . . . . . . . . . . . . . . . . . . . . . . . . 163.8.4 Parallelization . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 17

4 Conclusion 17

III Language Reference Manual 18

5 Lexical Convention 18

6 Tokens 186.1 Comments . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196.2 Constants . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 19

6.2.1 Character Literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196.2.2 Number Literal . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 196.2.3 String Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.2.4 Boolean Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.2.5 Nil Constant . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

6.3 Identifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 206.4 Keywords . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 20

7 Syntax Notation 23

8 Meaning of Identifiers 238.1 Basic Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 238.2 Derived Types . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 23

9 Objects and lvalues 24

10 Expressions 2410.1 Primary Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2410.2 Postfix Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

10.2.1 Array References . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2510.2.2 Function call . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 25

2

Page 3: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

10.3 Unary Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2610.4 Arithmetic Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2610.5 Relational Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2710.6 Conditional Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2710.7 Array Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2710.8 List Comprehension . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2810.9 Assignment Expressions . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2810.10Anonymous Function Definitions . . . . . . . . . . . . . . . . . . . . . . . 28

11 External Declarations 2911.1 Type Specifiers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 2911.2 Declarators . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3011.3 Initializers . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 30

12 Statements 3112.1 Expression Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3112.2 Compound Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3112.3 Selection Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3212.4 Iteration Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3212.5 Jump Statements . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 32

13 Scope 33

14 Preprocessing 3314.1 File Inclusion . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 33

15 Built-in Functions 3415.1 Array . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3415.2 Other . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 3415.3 I\O . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 34

16 Grammar 34

IV Project Plan 42

17 Process 4217.1 Weekly Meetings . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4217.2 Github . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4317.3 Google Apps . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4317.4 Testing . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 43

18 Roles and Responsibilities 43

19 Style Sheet 44

3

Page 4: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

20 Timeline and Project Log 4420.1 Contribution Graphs . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 44

V Language Evolution 45

21 Language Changes 45

22 Implementing Parallelism 46

23 Language Implementation Tools 46

VI Translator Architecture 47

24 Block Diagram 48

25 Interfaces 48

26 Implementation 49

VII Development and Run-time Environment 49

27 Development Environment 4927.1 Compiler Build Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . 4927.2 Makefile for the Project . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5027.3 Compilation Process . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5127.4 Editor Plugins . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5127.5 Runtime Environment . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 51

VIII Test Plan 51

28 Testing Methodology 52

29 Test Programs 52

IX Conclusions 53

30 Group Lessons and Advice for Future Groups 5430.1 Starting Early . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 54

31 Individual Lessons 5431.1 Logan - Have a Backup Plan . . . . . . . . . . . . . . . . . . . . . . . . . 5431.2 Sid - Motivating People is Hard . . . . . . . . . . . . . . . . . . . . . . . . 54

4

Page 5: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

31.3 Justin - Don’t Be Afraid to Ask for Help . . . . . . . . . . . . . . . . . . . 5431.4 Andrew - Pair Programming . . . . . . . . . . . . . . . . . . . . . . . . . 5531.5 Nathan - Projects Will Take as Much Time as You Let Them . . . . . . . 55

32 Advice for Future Groups 5532.1 Make Group Deadlines Hard Deadlines . . . . . . . . . . . . . . . . . . . . 55

33 Ideas for Improving the Course 5533.1 Example Timeline . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . . 5533.2 Possible Deadlines for Parts of the Compiler . . . . . . . . . . . . . . . . . 5633.3 More Specifics About Available Tools . . . . . . . . . . . . . . . . . . . . 56

X Appendix 56

5

Page 6: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Part I

Introduction

1 Motivation

Moore’s Law - the trend that the number of transistors that can fit on a chip doubles every2 years - has held true for half a century, driving innovations in all areas of technology.But recently chip manufacturers have moved away from ever-increasing clock speeds, theusual manifestation of Moore’s law, in favor of increasing the number of cores in CPUsto manage heat dissipation.

As consumer and business demands for speed grow, programs must now be writtendifferently in order to take advantage of multiple cores in order to utilize the powerof CPUs. However, the imperative programming paradigm that has prevailed in theprogramming community isn’t conducive to safe, parallel programming. Parallel pro-gramming is difficult and synchronization bugs are notoriously difficult to fix.

Many solutions to this problem have emerged, but each has its drawbacks. We willbriefly survey some of the most prominent existing solutions.

1.1 Locking

Locking controls access to memory that is accessible by several threads of execution.However, this requires that the programmer manually lock regions of code, which isboth tedious and error prone. This can lead to deadlock and race conditions that areextremely hard to debug.

1.2 Task-based Concurrency and the Actor Model

Task-based concurrency, used in languages like Ada and X10, doesn’t expose threadsto the programmer. Simple computations, not threads, are the unit of parallelization.The actor model, present in SmallTalk and Scala, treats different threads as actors thatcommunicate with each other via message passing. Since the actors don’t share state,this is much less error prone than locking since race conditions can be avoided entirely.These models can be elegant, but they still require manual division of labor on the partof the programmer, which introduces errors and overhead, especially when refactoringcode. We seek to avoid imposing this burden on programmers. Furthermore, they areoptimized for task-based parallelism when many of today’s problems demand data-basedparallelism.

1.3 Functional Programming

The functional paradigm minimizes side effects in favor of deterministic functions. Underthis model, functions could easily be parallelized because they execute independently.Functional languages like Haskell boast compilers that produce a great degree of paral-lelization.

6

Page 7: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

However, the functional paradigm is an unfamiliar way of coding for many program-mers and having state is often a preferable way of modeling a program. In particular,object-oriented approaches have proven useful in building large applications. These fac-tors have limited the adoption of purely functional languages.

Additionally, the benefits of parallelizing a purely functional programming languagesis limited because objects must frequently be copied as they are immutable. This meansthat cache hit rates drop significantly, main memory bandwidth becomes a bottleneck,and garbage collection becomes a pain1.

1.4 Implicit Parallelization

Some efforts to write implicitly parallel, imperative languages have been made2. How-ever, state- of-the-art compilers are not yet capable of good implicit parallelization.While some cases of potential parallelization are easy to recognize, many of them diffi-cult or impossible to find with static analysis3.

With this in mind, implicit parallelization may sound daunting, but it is importantto remember that the compiler can miss many potential opportunities for paralleliza-tion without sacrificing performance. Even with a modest ability to find parallelizationopportunities, the performance of parallel programs is still limited by the number ofprocessors, not the number of threads that run simultaneously. In fact, if too manythreads are created, the overhead of thread creation and execution will adversely affectperformance.

2 Introducing DotPar

With familiar C-like syntax, DotPar is a multi-paradigm language that implicitly par-allelizes code with minimal programming effort. By abstracting parallelization, DotParenables programmers to focus on designing and and architecting systems instead of onmicromanaging performance. Additionally, it provides the expressive power of functionallanguages and includes some imperative paradigms derived from C to ease the job of theprogrammer.

2.1 Automatic Parallelization

Based on static analysis of code and user annotations, DotPar can detect a large classof parallelization opportunities, speeding up execution considerably.

2.2 Strong Typing

Keeping DotPar strongly typed provides compile-time check for common mistakes thatshould never make it to production. This also enhances the compilers ability to parallelizethe code.

1http://bit.ly/dotpar12See http://bit.ly/dotpar2 and http://bit.ly/dotpar3 for examples.3http://bit.ly/dotpar4

7

Page 8: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

2.3 First-class Functions and Lexical Closures

By providing first-class functions and closures, DotPar provides a great degree of expres-siveness not possible in a language like Java. However, it is not as restrictive as a purelyfunctional language like Haskell because it provides some imperative constructs as well.

2.4 Robustness and Security

Through implicit parallelization, DotPar removes the risk of race conditions and dead-lock. And, since it runs on the JVM, the code is robust and portable. Furthermore,DotPar raises informative errors that allow the user to debug code efficiently and quickly,reducing development time.

2.5 Memory Management and Garbage Collection

Since DotPar runs on the JVM, garbage collection and memory management are handledautomatically, reducing programmer overhead.

Part II

Language Tutorial

Parallel algorithms tend to be described as operations on collections of values. Forinstance, “find the minimum neighbor for each vertex”, or “sum each row of a matrix”are data-centric operations that can be done in parallel. 4 This ability to operate inparallel over sets of data is often referred to as data-parallelism. It is important thatthis data-parallelism can be exploited to its full potential. We want to be able to runparallel functions in parallel to maximize the efficiency of the program. Thus, we seekto provide an effective nested data-parallel language.

The following tutorial will give a quick tour of the basics of the language and build upto defining complex array manipulations to enable you to start writing useful programsas soon as possible. To do that, we’ll concentrate on the basics: types, arithmeticexpressions, arrays, control flow, functions, list comprehensions, and some nifty built-infunctions.

3 Overview

3.1 Hello, World

DotPar was designed with simplicity and power in mind. To illustrate this, let’s getstarted with a traditional “Hello, World” program that prints the words “Hello, World”to stdout.

4http://www.cs.cmu.edu/ scandal/cacm/node4.html

8

Page 9: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

func main:void()

{

println("Hello, world!");

}

Just like in C and Java, the DotPar execution begins with a function named main.Thus, every program must contain a main function. In this main method, we use thebuilt-in function print to print the string “Hello, world”. A string is an array ofcharacters. We will explain exactly what functions, strings, and chars are later. Butfirst, let’s get this program running.

To compile and run this program there are three steps (assuming a UNIX-like system).First, we should save the function in a file called helloWorld.par. DotPar programsare stored in files that have the extension .par. Next, we run a compilation script onthe program file, like so:

scripts/dotparc.sh helloWorld.par

dotparc can accept multiple files. So, to compile all the par files in a directory, youwould run:

dotparc *.par

If the program residing in the file has no syntax errors and is a complete DotParprogram then the compiler will produce an executable file:

out.parc

If there are errors during compilation those will be printed to your console. In thisexample, running out.parc in the command line with:

./out.parc

will produce the output:

Hello, World

We’ll now walk you through some key features of the language. As you go along, trywriting and compiling these programs yourself.

3.2 Types And Arithmetic Expressions

DotPar contains the concept of types which are used to define a variable before its use.The definition of a variable will assign a store address for the variable and define thetype of data that will be held at that address. DotPar only three contains basic types:number, boolean, and char.

9

Page 10: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

3.2.1 Numbers and Basic Arithmetic Operators

All numbers in DotPar are double-precision 64-bit floating point numbers. This basictype is referred to as number. This simplicity allows for computations to be straightfor-ward while maintaining precision.

Our next program will illustrate the use of numbers and some basic arithmetic ex-pressions that can be used to manipulate numbers.

func main:void()

{

number a = 1;

number b = 2;

println(b);

println(a / b);

number c = a + b;

println(c);

}

Not that the output of the println statement is different from what many otherlanguages would produce. The statement will not return 2, but rather 2.0 becauseall numbers are doubles. Similarly, the last two print statements output .5 and 3.0,respectively.

Other basic arithmetic operators include - , *, and / which represent subtraction,multiplication, and division, respectively. There is also a remainder operator, \%.

3.2.2 Booleans and Operators

The boolean data type has only two possible values, true and false. This uses sim-ple flags that track true/false conditions. While this data type represents one bit ofinformation, but it is treated internally as a 32-bit entity since DotPar runs on the JVM.

The following program will give an elementary example of the use of booleans andtheir uses.

func main:void()

{

boolean b = true;

println(!b);

boolean c = false;

println(b && c);

println(c || b);

}

Since a boolean tracks a single condition, each print statement will output either trueor false. The symbol is the NOT unary operator and evaluates to the complement of itsargument. Since b is true, the first print statement will be “false”. The && operatorsignifies the binary operator AND which returns the conjunction of its two arguments.

10

Page 11: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

So the second print statement will return false, since true AND false evaluates to false.The || operator signifies the binary operator OR which returns the disjunction of its twooperators. Since false true evaluates to true, the last print statement will print true.

3.2.3 Char

The char data type is a single 16-bit Unicode character. Consider the following example:

func main:void()

{

char c = 'a';println(c);

}

The above will print out a. In practice, chars are most useful when used as insequences to form strings. This provides a nice transition to DotPar’s arrays.

3.3 Arrays and Control Flow

There are times when writing programs when you may want to store multiple items ofsame type together. Arrays provide this functionality. They are contiguous bytes inmemory, which facilitates easy searching and manipulation of lists of elements. Arraysare typed; an array can contain numbers, booleans, chars, or other arrays. Let’s takea look at some examples.

3.3.1 Revisiting Hello, World

func main:void()

{

char[] helloWorld = "Hello, World";

println(helloWorld);

}

The first program we learned to printed “Hello, World.” In this equivalent program,we explicitly create an array of type char in the first line of main. An arrays of charsis also often called a string. Arrays are very fundamental building blocks in DotPar.Rather than have many built-in types, DotPar has just a few types and gives users thepower to create more complicated types. Rather than having a built-in string type,DotPar treats strings as an arrays of characters. So, the first line creates a characterarray with each element being a character in the statement, “Hello, World”.

Arrays are where DotPar leverages its power as it can manipulate large sets of dataquickly by manipulating arrays, iterating through arrays, and applying functions toarrays.

11

Page 12: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

3.4 Array Iteration

3.4.1 the each method

Our next example program creates an array of type number and demonstrates a methodof iterating through an array. The first line creates an array arr of size 10 and populatesit with the numbers 1-10. Notice at the end of the line there is a //. This is a single-linecomment. // will cause the compiler to ignore the rest of the line. Comments are auseful to markup text to explain subtleties of the code. The second line demonstratesthe use of the built-in each function, which is used to iterate over an array element byelement. For each loops contain all of its statements within curly brackets. This for eachiterates through every number in arr and calls the function print on every element. Thisprints a list of the elements in arr to standard out.

func main:void()

{

number[] arr = [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]; //create the array

each(arr, func:void(number n) { println(n); });

}

3.4.2 the for loop

Another way to write the program above would be to use a for loop, which can be usedto iterated over the indices of an array by using a counter.

func main:void()

{

number[] arr = [1,2,3,4,5,6,7,8,9,10];

number i;

for(i = 0; i < len(arr); i = i + 1) {

println(arr[i]);

}

}

The variable i of type number is created and is initialized to 0. As long as i isless than the length of the array, the program prints arr[i] and increments i len isan example of a built-in function that returns a number whose value is the length ofthe array in its argument. In for loop we are again calling the print function andare passing in arr[i], which is the value in the array at index i. The for loop finallyincrements i by 1.

Arrays can be extended to arbitrarily many dimensions. For example, we create amatrix in the example below.

func main:void()

{

number[][] matrix = [ [1,2,3], [4,5,6], [7,8,9] ];

12

Page 13: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

println(matrix);

}

Here we are creating a two-dimensional 3x3 array with the numbers 1 through 9. Wehave a set of [ ] for each dimension we want create. We can fill this array with nestedbrackets by defining each row within brackets. Try running this program and see whatit prints!

3.5 Control Statements

Next, we’ll look at conditional statements, random number generation, and the fill

statement in DotPar.Let’s take a look at our next example program.

func main:void()

{

/*

* This program creates an array of length 10 and

* fills it with random numbers in the range [0-100).

* It then prints out each element with

* a message declaring it even or odd.

*/

number[] arr;

fill(arr, func:number(number index) { return rand(100); }, 10);

char[] even = "is even";

char[] odd = "is odd";

each(arr, func:void(number element) {

if (element % 2 == 0) {

println(element + " " + even);

} else {

println(element + " " + odd);

}

});

}

First, notice that the first few lines of the function demonstrate that /* ... */ canbe used to comment out an entire block of text.

Next, we create an number array arr. The next line calls the built-in functionfill(function, number) which takes in an array and a function as arguments andinserts values into the array according to the passed in function. In this case we arepassing in the rand(number) function. rand generates random numbers in the range [0,cap). Afterwards, we create two character arrays that contain the text “is even” and “is odd” which will be used later.

13

Page 14: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Next we have a call to our old friend, the each function, that iterates through eachelement in the array. Within the body of the each function we have our first exampleof if statements in DotPar. They follow the form:

if (conditional) {

// code

} elif (conditional) {

// code

} else {

// code

}

There must be exactly one if clause, any number of elif clauses, and no more thanone else clause.

The parenthetical after the if is the condition that must be met in order to run thecode within the braces. If this condition is true, the code with the braces of the else isrun. The conditional we test for in this example is element \% 2 = 0=. element is thevalue in the array, the \% is the arithmetic operation for remainer, the

==

print the number followed by the character sequence specifying it is even and within theelse we print the number followed by the character sequence specifying it is odd.

3.6 List Comprehensions

As a more complicated example, you may want to write something like:

number[] foo = [1,2,3,4,5];

number[] squares;

number i;

for(i = 0; i < len(squares) ; i = i + 1){

if (squares[i] % 2 == 0) {

squares[i] = exp(foo[i], 2);

}

}

This function creates an array with the square of the even numbers in the originalarray. But this is rather verbose, which seems unnecessary for a conceptually simpletask like this. DotPar provides an easy way to express this logic, borrowing the ideaof a list comprehension from Python and Haskell. We will illustrate the power of listcomprehensions by replacing the previous program fragment in one line:

number[] squares = [x * x for number x in [1, 2, 3, 4, 5] if (x % 2 == 0)];

You can also use an expanded notation for using n arrays, such as

number[] foo = [x * y for number x, number y in [1, 3, 9], [1, 2, 3] if (x != 1)];

14

Page 15: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

3.7 Working with Functions

In DotPar, functions are first-class, which means they can be assigned to variables orpassed to other functions as arguments. A function provides a convenient way to encap-sulate some computation, which can then be used without stressing its implementation.All functions are declared with the func reserved word, and can be nested within eachother.

A function definition has the form:

func function-name : return-type(optional parameter declarations)

{

declarations and statements;

return return-val;

}

3.7.1 Declaring an Array

Next, let’s fill a create a matrix of integers. This example will demonstrate how to passfunctions as parameters.

func main: void()

{

// This creates an array with 10 rows and 20 columns. The values default to 0.

number[10][20] b;

}

3.8 Idioms and Parallelism

Our next sample programs will contain more complex array manipulation and introducesome useful functions for arrays. These functions aren’t common in imperative languages,and they exhibit some nice properties of DotPar.

3.8.1 Map

map(array, function) calls function(item) for each of the array’s items and returnsan array of the return values. For example, let’s compute some cubes:

func main:void(){

func cube:number (number value, number index){

return value*value*value;

}

number[] a = [1, 2, 3, 4, 5];

map(a, cube);

}

Note that this example could have been computed more succinctly with a list com-prehension. However, in some cases, you may find a map(array,function) to be clearer.

15

Page 16: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

3.8.2 Reduce

reduce(array, function, original) returns a single value constructed by applyingthe reducing function repeatedly to the reduced value so far and each element of thearray. For instance, if you wanted to add all the elements of a numerical array, one coulddefine the function sum(number a, number b), and apply sum to the array.

Conceptually, this takes the first two elements of the array and apply sum to them.This returns the sum of the elements, our first result. Then, we take the third element ofthe array, and apply sum to our first result and the third element, obtaining our secondresult. We continue in this manner, until we have one result and no more elements toreduce.

However, in the case of For a concrete example, let’s compute the sum of the numbers1 to 10000:

func main:void(){

func sum:number (number a, number b) {

return a + b;

}

func filler:number(number index) {

return index + 1;

}

number[] x = fill(x, filler, 10000);

// 0 is the starting value, we do this so that we can return 0

// if the array is empty.

// Here we are filling the array with the values 0 - 10000 for

// use in the suming function

println(reduce(x, sum, 0));

}

3.8.3 Matrix Multiplication

In this example we introduce nested functions and explore how working with functionscan create clearer and better code. We’ll also take a look at the feature that gives DotParits name: implicit parallelism.

func matrixMultiply:number[][](number[][] a, number[][] b) {

func transpose: number[][](number[][] a) {

number[len(a[0])][len(a)] transposed;

number i;

number j;

for (i < 0; i < len(a); i = i + 1){

for (j < 0; j < len(a[0]); j = j+ 1){

transposed[j][i] = a[i][j];

16

Page 17: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

}

}

return transposed;

}

func sum:number(number a, number b) {

return a + b;

}

func dotprod:number(number[] a, number[] b) {

return reduce([x * y for number x, number y in a, b], sum );

}

if (len(a) != len(b[0])) {

return nil; // one can always return nil in place of a real value

}

return [[dotprod(row, col) for number[] col in transpose(b)] for number[] row in a];

}

The function transpose returns a transpose of the input matrix. The functionmatrixMultiply takes in arguments which are two two-dimensional arrays, a and b.The function dotprod accepts two one-dimensional arrays and calls the reduce function,which calls the sum function on each x, y pair which are elements in arrays a and b. Thefinal line returns a statement that pairs every row in a with every column in b and callsthe function dotprod.

3.8.4 Parallelization

These examples demonstrate the power of DotPar’s implicit parallelization.The map and reduce functions can be parallelized, given certain restrictions. If the

functions only access variables passed to them and do not mutate outside state, a mapcan be parallelized. A function passed to a reduce must also be associative. For instance,the computation done by map and reduce can be done in O(n/m) time, where n is thesize of the array and m the number of processors. In the transpose function from theprevious section, each cell in the new matrix can be computed independently of theother, so a DotPar implementation can parallelize this multiplication if it is appropriategiven the computer’s architecture and the nature of the computation.

4 Conclusion

In this tutorial we have covered compiling your programs and writing basic assignmentand logical statements all the way through to more complicated features like map, reduce,list comprehensions, and nested functions. With all of the these tools now available, you

17

Page 18: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

should now be able to unleash the performance power that DotPar provides for datamanipulation.

Part III

Language Reference Manual

DotPar is a flexible language that provides implicit nested data parallelism with power-ful first-class functions while providing a familiar imperative interface. The focus is onparallel performance for arrays and loops, since these are often the source of performancebottlenecks.

Previous implementations of nested data-parallel languages have relied on the use ofpurely functional paradigms which can make the learning-curve steep and difficult tothose who just want to pick up a new tool. DotPar provides a multi-paradigm nesteddata-parallel language with a friendly imperative style in addition to powerful functions.Its implicit parallelism focuses on the performance of arrays and loops, but its imple-mentation requirements are flexible enough for other forms of parallelism as well.

5 Lexical Convention

A program consists of zero or more input statements, and one or more lines stored infiles. These are translated in several phases, which are described in Section 13. Thefirst phases do low-level lexical transformations, which reduce the program to a series oftokens.

lines:imports opt external declarationlines external declaration

imports:imports import declarationimport declaration

import declarations will be explained in detail in Section 11.

6 Tokens

DotPar has 5 token classes: identifiers, keywords, literals, operators, and other separa-tors. All white space, collectively including blanks, horizontal and vertical tabs, newlines,and comments as described below are ignored except as they separate certain tokens.Sometimes white space is required to separate otherwise adjacent identifiers, keywords,and literals.

18

Page 19: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

6.1 Comments

The characters /* introduce a block comment, which terminates with the characters */.In addition, // adds line comments which converts everything that follows to a commentuntil the end of the line. Comments do not nest, and they do not occur within string orcharacter literals.

6.2 Constants

Constants are character, number, or string literals, as well as true, false, or nil.constant

• CHAR LITERAL

• NUM LITERAL

• STRING LITERAL

• TRUE

• FALSE

• NIL

6.2.1 Character Literal

A character constant specifies a single ascii character surrounded by single quotes, suchas ‘a’. If one is so inclined, he or she can include such control characters as newlines byusing traditional backslash delimited escape codes. For instance, a newline is ‘\n’, and‘\0’ is the null character. A notable exception is the backslash itself, which is merely ‘\’.

A char literal has the type char and is initialized with the given ascii character. Thebehavior of a program that attempts to alter a char literal is undefined.

6.2.2 Number Literal

There are two ways to represent literal numbers with DotPar: the first is an integralformat, without a decimal point, and the second is a floating point representation, in-cluding a decimal point.

The integral representation is merely a series of digits without spaces between them, like31 or 42. Integral number literals are only available for base 10.

The floating point representation is a series of digits with a period embedded withinor prepended to the front, and without spaces anywhere. Examples include 3.14159, or.11235, but not 125. with a trailing period.

The floating point representation is restricted to base 10, and there is no support for

19

Page 20: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

scientific notation.

A number literal has a type number and is initialized with the given value: if the givenliteral is too large, the behavior of the program will be undefined.

6.2.3 String Constant

A string literal is a sequence of ascii characters surrounded by double quotes as in “. . .”. A string has type ‘array of characters’ and is initialized with the given characters. Onemay include traditional backslash escape codes, similar to character constants explainedabove. The behavior of a program that attempts to alter a string literal is undefined.

6.2.4 Boolean Constant

There are only two values for a boolean constant, true or false. Their literal value isrespectively represented as true and false, not decorated by any special characters.

6.2.5 Nil Constant

nil is a special literal that can stand in for any other type of value. nil evaluates tofalse. nil and false are the only values in the language that evaluate to false.

6.3 Identifiers

An identifier is a sequence of letters and digits, and underscores. The first charactermust be a letter. Identifiers are case-sensitive and may have any length.

6.4 Keywords

The following identifiers are reserved for use as keywords. This include built-in functions,Scala keywords, and keywords reserved for future use in the language.

• abstract

• case

• catch

• class

• def

• do

• else

• extends

• false

20

Page 21: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

• final

• finally

• for

• forSome

• if

• implicit

• import

• lazy

• match

• new

• null

• object

• override

• package

• private

• protected

• return

• sealed

• super

• this

• throw

• trait

• try

• true

• type

• val

• var

21

Page 22: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

• while

• with

• yield

• cat

• each

• fill

• filter

• len

• map

• reduce

• zip

• acos

• asin

• atan

• cos

• exp

• ln

• log

• sin

• sqrt

• tan

• ceil

• floor

• trunc

• round

• rand

• print

22

Page 23: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

• println

• printerr

• read

• readln

7 Syntax Notation

The syntax in this manual has categories written in italics and literals and charactersin plain font. Optional terminal and nonterminal symbols have the subscript “opt,” likebelow:

{expression opt}*An non-terminal with the name foo opt will either go to “the empty string” or foo.

Similarly, lists follow the format of imports above. To avoid repetition, rules that followthis rigid structure will not be explained in-depth. However, they will be included in thecomplete grammar at the end of this reference manual.

8 Meaning of Identifiers

Identifiers are simply the names given to functions and variables. A variable is used tostore data and points to a specific location. The interpretation of the data is based onthe variable’s type. Identifiers are limited to the scope within which they are definedand are only accessible within that scope. This can be only within a specific function oran entire program.All variables declared are initialized to a default value: number = 0, boolean = false,char = “”, number[] = [].

8.1 Basic Types

DotPar contains three basic types: number, boolean and char. Variables of type numberare 64-bit double precision floating point numbers. Boolean variables have only twopossible values: true and false. They are internally treated as a 32-bit entities. Charvariables can store any member of the set of characters. Every character has its valueas equivalent to the integer code for the character.

8.2 Derived Types

These include arrays and functions which can be constructed from the basic types. De-rived types include:

• arrays of elements of a given type

• functions accepting variables of certain types, and returning variables of a giventype

23

Page 24: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

There are infinitely many derived types. Type declaration syntax are described in moredetail below.

9 Objects and lvalues

A variable is a named region of storage and an lvalue is an expression referring to thatvariable. For example, an lvalue expression can be an identifier with a specified type.Like in C, the term “lvalue” originates from the term left-value which indicates that itwas on the left side of the assignment operator. Each operator listed below can expectlvalue operands and yields an lvalue.

10 Expressions

The precedence of expression operators is the same as the order of the major subsec-tions outlined below in this section, with highest precedence first. In all subsections,precedence follows C-style conventions. Left- or right-associativity is specified in eachsubsection for the operators discussed therein. The grammar given in Section 13 incor-porates the precedence and associativity of the operators.

The precedence and associativity of the operators is fully specified, but the order ofevaluation of expressions is undefined, even if the subexpressions involve side effects.However, each operator combines the values it produces by its operands in a way that iscompatible with the parsing of the containing expression in which it appears. The han-dling of overflow, divide check, and other exceptions in expression evaluation is undefinedby the language spec.

10.1 Primary Expressions

Primary expressions are identifiers, constants, string literals, or expressions in parenthe-ses.

primary expression:IDENTIFIERconstant(expression)

An identifier is a primary expression. Its type is specified by its declaration, which mustoccur earlier in the program.

A constant is a primary expression. Its type depends on its form as discussed in section2.2

A string literal is a primary expression. It is actually converted to an array of characters,but string literals are included as part of the grammar for programmer convenience.

24

Page 25: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

A parenthesized expression is a primary expression whose type and value are identicalto those of the unadorned expression.

10.2 Postfix Expressions

The operators in postfix expressions group left to right.

postfix expression:primary expressionpostfix expression [ expression ]postfix expression ( argument expression list pt)

10.2.1 Array References

A postfix expression followed by an expression in square brackets is a postfix expressiondenoting a subscripted array reference. The postfix expression must evaluate to an arrayand the expression must evaluate to a type number.

10.2.2 Function call

A postfix expression followed by the function designator, followed by parentheses contain-ing a possibly empty, comma-separated list of assignment expressions, which constitutethe arguments to the given function.

The term argument is used for an expression passed by a function call; the term param-eter is used for an input object (or its identifier) received by a function definition, ordescribed in function declaration. The scope is lexical.

Arguments are passed by value. A function may change the values of its parameterobjects, which are copies of the argument expressions, but these changes cannot affectthe values of the arguments.

However, array references are passed by value, so a function may modify the contents ofan array passed to it.

Type agreement is strict between the parameters and arguments. Thus, DotPar isstrongly typed. By some definitions it is not statically typed because it is possible to geta type error at runtime. Because DotPar does not support overloading or generics, thereis no easy way to support a function like println which accepts an arbitrary type. Thus,DotPar compilers can accept a more flexible, internally used Any type. The same is truefor functions like map and reduce. This gives DotPar great flexibility. Unfortunately, italso makes it possible for type errors to occur at run time.

The order of evaluation of arguments is unspecified. In addition, the arguments and their

25

Page 26: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

side effects need not be fully evaluated before the function is entered if the compiler canguarantee that this does not affect the correctness of the program.

10.3 Unary Expressions

Expressions with unary operators group right-to-left.

unary experession:postfix expressionNOT unary expressionSUB unary expression

The NOT unary expresssion refers to the ! operator. The operand of the ! operatormust have a boolean type, and the result is true if the value of its operand evaluates tofalse, and false otherwise. The type of the result is boolean.

The SUB unary expression refers to the - operator. The operand of the - operator musthave a number type, and the result is the negation of the value of its operand. The typeof the result is number.

10.4 Arithmetic Expressions

Arithmetic Expressions are grouped left to right, and include: %, /, *, + , and -. Alloperators return number types.

arithmetic expression:unary expressionarithmetic expression REM arithmetic expressionarithmetic expression DIV arithmetic expressionarithmetic expression MULT arithmetic expressionarithmetic expression ADD arithmetic expressionarithmetic expression SUB arithmetic expression

REM refers to the % operator, the remainder which refers to the remaining value afterquotient the first operand by the second. Note that the remainder of a negative valuewill be negative.

26

Page 27: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

10.5 Relational Expressions

The relational operators group left-to-right.

relational expression:arithmetic expressionrelational expression GEQ relational expressionrelational expression GT relational expressionrelational expression LT relational expressionrelational expression LEQ relational expressionrelational expression EQ relational expressionrelational expression NEQ relational expression

The GEQ refers to >= operators(greater or equal), GT to >(greater), LT to <(lessthan), LEQ to <= (less than or equal), EQ to == (equality), and NEQ to != (notequality). All yield boolean values. The usual arithmetic conversions are performed onarithmetic operands. GEQ, GT, LEQ, and LT have higher precedence than EQ andNEQ.

10.6 Conditional Expressions

The conditional operators group left-to-right and include && and ||.

conditional expression:relational expressionconditional expression OR conditional expressionconditional expression AND conditional expression

OR refers to the ||operator which performs a logical OR on its two operands which mustbe of type boolean. The result is of type boolean.

AND refers to the && operator which performs a logical AND on its two operands whichmust be of type boolean. The result is of type boolean.

10.7 Array Expressions

Array Expressions are conditional statements, or list comprehnesionsand initializer list optenclosed in square brackets ‘[’ ,‘]’.

array expression:conditional expression[ list comprehension ][ initializer list opt ]

An initializer list is used in the creation of array literals.

27

Page 28: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

10.8 List Comprehension

*List comprehensions are used to succinctly create an array. They are equivalent inpower to maps and filters, but are often a more convenient syntax to use. Although thelist comprehension may be parallelized during its execution, the ordering of the resultingarray is deterministic.

list comprehension:array expression FOR paren parameter list opt IN array expression if comp optarray expression FOR paren parameter list opt IN paren multi array expression list opt

if comp opt

The list comprehension syntax and behavior is very similar to Python’s.

10.9 Assignment Expressions

There is one assignment operator =, and it is not used more than once per statement.

assignment expression:array expressionanonymous function definitionpostfix expression ASSIGN array expressionpostfix expression ASSIGN function definitionpostfix expression ASSIGN anonymous function definition

The assignment operator requires an lvalue as a left operand, and the lvalue must bemutable.

The assignment operator assigns array expression, function definition and anonymous function definitionto postfix expressions.

10.10 Anonymous Function Definitions

Anonymous function definitions can be used in assignments. They are identical to regularfunction definitions, except they lack an identifier.

anonymous function definition:FUNC: type specifier (parameter list opt) compound statement

28

Page 29: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

11 External Declarations

External declarations form the basic building blocks of lines of code.

external declaration:function definitiondeclaration

The two top-level declarations are function definitions and declarations. Function defi-nitions are defined as:

function definition:FUNC IDENTIFIER: type specifier (parameter list opt) compound statement)

compound statement

Unlike C, functions the return type of a function is defined after the name. With po-tentially complicated return types, having the type come after the name makes readingthe code easier. They also have the func keyword so that function syntax for types,definitions, and assignments.

Declarations inform the interpretation given to each identifier. Implementations decidewhen to reserve storage space associated with the identifier. Declarations have the form

declaration:type specifier declarator;type specifier declarator ASSIGN initializer;

where ASSIGN is =. A declaration must have one and only one declarator.

11.1 Type Specifiers

A type specifier is either a basic or derived type, examples of which are:

• number

• char[]

• func:number[](char[])

* At most one type-specifier may be given in a declaration. Type specifiers have theform:

29

Page 30: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

type specifier:type specifier [arithmetic expression]type specifier [ ]basic typeVOIDfunc specifier

func specifier:func: type specifier (type list)func: type specifier (parameter list opt)

basic type:NUMBERCHARBOOLEAN

11.2 Declarators

Declarators have the syntax:

declarator:IDENTIFIER(declarator)

11.3 Initializers

* initializer:array expressionanonymous function definition

As some example types, we can see that array types include number[] and number[][].For example array can be declared initialized as: number[] a = [1, 2];

An example function declaration is func:number[](number){ } foo. This function returnsan array of numbers, and accepts a number as an argument, and is named foo. Thissyntax allows us to concisely declare variables as functions, which is important for alanguage with first-class functions.

Note that we could have given a name to the number parameter if we chose to using aparameter list, which is a series of parameter declarations:

parameter declaration:type specifier declarator

30

Page 31: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Although this identifier would not be used, it may be desirable to name parameter theso that the declaration is self-documenting.

12 Statements

Statements are executed sequentially and have no value. The types of statements inDotPar are listed below:

statement:expression statementcompound statementselection statementiteration statementjump statement

12.1 Expression Statements

Most statements are expressions of the form

expression-statement:expression opt;

These statements are mostly assignments and function calls. The side effects of anexpression need not be computed before the execution of the following statement if thecompiler can guarantee that this does not affect correctness. This is different from otherlanguages like C.

12.2 Compound Statements

Compound statements are multiple statements executed when one is expected. Forexample, the body of a function is a compound statement. In DotPar, if statements andfor loops also require compound statements, even if they have single line bodies.

compound statement:statement list opt

statement list :statement list opt statementstatement list opt declarationstatement list opt function definition

Identifiers can only be declared once within the scope of a function and cannot be thesame as the identifiers passed into a function.

31

Page 32: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

12.3 Selection Statements

Selection statements allow for the choice between multiple control flows.

selection statement:if elifs opt else opt

if:IF ‘(’ expression ‘)’ compound statement

else opt:else

else:ELSE compound statement

elifs opt:elifs

elifs:ELIF ‘(’ expression ‘)’ compound statementelifs ELIF ‘(’ expression ‘)’ compound statement

Note that IF, ELSE, and ELIF, are used to distinguish the terminal if, else, and elifkeywords from the if, else, and elif non-terminals.

The expression within the if statement must evaluate to either true or false. When it istrue the substatement is executed. With an if else statement the else substatement isexecuted when the expression evaluates to false. Every else is connected with the first ifstatement above it that is unconnected to an else.

12.4 Iteration Statements

Iteration statements specify looping.

iteration-statement:for (expression opt; expression opt; expression opt) compound statement

The first expression is evaluated once and marks the start of the loop. Note that a newvariable cannot be declared as part of this operation. The second expression is coercedto a boolean; if false, it terminates the loop. The third expression is evaluated after eachiteration so it specifies the re-initialization for the loop.

Again, note that curly braces must be used.

12.5 Jump Statements

Jump statements, once they are reached, always transfer control regardless of any con-dition.

32

Page 33: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

jump statement:RETURN expression opt ‘;’

A function provides the value back to its caller via the return statement. It returns thevalue of the expression when it is evaluated. The expression once evaluated is interpretedas the type specified in the function declaration.

13 Scope

A program can be compiled from several .par files. Scoping is lexical. Thus, identifiers inDotPar have one top-level namespace in which variables and functions are defined, witha shared namespace between the two. Imports made in a file are accessible anywhereelse in that file. An identifier declared outside of any function can be accessed anywherein the program. An identifier declared in a block is available anywhere within the block,including inner functions. Note that this means that DotPar has closures. That is, ifan inner function is returned from a function, it maintains access to the variables of theouter function.

14 Preprocessing

A preprocessor performs the inclusion of named files. Lines beginning with importcommunicate with the preprocessor. The syntax of these lines is independent of the restof the language; they must only appear at the beginning of the file.

14.1 File Inclusion

Imports have the syntax:

imports:imports import declarationimport declaration

import declarations:IMPORT IDENTIFIER

A control line of the formimport module;

means this line will be replaced by the contents of the file named filename, with extension.par. The named file is searched for in the current directory, and each file is importedonly once per program. Import statements can be nested, so every file can include them.

33

Page 34: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

15 Built-in Functions

The language includes several built-in functions that provide basic building blocks formore complex user-defined functions. These appear to the user as regular library func-tions. They can be shadowed by user functions to avoid having an unreasonable amountof reserved words. Many of these functions are self-explanatory and, as such, their formalprototypes are not given. Further explanation is present where warranted.

Finally, note that many useful functions, such as string containment, are not included.These are more appropriate as libraries than language features.

15.1 Array

cat(arr, other\_arr) // concatenate two arrays and return the result

each(arr, fn(element)) // iterate through an array

fill(fn(index), len) // fill an array using a function.

filter(arr, fn(element)) // selects certain elements from an

// array; the filter function returns a boolean

len(arr) // array length

map(arr, fn(element)) //runs function fn on each element of the array

reduce(arr, fn, base) // reduce operation. The last argument is the

// initial value.

15.2 Other

rand(n)

15.3 I\O

print(s)

println(s)

printerr(s)

readln()

16 Grammar

%{ open Ast

open Printf %}

%token IMPORT

%token BOOLEAN CHAR FUNC NUMBER VOID

%token IF ELSE ELIF FOR IN RETURN

34

Page 35: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

%token SEMI COMMA COLON LPAREN RPAREN LBRACE RBRACE LBRACK RBRACK

%token AND OR NOT GT LT GEQ LEQ EQ NEQ ASSIGN

%token ADD SUB MULT DIV REM

%token <string> STRING_LITERAL

%token <char> CHAR_LITERAL

%token <float> NUM_LITERAL

%token <string> IDENTIFIER

%token <bool> TRUE

%token <bool> FALSE

%token NIL

%token EOF

%left OR

%left AND

%left EQ NEQ

%left GT GEQ LT LEQ

%left ADD SUB

%left MULT DIV REM

%right UMINUS

%start program

%type <Ast.program> program

%%

program:

| lines { Program (fst($1), snd($1), (make_global_table None);) }

lines:

| imports_opt external_declaration { ($1, [$2]) }

| lines external_declaration { (fst($1), $2 :: snd($1)) }

imports_opt:

| imports { $1 }

| /* empty */ { [] }

imports:

35

Page 36: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

| imports import_declaration { $2 :: $1 }

| import_declaration { [$1] }

import_declaration:

| IMPORT IDENTIFIER SEMI { Import $2 }

constant:

| CHAR_LITERAL { Char_literal $1 }

| NUM_LITERAL { Number_literal $1 }

| STRING_LITERAL { String_literal $1 }

| TRUE { Boolean_literal $1 }

| FALSE { Boolean_literal $1 }

| NIL { Nil_literal }

argument_expression_list:

| assignment_expression { $1 :: [] }

| argument_expression_list COMMA assignment_expression { $3 :: $1 }

| /* empty */ { [] }

postfix_expression:

| primary_expression { $1 }

| postfix_expression LBRACK expression RBRACK { Array_access ($1, $3) }

| postfix_expression LPAREN argument_expression_list RPAREN

{ Function_call ($1, $3) }

unary_expression:

| postfix_expression { $1 }

| NOT unary_expression { Unop(Not, $2) }

| SUB unary_expression %prec UMINUS { Unop(Neg, $2) }

arithmetic_expression:

| unary_expression { $1 }

| arithmetic_expression REM arithmetic_expression { Binop ($1,Mod,$3) }

| arithmetic_expression DIV arithmetic_expression { Binop ($1,Div,$3) }

| arithmetic_expression MULT arithmetic_expression { Binop ($1,Mult,$3) }

| arithmetic_expression ADD arithmetic_expression { Binop ($1,Add,$3) }

| arithmetic_expression SUB arithmetic_expression { Binop ($1,Sub,$3) }

relational_expression:

| arithmetic_expression { $1 }

| relational_expression GEQ relational_expression { Binop ($1,Geq,$3) }

| relational_expression GT relational_expression { Binop ($1,Gt,$3) }

| relational_expression LT relational_expression { Binop ($1,Lt,$3) }

| relational_expression LEQ relational_expression { Binop ($1,Leq,$3) }

36

Page 37: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

| relational_expression EQ relational_expression { Binop ($1,Eq,$3) }

| relational_expression NEQ relational_expression { Binop ($1,Neq,$3) }

conditional_expression:

| relational_expression { $1 }

| conditional_expression OR conditional_expression { Binop ($1,Or,$3) }

| conditional_expression AND conditional_expression { Binop ($1,And,$3) }

opt_paren_multi_array_expression_list:

| LPAREN multi_array_expression_list RPAREN { $2 }

| multi_array_expression_list { $1 }

/* Has at least two array_expressions */

multi_array_expression_list:

| array_expression COMMA array_expression { $1 :: $3 :: [] }

| array_expression COMMA array_expression COMMA array_expression_list

{ $1 :: $3 :: $5 } /* !! check this, might have to reverse $5 */

array_expression_list:

| array_expression { [$1] }

| array_expression_list COMMA array_expression { $3 :: $1 }

array_expression:

| conditional_expression { $1 }

| LBRACK list_comprehension RBRACK { $2 }

| LBRACK initer_list RBRACK { Array_literal $2 }

list_comprehension:

| array_expression FOR paren_parameter_list_opt IN array_expression if_comp

{ List_comprehension ($1, $3, [$5], $6, (make_symbol_table None);) }

| array_expression FOR paren_parameter_list_opt IN

opt_paren_multi_array_expression_list if_comp

{ List_comprehension ($1, $3, $5, $6, (make_symbol_table None);) }

/* optionally will have parentheses. part of list comprehension */

if_comp:

| IF expression { $2 }

| { Empty_expression }

assignment_expression:

| array_expression { $1 }

| anonymous_function_definition { $1 }

| postfix_expression ASSIGN array_expression { Assignment_expression ($1,$3) }

| postfix_expression ASSIGN function_definition

37

Page 38: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

{ Assignment_expression ($1, Function_expression $3) }

| postfix_expression ASSIGN anonymous_function_definition

{ Assignment_expression ($1, $3) }

expression:

| assignment_expression { $1 }

| { Empty_expression }

primary_expression:

| IDENTIFIER { Variable $1 }

| constant { $1 }

| LPAREN expression RPAREN { $2 }

type_specifier:

| type_specifier LBRACK arithmetic_expression RBRACK

{ Fixed_array_type ($1, $3) }

| type_specifier LBRACK RBRACK { Array_type $1 }

| basic_type { Basic_type $1 }

| VOID { Basic_type Void_type }

| func_specifier { $1 }

func_specifier:

| FUNC COLON type_specifier LPAREN type_list RPAREN

{ Func_type ($3, $5, (ref(make_symbol_table None);)) }

| FUNC COLON type_specifier LPAREN parameter_list RPAREN

{ Func_param_type ($3, $5) }

basic_type:

| NUMBER { Number_type }

| CHAR { Char_type }

| BOOLEAN { Boolean_type }

declaration:

| type_specifier declarator SEMI { Declaration ($1, $2) }

| type_specifier declarator ASSIGN initer SEMI

{ Declaration_expression ($1, $2, $4) }

declarator:

| IDENTIFIER { Variable $1 }

| LPAREN declarator RPAREN { $2 }

type_list:

| type_specifier { [$1] }

| type_list COMMA type_specifier { $3 :: $1 }

38

Page 39: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

parameter_list:

| parameter_declaration { [$1] }

| parameter_list COMMA parameter_declaration { $3 :: $1 }

| { [] }

/* this is a list comprehension thing */

paren_parameter_list_opt:

| LPAREN parameter_list RPAREN { $2 }

| parameter_list { $1 }

parameter_declaration:

| type_specifier declarator { Param ($1, $2) }

initer:

| array_expression { $1 }

| anonymous_function_definition { $1 }

initer_list:

| initer { [$1] }

| initer_list COMMA initer { $3 :: $1 }

| { [] }

expression_statement:

| expression SEMI { Expression $1 }

compound_statement:

| LBRACE statement_list RBRACE { $2 }

/* Allows statements and declarations to be interwoven. */

statement_list :

| statement_list statement { $2 :: $1 }

| statement_list declaration { (Expression $2) :: $1 }

| statement_list function_definition { $2 :: $1 }

| { [] }

selection_statement:

| if_statement elifs_opt else_statement

{ Selection {if_cond=$1.if_cond;

if_body=$1.if_body;

if_sym_tabl=$1.if_sym_tabl;

else_body=$3.else_body;

else_sym_tabl=$3.else_sym_tabl;

elif_conds=$2.elif_conds;

39

Page 40: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

elif_bodies=$2.elif_bodies;

elif_sym_tabl=$2.elif_sym_tabl;

} }

if_statement:

| IF LPAREN expression RPAREN compound_statement

{ {if_cond=$3;

if_body=$5;

if_sym_tabl=(make_symbol_table None);

else_body=[];

else_sym_tabl=(make_symbol_table None);

elif_conds=[];

elif_bodies=[];

elif_sym_tabl=[];

} }

else_statement:

| ELSE compound_statement

{ {if_cond=Nil_literal;

if_body=[];

if_sym_tabl=(make_symbol_table None);

else_body=$2;

else_sym_tabl=(make_symbol_table None);

elif_conds=[];

elif_bodies=[];

elif_sym_tabl=[];

} }

| { {if_cond=Nil_literal;

if_body=[];

if_sym_tabl=(make_symbol_table None);

else_sym_tabl=(make_symbol_table None);

else_body=[];

elif_conds=[];

elif_bodies=[];

elif_sym_tabl=[];

} }

elifs:

| ELIF LPAREN expression RPAREN compound_statement

{ {if_cond=Nil_literal;

if_body=[];

if_sym_tabl=(make_symbol_table None);

else_sym_tabl=(make_symbol_table None);

else_body=[];

40

Page 41: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

elif_conds=[$3];

elif_bodies=[$5];

elif_sym_tabl=[(make_symbol_table None);];

} }

| elifs ELIF LPAREN expression RPAREN compound_statement

{ {if_cond=Nil_literal;

if_body=[];

if_sym_tabl=(make_symbol_table None);

else_sym_tabl=(make_symbol_table None);

else_body=[];

elif_conds=$4 :: $1.elif_conds;

elif_bodies=$6 :: $1.elif_bodies;

elif_sym_tabl=(make_symbol_table None) :: $1.elif_sym_tabl;

} }

elifs_opt:

| elifs { $1 }

| { {if_cond=Nil_literal;

if_body=[];

if_sym_tabl=(make_symbol_table None);

else_sym_tabl=(make_symbol_table None);

else_body=[];

elif_conds=[];

elif_bodies=[];

elif_sym_tabl=[];

} }

iteration_statement:

| FOR LPAREN expression SEMI expression SEMI expression RPAREN

compound_statement

{ Iteration ($3, $5, $7, $9, (make_symbol_table None),

(make_symbol_table None);) }

/* declaration already has a SEMI */

| FOR LPAREN declaration expression SEMI expression RPAREN

compound_statement

{ Iteration ($3, $4, $6, $8, (make_symbol_table None),

(make_symbol_table None);)}

jump_statement:

| RETURN expression SEMI { Jump $2 }

statement:

| expression_statement { $1 }

| compound_statement { Statements $1 }

| selection_statement { $1 }

41

Page 42: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

| iteration_statement { $1 }

| jump_statement { $1 }

anonymous_function_definition:

| FUNC COLON type_specifier LPAREN parameter_list RPAREN compound_statement

{ Anonymous_function ($3, $5, $7, (make_symbol_table None);) }

function_definition:

| FUNC IDENTIFIER COLON type_specifier LPAREN parameter_list RPAREN

compound_statement

{ Function_definition ($2, $4, $6, $8, (make_symbol_table None);) }

/* Top level */

external_declaration:

| function_definition { $1 }

| declaration { Expression $1 }

Part IV

Project Plan

Project Manager - Logan

17 Process

The initial planning of our language took place during in-person meetings where we wereable to discuss and critique ideas quickly and effectively. We explored the differencesbetween an application and a language, which resulted in being to able to eliminate manyof our ideas. We eventually narrowed our scope down to focusing on parallelization.After getting constructive feedback on our white paper we realized that our scope wastoo broad for the amount of time we had to complete the project. We then decidedto narrow this scope down to parallelization around data manipulation, specifically inarrays.

17.1 Weekly Meetings

Through out the course of the project, we have held weekly in-person meets on Fridays.This was an opportunity for the group to check in and make sure everyone was on thesame page. After an hour or so everyone would break up and work on their respectivetasks for that week while still allowing for collaboration.

42

Page 43: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

17.2 Github

GitHub became an invaluable resource to the group as a shared repository and versioncontrol. In addition, we used the GitHub issues and milestones features to set goals forwhen certain parts should be completed. Issues allowed us to post tasks and problemsthat can be assigned to an individual or multiple people. This easily allowed us to seewhat was left to do and what had been accomplished already. This also allowed us tohave a scheduling system that was directly tied to our repository and stay on track.

17.3 Google Apps

While working remotely, we used gmail and gchat extensively for communication. GoogleCalendar and docs were also used for meeting planning and drafting the various requiredpapers throughout the semester.

17.4 Testing

We created tests at each stage of development to ensure that our work was correct andthat we could proceed without causing problems in the next stage.

18 Roles and Responsibilities

The assigned roles for the group are:

• Logan Donovan Project Manager

• Sid Nair Language Guru

• Justin Hines System Architect

• Nathan Hwang - System Integrator

• Andrew Hitti - System Tester

While all of the positions come with specific tasks assigned to them, each memberof the group was able to contribute to the high-level design of the language. From thereeach person became responsible for a specific portion of the project and then receivedhelp from other group member as needed. Nathan worked on the parser and abstractsyntax tree creation. Andrew and Justin built the semantic analysis and code generation.Sid was responsible for parallelization in Scala. Logan wrote the sample programs andserved as the primary coordinator between individuals to help make sure everyone wasworking together. In addition she wrote a good deal of the original type checking system,which was later replaced with a more efficient model and no longer, appears in our code.Despite these roles, all group members participated in a great deal of cross-collaboration.

43

Page 44: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

19 Style Sheet

• Two spaces as indentation

• File names and values use underscores

• Functions use lowercaseUppercase word concatentaion

We used an OCAML Style guide that can be found at:http://caml.inria.fr/resources/doc/guides/guidelines.en.html

A latex style sheet was also created to allow members to function independently. It isavailable on the GitHub repository.

20 Timeline and Project Log

February 22 - White Paper Due

March 21 - Language Reference Manual and Language Tutorial Due

March 30 - Front End Completed

April 13 - Basic Backend

April 27 - Finished Backend + Array Implementation

May 6 - Parallelization

These dates indicate basic completion, but may not include all bug fixes as many werediscovered through further development.We had weekly meetings on Fridays for at least 2 hours starting on February 24th. Thebest way to see the actual process and the work done is to look at the issues log. Thatcan be found at:

https://github.com/sidnair09/DotPar

20.1 Contribution Graphs

Figure 1: A graph showing the commit history of the project over the course of thesemester.

44

Page 45: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Figure 2: Diagram of Contributions to the Project. thenoviceof is Nathan, sidnair09 isSid, JustinHines is Justin and Andrew since they pair programmed together and LoganDis Logan.

Part V

Language Evolution

Language Guru - Sid

21 Language Changes

The LRM changed very little during the course of our implementation. This is becausewe were cautious when writing the LRM and anticipated that features we thought wouldbe simple might end up being difficult to implement. In fact, our LRM did not requireany parallelism in a DotPar compiler implementation. This is because the language couldconceivably be run in an environment in which parallelism is infeasible or undesirable;adding unnecessary restrictions on the compiler writer seemed imprudent.

The biggest issue we we came across was dealing with overloading. For instance,we wanted to overload the map function, but support neither function overloading norinheritance. Thus, we could not support using map on an array of numbers and an arrayof strings, for example. To solve this, we introduced an Any type which can be used forbuilt-in functions. This maintains the paradigms of our type system, but means that itis possible to get a type error after semantic analysis, which violates some definitions ofstatic typing.

Another feature on our wishlist that we wanted to implement but were forced tocut was Java interoperability. Java interoperability would make our language practicalin the real world. Unfortunately, it would require generating an AST from bytecode in

45

Page 46: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

order to do dependency analysis and to do type checking. That said, compiled DotParcode can be used with Java code, just not the other way around.

22 Implementing Parallelism

Parallelization was another challenging feature. Because it is the selling point of thelanguage, we will briefly discuss the implementation details. We decided that our focuswould be data parallelism on for loops and special constructs like the map and reduce

functions and list comprehensions. We did not want to parallelize lines outside of blocks.This would require constructing a full data flow graph and generating code that wouldwait for all dependencies to be computed before executing a piece of code. While notinfeasible, this introduced additional complexity for minimal gain because the most ex-pensive computations tend to be in loops.

Due to time constraints, our analysis of data dependencies is limited to the variablelevel. Thus, if each iteration of a loop from i to n writes to a[i] for some array a,our code will think that there is a dependence with between each iteration of theseloops. This is known as a false dependency. Although every correct dependency analysisprogram provably must generates some false dependencies, many common cases can beoptimized. There are well-known tests for array dependencies such as the Banerjee GCDtest and the Delta test. Because these tests are well-known and our language providesways to naturally parallelize maps and reduces even without this analysis we decidednot to implement these tests.

First, we call a function that has side effects impure. Since we only parallelize maps,reduces, and list comprehensions, the only functions which are impure are ones thatmake assignments to variables outside their immediate scope.

Detecting associativity of functions is more difficult. Detecting it in the general caseis an open problem in the field. Without detecting these properties, however, it becomesimpossible to parallelize certain classes of problems. For instance, to parallelize a reduce,the reduction function must be associative. Fortunately, we are able to detect simpleinstances of associativity, such as a sum or product function.

Thus, though we do miss some opportunities for parallelism, we also never incorrectlydetect an opportunity for parallelism, so DotPar does not introduce race conditions.

23 Language Implementation Tools

To implement the language, we wrote the compiler in OCaml and used Scala as ourtarget language.

We used OCaml because we wanted to learn a functional paradigm. OCaml is alsooften used for writing compilers, so many relevant tools and resources are available. Weused ocamllex and ocamlyacc for lexing and parsing, respectively. The tools are similarto lex and yacc, but have slightly different syntax that is more fitting for OCaml.

We compiled down to Scala for several reasons. First, it gives us first-class functionsand closures for free. If we compiled down to something like Java, implementing closures

46

Page 47: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

would involve creating wrapper classes for functions to make them first-class and wrap-ping scopes in objects to implement closures. Second, it was more natural to compile ourimperative constructs into Scala than it would have been to compile it to a functionallanguage like Haskell or even a multiparadigm language like OCaml, which errs towardsthe functional. Additionally, Scala is threaded, and allows us to have more granularcontrol over parallelism than a language like JavaScript. Furthermore, Scala is staticallyand strongly typed. Since our language does some type checking and requires types tobe specified, it makes sense to use a language that makes use of these constructs sincestatically typed language tend to be faster than dynamically typed languages. Finally,Scala runs on the JVM. While this was not as essential as the other requirements, itis nice that any code that runs on the JVM will be able to use compiled DotPar code.In the future, we could leverage the virtual machine to use other code that runs on theJVM in DotPar code.

We require no unusual libraries. An OCaml compiler, ocamlyacc, ocamllex, andScala 2.9 or greater must be installed.

In order to keep the LRM and compiler consistent, we followed agile developmentpractices and had quick iterations that conformed to the language spec. We also kepttrack of the features we had left to implement using GitHub. This is explained in moredetail in Part IV, Section 18.2.

Part VI

Translator Architecture

System Architect - Justin

47

Page 48: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

24 Block Diagram

source.par

ocamllex

ocamlyacc

TOKENS

transform

astsymbol table

semantic analyzer

modi!ed ast

modi!ed ast

parallelizer

generator

scalacompiler

user input

output.jar

JVM

importshandled

Figure 3: Block Diagram of the DotPar compiler.

25 Interfaces

Dotpars interface, as seen above, follows a standard compiler form. The raw source fileis fed into the lexer, OCamllex, which outputs a series of tokens that is then parsedby OCamlyacc. OCamlyacc uses a our custom type system to output an abstract syntaxtree.

Our abstract syntax tree then undergoes a series of modifications and optimizationsbefore it is complied down into scala and fed into the scala compiler. The first transformis done by the aptly named transform.ml which reverses nodes with a tree. The lexerrecurses right to left, and thus all lists are stored backeds and need to be reversed. Thetransform outputs a modified AST which is then fed into the semantic analyzer.

The semantic analyzer performs an extensive amount of type checking, making senseof a user’s source code, throwing errors where appropriate, recovering from small errorsif possible, and adding necessary information to the symbol table.

Next, the parallelizer, in which the main optimizations are performed, takes themodified AST from the semantic analyzer. The first pass performs a series of tests tofind, and declare certain functions as pure. A second pass marks certain functions asassociative. These functions that are pure can be parallelized in maps and list compre-hensions, and pure and associative functions can be parallelized in reduces as well. Ouroptimizatinos focus on major bottlenecks of source programs including maps, reduces,and list comprehensions.

48

Page 49: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Lastly, the parallelizer outputs a modified AST which is fed into the generator, whichdoes a pass over the ast, translating the tree into and creating parallelized functions whereapplicable, which is lastly fed into the scala compiler. The scala compiler then compliesdown the JVM which can then be run on a target machine.

Dotpar uses a total of 5 different passes over the abstract syntax, performing checksand optimizations at each state to insure relability. With this comes the added benefitthgat a program is not required to be parallizable in order to run. Dotpar will onlyattempt to parallelize a program after extensive checks which gives add compile securityand relability.

26 Implementation

The original grammar, written using yacc and lex, was designed and implemented SidNair and Nathan Hwang. The conversion to OCaml was done by Sid Nair and JustinHines. The parsing to create the AST was was completed by Nathan Hwang and JustinHines. An additional transformation pass on the tree was done by Nathan Hwang.The semantic analysis pass was implemented by Justin Hines and Andrew Hitti withhelp from Logan Donovan. The parallelizer was implemented by Sid Nair with minorcontributions from Justin Hines and Nathan Hwang. The Generator was implementedby Nathan Hwang with signifcant contributions from Sid Nair, Andrew Hitti, and JustinHines. Test cases were written by all group members, but the primary contributors wereNathan Hwang, Andrew Hitti, and Logan Donovan.

Part VII

Development and Run-timeEnvironment

System Integrator - Nathan

27 Development Environment

In order to effectively develop the Dotpar compiler, we used standard development toolsto streamline our development cycle, ensure a consistent build across platforms, andgenerate binaries from Dotpar code.

27.1 Compiler Build Process

We used a Makefile to manage our build and testing process, relying on this standardtool in the open source world to abstract away the act of building the compiler anew.

Holding with tradition, the Makefile provides familiar commands such as all, clean,and test, that allows each in turn to generate a binary, clean up generated intermediate

49

Page 50: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

files, and run the test suites on the generated binary, respectively.Additionally, we provide a fine-grained access to our test suites with such commands

as parser test, semantic test, and full test that run the seperate test suites andtarget the major parts of our compiler in turn. The test suites themselves are detailedin the Testing Methodology section.

To manage the code and facilitate collaboration, we used the source control systemgit, In particular, we used branches in order to allow development in different areas atthe same time. Also, we leveraged the excellent git hosting tool github.com, as detailedin the Github section.

The dotpar compiler itself is built on OCaml, as detailed in Language ImplementationTools. We invoke the OCaml compiler from our Makefile, compiling each separate part ofthe compiler with ocamlc -c and then linking them together with ocamlc -o in discretesteps. The compiler itself has no dependencies on external OCaml libraries that are notpackaged with the standard OCaml environment, which enhances it’s portability.

27.2 Makefile for the Project

SRC = src

OCAML_PATH = $(SRC)/ocaml

all: bin compiler

bin:

mkdir bin

compiler:

cd $(OCAML_PATH); \

ocamlc -c ast.ml; \

ocamlc -c transform.ml; \

ocamlyacc parser.mly; \

ocamlc -c parser.mli; \

ocamllex scanner.mll; \

ocamlc -c scanner.ml; \

ocamlc -c parser.ml; \

ocamlc -c semantic.ml; \

ocamlc -c parallelizer.ml; \

ocamlc -c generate.ml; \

ocamlc -c compile.ml; \

ocamlc -c dotpar.ml; \

ocamlc -o ../../bin/dotpar scanner.cmo ast.cmo transform.cmo \

parser.cmo semantic.cmo parallelizer.cmo generate.cmo compile.cmo \

dotpar.cmo

clean_ocaml:

50

Page 51: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

rm -f bin/dotpar

cd $(OCAML_PATH); \

rm -f *.cmo scanner.ml parser.ml parser.mli *.cmi

test: parser_test semantic_test full_test

parser_test: compiler

python tests/parser_test.py

semantic_test: compiler

python tests/semantic_test.py

full_test: compiler

scala tests/full_test.scala

clean: clean_ocaml

.PHONY: all compiler test clean

27.3 Compilation Process

The process of actually creating a binary from the generated compiler is facilitated by adifferent program, a shell script named dotparc. While dotpar takes Dotpar source codeand generates Scala code, dotparc reads in Dotpar files, runs them through dotpar, andcompiles the resulting Scala code to an executable jar file. It does this by invoking theScala compiler scalac on the output Scala code, and packing the class files into a jaralong with the requisite Scala and Dotpar support libraries. Then this jar is ready to berun on the JVM.

27.4 Editor Plugins

In order to facilitate writing tests, we wrote plugins for the popular Vim and Emacs texteditors to do code highlighting and indentation for Dotpar files.

27.5 Runtime Environment

Our run-time is based on the JVM, as we leverage Scala, which compiles down to JVMbytecode. More specifically, the generated JVM bytecode relies on the included supportScala library files: without these, the program will not run as compiled by scalac.

Finally, we include some supporting Scala code specifically tailored to enabling ourgenerated Dotpar code to run by extending the behavior of the JVM and Scala runtimesto provide the semantics required by the DotPar code.

51

Page 52: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Part VIII

Test Plan

System Tester - Andrew

28 Testing Methodology

For Dotpar we followed a very strict Test Driven Development plan. The tests for bothour parser and semantic analyzer were written weeks before either was implemented. Tobegin thinking about how to design a comprehensive test suite I began with my IDE.Since Dotpar is a typed, compiled language I figured the warnings and errors an IDElike Eclipse throws for Java would also pertain to our language. After that initial roundof tests, I walked through the sample programs in our whitepaper and LRM to ensurethey were present in the test suite. I also created variations of each of them that shouldeither pass or fail.

Nathan and I pair programmed two python scripts, parser_test.py and semantic_test.py,that contained our tests with a table of contents at the top to allow for a high level viewof our test cases. It also gives us the ability to run them through our compiler andspit out each test that went wrong with its intended behavior. This bit of optimizationwas extremely helpful! With a single command, make semantic\_test, we were able tocompile all our sources and run the python script that told us what it was supposed todo.

In addition to the test cases, when Justin and I were writing the semantic analyzer,he had the idea to add debug statements that would print out which portion of codewas running on which values. So, when we fed a single test into our compiler it wouldspit out what is was doing, adding to the symbol table, checking types, checking jumpstatements, etc.. We could then trace whether or not we were wrapping array accessesin their proper types and comparing nested types correctly.

We also created a make full test that compiles a series of test programs written inDotpar and compares them to scala equivalents. This ensures that our code generationmatches expected scala output. Overall, it was very helpful to think of test cases in termsof pairs, a situation in which something should pass and a situation in which it shouldnot. So, for every concept, like brace matching, type checking, comment nesting, etc.we would ensure that there were variants of each that were supposed to be correct andsupposed to be incorrect. This was very helpful because sometimes our analyzer wouldfail tests that were supposed to fail but for the wrong reasons, or just fail everything soit looked like half our tests passed but in reality only for the shallow reason that nothingreally worked.

29 Test Programs

Sample test cases:

52

Page 53: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

func main:void() {

func fizzBuzz:void() {

char[] fb = "FizzBuzz";

char[] f = "Fizz";

char[] b = "Buzz";

for (number i = 1; i < 101; i = i + 1){

if (i % 15 == 0){

println(fb);

} elif(i % 3 == 0) {

println(f);

} elif(i % 5 == 0) {

println(b);

} else {

println(i);

}

}

}

}

func main:void() {

func fib:number(number n) {

if (n == 0) {

return 0;

}

elif (n == 1) {

return 1;

} else {

return fib(n-1) + fib(n-2);

}

}

}

func main:void() {

number[] xx = [1, 2];

number[] yy = [1, 2];

[x*y for number x, number y in xx,yy if (x==1)];

}

53

Page 54: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

Part IX

Conclusions

30 Group Lessons and Advice for Future Groups

30.1 Starting Early

Starting early and spreading out work is an integral part of any project, especially onethat requires as much work as this one. Having plenty of time at each stage is importantto make sure that you are completely ready to move onto the next stage. While the designstage may seem less important than the implementation phases, being more thoughtfulat that stage could have saved us many of the headaches we experienced down the road.Despite our best efforts, finishing still came down to late-night final pushes to fix theremaining bugs.

31 Individual Lessons

31.1 Logan - Have a Backup Plan

We started off the semester with a great group of individuals who are all capable andmotivated. We were able to assign appropriate roles based on each member’s skillsetand what they wanted to do. Even with that, you have to count on things going wrong.I didn’t plan on getting very sick for over a month or having to miss a week and ahalf of school. The biggest problem was that it made it hard to communicate with theteam and help coordinate who was taking over my responsibilities temporarily. Nathanreally stepped up and helped keep the group on track with our schedule. I could havealso been better about following up when people couldn’t make meetings due to othercommitments. Occasionally, it would be the next weekly meeting before I got caught upon their progress.

31.2 Sid - Motivating People is Hard

Each group member has other commitments that they need to balance with our project.Other classes, extracurricular activities and life often conflict with goals that we haveset. There is no way to avoid the amount of time that needs to be invested, whetherit put in today or tomorrow. To that extent motivating people to make this a priorityand put in long hours during the middle of the semester can be very difficult and issomething that we have struggled with.

31.3 Justin - Don’t Be Afraid to Ask for Help

During the course of the project I sometimes found myself struggling with a problemwhen it was actually unnecessary. This was because it came from a misunderstanding ofa part of our design or not knowing everything about the new tools or concepts that we

54

Page 55: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

were using. Asking for help would have been a much more efficient way of trying to solvethe problems that I was struggling with than just trying to keep “googling” everythingthat I could think of. Sometimes you don’t want to ask because you feel like that issomething you should already know, but supporting each other is an important part ofhaving an effective group.

31.4 Andrew - Pair Programming

Pair programming is fun and helpful. When we worked in pairs on one computer wewrote better code, faster. Having two sets of eyes looking out for syntax errors andkeeping logic consistent went a very long way in helping to reduce bugs and accomplishtasks in a more timely fashion. I felt this was helpful especially in the beginning whenwe were all learning OCaml because I would remember some syntax and functions andmy partner would remember others. In this respect, we were able to learn from eachother. The knowledge transfer experience contributed to the cohesion of our group.

31.5 Nathan - Projects Will Take as Much Time as You Let Them

I learned that no matter how ambitious or puny a project may seem at first, the expec-tations and goals attached to that project can and will grow and shrink as availabilitybecomes more or less scarce: in other words, projects will grow to fill the time allotted tothem. We originally envisioned the project being functionally done a week before it wasdue, but since we had that buffer time, why not use it? And now here we are, pushingthis up to the last minute. This lesson can also be known as “self discipline is hard”.

32 Advice for Future Groups

32.1 Make Group Deadlines Hard Deadlines

In addition to the lessons we have learned, we would say the most important piece ofadvice to pass on is to stick to the deadlines you set for yourself. These create a dominoeffect so that even a small pushback can lead you to run out of time. It is hard to thinkof a group deadline the same way that you think of a deadline set by a professor, but itis imperative for the completion of your project.

33 Ideas for Improving the Course

33.1 Example Timeline

Since this is the first time most people taking PLT have written a language, it wouldbe useful to benefit from the experience of past groups and the professor by having asample/suggested timeline for completing certain aspects of our language and compiler,not just the documents that we need to turn in. This can be fine-tuned with a TA basedon the language specifics but it would be a good start and help groups be more realistic.

55

Page 56: DotPar Final Report Team 3 - cs.columbia.eduaho/cs4115_Spring-2012/lectures/12-05-09_Team3... · DotPar Final Report Team 3 Logan Donovan - lrd2127@columbia.edu Justin Hines - jph2149@columbia.edu

33.2 Possible Deadlines for Parts of the Compiler

While learning self-discipline is extremely important and a valuable lesson from the class,having to turn parts of your compiler into your TA would be a great way to make sureteams don’t fall behind. There is nothing like an assigned deadline to make people getwork done on time. These would have to be general to account for the differences inlanguages.

33.3 More Specifics About Available Tools

The inclass explainations of lex and yacc were very helpful for our group. We feel likethis approach could be beneficial in helping groups understand the pros and cons ofchoosing OCaml versus java in addition to other tools that are available to them. Thiswould also help to make the class portion of the course and the project part of the coursemore cohesive.

Part X

Appendix

The code is in the attached PDFs

56