├── .gitignore ├── .travis.yml ├── LICENSE.txt ├── Makefile ├── README.md ├── dune-project ├── ppx_const.opam ├── src ├── dune └── ppx_const.ml └── src_test ├── dune ├── test_compose_ppx_getenv.ml └── test_ppx_const.ml /.gitignore: -------------------------------------------------------------------------------- 1 | /_build 2 | *.install 3 | .merlin 4 | -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: c 2 | install: wget https://raw.githubusercontent.com/ocaml/ocaml-ci-scripts/master/.travis-opam.sh 3 | script: bash -ex .travis-opam.sh 4 | env: 5 | global: 6 | - PACKAGE=ppx_const 7 | matrix: 8 | - OCAML_VERSION=4.04 9 | - OCAML_VERSION=4.05 10 | - OCAML_VERSION=4.06 11 | - OCAML_VERSION=4.07 12 | - OCAML_VERSION=4.08 13 | - OCAML_VERSION=4.09 14 | - OCAML_VERSION=4.10 15 | - OCAML_VERSION=4.11 16 | os: 17 | - linux 18 | -------------------------------------------------------------------------------- /LICENSE.txt: -------------------------------------------------------------------------------- 1 | Written in 2015 by Andi McClure with contributions by Reynir Björnsson. 2 | 3 | This software is distributed under the Creative Commons Zero license. This 4 | means the work is released in the public domain in all jurisdictions where 5 | that is legal, and made available under a public-domain-like license where 6 | it is not. The intent is that the software may be reused by anyone in any 7 | fashion without restriction or attached obligation. Details below. 8 | 9 | ---- 10 | 11 | Creative Commons Legal Code 12 | 13 | CC0 1.0 Universal 14 | 15 | CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE 16 | LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN 17 | ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS 18 | INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES 19 | REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS 20 | PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM 21 | THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED 22 | HEREUNDER. 23 | 24 | Statement of Purpose 25 | 26 | The laws of most jurisdictions throughout the world automatically confer 27 | exclusive Copyright and Related Rights (defined below) upon the creator 28 | and subsequent owner(s) (each and all, an "owner") of an original work of 29 | authorship and/or a database (each, a "Work"). 30 | 31 | Certain owners wish to permanently relinquish those rights to a Work for 32 | the purpose of contributing to a commons of creative, cultural and 33 | scientific works ("Commons") that the public can reliably and without fear 34 | of later claims of infringement build upon, modify, incorporate in other 35 | works, reuse and redistribute as freely as possible in any form whatsoever 36 | and for any purposes, including without limitation commercial purposes. 37 | These owners may contribute to the Commons to promote the ideal of a free 38 | culture and the further production of creative, cultural and scientific 39 | works, or to gain reputation or greater distribution for their Work in 40 | part through the use and efforts of others. 41 | 42 | For these and/or other purposes and motivations, and without any 43 | expectation of additional consideration or compensation, the person 44 | associating CC0 with a Work (the "Affirmer"), to the extent that he or she 45 | is an owner of Copyright and Related Rights in the Work, voluntarily 46 | elects to apply CC0 to the Work and publicly distribute the Work under its 47 | terms, with knowledge of his or her Copyright and Related Rights in the 48 | Work and the meaning and intended legal effect of CC0 on those rights. 49 | 50 | 1. Copyright and Related Rights. A Work made available under CC0 may be 51 | protected by copyright and related or neighboring rights ("Copyright and 52 | Related Rights"). Copyright and Related Rights include, but are not 53 | limited to, the following: 54 | 55 | i. the right to reproduce, adapt, distribute, perform, display, 56 | communicate, and translate a Work; 57 | ii. moral rights retained by the original author(s) and/or performer(s); 58 | iii. publicity and privacy rights pertaining to a person's image or 59 | likeness depicted in a Work; 60 | iv. rights protecting against unfair competition in regards to a Work, 61 | subject to the limitations in paragraph 4(a), below; 62 | v. rights protecting the extraction, dissemination, use and reuse of data 63 | in a Work; 64 | vi. database rights (such as those arising under Directive 96/9/EC of the 65 | European Parliament and of the Council of 11 March 1996 on the legal 66 | protection of databases, and under any national implementation 67 | thereof, including any amended or successor version of such 68 | directive); and 69 | vii. other similar, equivalent or corresponding rights throughout the 70 | world based on applicable law or treaty, and any national 71 | implementations thereof. 72 | 73 | 2. Waiver. To the greatest extent permitted by, but not in contravention 74 | of, applicable law, Affirmer hereby overtly, fully, permanently, 75 | irrevocably and unconditionally waives, abandons, and surrenders all of 76 | Affirmer's Copyright and Related Rights and associated claims and causes 77 | of action, whether now known or unknown (including existing as well as 78 | future claims and causes of action), in the Work (i) in all territories 79 | worldwide, (ii) for the maximum duration provided by applicable law or 80 | treaty (including future time extensions), (iii) in any current or future 81 | medium and for any number of copies, and (iv) for any purpose whatsoever, 82 | including without limitation commercial, advertising or promotional 83 | purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each 84 | member of the public at large and to the detriment of Affirmer's heirs and 85 | successors, fully intending that such Waiver shall not be subject to 86 | revocation, rescission, cancellation, termination, or any other legal or 87 | equitable action to disrupt the quiet enjoyment of the Work by the public 88 | as contemplated by Affirmer's express Statement of Purpose. 89 | 90 | 3. Public License Fallback. Should any part of the Waiver for any reason 91 | be judged legally invalid or ineffective under applicable law, then the 92 | Waiver shall be preserved to the maximum extent permitted taking into 93 | account Affirmer's express Statement of Purpose. In addition, to the 94 | extent the Waiver is so judged Affirmer hereby grants to each affected 95 | person a royalty-free, non transferable, non sublicensable, non exclusive, 96 | irrevocable and unconditional license to exercise Affirmer's Copyright and 97 | Related Rights in the Work (i) in all territories worldwide, (ii) for the 98 | maximum duration provided by applicable law or treaty (including future 99 | time extensions), (iii) in any current or future medium and for any number 100 | of copies, and (iv) for any purpose whatsoever, including without 101 | limitation commercial, advertising or promotional purposes (the 102 | "License"). The License shall be deemed effective as of the date CC0 was 103 | applied by Affirmer to the Work. Should any part of the License for any 104 | reason be judged legally invalid or ineffective under applicable law, such 105 | partial invalidity or ineffectiveness shall not invalidate the remainder 106 | of the License, and in such case Affirmer hereby affirms that he or she 107 | will not (i) exercise any of his or her remaining Copyright and Related 108 | Rights in the Work or (ii) assert any associated claims and causes of 109 | action with respect to the Work, in either case contrary to Affirmer's 110 | express Statement of Purpose. 111 | 112 | 4. Limitations and Disclaimers. 113 | 114 | a. No trademark or patent rights held by Affirmer are waived, abandoned, 115 | surrendered, licensed or otherwise affected by this document. 116 | b. Affirmer offers the Work as-is and makes no representations or 117 | warranties of any kind concerning the Work, express, implied, 118 | statutory or otherwise, including without limitation warranties of 119 | title, merchantability, fitness for a particular purpose, non 120 | infringement, or the absence of latent or other defects, accuracy, or 121 | the present or absence of errors, whether or not discoverable, all to 122 | the greatest extent permissible under applicable law. 123 | c. Affirmer disclaims responsibility for clearing rights of other persons 124 | that may apply to the Work or any use thereof, including without 125 | limitation any person's Copyright and Related Rights in the Work. 126 | Further, Affirmer disclaims responsibility for obtaining any necessary 127 | consents, permissions or other rights required for any use of the 128 | Work. 129 | d. Affirmer understands and acknowledges that Creative Commons is not a 130 | party to this document and has no duty or obligation with respect to 131 | this CC0 or use of the Work. -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | build: 2 | dune build 3 | 4 | test: 5 | dune runtest -f 6 | 7 | clean: 8 | dune clean 9 | 10 | .PHONY: build test clean 11 | 12 | release: 13 | @if [ -z "$(VERSION)" ]; then echo "Usage: make release VERSION=1.0.0"; exit 1; fi 14 | git commit -pm "Prepare for release." 15 | git tag -a v$(VERSION) -m "Version $(VERSION)" 16 | git push origin v$(VERSION) 17 | 18 | .PHONY: release 19 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | ppx_const 2 | ========= 3 | 4 | This is an OCaml language extension implementing `if%const` and `match%const` statements. The `if%const` and `match%const` are evaluated at compile time, and the appropriate clause substituted without the ignored clause(s) being fully compiled. This allows you to avoid consequences such as module inclusion or type inference changes which would otherwise have resulted from the ignored clause(s). 5 | 6 | In other words, ppx\_const works like `#if` in the C preprocessor, but is implemented entirely within the OCaml language using the [ppx](http://whitequark.org/blog/2014/04/16/a-guide-to-extension-points-in-ocaml/) mechanism. In conjunction with [ppx_getenv](https://github.com/whitequark/ppx_getenv), this provides a lightweight alternative to Cppo. 7 | 8 | This software was written by Andi McClure <>, based on whitequark's ppx\_getenv sample. Significant upgrades were contributed by Kate Deplaix. 9 | 10 | Because ppx\_const is based on ppx, it requires OCaml 4.02 or newer. 11 | 12 | Usage 13 | ----- 14 | 15 | ppx\_const may be invoked with either `if%const` or `match%const`. 16 | 17 | ### if%const 18 | 19 | `if%const` may be invoked with either of: 20 | 21 | if%const COND then A else B 22 | if%const COND then A 23 | 24 | COND must be one of the following: 25 | 26 | * `true` 27 | * `false` 28 | * An expression consisting of two literals (strings, ints, floats) and either the `<>` or `=` operator. 29 | 30 | COND may also contain extension nodes (including `if%const`s) as long as they evaluate to a constant expression by the time ppx\_const sees them. 31 | 32 | A and B are not required to be of the same type. Like with normal `if`, the return type of `if%const false then X` is unit. 33 | 34 | ### match%const 35 | 36 | ppx\_const can also be invoked with the following: 37 | 38 | match%const MATCHED with P\_1 -> E\_1 | ... | P\_n -> E\_n 39 | 40 | MATCHED and P\_1..P\_n must be either literals (strings, ints, floats) or one of the special constructors `true` and `false`. Like with `if%const`, MATCHED may contain extension nodes, although the P1..P\_n may not. 41 | 42 | The patterns P\_1..P\_n may also be variables or `_`, in which case they will always match. Matching on a variable name will "bind" a variable by that name in the match expression: If a pattern P\_i is a variable `x` then the expression, if matched, will compile to `let x = MATCHED in E_i`. 43 | 44 | 45 | An example: Using ppx_const with ppx\_gentenv 46 | --------------------------------------------- 47 | 48 | Say your program has a Graph module with heavyweight dependencies (cairo or whatever). Some users may prefer to compile your program without the graph feature, so that they don't have to install the dependencies. You can do this by installing ppx\_const and ppx\_getenv, and invoking the graph feature like this: 49 | 50 | if%const [%getenv "BUILD_OMIT_GRAPH"] = "" then 51 | Graph.create filename 52 | else 53 | print_endline "Graph feature not available." 54 | 55 | In this example, when you build, if the `BUILD_OMIT_GRAPH` environment variable is set to a nonempty string then the `Graph.create` call will be omitted entirely from the compiled binary. If this is the only invocation of Graph, then the Graph module and all its dependencies will also be omitted from the binary. If you do not set this environment variable, the `[%getenv` check will become an empty string at build time and the graph function will be included. 56 | 57 | **OCamlbuild users take note:** For this example to work, you'll need to make **certain** that ppx\_getenv runs before ppx\_const, so that `if%const` sees a constant string and not the `[%` node. If you are using Dune, then Dune will take care of this for you. In OCamlbuild, you set a load order by ordering the packages like this in your `_tags` file: 58 | 59 | <*>: package(ppx_getenv, ppx_const) 60 | 61 | License 62 | ------- 63 | 64 | [Creative Commons Zero](LICENSE.txt) ("public domain or equivalent") 65 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.0) 2 | (name ppx_const) 3 | (version 2.0.1) 4 | 5 | (allow_approximate_merlin) 6 | (generate_opam_files true) 7 | 8 | (maintainers "Andi McClure ") 9 | (authors "Andi McClure ") 10 | (license "Creative Commons Zero") 11 | (source (github mcclure/ppx_const)) 12 | (package 13 | (name ppx_const) 14 | (synopsis "Compile-time \"if\" statement for conditional inclusion of code") 15 | (description "\ 16 | This is a ppx extension which adds `if#const` and `match#const` constructs to 17 | OCaml. They behave like normal `if` and `match`, but conditions are evaluated 18 | at compile time and AST sections not selected are excluded from the program 19 | completely. In conjunction with ppx_getenv, this can be used for conditional 20 | compilation of code. 21 | ") 22 | (tags ("syntax")) 23 | (depends 24 | (ocaml (>= 4.04.0)) 25 | (ppxlib (>= 0.18.0)) 26 | (ounit2 :with-test) 27 | (ppx_getenv (and :with-test (>= 2.0))) 28 | (odoc :with-doc))) 29 | -------------------------------------------------------------------------------- /ppx_const.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "2.0.1" 4 | synopsis: "Compile-time \"if\" statement for conditional inclusion of code" 5 | description: """ 6 | This is a ppx extension which adds `if#const` and `match#const` constructs to 7 | OCaml. They behave like normal `if` and `match`, but conditions are evaluated 8 | at compile time and AST sections not selected are excluded from the program 9 | completely. In conjunction with ppx_getenv, this can be used for conditional 10 | compilation of code. 11 | """ 12 | maintainer: ["Andi McClure "] 13 | authors: ["Andi McClure "] 14 | license: "Creative Commons Zero" 15 | tags: ["syntax"] 16 | homepage: "https://github.com/mcclure/ppx_const" 17 | bug-reports: "https://github.com/mcclure/ppx_const/issues" 18 | depends: [ 19 | "dune" {>= "2.0"} 20 | "ocaml" {>= "4.04.0"} 21 | "ppxlib" {>= "0.18.0"} 22 | "ounit2" {with-test} 23 | "ppx_getenv" {with-test & >= "2.0"} 24 | "odoc" {with-doc} 25 | ] 26 | build: [ 27 | ["dune" "subst"] {pinned} 28 | [ 29 | "dune" 30 | "build" 31 | "-p" 32 | name 33 | "-j" 34 | jobs 35 | "@install" 36 | "@runtest" {with-test} 37 | "@doc" {with-doc} 38 | ] 39 | ] 40 | dev-repo: "git+https://github.com/mcclure/ppx_const.git" 41 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (public_name ppx_const) 3 | (kind ppx_rewriter) 4 | (preprocess (pps ppxlib.metaquot)) 5 | (libraries ppxlib)) 6 | -------------------------------------------------------------------------------- /src/ppx_const.ml: -------------------------------------------------------------------------------- 1 | open Ppxlib 2 | 3 | (* Shadow polymorphic equality functions from the standard library to avoid 4 | future issues. We can be sure these will never be called because 5 | of the unit->unit type signatures. *) 6 | let (=) : unit -> unit -> bool = (=) 7 | let (<>) : unit -> unit -> bool = (=) 8 | let (>) : unit -> unit -> bool = (=) 9 | let (<) : unit -> unit -> bool = (=) 10 | let (>=) : unit -> unit -> bool = (=) 11 | let (<=) : unit -> unit -> bool = (=) 12 | let compare : unit -> unit -> bool = (=) 13 | let min : unit -> unit -> unit = min 14 | let max : unit -> unit -> unit = max 15 | 16 | type const_expr = 17 | | Const of constant 18 | | True 19 | | False 20 | 21 | let const_eq x y = match x, y with 22 | | Pconst_integer (x, _suff1), Pconst_integer (y, _suff2) -> 23 | (* Ignore suffixes. It doesn't really matter if they're different types 24 | as long as their values match *) 25 | String.equal x y 26 | | Pconst_char x, Pconst_char y -> 27 | Char.equal x y 28 | | Pconst_string (x, _, _delim1), Pconst_string (y, _, _delim2) -> 29 | String.equal x y 30 | | Pconst_float (x, _suff1), Pconst_float (y, _suff2) -> 31 | (* Ignore suffixes. It doesn't really matter if they're different types 32 | as long as their values match *) 33 | String.equal x y 34 | | Pconst_integer _, _ 35 | | Pconst_char _, _ 36 | | Pconst_string _, _ 37 | | Pconst_float _, _ -> 38 | false 39 | 40 | let const_neq x y = not (const_eq x y) 41 | 42 | let const_expr_eq x y = match x, y with 43 | | Const x, Const y -> const_eq x y 44 | | True, True 45 | | False, False -> true 46 | | Const _, _ 47 | | True, _ 48 | | False, _ -> false 49 | 50 | let traverse = object 51 | inherit Ppxlib.Ast_traverse.map as super 52 | 53 | method! expression = 54 | (* Create a recursive function and then immediately return it *) 55 | let rec process expr = 56 | (* Shared error handler used by multiple cases below *) 57 | let didnt_find_if loc = 58 | Location.raise_errorf ~loc 59 | "[%%const] accepts an if statement, e.g. if%%const true then 1, or a match statement" 60 | in 61 | match expr with 62 | (* Is this an extension node? *) 63 | | { pexp_desc = Pexp_extension ({ txt = "const"; loc }, pstr); _ } -> 64 | begin match pstr with 65 | | PStr [{ pstr_desc = Pstr_eval (exp,_); _ }] -> 66 | (* Unpack expression, then recurse to handle internal if%matches and match on result *) 67 | begin match process exp with 68 | (* Syntax extension 1 -- ifthenelse *) 69 | | { pexp_loc = loc; 70 | pexp_desc = Pexp_ifthenelse (cond, then_clause, else_opt); _ } -> 71 | (* Used by = and <> *) 72 | let pairTest x y op = 73 | match x,y with 74 | | Pexp_constant x, Pexp_constant y -> op x y 75 | | _ -> 76 | Location.raise_errorf ~loc:cond.pexp_loc 77 | "[%%const if...] does not know how to compare these two expressions" 78 | in 79 | (* Evaluate conditional *) 80 | let which = match cond with 81 | | [%expr true] -> true 82 | | [%expr false] -> false 83 | | [%expr [%e? x] = [%e? y]] -> 84 | pairTest x.pexp_desc y.pexp_desc const_eq 85 | | [%expr [%e? x] <> [%e? y]] -> 86 | pairTest x.pexp_desc y.pexp_desc const_neq 87 | | _ -> 88 | Location.raise_errorf ~loc:cond.pexp_loc 89 | "[%%const if...] does not know how to interpret this kind of expression" 90 | in 91 | (* Depending on value of conditional, replace self extension node with either the then or else clause contents *) 92 | if which then then_clause else (match else_opt with Some x -> x | _ -> 93 | (* Or, if the else clause is selected but is not specified, a () *) 94 | Ast_helper.with_default_loc loc (fun _ -> [%expr ()])) 95 | 96 | (* Syntax extension 1 -- match *) 97 | | { pexp_loc = match_loc; 98 | pexp_desc = Pexp_match (match_expr, cases); _ } -> 99 | (* Basic syntax-check expression *) 100 | let matched_expr = match match_expr with 101 | | { pexp_desc = Pexp_constant c; _ } -> Const c 102 | | [%expr true] -> True 103 | | [%expr false] -> False 104 | | _ -> 105 | Location.raise_errorf ~loc:match_expr.pexp_loc 106 | "[%%const match...] does not know how to interpret this kind of expression" 107 | in 108 | (* Syntax-check, bar "when" *) 109 | let check_case (case : case) = match case with 110 | | { pc_guard = Some guard; _ } -> 111 | Location.raise_errorf ~loc:guard.pexp_loc 112 | "[%%const match...] Guards are not allowed in match%%const" 113 | | { pc_lhs = { ppat_desc = Ppat_constant _; _ }; _ } 114 | | { pc_lhs = [%pat? true]; _ } 115 | | { pc_lhs = [%pat? false]; _ } 116 | | { pc_lhs = { ppat_desc = Ppat_var _; _ }; _ } 117 | | { pc_lhs = [%pat? _]; _ } -> () 118 | | { pc_lhs; _ } -> 119 | Location.raise_errorf ~loc:pc_lhs.ppat_loc 120 | "[%%const match...] Bad pattern in match%%const" in 121 | let () = List.iter check_case cases in 122 | (* Evaluate match, check | expressions one by one *) 123 | let rec find_match cases = match cases with 124 | | case :: cases -> 125 | let handle_const expr = 126 | if const_expr_eq matched_expr expr 127 | then case.pc_rhs 128 | (* When matches are not found, recurse *) 129 | else find_match cases 130 | in 131 | begin match case.pc_lhs with 132 | (* _ always matches *) 133 | | [%pat? _] -> case.pc_rhs 134 | (* Variable names always match become "bindings", as with normal match *) 135 | | { ppat_desc = Ppat_var _; _ } -> 136 | [%expr let [%p case.pc_lhs] = [%e match_expr] in [%e case.pc_rhs]] 137 | (* Constants get tested for equality *) 138 | | { ppat_desc = Ppat_constant const; _ } -> handle_const (Const const) 139 | (* true and false are special case *) 140 | | [%pat? true] -> handle_const True 141 | | [%pat? false] -> handle_const False 142 | | _ -> 143 | Location.raise_errorf ~loc:case.pc_lhs.ppat_loc 144 | "[%%const match] Bad pattern" 145 | end 146 | | [] -> Location.raise_errorf ~loc:match_loc 147 | "[%%const match...] No match case succeeded!" 148 | in find_match cases 149 | (* Failed to match Pexp_ifthenelse, so fail *) 150 | | _ -> didnt_find_if loc 151 | end 152 | (* Failed to match Pstr, so fail *) 153 | | _ -> didnt_find_if loc 154 | end 155 | (* Failed to match Pexp_extension, so hand this off to the default mapper. *) 156 | | expr -> super#expression expr 157 | in 158 | process 159 | end 160 | 161 | let () = Ppxlib.Driver.register_transformation ~impl:traverse#structure "const" 162 | -------------------------------------------------------------------------------- /src_test/dune: -------------------------------------------------------------------------------- 1 | (test 2 | (name test_ppx_const) 3 | (preprocess (pps ppx_const)) 4 | (modules test_ppx_const) 5 | (libraries ounit2)) 6 | 7 | (env (_ (env-vars (PPX_GETENV_CHECK 42)))) 8 | 9 | (test 10 | (name test_compose_ppx_getenv) 11 | (preprocess (pps ppx_getenv ppx_const)) 12 | (modules test_compose_ppx_getenv) 13 | (libraries ounit2)) 14 | -------------------------------------------------------------------------------- /src_test/test_compose_ppx_getenv.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | 3 | let test _ = 4 | assert_equal "BLARG" @@ if%const [%getenv "PPX_GETENV_CHECK"] = "42" then "BLARG" else 3 5 | 6 | let suite = "Test ppx_const combination with ppx_getenv" >::: [ 7 | "test_ppx_const_compose_ppx_getenv" >:: test; 8 | ] 9 | 10 | let () = 11 | run_test_tt_main suite 12 | -------------------------------------------------------------------------------- /src_test/test_ppx_const.ml: -------------------------------------------------------------------------------- 1 | open OUnit2 2 | 3 | let test_ppx_const _ = 4 | (* notice none of these tests will even compile if ppx_constconst isn't working *) 5 | 6 | (* if%const tests *) 7 | assert_equal "BLARG" @@ if false then "WRONG" else if%const 3=3 then "BLARG" else 3; 8 | assert_equal "BLAAAARG" @@ if false then "WRONG" else if%const 3=4 then 4 else "BLAAAARG"; 9 | assert_equal "...blarg" @@ if%const true then (if%const false then 5 else "...blarg") else 3; 10 | assert_equal "Blarg." @@ if%const 2=if%const 1=0 then 3 else 2 then "Blarg." else 1; 11 | assert_equal "Blarg" @@ if%const 3 <> 3 then 4 else if%const 4 <> 3 then "Blarg" else 6; 12 | 13 | (* match%const tests *) 14 | assert_equal "match1" @@ (match%const 42 with _ -> "match1"); 15 | assert_equal "match2" @@ (match%const true with true -> "match2" | false -> "bogus"); 16 | assert_equal "match3" @@ (match%const 3 with 1 -> () | 3 -> "match3" | 3 -> "bogus"); 17 | assert_equal 8 @@ 3 + (match%const "five" with "goodbye type safety" -> () | "five" -> 5); 18 | assert_equal "match5" @@ "match" ^ string_of_int (match%const 4 with 11 -> 11 | four -> four + 1); 19 | assert_equal "three" @@ 20 | match%const (if%const true then "3" else "4") with 21 | | "2" -> (if%const true then "two" else "three") 22 | | "3" -> (if%const false then "five" else "three") 23 | 24 | let suite = "Test ppx_const" >::: [ 25 | "test_ppx_const" >:: test_ppx_const; 26 | ] 27 | 28 | let _ = 29 | run_test_tt_main suite 30 | --------------------------------------------------------------------------------