Skip to content

Latest commit

 

History

History
130 lines (106 loc) · 3.92 KB

Conditional-compilation-support.adoc

File metadata and controls

130 lines (106 loc) · 3.92 KB

Conditional compilation support - static if

It is quite common that people want to write code works with different versions of compilers and libraries.

People used to use preprocessors like C preprocessor for C family languages. In OCaml community there are several preprocessors: cppo, ocp-pp, camlp4 IFDEF macros, optcomp and ppx optcomp.

Instead of using a preprocessor, BuckleScript adds language level static if compilation to the language. It is less powerful than other preprocessors since it only support static if, no #define, #undefine, #include, but there are several advantages.

  • It’s very small (only around 500 LOC) and highly efficient. There is no added pass, everything is done in a single pass. It is easy to rebuild the pre-processor in a stand alone file, with no dependencies on compiler libs to back-port it to old OCaml compilers

  • It’s purely functional and type safe, easy to work with IDEs like merlin

Concrete syntax

static-if
| HASH-IF-BOL conditional-expression THEN // (1)
   tokens
(HASH-ELIF-BOL conditional-expression THEN) *
(ELSE-BOL tokens)?
HASH-END-BOL

conditional-expression
| conditional-expression && conditional-expression
| conditional-expression || conditional-expression
| atom-predicate

atom-predicate
| atom operator atom

operator
| (= | < | > | <= | >= | =~ )

atom
| UIDENT | INT | STRING | FLOAT
  1. IF-BOL means #IF should be in the beginning of a line

Typing rules

  • type of INT is int

  • type of STRING is string

  • type of FLOAT is float

  • value of UIDENT comes from either built-in values (with documented types) or environment variable, if it is literally true, false then it is bool, else if it is parsable by int_of_string then it is of type int, else if it is parsable by float_of_string then it is float, otherwise it would be string

  • In lhs operator rhs, lhs and rhs are always the same type and return boolean. =~ is a semantics version operator which requires both sides to be string

Evaluation rules are obvious. =~ respect semantics version, for example, the underlying engine

semver Location.none "1.2.3" "~1.3.0" = false;;
semver Location.none "1.2.3" "^1.3.0" = true ;;
semver Location.none "1.2.3" ">1.3.0" = false ;;
semver Location.none "1.2.3" ">=1.3.0" = false ;;
semver Location.none "1.2.3" "<1.3.0" = true ;;
semver Location.none "1.2.3" "<=1.3.0" = true ;;
semver Location.none "1.2.3" "1.2.3" = true;;

Examples

lwt_unix.mli
type open_flag =
    Unix.open_flag =
  | O_RDONLY
  | O_WRONLY
  | O_RDWR
  | O_NONBLOCK
  | O_APPEND
  | O_CREAT
  | O_TRUNC
  | O_EXCL
  | O_NOCTTY
  | O_DSYNC
  | O_SYNC
  | O_RSYNC
#if OCAML_VERSION =~ ">=3.13" then
  | O_SHARE_DELETE
#end
#if OCAML_VERSION =~ ">=4.01" then
  | O_CLOEXEC
#end

Changes to command line options

For BuckleScript users, nothing needs to be done (it is baked in the language level), for non BuckleScript users, we provide an external pre-processor, so it will work with other OCaml compilers too. Note that the {BuckleScript}/blob/master/jscomp/bin/bspp.ml[bspp.ml] is a stand alone file, so that it even works without compilation. .Example

bsc.exe -c lwt_unix.mli
ocamlc -pp 'bspp.exe' -c lwt_unix.mli
ocamlc -pp 'ocaml -w -a bspp.ml' -c lwt_unix.mli
Warning

This is a very small extension to the OCaml language, it is backward compatible with OCaml language with such exception.

let f x =
  x
#elif // (1)
  1. #elif at the beginning of a line is interpreted as static if, there is no issue with #if or #end, since they are already keywords