(* Абстрактный синтаксис для λ-исчисления: e ::= x | λx.e | e e В нашем конкретном синтаксисе, 1. λx.e заменено на fun x -> e 2. Допускается (е). Парсер превращает это в e 3. Допускается let x = e in e'. Парсер преврящает в (λx.e') e *) type e = | Variable of string | Lambda of string * e | Apply of e * e (* От абстрактного синтаксиса обратно в конкретный. *) let rec show e = match e with | Variable x -> x | Lambda (x, e) -> Printf.sprintf "(fun %s -> %s)" x (show e) | Apply (e1, e2) -> Printf.sprintf "(%s %s)" (show e1) (show e2) (* Считывает весь стандартный вход. *) let rec read () = match Stdlib.read_line () with | exception End_of_file -> "" | s -> s ^ " " ^ (read ()) (* Очень плохой токенизатор. На самом деле надо использовать ocamllex или sedlex. *) let tokenize s = let rec insert token strings = match strings with | s::s'::strings -> s::token::(insert token (s'::strings)) | _ -> strings in String.split_on_char '(' s |> insert "(" |> List.map (String.split_on_char ')') |> List.map (insert ")") |> List.flatten |> List.map (String.split_on_char ' ') |> List.flatten |> List.map String.trim |> List.filter ((<>) "") (* Сам парсер. Первые две функции находят последовательность приложений функций, то есть e1 e2 e3 e4... Третья разбирает каждую e1, e2, ..., определяя, это переменная, лямбда или скобка, содержащая общее выражение. *) let rec parse_general input = let (e, input) = parse_non_apply input in match input with | ")"::_ | "in"::_ | [] -> (e, input) | _ -> parse_apply ~e1:e input and parse_apply ~e1 input = let (e2, input) = parse_non_apply input in let e = Apply (e1, e2) in match input with | ")"::_ | "in"::_ | [] -> (e, input) | _ -> parse_apply ~e1:e input and parse_non_apply input = match input with | "("::input -> let (e, input) = parse_general input in begin match input with | ")"::input -> (e, input) | _ -> failwith "unmatched '('" end | "fun"::x::"->"::input -> let (e, input) = parse_general input in (Lambda (x, e), input) | "let"::x::"="::input -> let (e1, input) = parse_general input in begin match input with | "in"::input -> let (e2, input) = parse_general input in (Apply (Lambda (x, e2), e1), input) | _ -> failwith "expected 'in'" end | x::input -> (Variable x, input) | [] -> failwith "expected an expression" let parse s = s |> tokenize |> parse_general |> fst (* Интерпретатор. *) (* Под λx.e, в e заменяет x на y. Если е содержит λ как подвыражение, и эта λ затеняет x, там замена не происходит. *) let rec alpha_vary x y e = match e with | Variable z when z = x -> Variable y | Variable z -> Variable z | Lambda (z, e) when z = x -> Lambda (z, e) | Lambda (z, e) -> Lambda (z, alpha_vary x y e) | Apply (e1, e2) -> Apply (alpha_vary x y e1, alpha_vary x y e2) (* Свободные переменные выражения e. То есть те, которые не происходит от окружающей λ. Например, в λx.x y, у свободная переменная, потому что не происходит от окружающей её λ. Свободные переменные могут быть захвачены внешней λ если в λ их не переименовать, то есть если наивно вычислить (λx.λy.x у) у, результат будет λу.у у, но внешняя у не должна ссылаться на внутреннюю y в λy. Для этого во время замены в λy.x у, внутреннюю y надо α-переименовать используя alpha_vary в, например _у1, и результат вычисления будет λ_у1.у _у1. *) let rec free_variables e = match e with | Variable x -> [x] | Lambda (x, e) -> List.filter ((<>) x) (free_variables e) | Apply (e1, e2) -> (free_variables e1) @ (free_variables e2) (* Добавляет приблизительно уникальный суффикс к переменной. Парсер не проверяет, что такие суффиксы не могут быть введены вручную. *) let unique = let last_suffix = ref 0 in fun x -> incr last_suffix; Printf.sprintf "_%s%i" x !last_suffix (* Заменяет в e все случаи x на e'. Избегает захвата свободных переменных. Смотрите комметарий выше у free_variables. *) let rec substitute e e' x = match e with | Variable y when y = x -> e' | Variable y -> Variable y | Lambda (y, e'') -> if y = x then Lambda (y, e'') else let (y, e'') = if List.mem y (free_variables e') then let y' = unique y in let e'' = alpha_vary y y' e'' in (y', e'') else (y, e'') in Lambda (y, substitute e'' e' x) | Apply (e1, e2) -> Apply (substitute e1 e' x, substitute e2 e' x) (* Вычисляет следующий шаг e' вычисления e. Если таковой имеется, результат Some e'. Если вычисление невозможно, результат None. *) let rec step e = match e with | Apply (Apply (Variable "print", Variable x), e) -> print_endline x; Some e | Apply (Lambda (x, e1), e2) -> begin match step e2 with | Some e2' -> Some (Apply (Lambda (x, e1), e2')) | None -> Some (substitute e1 e2 x) end | Apply (e1, e2) -> begin match step e1 with | Some e1' -> Some (Apply (e1', e2)) | None -> match step e2 with | Some e2' -> Some (Apply (e1, e2')) | None -> None end | _ -> None (* Многократно применяет step пока вычисление не становится невозможным. *) let rec eval e = match step e with | Some e' -> eval e' | None -> e (* Аналогичен eval, но выводит шаги *) let rec trace e = show e |> print_endline; match step e with | Some e' -> trace e' | None -> e let _ = read () |> parse |> eval |> show |> print_endline