The revised syntax
The revised syntax is an alternative syntax of OCaml. It is close to the normal syntax. We present here only the differences between the two syntaxes.
Notice that there is a simple way to know how the normal syntax is written in revised syntax: write the code in a file "foo.ml" in normal syntax and type, in a shell:
camlp5o pr_r.cmo pr_rp.cmo foo.ml
And, conversely, how a file "bar.ml" written in revised syntax is displayed in normal syntax:
camlp5r pr_o.cmo pr_op.cmo bar.ml
Even simpler, without creating a file:
camlp5o pr_r.cmo pr_op.cmo -impl - ... type in normal syntax ... ... type control-D ... camlp5r pr_o.cmo pr_rp.cmo -impl - ... type in revised syntax ... ... type control-D ...
Lexing
- The character quote (') can be written without backslash:
OCaml Revised '\'' '''
Modules, Structure and Signature items
- Structure and signature items always end with a single semicolon which is required.
- In structures, the declaration of a value is introduced by the
keyword "value", instead of "let":
OCaml Revised let x = 42;; value x = 42; let x = 42 in x + 7;; let x = 42 in x + 7; - In signatures, the declaration of a value is also introduced by
the keyword "value", instead of "val":
OCaml Revised val x : int;; value x : int; - In signatures, abstract module types are represented by a quote
and an (any) identifier:
OCaml Revised module type MT;; module type MT = 'a; - Functor application uses currying. Parentheses are not
required for the parameters:
OCaml Revised type t = Set.Make(M).t;; type t = (Set.Make M).t; module M = Mod.Make (M1) (M2);; module M = Mod.Make M1 M2; - It is possible to group several declarations together either in
an interface or in an implementation by enclosing them between
"declare" and "end" (this is useful when using syntax extensions
to generate several declarations from one). Example in an
interface:
declare type foo = [ Foo of int | Bar ]; value f : foo -> int; end;
Expressions and Patterns
Imperative constructions
- The sequence is introduced by the keyword "do" followed by "{"
and terminated by "}"; it is possible to put a semicolon after the
last expression:
OCaml Revised e1; e2; e3; e4 do { e1; e2; e3; e4 } - The "do" after the "while" loop and the "for" loop are followed
by a "{" and the loop end with ng="0" cellpadding="0">
er
syntax.
q_phony.cmo
This extension kit is designed for pretty printing and must be loaded after a language pretty printing kit (in normal or in revised syntax). It prevents the expansions of quotations, transforming them into variables. The pretty printing then keeps the initial (source) form.
The macros (extension "pa_macro.cmo") are also displayed in their initial form, instead of expanded.
A full example: lambda terms
This example allows to represent lambda terms by a concrete syntax and to be able to combine them using antiquotations.
A lambda term is defined like this:
type term = [ Lam of string and term | App of term and term | Var of string ] ;Examples:
value fst = Lam "x" (Lam "y" (Var "x")); value snd = Lam "x" (Lam "y" (Var "y")); value delta = Lam "x" (App (Var "x") (Var "x")); value omega = App delta delta; value comb_s = Lam "x" (Lamb "y" (Lamb "z" (App (App (Var "x") (Var "y")) (App (Var "x") (Var "z")))));Since combinations of lambda term may be complicated, The idea is to represent them by quotations in concrete syntax. We want to be able to write the examples above like this:
value fst = << \x.\y.x >>; value snd = << \x.\y.y >>; value delta = << \x.x x >> value omega = << ^delta ^delta >>; value comb_s = << \x.\y.\z.(x y)(x z) >>;
which is a classic representation of lambda terms.
Notice, in the definition of "omega", the use of the caret ("^") sign to specify antiquotations. Notice also the simplicity of the representation of the expression defining "comb_s".
Here is the code of the quotation expander, term.ml. The expander uses the extensible grammars. It has its own lexer (using the stream lexers) because the lexer of OCaml programs ("Plexer.gmake ()"), cannot recognize the backslashes alone.
Lexer
(* lexer *) #load "pa_lexer.cmo"; value rec ident = lexer [ [ 'a'-'z' | 'A'-'Z' | '0'-'9' | '-' | '_' | '\128'-'\255' ] ident! | ] ; value empty _ = parser [: _ = Stream.empty :] -> []; value rec next_tok = lexer [ "\\" -> ("BSLASH", "") | "^" -> ("CARET", "") | 'a'-'z' ident! -> ("IDENT", $buf) | "(" -> ("", "(") | ")" -> ("", ")") | "." -> ("", ".") | empty -> ("EOS", "") | -> raise (Stream.Error "lexing error: bad character") ] ; value rec skip_spaces = lexer [ " \n\r"/ skip_spaces! | ]; value record_loc loct i (bp, ep) = do { if i >= Array.length loct.val then do { let newt = Array.init (2 * Array.length loct.val + 1) (fun i -> if i < Array.length loct.val then loct.val.(i) else Ploc.dummy) in loct.val := newt; } else (); loct.val.(i) := Ploc.make_unlined (bp, ep) }; value lex_func cs = let loct = ref [| |] in let ts = Stream.from (fun i -> do { ignore (skip_spaces $empty cs : list char); let bp = Stream.count cs in let r = next_tok $empty cs in let ep = Stream.count cs in record_loc loct i (bp, ep); Some r }) in (ts, fun i -> loct.val.(i)) ; value lam_lex = {Plexing.tok_func = lex_func; Plexing.tok_using _ = (); Plexing.tok_removing _ = (); Plexing.tok_match = Plexing.default_match; Plexing.tok_text = Plexing.lexer_text; Plexing.tok_comm = None} ;Parser
(* parser *) #load "pa_extend.cmo"; #load "q_MLast.cmo"; value g = Grammar.gcreate lam_lex; value expr_term_eos = Grammar.Entry.create g "term"; value patt_term_eos = Grammar.Entry.create g "term"; EXTEND GLOBAL: expr_term_eos patt_term_eos; expr_term_eos: [ [ x = expr_term; EOS -> x ] ] ; expr_term: [ [ BSLASH; i = IDENT; "."; t = SELF -> <:expr< Lam $str:i$ $t$ >> ] | [ x = SELF; y = SELF -> <:expr< App $x$ $y$ >> ] | [ i = IDENT -> <:expr< Var $str:i$ >> | CARET; r = expr_antiquot -> r | "("; t = SELF; ")" -> t ] ] ; expr_antiquot: [ [ i = IDENT -> let r = let loc = Ploc.make_unlined (0, String.length i) in <:expr< $lid:i$ >> in <:expr< $anti:r$ >> ] ] ; patt_term_eos: [ [ x = patt_term; EOS -> x ] ] ; patt_term: [ [ BSLASH; i = IDENT; "."; t = SELF -> <:patt< Lam $str:i$ $t$ >> ] | [ x = SELF; y = SELF -> <:patt< App $x$ $y$ >> ] | [ i = IDENT -> <:patt< Var $str:i$ >> | CARET; r = patt_antiquot -> r | "("; t = SELF; ")" -> t ] ] ; patt_antiquot: [ [ i = IDENT -> let r = let loc = Ploc.make_unlined (0, String.length i) in <:patt< $lid:i$ >> in <:patt< $anti:r$ >> ] ] ; END; value expand_expr s = Grammar.Entry.parse expr_term_eos (Stream.of_string s) ; value expand_patt s = Grammar.Entry.parse patt_term_eos (Stream.of_string s) ; Quotation.add "term" (Quotation.ExAst (expand_expr, expand_patt)); Quotation.default.val := "term";Compilation and test
Compilation:
ocamlc -pp camlp5r -I +camlp5 -c term.ml
Example, in the toplevel, including a semantic error, correctly underlined, thanks to the antiquotation nodes:
$ ocaml -I +camlp5 camlp5r.cma Objective Caml version ... Camlp5 Parsing version ... # #load "term.cmo"; # type term = [ Lam of string and term | App of term and term | Var of string ] ; type term = [ Lam of string and term | App of term and term | Var of string ] # value comb_s = << \x.\y.\z.(x y)(x z) >>; value comb_s : term = Lam "x" (Lam "y" (Lam "z" (App (App (Var "x") (Var "y")) (App (Var "x") (Var "z"))))) # value omega = << ^delta ^delta >>; Characters 18-23: value omega = << ^delta ^delta >>; ^^^^^ Unbound value delta # value delta = << \x.x x >>; value delta : term = Lam "x" (App (Var "x") (Var "x")) # value omega = << ^delta ^delta >>; value omega : term = App (Lam "x" (App (Var "x") (Var "x"))) (Lam "x" (App (Var "x") (Var "x")))↑