6 changed files with 334 additions and 0 deletions
@ -0,0 +1,26 @@
|
||||
CC = ocamlc
|
||||
MAIN = main
|
||||
|
||||
all: $(MAIN) |
||||
|
||||
$(MAIN): tools.cmo lambda.cmo parser.cmo lexer.cmo main.cmo |
||||
$(CC) $^ -o $(MAIN)
|
||||
|
||||
parser.ml: parser.mly |
||||
menhir --infer $^
|
||||
$(CC) -c parser.mli
|
||||
|
||||
lexer.ml: lexer.mll |
||||
ocamllex $^
|
||||
|
||||
%.cmo: %.ml |
||||
$(CC) -c $^
|
||||
|
||||
%.cmi: %.mli |
||||
$(CC) -c $^
|
||||
|
||||
.PHONY: clean |
||||
|
||||
clean: |
||||
@echo "Project clean."
|
||||
@rm -rf *.cmi *.cmo *.cmx *.mli *.o parser.ml lexer.ml parser.conflicts main
|
@ -0,0 +1,144 @@
|
||||
(*Terme du Lambda-Calcul*) |
||||
type term = |
||||
| Var of string |
||||
| Lambda of string * term |
||||
| App of term * term |
||||
|
||||
|
||||
let to_var x = Var x |
||||
|
||||
let to_lambda x t = Lambda (x, t) |
||||
|
||||
let to_app t u = App (t, u) |
||||
|
||||
(*Applique une substitution de x par sub à un terme t*) |
||||
let rec apply x sub = function |
||||
| Var (y) -> if y = x then sub else Var (y) |
||||
| Lambda (y,t) -> Lambda (y, apply x sub t) |
||||
| App (t1, t2) -> App (apply x sub t1, apply x sub t2) |
||||
|
||||
(*Applique une liste de substition à un terme*) |
||||
let rec subst t sub_list = |
||||
List.fold_left (fun t (x, sub) -> apply x sub t) t sub_list |
||||
|
||||
(* Applique la beta réduction, qui transforme (lambda x. t)u en t[x:=u] *) |
||||
let rec beta = function |
||||
| App (Lambda (x, t), u) -> subst t [(x, u)] |
||||
| _ -> failwith "This term is not a redex" |
||||
|
||||
(* --------------------------------------- |
||||
Exemples |
||||
--------------------------------------- *) |
||||
|
||||
let x = Var "x";; |
||||
let y = Var "y";; |
||||
let a = Var "a" ;; |
||||
let b = Var "b" ;; |
||||
let i = Lambda ("x", x);; |
||||
|
||||
(*(λx. x x) a — duplication*) |
||||
let n1 = App (Lambda ("x", App (x, x)), a);; |
||||
(*(λx. x x) (λa. a) — passage de fonction / ordre supérieur*) |
||||
let n2 = App (Lambda ("x", App (x, x)), Lambda (("a"), a));; |
||||
(*(λx. x (λx.x)) y — variable libres/liées*) |
||||
let n3 = App (Lambda ("x", App (x, Lambda ("x", x) ) ), y) ;; |
||||
(*(λx. x (λy. x y)) y — capture de variable*) |
||||
let n4 = App (Lambda ("x", App(x, Lambda ("y", App (x, y)))), y);; |
||||
(*(λxy. x) a b — fonction à deux entrées et effacement*) |
||||
let n5 = App ( App ( Lambda ("x", ( Lambda ("y", x))), a), b);; |
||||
(*(λxy. x) a — application partielle pour deux entrées*) |
||||
let n6 = App ( Lambda ("x", ( Lambda ("y", x))), a);; |
||||
(*(λxy. x) I a b — sur application pour deux entrées*) |
||||
let n7 = App (App ( App (Lambda ("x", ( Lambda ("y", x))), i), a), b);; |
||||
(*(λx. x x) (I I) — duplication inutile dans une stratégie*) |
||||
let n8 = App ( Lambda ("x", App (x, x)), App ( i, i));; |
||||
(*(λxy. y) (I I) I — différence de complexité selon la stratégie*) |
||||
let n9 = App ( App (Lambda ("x", ( Lambda ("y", y))), App (i, i)), i);; |
||||
|
||||
(* --------------------------------------- |
||||
Display |
||||
--------------------------------------- *) |
||||
|
||||
let rec string_of_list printer sep = function |
||||
| [] -> "" |
||||
| [x] -> printer x |
||||
| h::t -> |
||||
(printer h) ^ sep ^ (string_of_list printer sep t) |
||||
|
||||
|
||||
let rec string_of_term = function |
||||
| Var x -> x |
||||
| Lambda (x, Lambda (y, t)) -> "λ" ^ x ^ y ^ ". " ^ string_of_term t ^ "" |
||||
| Lambda (x, t) -> "λ" ^ x ^ ". " ^ string_of_term t ^ "" |
||||
| App (Var x, Var y) -> x ^ " " ^ y |
||||
| App (t, Var x) -> "(" ^ string_of_term t ^ ") " ^ x |
||||
| App ( App ( t, u), v) -> "(" ^ string_of_term (App (t, u)) ^ ") " ^ string_of_term v |
||||
| App ( u, App ( t, v)) -> string_of_term u ^ " (" ^ string_of_term (App (t, v)) ^ ")" |
||||
| App ( Lambda (x, App (t, y)), v) -> "(" ^ string_of_term (Lambda (x, App (t, y))) ^ ") " ^ string_of_term v |
||||
| App ( Lambda (x, t), u) -> string_of_term (Lambda (x,t)) ^ " (" ^ string_of_term u ^")" |
||||
| App (t, u) -> string_of_term t ^ " (" ^ string_of_term u ^ ")" |
||||
|
||||
|
||||
(* --------------------------------------- |
||||
Réduction |
||||
--------------------------------------- *) |
||||
|
||||
exception Irreductible |
||||
(* Vérifie si un terme est une valeur, c'est à dire qu'on ne peut plus le réduire *) |
||||
let is_value = function |
||||
| Lambda (_, _) -> true |
||||
| Var _ -> true |
||||
| _ -> false |
||||
|
||||
(* Vérifie si un lambda est linéaire, i.e. qu'il n'y a qu'une seule occurence de son paramètre *) |
||||
|
||||
let rec count_freevars x = function |
||||
| Lambda (y, t) -> count_freevars x t |
||||
| App (t, u) -> (count_freevars x t) + (count_freevars x u) |
||||
| Var y -> if x = y then 1 else 0 |
||||
|
||||
let rec is_linear = function |
||||
| Lambda (x, t) -> (count_freevars x t) = 1 && is_linear t |
||||
| App(t, u) -> is_linear t && is_linear u |
||||
| Var x -> true |
||||
|
||||
(* Réduit d'un pas les termes par l |
||||
'extérieur puis l'intérieur *) |
||||
let rec lo_reduction = function |
||||
| App ( Lambda (x, t), u) as redex -> (beta redex) |
||||
| App (Var x, (_ as t)) -> App (Var x, lo_reduction t) |
||||
| App (App (_,_) as t, Var x) -> App (lo_reduction t, Var x) |
||||
| App (App (_,_) as t, (_ as u)) -> App (lo_reduction t, u) |
||||
(*| App (t, u) -> (App (lo_reduction t, lo_reduction u))*) |
||||
| Lambda (x, t) -> Lambda (x, lo_reduction t) |
||||
| _ -> raise Irreductible |
||||
|
||||
(* Boucle sur la lo_reduction pour appliquer cette stratégie de réduction jusqu'à la fin *) |
||||
let rec lo_eval t = |
||||
try let t' = lo_reduction t in |
||||
lo_eval t' with Irreductible -> t |
||||
|
||||
(* Réduit d'un pas les termes en commençant par l'argument puis réduit les fonctions *) |
||||
let rec cbv = function |
||||
| App ( Lambda (x, t), v) as redex when is_value v -> (*cbv*) (beta redex) |
||||
| App (t, u) when is_value u -> (App (cbv t, u)) |
||||
| App (t, u) -> (App (t, cbv u)) |
||||
| Lambda (x, t) -> raise Irreductible |
||||
| Var x -> raise Irreductible |
||||
|
||||
(* Boucle sur call_by_value pour appliquer cette stratégie de réduction jusqu'à la fin *) |
||||
let rec cbv_eval t = |
||||
try let t' = cbv t in |
||||
cbv_eval t' with Irreductible -> t |
||||
|
||||
(* Réduit d'un pas les termes en commençant par la fonction puis passe à l'argument*) |
||||
let rec cbn = function |
||||
| App ( Lambda (x, t), v) as redex -> beta redex |
||||
| App (t, u) -> (App (cbn t, u)) |
||||
| Lambda (x, t) -> raise Irreductible |
||||
| Var x -> raise Irreductible |
||||
|
||||
(* Boucle sur call_by_name pour appliquer cette stratégie de réduction jusqu'à la fin *) |
||||
let rec cbn_eval t = |
||||
try let t' = cbv t in |
||||
cbn_eval t' with Irreductible -> t |
@ -0,0 +1,18 @@
|
||||
{ |
||||
open Parser |
||||
exception Eof |
||||
} |
||||
|
||||
let var_id = ['a'-'z' 'A'-'Z' '0'-'9']+ |
||||
let space = [' ' '\t']+ |
||||
let newline = '\r' | '\n' | "\r\n" |
||||
|
||||
rule read = parse |
||||
| var_id { VAR (Lexing.lexeme lexbuf) } |
||||
| '(' { LEFT_PAR } |
||||
| ')' { RIGHT_PAR } |
||||
| '.' { DOT } |
||||
| '\\' { LAMBDA } |
||||
| space { SPACE } |
||||
| newline { read lexbuf } |
||||
| eof { exit 0 } |
@ -0,0 +1,100 @@
|
||||
open Lambda |
||||
open Parser |
||||
open Tools |
||||
|
||||
(* --------------------------------------- |
||||
Prompt |
||||
--------------------------------------- *) |
||||
|
||||
let welcome () = print_endline "Use 'help' for the list of commands." |
||||
|
||||
let tab = repeat_string "\t" |
||||
|
||||
let line = repeat_string "-" |
||||
|
||||
let commands_list () = |
||||
print_endline ("Command" ^ tab 4 ^ "Description (shortcut)"); |
||||
print_endline (line 70); |
||||
print_endline ("exit" ^ tab 4 ^ "Exits the program"); |
||||
print_endline ("display <lambda term>" ^ tab 2 ^ "Displays the written lambda with the pretty printer"); |
||||
print_endline ("cbn -f <filename>" ^ tab 2 ^ "Eval the lambda term <filename> using call by name strategy"); |
||||
print_endline ("cbv -f <filename>" ^ tab 2 ^ "Eval the lambda term <filename> using call by value strategy"); |
||||
print_endline ("lo -f <filename>" ^ tab 2 ^ "Eval the lambda term <filename> using the lo reduction strategy") |
||||
|
||||
let prompt () = print_string "> " |
||||
let last_command : string option ref = ref None |
||||
|
||||
(* --------------------------------------- |
||||
Main function |
||||
--------------------------------------- *) |
||||
|
||||
let _ = |
||||
welcome (); |
||||
while true do |
||||
prompt (); |
||||
let input = read_line () in |
||||
begin match String.split_on_char ' ' input with |
||||
| ["exit"] -> exit 0 |
||||
| ["help"] -> commands_list () |
||||
(*| _ as string_list -> |
||||
let lexbuf = Lexing.from_string (String.concat " " string_list) in |
||||
(try |
||||
let t = termc Lexer.read lexbuf in |
||||
(* print_endline (String.concat " " string_list); *) |
||||
print_endline (string_of_term t) |
||||
with _ -> print_endline "Syntax error. Please try again.")*) |
||||
| "display"::string_list -> |
||||
let lexbuf = Lexing.from_string (String.concat " " string_list) in |
||||
(try |
||||
let t = termc Lexer.read lexbuf in |
||||
print_endline (string_of_term t) |
||||
with _ -> print_endline "Syntax error. Please try again.") |
||||
| ["cbn"; "-f"; filename] -> |
||||
begin try |
||||
let lexbuf = Lexing.from_channel (open_in filename) in |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = cbn_eval t in |
||||
print_endline (string_of_term result) |
||||
with Sys_error f -> print_endline f |
||||
end |
||||
| ["cbv"; "-f"; filename] -> |
||||
begin try |
||||
let lexbuf = Lexing.from_channel (open_in filename) in |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = cbv_eval t in |
||||
print_endline (string_of_term result) |
||||
with Sys_error f -> print_endline f |
||||
end |
||||
| ["lo"; "-f"; filename] -> |
||||
begin try |
||||
let lexbuf = Lexing.from_channel (open_in filename) in |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = lo_eval t in |
||||
print_endline (string_of_term result) |
||||
with Sys_error f -> print_endline f |
||||
end |
||||
| "cbn"::string_list -> |
||||
let lexbuf = Lexing.from_string (String.concat " " string_list) in |
||||
(try |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = cbn_eval t in |
||||
print_endline (string_of_term result) |
||||
with _ -> print_endline "Syntax error. Please try again.") |
||||
| "cbv"::string_list -> |
||||
let lexbuf = Lexing.from_string (String.concat " " string_list) in |
||||
(try |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = cbv_eval t in |
||||
print_endline (string_of_term result) |
||||
with _ -> print_endline "Syntax error. Please try again.") |
||||
| "lo"::string_list -> |
||||
let lexbuf = Lexing.from_string (String.concat " " string_list) in |
||||
(try |
||||
let t = termc Lexer.read lexbuf in |
||||
let result = lo_eval t in |
||||
print_endline (string_of_term result) |
||||
with _ -> print_endline "Syntax error. Please try again.") |
||||
| _ -> |
||||
print_endline "Invalid command. Please type 'help' for the list of commands." |
||||
end |
||||
done |
@ -0,0 +1,25 @@
|
||||
%token LEFT_PAR RIGHT_PAR |
||||
%token <string> VAR |
||||
%token DOT |
||||
%token LAMBDA |
||||
%token SPACE |
||||
%left SPACE |
||||
%token END |
||||
|
||||
%start <Lambda.term> termc |
||||
|
||||
%% |
||||
|
||||
(* |
||||
mainc: |
||||
| t = termc END { t }*) |
||||
|
||||
termc: |
||||
| x = VAR { Lambda.to_var x } |
||||
| LAMBDA ; x = VAR ; DOT; SPACE; t = termc { Lambda.to_lambda x t } |
||||
| LEFT_PAR ; t = termc ; SPACE ; u = termc ; RIGHT_PAR { Lambda.to_app t u } |
||||
| LEFT_PAR ; t = termc ; RIGHT_PAR { t } |
||||
(*| tu = separated_pair (termc, SPACE, termc) {let (t, u) = tu in Lambda.to_app t u }*) |
||||
|
||||
(* %inline sp: |
||||
| SPACE { ( ) } *) |
@ -0,0 +1,21 @@
|
||||
(* --------------------------------------- |
||||
Few useful functions |
||||
--------------------------------------- *) |
||||
|
||||
let lift_pairl f (x, y) = (f x, y) |
||||
let lift_pairr f (x, y) = (x, f y) |
||||
let lift_pair f p = lift_pairr f (lift_pairl f p) |
||||
|
||||
let rec repeat_string s n = if n=0 then "" else s ^ repeat_string s (n-1) |
||||
|
||||
let foldi_left f acc l = |
||||
snd (List.fold_left (fun (i, acc') x -> (i+1, f i acc' x)) (0, acc) l) |
||||
|
||||
let without i l = foldi_left (fun j acc x -> if i=j then acc else acc@[x]) [] l |
||||
|
||||
(* --------------------------------------- |
||||
List monad (with index) |
||||
--------------------------------------- *) |
||||
|
||||
let return x = [x] |
||||
let (>>=) l f = List.flatten (List.mapi f l) |
Loading…
Reference in new issue