___ ___ ___ ___ ___
/\ \ /\ \ /\__\ ___ /\__\ /\ \
/::\ \ /::\ \ /:/ / /\ \ /::| | /::\ \
/:/\:\ \ /:/\:\ \ /:/ / \:\ \ /:|:| | /:/\:\ \
/::\~\:\ \ /:/ \:\ \ /:/ / ___ /::\__\ /:/|:| |__ /::\~\:\ \
/:/\:\ \:\__\ /:/__/ \:\__\ /:/__/ /\__\ __/:/\/__/ /:/ |:| /\__\ /:/\:\ \:\__\
\/__\:\ \/__/ \:\ \ /:/ / \:\ \ /:/ / /\/:/ / \/__|:|/:/ / \:\~\:\ \/__/
\:\__\ \:\ /:/ / \:\ /:/ / \::/__/ |:/:/ / \:\ \:\__\
\/__/ \:\/:/ / \:\/:/ / \:\__\ |::/ / \:\ \/__/
\::/ / \::/ / \/__/ /:/ / \:\__\
\/__/ \/__/ \/__/ \/__/
Make sure ocamlbuild is installed. Then just type make
By default ./fouine is an interpretor.
To use it type ./fouine. Several options are available:
-debugto pretty print the instructions after they are inputted, or complementary informations when compiling.-nocolorationto deactivate the syntax coloration. It is activated by default.-noinferenceto deactivate type inference (activated by default)-interm FILEto save the compiled code in the fileFILE-o FILEto save transformed code in a file-Rto activate the transformation suppressing references-Eto activate the cps transformation (expressions are made by continuations)ERto activate both transformations-nobuildinsto deactivate the buildins-noinferenceto deactivate the inference-machine (Z|S|J)if you want to compile your fouine file.Z|S|Jdetermines for which machine it will be compiled. It is important to note that the different machines don't support pattern matching and constructors !Zis a ZINC machineSis a secd machineJis a joint interpretation / secd machine. It will interpret the program until "pure" expressions (only made of arithmetical expressions) is found. It then switch to a secd machine for this expression.
- a file can be passed to
./fouine. In that case, this file will be executed. Otherwise./fouinewill be launched under the repl mode
The script test.sh will test Fouine with the files found in the folder tests/
The syntax - and the functionnalities - are similar with Caml. Here is a summary:
-
Basic mathematical operators :
+,-, /, *, =, <>, <, >, <=, >=, and, or, not.- Operators
+,-, /, *have typesint -> int-> int. and, or, nothave typesbool-> bool-> boolandbool->bool=, <>, <, >, <=, >=have types'a->'a->intDepending on how the interpreter / compiler is launch, these operators are either buildins or not. With the option-nobuildins, or if it is compiled for aZINCmachine, then they aren't buildins. Otherwise they are : we can therefore change their behaviour by redefining+,*, ...
- Operators
-
Branching:
if condition then foo else bar.fooandbarmust have the same type. Expressions likeif cond then exprworks only ifexpris of typeunit -
Functions:
fun a-> fun b -> expris a two variables anonymous fonction -
Affectation and variables:
let ident = exp in expressionwill affect to the identifieridentthe valueexpr.let ident a b c = expr in expressionis a shortcut forlet ident = fun a-> fun b-> fun c->expr in expression. Expressions of the formlet ident = exprare only accepted on the top level.let rec ident = exprwill behave almost identically aslet ident = expr:expris assigned toident, butidentcan be present inexpr, thus allowing recursive functions.
-
References. References are almost likes pointer. They are mutable. We can access to the value of a reference with the operator
!, and modify the value of a reference with:=(typeref 'a -> 'a -> unit).let a = ref 6 in let _ = a := 8 in !a (* <- it is 8 *)
-
Underscore (
_). It matches everything. These instructions are valid:let _ = exprlet f x _ = x in f
-
Exceptions:
- An exception can be emitted with the instruction
raise nwhere n is an integer. - An exception can be caught with a bloc of the form
try foo with E x -> bar. Ifxis a constant, thenbaris executed only iffooraised an exception havingxhas value. Otherwise,baris executed iffooraised an exception
- An exception can be emitted with the instruction
-
array: integer fixed-length arrays can be created with the syntax
makeArray nwherenis the length. An element can be accessed with the syntaxarray.(index), and affected witharray.(index) <- value -
prInt:
prInt exprwill printexprand return the valueexpr. It has typeint -> int -
Files. The command
open "file"will open a fouine file and will load its code as a module (functions declared in this file will be accessible with the syntaxfile.function. If the file doesn't exists, or if it contains a parsing error, the loaded code will be(). Beware: paths are relatives to the interpreter files -
Tuples:
(a, b, c)will create a three dimensionnal tuple. You can match on the elements:let x, y = 1, 2 -
Types and constructors
- Declarations are like in Caml with the syntax:
type ('a, ..., 'b) type_name = | Constr1 of (type_arguments1) .... | Constrn of (type_argumentsn) - Types are recursives:
let 'a test = None of 'a test - Constructors aren't force to have arguments
- Declarations are like in Caml with the syntax:
-
Pattern matching: expression like
let 0, (), (x, _), Constr y = ....orfun (x, Constr (a, b)) -> ...are valid. You can explicitely match a value with the syntaxmatch expr with | pattern_1 -> expr1 | ... -> ... | pattern_n -> exprn. Ifexprmatches withpatterni, thenexpriwill be executed -
;;are required at the end of an expression -
We can redefined a number of operators (infix or prefix). The syntax is the same than in caml:
let (@@) a b = .... -
List: An empty list can be build with
[], two list can be concatenated with@and an element can be inserted to the front with::. They are compatible with pattern matching (their definition is simplytype 'a list = None | Elem of ('a * 'a list)). They are not working with compilation. -
modules. A module can be defined as follow:
module Test : sig ... end = struct .. end;;. Signatures can be also providedmodule type TestSig = sig type t;; type t2 = int;; val f : int -> int -> int end;; module Test : TestSig = struct type t = int;; let f a b = a + b;; end;;It is important to note that signatures only checks for the presence of the elements. They are not as powerfull as in OCaml. -
Type constraints. Type constraints are working if no
refare presents.let f x : int = x;; int -> intlet f (x : bool) = x;; bool -> bool((fun x -> x) : int -> int) -
Base types:
'a -> 'b,'a ref,int array,int,bool
The constructors of our types have only an argument which is a tuple. From there, certains expressions which are not working in Caml are working in Fouine:
>>> type 'a ok = Machin of 'a;;
>>> let a = Machin 3;;
val a : int ok = Machin (3)
>>> let Machin b = a;;
val b : int = 3Two types transformations can be applied. The first one will remove the references and simulate them using an array (transformation R) The second one do a cps transformation of the code (transformation E). Recursive functions are transformed using an Y-combinator. The two transformations can be applied at the same time (transformation ER). Typing errors can then appear because our fouine langage is converted into a subset of fouine very similar to lambda calculus. Therefore our typing system isn't powerfull enough to type these expressions. (but the same problem arise in Caml)
inference.ml: type inferencebuildins.ml: buildins functions definition (apport from basic mathematical operators)inference_old.ml: old type inference. Kept for archeleogical purposestransformations.ml: code transformationsprettyprint.ml: fouine pretty printerbinop.ml: binary operators functionsshared.ml: buildins declarations and variables environmentstypes.ml: type definitions and utilities around typescommons.ml: file containing elements usefull everywhere in the codeerrors.ml: errors les erreursfile.ml: everything to deal with filesmain.ml: repl, main and loading filesinterpret.ml: interpretercompilB.ml: fouine to SECD machine bytecode compilationsecdB.ml: to execute SECD bytecodeutils.ml: display, debug and utilities functions for the SECD simulatorbruijn.ml: De bruijn indicesdream.ml: environment used for the secd and the bruijn indicesexpr.ml: declaration of the types used to define our astparser.mly,lexer.mll: parsing and lexingbruijnZ.ml,compilZ.ml,secdZ.ml: de bruijn indices, compilation and simulation for a ZINC machinejit.ml: mixed machine