CamlTrax
This page is about CamlTrax, a layout preprocessor for OCaml.
Introduction
OCaml is a fantastic programming language, but I, like many Haskellers,
wish it had layout-based parsing to avoid all the parens. A friend
recently introduced me to OCaml+TWT, a layout
preprocessor for OCaml, but I don't like how it handles layout, and I
have trouble, e.g., reading nested "if"s. So, I wrote CamlTrax, my own
preprocessor. It is
at least as retarded
as OCaml+TWT, but in a way that I find less irritating.
News
- 2008-09-26–Another little bug fix. ("[" and "{" behaved strangely,
e.g., "x.[y]" would be rewritten to "[x.y]"; this was because I
neglected to emit the previous token when I encountered these braces.)
- 2008-09-01–I've uploaded a minor bug fix. (There was a problem with the
position adjustment used for "match" expressions; it allowed the cases
to be directly beneath the "match" keyword, but the expression following
the ";" was incorrectly grouped with the last case; this has been fixed.)
- 2008-08-17–I've been working on some modifications to CamlTrax.
Primarily, these are aimed at cleaning up the code and at allowing more
flexible use of braces (i.e., "{", "}", "[", and "]" are actually
recognised as braces that must be nested properly). Keywords need to be
escaped differently, so this is not backwards compatible, but there is a
flag to engage compatibility with the previous syntax.
- 2008-04-05–Uploaded the latest version of CamlTrax; it incorporates
some minor fixes; I've been using this version for several months and
consider it (relatively) stable.
- 2007-07-18–Totally rewrote CamlTrax; it's much less retarded now, and
the changes are
totally not
backwards compatible.
- 2007-07-07–Uploaded first release of CamlTrax.
Download
- CamlTrax is open-source; the only available download is the OCaml
source (requires OCaml+TWT (or CamlTrax!) to compile):
Demonstrations
|
| Part of the source code for CamlTrax.
while (snd $ Stack.top st) <> lparenF
do emit $ rparen $ Stack.top st ;
ignore $ Stack.pop st ;
if (snd $ Stack.top st) = lparenF
then emit $ rparen $ Stack.top st ;
ignore $ Stack.pop st ;
advance t
| Generated OCaml equivalent.
(* Generated by *
* CamlTrax *
* Version 3 *)
( while (snd ( Stack.top st )) <> lparenF
do emit ( rparen ( Stack.top st ; ) )
ignore ( Stack.pop st ) done );
( if (snd ( Stack.top st )) = lparenF
then ( emit ( rparen ( Stack.top st ) );
ignore ( Stack.pop st ) ) );
advance t
|
|
| Composition & infix application.
fun env -> map $ second $ drop . eval env . lift
| Generated OCaml equivalent.
(* Generated by *
* CamlTrax *
* Version 3 *)
fun env -> map ( second ( drop ++ eval env ++ lift ) )
|
|
| Nested "if"s.
if is_tcon c
then KType
else if is_pcon c
then KProp
else raise $ TypeError "Kind error."
| Generated OCaml equivalent.
(* Generated by *
* CamlTrax *
* Version 3 *)
( if is_tcon c
then ( KType )
else ( ( if is_pcon c
then ( KProp )
else ( raise ( TypeError "Kind error." ) ) ) ) )
|
More Information
If you decide to try out CamlTrax, there are a few things you should keep
in mind.
- You can disable layout for a particular keyword by enclosing it
in "!"'s. There mustn't be spaces between the braces and the
keyword. This is obligatory for the top-level "let"'s occurring
within a module definition.
- CamlTrax is column-based and operates by looking for
"pushers"—keywords whose scopes will be explicitly delimited
with parens in the generated code—and putting them on the stack
when they're found. Any subsequent line that has non-space text
at or before the column where the pusher was found closes the
block (i.e., triggers right-paren insertion). This includes
several normal OCaml keywords and a couple "pseudo-keywords"
(e.g., "$")
- Scope can be explicitly delimited with parens, but these
do not
disable layout; rather, they force close-scope parens for all
pushers encountered between them (assuming those pushers have not
already been closed).
- "." is a
huge
hack. I always define function composition as "++" in OCaml, and
"." (occurring surrounded by whitespace) is replaced by "++"; if you've
not
defined this binding,
don't
use ".".

This page was generated by WebGen on PhoenixFyre on Fri Sep 26 10:31:41 EDT 2008.