MiniOOL is a mini object-oriented language. Written in OCaml, this mini programming language features integer arithmetic, boolean operations, control structures, recursive procedures, parallelism, and basic objects with fields.
Created by Zachary Ferguson for CSCI-GA.3110: Honors Programming Languages (Fall 2018) at New York University.
See ferguson-zachary-MiniOOL-report.pdf
for a discussion of design decisions,
implementation details, and some examples covering the features of MiniOOL.
MiniOOL can be built using the included make file. Run make
to compile the
source code and run make examples
to run the example programs. Optionally
make docs
will build the documentation for the source code using ocamldoc
.
The only external dependency for MiniOOL is
Menhir for parsing. Menhir can be
installed using opam using the
command opam install menhir
.
To start an interactive session run ./MiniOOL
from the project directory.
The optional command line arguments to MiniOOL are
Usage: MiniOOL [--verbose] [--no-logo] [-help|--help]
--verbose Verbosly print steps of interpretation
--no-logo Do not display the logo and information at start up
-help Display this list of options
--help Display this list of options
To run a program from a source file, source.mini
use the command
printf "$(cat source.mini | tr -d '\n')\n" | ./MiniOOL --no-logo
var x
: Declare a variablex
.e₀(e₁)
: Call procedure evaluated from e₀ with an argument ofe₁
evaluated, procedures do not return a value explicitly and are not expressions.malloc(x)
: Allocatex
on the heap, making it an object with unlimited fields.x = e
: Assign the value of the expressione
tox
.e₀.e₁ = e₂
: Assign the value of the expressione₂
to the fielde₁
of variablee₀
.skip
: Do nothing.{C₀; C₁; ...}
: Execute commands sequentially.while b C | while b do C
: While b evaluates to true execute the command C.if b C₀ else C₁ | if b then C₀ else C₁
: If b evaluates to true execute the command C₀ else execute C₁.{C₀ ||| C₁}
: Execute commands C₀ and C₁ in parallel.atom(C)
: Execute commands C without executing any other command.
(['A'-'Z'])(['a'-'z'] | ['A'-'Z'] | ['0'-'9'] | '_')*
: Fields[0-9]+
: Integerse₀ + e₁ | e₀ - e₁ | e₀ * e₁ | e₀ / e₁ | e₀ % e₁ | e₀ mod e₁ | -e₀
: Arithmetic operationsnull
: Null location(['a'-'z'])(['a'-'z'] | ['A'-'Z'] | ['0'-'9'] | '_')*
: Variablese₀.e₁
: Field accessproc y: C
: Procedure declaration
true | false
: Boolean literalse₀ == e₁ | e₀ != e₁ | e₀ <> e₁ | e₀ < e₁ | e₀ <= e₁ | e₀ > e₁ | e₀ >= e₁
: Comparison operationsnot b | !b | b₀ && b₁ | b₀ and b₁ | b₀ || b₁ | b₀ or b₁
: Boolean operations
\exit
|\quit
: Exit MiniOOL\help
: Display helpful information about and how to use MiniOOL.\clear
: Clear the stack and heap of all values (this will also reset the unique naming of variables).\verbose
: Verbosely display the steps of interpretation including the abstract syntax tree (same as the command line option --verbose).\quiet
: The opposite of\verbose
, turn off verbose information.
Provided in the examples/
directory are some example programs for MiniOOL. Run
all of these examples by using the command make examples
.
This example program compute factorial of 17 by defining a recursive procedure to compute factorial.
var r;
var factorial;
factorial = proc i:
if (i <= 0) {
r = 1;
} else {
factorial(i - 1);
r = i * r;
};
factorial(17);
r = 355687428096000 - r
The resulting value of r
is 0
Objects are created by using the malloc(x)
operator. All fields of an object
are implicitly declared as null
. Fields are accessed using the e₀.e₁
operator where all fields start with an uppercase letter.
var x; malloc(x);
x.C = 0;
x.F = proc y: if y < 1 then x.R = x.C else x.F(y - 1);
x.F(2);
var r;
r = x.R;
The resulting value of r
is 0.
In MiniOOL, procedures only have one formal parameter. Although this may seem
a limitation to procedures, in fact a procedure can take multiple arguments by
using “currying”. While a procedure sum(x, y)
is not directly possible it can
be achieved by defining a procedure sum(x)
that “returns” a procedure
foo(y) = x + y
that takes one argument and “returns” the sum of x
and y
.
The following MiniOOL program shows how to define and use such a procedure:
var r;
var sum;
sum = proc x: {
r = proc y: {
r = x + y;
};
};
var x; x = 100;
var y; y = -100;
sum(x);
r(y);
The resulting value of r
is 0. The value of x
is accessible in
r = proc y: {r = x + y;};
because it creates a closure containing the current
stack, which stores a location for the value of x.
A procedure can be defined that takes an unlimited number of parameters. This is done by defining a procedure that takes one argument and "returns" two values: the intermediate results and a procedure to continue computations.
var r;
malloc(r);
var sum;
sum = proc x: {
r.Result = x;
r.SumRest = proc y: {
r.Result = r.Result + y;
};
};
sum(0);
var i; i = 1;
while (i <= 100) {
r.SumRest(i);
i = i + 1
};
r = 5050 - r.Result
The resulting value of r
is 0.
The |||
operator allows two commands to be run in parallel, but can lead to
uncertain outcomes.
var x; x = 10; var r; {if x == 10 then r = x * 2 else skip ||| x = 5}
Depending on the order of commands evaluated by the parallel operator the resulting value of r can be:
r = null
ifx = 5
is run first resulting inx == 10
being false andr
not being setr = 10
if the checkx == 10
is evaluated first then the commandx = 5
is evaluated and the commandr = x * 2
is evaluated last.r = 20
if the checkx == 10
and commandr = x * 2
are evaluated before the commandx = 5
is evaluated.
By using the atom(C)
operator the enclosed commands, C
, run without
interruption.
var x; x = 10; var r; {atom(if x == 10 then r = x * 2 else skip) ||| x = 5}
Depending on the order of commands evaluated by the parallel operator the resulting value of r can be:
r = null
ifx = 5
is run first resulting inx == 10
being false andr
not being setr = 20
if the commandatom(if x == 10 then r = x * 2 else skip)
is evaluated before the commandx = 5
.
Any undeclared variable is implicitly declared as global with dynamic scope.
var r; var p; p = proc y: r = y+h; h=2; p(4);
The resulting value of r is 6 in this example. The variable h
is never
declared, so it is treated as an implicit global variable. Although, h is
initialized after the procedure p
is declared, the value of h
is used
inside of p
dynamically. After renaming variable to be unique the program is
var r₀; var p₀; p₀ = proc y₀: r₀ = y₀+h₋₁; h₋₁=2; p₀(4);
All of the source code for MiniOOL is written in OCaml and can be found in the
src
directory.
MiniOOL.ml
: Main cmd-line interface for MiniOOLlexer.mll
: Lexer tokens and regular expressions for MiniOOL.parser.mly
: Menhir parser for MiniOOL. Takes the tokens from the lexer and builds an abstract syntax tree for of the input program. Runs the input program by checking the scope, uniquely renaming vairables, and evaluating the commands.abstractSyntaxTree.ml
: Abstract syntax tree types for MiniOOL.abstractSyntaxTreeString.ml
: Create a string of a tree of the abstract syntax tree types for MiniOOL.programString.ml
: Create a string of of the abstract syntax tree types for MiniOOL.staticSemantics.ml
: Static semantics for MiniOOL. Checks if an identifier is in scope.semanticDomains.ml
: Type definitions for the semantic domains of MiniOOL.operationalSemantics.ml
: Evaluate the abstract syntax tree of a MiniOOL program.flags.ml
: Global flags for different options.utils.ml
: Utilities for MiniOOL