├── .gitattributes ├── .github └── workflows │ ├── ci.yml │ └── print-esy-cache.js ├── .gitignore ├── .ocamlformat ├── CHANGES.md ├── LICENSE ├── README.md ├── VERSION ├── dune-project ├── logs-ppx.opam ├── nix ├── default.nix ├── generic.nix └── sources.nix ├── package.json ├── scripts └── release.sh ├── shell.nix ├── src ├── Ppx.ml └── dune └── test ├── dune ├── pp.ml ├── test.expected.ml └── test.ml /.gitattributes: -------------------------------------------------------------------------------- 1 | # Supresses the lock folder from the diffs 2 | esy.lock/* linguist-generated=true 3 | 4 | # Tell github that .re and .rei files are Reason 5 | *.re linguist-language=Reason 6 | *.rei linguist-language=Reason 7 | 8 | # Declare shell files to have LF endings on checkout 9 | # On Windows, the default git setting for `core.autocrlf` 10 | # means that when checking out code, LF endings get converted 11 | # to CRLF. This causes problems for shell scripts, as bash 12 | # gets choked up on the extra `\r` character. 13 | * text eol=lf 14 | 15 | # Source code 16 | *.bash text eol=lf 17 | *.bat text eol=crlf 18 | *.cmd text eol=crlf 19 | *.coffee text 20 | *.css text 21 | *.htm text diff=html 22 | *.html text diff=html 23 | *.inc text 24 | *.ini text 25 | *.js text 26 | *.json text 27 | *.jsx text 28 | *.less text 29 | *.ls text 30 | *.map text -diff 31 | *.od text 32 | *.onlydata text 33 | *.php text diff=php 34 | *.pl text 35 | *.ps1 text eol=crlf 36 | *.py text diff=python 37 | *.rb text diff=ruby 38 | *.sass text 39 | *.scm text 40 | *.scss text diff=css 41 | *.sh text eol=lf 42 | *.sql text 43 | *.styl text 44 | *.tag text 45 | *.ts text 46 | *.tsx text 47 | *.xml text 48 | *.xhtml text diff=html 49 | 50 | # Docker 51 | Dockerfile text 52 | 53 | # Documentation 54 | *.ipynb text 55 | *.markdown text 56 | *.md text 57 | *.mdwn text 58 | *.mdown text 59 | *.mkd text 60 | *.mkdn text 61 | *.mdtxt text 62 | *.mdtext text 63 | *.txt text 64 | AUTHORS text 65 | CHANGELOG text 66 | CHANGES text 67 | CONTRIBUTING text 68 | COPYING text 69 | copyright text 70 | *COPYRIGHT* text 71 | INSTALL text 72 | license text 73 | LICENSE text 74 | NEWS text 75 | readme text 76 | *README* text 77 | TODO text 78 | 79 | # Templates 80 | *.dot text 81 | *.ejs text 82 | *.haml text 83 | *.handlebars text 84 | *.hbs text 85 | *.hbt text 86 | *.jade text 87 | *.latte text 88 | *.mustache text 89 | *.njk text 90 | *.phtml text 91 | *.tmpl text 92 | *.tpl text 93 | *.twig text 94 | *.vue text 95 | 96 | # Configs 97 | *.cnf text 98 | *.conf text 99 | *.config text 100 | .editorconfig text 101 | .env text 102 | .gitattributes text 103 | .gitconfig text 104 | .htaccess text 105 | *.lock text -diff 106 | package-lock.json text -diff 107 | *.toml text 108 | *.yaml text 109 | *.yml text 110 | browserslist text 111 | Makefile text 112 | makefile text 113 | 114 | # Heroku 115 | Procfile text 116 | 117 | # Graphics 118 | *.ai binary 119 | *.bmp binary 120 | *.eps binary 121 | *.gif binary 122 | *.gifv binary 123 | *.ico binary 124 | *.jng binary 125 | *.jp2 binary 126 | *.jpg binary 127 | *.jpeg binary 128 | *.jpx binary 129 | *.jxr binary 130 | *.pdf binary 131 | *.png binary 132 | *.psb binary 133 | *.psd binary 134 | # SVG treated as an asset (binary) by default. 135 | *.svg text 136 | # If you want to treat it as binary, 137 | # use the following line instead. 138 | # *.svg binary 139 | *.svgz binary 140 | *.tif binary 141 | *.tiff binary 142 | *.wbmp binary 143 | *.webp binary 144 | 145 | # Audio 146 | *.kar binary 147 | *.m4a binary 148 | *.mid binary 149 | *.midi binary 150 | *.mp3 binary 151 | *.ogg binary 152 | *.ra binary 153 | 154 | # Video 155 | *.3gpp binary 156 | *.3gp binary 157 | *.as binary 158 | *.asf binary 159 | *.asx binary 160 | *.fla binary 161 | *.flv binary 162 | *.m4v binary 163 | *.mng binary 164 | *.mov binary 165 | *.mp4 binary 166 | *.mpeg binary 167 | *.mpg binary 168 | *.ogv binary 169 | *.swc binary 170 | *.swf binary 171 | *.webm binary 172 | 173 | # Archives 174 | *.7z binary 175 | *.gz binary 176 | *.jar binary 177 | *.rar binary 178 | *.tar binary 179 | *.zip binary 180 | 181 | # Fonts 182 | *.ttf binary 183 | *.eot binary 184 | *.otf binary 185 | *.woff binary 186 | *.woff2 binary 187 | 188 | # Executables 189 | *.exe binary 190 | *.pyc binary 191 | 192 | # RC files (like .babelrc or .eslintrc) 193 | *.*rc text 194 | 195 | # Ignore files (like .npmignore or .gitignore) 196 | *.*ignore text 197 | -------------------------------------------------------------------------------- /.github/workflows/ci.yml: -------------------------------------------------------------------------------- 1 | name: CI 2 | 3 | on: 4 | push: 5 | branches: 6 | - main 7 | 8 | jobs: 9 | build-esy: 10 | runs-on: ${{ matrix.os }} 11 | strategy: 12 | fail-fast: false 13 | matrix: 14 | os: 15 | - macos-latest 16 | - ubuntu-latest 17 | - windows-latest 18 | steps: 19 | - uses: actions/checkout@v2 20 | - uses: actions/setup-node@v1 21 | with: 22 | node-version: 12 23 | - name: Install esy 24 | run: npm install -g esy@0.6.7 25 | 26 | - name: Try to restore install cache 27 | uses: actions/cache@v2 28 | with: 29 | path: ~/.esy/source 30 | key: source-${{ matrix.os }}-${{ hashFiles('**/index.json') }} 31 | 32 | - name: Install dependencies 33 | run: esy install 34 | 35 | - name: Print esy cache 36 | id: print_esy_cache 37 | run: node .github/workflows/print-esy-cache.js 38 | 39 | - name: Try to restore dependencies cache 40 | id: deps-cache 41 | uses: actions/cache@v2 42 | with: 43 | path: ${{ steps.print_esy_cache.outputs.esy_cache }} 44 | key: build-${{ matrix.os }}-${{ hashFiles('**/index.json') }} 45 | restore-keys: build-${{ matrix.os }}- 46 | 47 | - name: Build dependencies 48 | if: steps.deps-cache.outputs.cache-hit != 'true' 49 | run: esy build-dependencies 50 | 51 | - name: Build project 52 | run: esy build 53 | 54 | - name: Run tests 55 | run: | 56 | esy test 57 | env: 58 | OCAML_ERROR_STYLE: "short" 59 | OCAML_COLOR: "never" 60 | 61 | - uses: actions/upload-artifact@v2 62 | with: 63 | name: ${{ matrix.os }} 64 | path: _build/default/bin/Bin.exe 65 | 66 | # Cleanup build cache if dependencies have changed 67 | - name: Clean build cache 68 | if: steps.deps-cache.outputs.cache-hit != 'true' 69 | run: esy cleanup . 70 | build-opam: 71 | strategy: 72 | fail-fast: false 73 | matrix: 74 | os: 75 | - macos-latest 76 | - ubuntu-latest 77 | - windows-latest 78 | ocaml-version: 79 | - 4.13.0 80 | - 4.11.2 81 | - 4.8.1 82 | runs-on: ${{ matrix.os }} 83 | steps: 84 | - name: Checkout code 85 | uses: actions/checkout@v2 86 | 87 | - name: Use OCaml ${{ matrix.ocaml-version }} 88 | uses: avsm/setup-ocaml@v1 89 | with: 90 | ocaml-version: ${{ matrix.ocaml-version }} 91 | - run: opam install . --deps-only --with-doc --with-test 92 | - run: opam exec -- dune build 93 | - run: opam exec -- dune runtest 94 | -------------------------------------------------------------------------------- /.github/workflows/print-esy-cache.js: -------------------------------------------------------------------------------- 1 | const fs = require("fs"); 2 | const os = require("os"); 3 | const path = require("path"); 4 | 5 | const ESY_FOLDER = process.env.ESY__PREFIX 6 | ? process.env.ESY__PREFIX 7 | : path.join(os.homedir(), ".esy"); 8 | const esy3 = fs 9 | .readdirSync(ESY_FOLDER) 10 | .filter((name) => name.length > 0 && name[0] === "3") 11 | .sort() 12 | .pop(); 13 | console.log(`::set-output name=esy_cache::${path.join(ESY_FOLDER, esy3, "i")}`); 14 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | *.annot 2 | *.cmo 3 | *.cma 4 | *.cmi 5 | *.a 6 | *.o 7 | *.cmx 8 | *.cmxs 9 | *.cmxa 10 | 11 | # ocamlbuild working directory 12 | _build/ 13 | 14 | # ocamlbuild targets 15 | *.byte 16 | *.native 17 | 18 | # oasis generated files 19 | setup.data 20 | setup.log 21 | 22 | # Merlin configuring file for Vim and Emacs 23 | .merlin 24 | 25 | # Dune generated files 26 | *.install 27 | 28 | # Local OPAM switch 29 | _opam/ 30 | 31 | # Esy generated files 32 | _esy/ 33 | _release/ 34 | _export/ 35 | npm-debug.log 36 | yarn-error.log 37 | node_modules/ 38 | 39 | /lib/* 40 | !/lib/js/* 41 | /bundledOutputs/ 42 | npm-debug.log 43 | .DS_Store 44 | .bsb.lock 45 | 46 | # Yarn Integrity file 47 | .yarn-integrity 48 | 49 | # dotenv environment variables file 50 | .env 51 | .env.test 52 | 53 | # parcel-bundler cache (https://parceljs.org/) 54 | .cache 55 | 56 | # Vim 57 | .*.swp 58 | 59 | *.bs.js 60 | **.xre 61 | 62 | .vscode 63 | esy.lock 64 | -------------------------------------------------------------------------------- /.ocamlformat: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ulrikstrid/logs-ppx/191f799f37f9914655c5499ca57871b5a26356f4/.ocamlformat -------------------------------------------------------------------------------- /CHANGES.md: -------------------------------------------------------------------------------- 1 | ## 0.2.0 2 | 3 | - More hygenic output of logging function `m` -> `logger-function` 4 | - Breaking: Emit `Log` instead of `Logs` since that is the recommendation 5 | 6 | ## 0.1.0 7 | 8 | - Initial release 9 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | BSD 3-Clause License 2 | 3 | Copyright (c) 2020, Ulrik Strid 4 | All rights reserved. 5 | 6 | Redistribution and use in source and binary forms, with or without 7 | modification, are permitted provided that the following conditions are met: 8 | 9 | 1. Redistributions of source code must retain the above copyright notice, this 10 | list of conditions and the following disclaimer. 11 | 12 | 2. Redistributions in binary form must reproduce the above copyright notice, 13 | this list of conditions and the following disclaimer in the documentation 14 | and/or other materials provided with the distribution. 15 | 16 | 3. Neither the name of the copyright holder nor the names of its 17 | contributors may be used to endorse or promote products derived from 18 | this software without specific prior written permission. 19 | 20 | THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" 21 | AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE 22 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 23 | DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE 24 | FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL 25 | DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR 26 | SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER 27 | CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, 28 | OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE 29 | OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 30 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # logs-ppx 2 | 3 | A simple ppx to remove some boilerplate when using the excelent [Logs library](https://github.com/dbuenzli/logs). 4 | 5 | ## Usage 6 | 7 | Add this to your dune stanza for your executable 8 | 9 | ```lisp 10 | (preprocess 11 | (pps logs-ppx)) 12 | ``` 13 | 14 | and make sure that the `logs` library has also been added to the `libraries` field, e.g., 15 | 16 | ```lisp 17 | (library 18 | (name foo) 19 | (libraries logs)) 20 | ``` 21 | 22 | Then you use it like this in OCaml: 23 | 24 | ```ocaml 25 | (* Convention to avoid name clashes *) 26 | module Log = Logs 27 | 28 | [%log debug "Hello %s!" "world"] 29 | (* Which genrates the following *) 30 | Logs.debug (fun m -> m "Hello %s!" "world") 31 | ``` 32 | 33 | And in Reason it looks like this: 34 | 35 | ```reason 36 | [%log debug("Hello %s!", "world")]; 37 | // Which generates the following 38 | Logs.debug(m => m("Hello %s!", "world")); 39 | ``` 40 | 41 | ## Thanks 42 | 43 | A huge thanks goes to @davesnx for a lot of help and his [starter repo](https://github.com/davesnx/ppxlib-simple-example) 44 | -------------------------------------------------------------------------------- /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.0 2 | -------------------------------------------------------------------------------- /dune-project: -------------------------------------------------------------------------------- 1 | (lang dune 2.8) 2 | 3 | (name logs-ppx) 4 | 5 | (version 0.2.0) 6 | 7 | (generate_opam_files true) 8 | 9 | (source 10 | (github ulrikstrid/logs-ppx)) 11 | 12 | (license BSD-3-Clause) 13 | 14 | (authors "Ulrik Strid") 15 | 16 | (maintainers "ulrik.strid@outlook.com") 17 | 18 | (package 19 | (name logs-ppx) 20 | (synopsis "PPX to cut down on boilerplate when using Logs") 21 | (description 22 | "PPX to remove the closure when logging with Logs. The PPX will still generate the closure which keeps the nice properties of the Logs library.") 23 | (depends 24 | (ocaml 25 | (>= 4.8.0)) 26 | dune 27 | ppxlib 28 | (logs :with-test) 29 | )) 30 | -------------------------------------------------------------------------------- /logs-ppx.opam: -------------------------------------------------------------------------------- 1 | # This file is generated by dune, edit dune-project instead 2 | opam-version: "2.0" 3 | version: "0.2.0" 4 | synopsis: "PPX to cut down on boilerplate when using Logs" 5 | description: 6 | "PPX to remove the closure when logging with Logs. The PPX will still generate the closure which keeps the nice properties of the Logs library." 7 | maintainer: ["ulrik.strid@outlook.com"] 8 | authors: ["Ulrik Strid"] 9 | license: "BSD-3-Clause" 10 | homepage: "https://github.com/ulrikstrid/logs-ppx" 11 | bug-reports: "https://github.com/ulrikstrid/logs-ppx/issues" 12 | depends: [ 13 | "ocaml" {>= "4.8.0"} 14 | "dune" {>= "2.8"} 15 | "ppxlib" 16 | "logs" {with-test} 17 | "odoc" {with-doc} 18 | ] 19 | build: [ 20 | ["dune" "subst"] {dev} 21 | [ 22 | "dune" 23 | "build" 24 | "-p" 25 | name 26 | "-j" 27 | jobs 28 | "@install" 29 | "@runtest" {with-test} 30 | "@doc" {with-doc} 31 | ] 32 | ] 33 | dev-repo: "git+https://github.com/ulrikstrid/logs-ppx.git" 34 | -------------------------------------------------------------------------------- /nix/default.nix: -------------------------------------------------------------------------------- 1 | { pkgs ? import ./sources.nix {}, doCheck ? false }: 2 | 3 | { 4 | native = pkgs.callPackage ./generic.nix { 5 | inherit doCheck; 6 | }; 7 | 8 | musl64 = 9 | let 10 | pkgsCross = pkgs.pkgsCross.musl64.pkgsStatic; 11 | 12 | in 13 | pkgsCross.callPackage ./generic.nix { 14 | static = true; 15 | inherit doCheck; 16 | ocamlPackages = pkgsCross.ocamlPackages; 17 | }; 18 | } 19 | -------------------------------------------------------------------------------- /nix/generic.nix: -------------------------------------------------------------------------------- 1 | { pkgs, stdenv, lib, ocamlPackages, static ? false, doCheck }: 2 | 3 | with ocamlPackages; 4 | 5 | rec { 6 | oidc = buildDunePackage { 7 | pname = "logs-ppx"; 8 | version = "0.2.0-dev"; 9 | 10 | src = lib.filterGitSource { 11 | src = ./..; 12 | dirs = [ "src" "test" ]; 13 | files = [ "dune-project" "logs-ppx.opam" ]; 14 | }; 15 | 16 | useDune2 = true; 17 | 18 | buildInputs = [ 19 | logs 20 | ]; 21 | 22 | propagatedBuildInputs = [ 23 | ppxlib 24 | ]; 25 | 26 | inherit doCheck; 27 | 28 | meta = { 29 | description = "Base functions and types to work with OpenID Connect."; 30 | license = stdenv.lib.licenses.bsd3; 31 | }; 32 | }; 33 | } 34 | -------------------------------------------------------------------------------- /nix/sources.nix: -------------------------------------------------------------------------------- 1 | { ocamlVersion ? "4_11" }: 2 | let 3 | overlays = 4 | builtins.fetchTarball 5 | https://github.com/anmonteiro/nix-overlays/archive/71e29d7504cde79d162b7911856bfca7420cfc69.tar.gz; 6 | 7 | in 8 | import "${overlays}/sources.nix" { 9 | overlays = [ 10 | (import overlays) 11 | (self: super: { 12 | ocamlPackages = super.ocaml-ng."ocamlPackages_${ocamlVersion}"; 13 | 14 | pkgsCross.musl64.pkgsStatic = super.pkgsCross.musl64.pkgsStatic.appendOverlays [ 15 | (self: super: { 16 | ocamlPackages = super.ocaml-ng."ocamlPackages_${ocamlVersion}"; 17 | }) 18 | ]; 19 | }) 20 | ]; 21 | } 22 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "logs-ppx", 3 | "version": "0.2.0", 4 | "description": "This is an example of ppblix", 5 | "author": "Ulrik Strid ", 6 | "keywords": [ 7 | "reason", 8 | "ocaml", 9 | "ppx", 10 | "logs" 11 | ], 12 | "esy": { 13 | "build": "dune build -p logs-ppx", 14 | "buildDev": [ 15 | [ 16 | "dune", 17 | "build", 18 | "--promote-install-files", 19 | "--root", 20 | "." 21 | ] 22 | ], 23 | "buildEnv": { 24 | "ODOC_SYNTAX": "re" 25 | }, 26 | "release": { 27 | "bin": [ 28 | "Bin" 29 | ] 30 | } 31 | }, 32 | "devDependencies": { 33 | "@opam/logs": "*", 34 | "@opam/merlin": "*", 35 | "ocaml": "4.10.0", 36 | "@opam/dune": "2.7.1", 37 | "@opam/ppxlib": "0.14.0", 38 | "@opam/ocaml-lsp-server": "ocaml/ocaml-lsp:ocaml-lsp-server.opam#master", 39 | "@opam/ocamlformat": "*", 40 | "@opam/odoc": "*" 41 | }, 42 | "scripts": { 43 | "build": "esy dune build -p logs-ppx", 44 | "watch": "esy dune build -p logs-ppx --watch", 45 | "test": "esy b dune runtest", 46 | "promote": "esy dune promote", 47 | "doc": "esy dune build @doc", 48 | "doc-path": "esy echo #{self.target_dir}/default/_doc/_html/index.html", 49 | "format": "esy dune build @fmt --auto-promote", 50 | "utop": "esy dune utop lib -- -implicit-bindings" 51 | } 52 | } 53 | -------------------------------------------------------------------------------- /scripts/release.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | TAG="$1" 4 | 5 | if [ -z "$TAG" ]; then 6 | printf "Usage: ./dune-release.sh \n" 7 | printf "Please make sure that dune-release is available.\n" 8 | exit 1 9 | fi 10 | 11 | step() 12 | { 13 | printf "Continue? [Yn] " 14 | read action 15 | if [ "x$action" == "xn" ]; then exit 2; fi 16 | if [ "x$action" == "xN" ]; then exit 2; fi 17 | } 18 | 19 | dune-release tag "$TAG" -v 20 | echo "Next step: distrib" 21 | step 22 | dune-release distrib -p logs-ppx -t "$TAG" -v 23 | echo "Next step: publish distrib" 24 | step 25 | dune-release publish distrib -p logs-ppx -t "$TAG" -v 26 | echo "Next step: opam pkg" 27 | step 28 | dune-release opam pkg -p logs-ppx -t "$TAG" -v 29 | echo "Next step: opam submit" 30 | step 31 | dune-release opam submit -p logs-ppx -t "$TAG" -v 32 | -------------------------------------------------------------------------------- /shell.nix: -------------------------------------------------------------------------------- 1 | let 2 | pkgs = import ./nix/sources.nix { }; 3 | inherit (pkgs) lib; 4 | oidcPkgs = pkgs.recurseIntoAttrs (import ./nix { inherit pkgs; }).native; 5 | logsPpxDrvs = lib.filterAttrs (_: value: lib.isDerivation value) oidcPkgs; 6 | 7 | filterDrvs = inputs: 8 | lib.filter 9 | (drv: 10 | # we wanna filter our own packages so we don't build them when entering 11 | # the shell. They always have `pname` 12 | !(lib.hasAttr "pname" drv) || 13 | drv.pname == null || 14 | !(lib.any (name: name == drv.pname || name == drv.name) (lib.attrNames logsPpxDrvs))) 15 | inputs; 16 | in 17 | with pkgs; 18 | 19 | (mkShell { 20 | inputsFrom = lib.attrValues logsPpxDrvs; 21 | buildInputs = with ocamlPackages; [ 22 | merlin 23 | ocamlformat 24 | dune-release 25 | cacert 26 | curl 27 | git 28 | ]; 29 | }).overrideAttrs (o: { 30 | propagatedBuildInputs = filterDrvs o.propagatedBuildInputs; 31 | buildInputs = filterDrvs o.buildInputs; 32 | }) 33 | -------------------------------------------------------------------------------- /src/Ppx.ml: -------------------------------------------------------------------------------- 1 | open Ppxlib 2 | module Builder = Ast_builder.Default 3 | 4 | let expander ~ctxt e l = 5 | let loc = Expansion_context.Extension.extension_point_loc ctxt in 6 | let log_loc, log_type = 7 | match e.pexp_desc with 8 | | Pexp_ident { loc; txt = Lident log_type } -> (loc, log_type) 9 | | _ -> (loc, "info") 10 | in 11 | Builder.eapply ~loc 12 | (Builder.pexp_ident ~loc:log_loc 13 | { txt = Ldot (Lident "Log", log_type); loc }) 14 | [ 15 | Builder.pexp_fun ~loc Nolabel None 16 | (Builder.ppat_var ~loc { txt = "logger-function"; loc }) 17 | (Builder.eapply ~loc 18 | (Builder.pexp_ident ~loc { txt = Lident "logger-function"; loc }) 19 | (List.map snd l)); 20 | ] 21 | 22 | let extension = 23 | let pattern = 24 | let open Ast_pattern in 25 | (* this grabs the first argument from the apply and 26 | then passes it into Log.sexp's [log] parameter. 27 | All the arguments of apply are parsed as a message. *) 28 | pstr (pstr_eval (pexp_apply __ __) nil ^:: nil) 29 | in 30 | Context_free.Rule.extension 31 | (Extension.V3.declare "log" Expression pattern expander) 32 | 33 | let () = Driver.register_transformation ~rules:[ extension ] "logs-ppx" 34 | -------------------------------------------------------------------------------- /src/dune: -------------------------------------------------------------------------------- 1 | (library 2 | (name logs_ppx) 3 | (public_name logs-ppx) 4 | (kind ppx_rewriter) 5 | (libraries ppxlib)) 6 | -------------------------------------------------------------------------------- /test/dune: -------------------------------------------------------------------------------- 1 | (executable 2 | (name pp) 3 | (modules pp) 4 | (libraries logs-ppx ppxlib)) 5 | 6 | (rule 7 | (targets test.actual.ml) 8 | (deps 9 | (:pp pp.exe) 10 | (:input test.ml)) 11 | (action 12 | (run ./%{pp} --impl %{input} -o %{targets}))) 13 | 14 | (rule 15 | (alias runtest) 16 | (action 17 | (diff test.expected.ml test.actual.ml))) 18 | -------------------------------------------------------------------------------- /test/pp.ml: -------------------------------------------------------------------------------- 1 | let () = Ppxlib.Driver.standalone () 2 | -------------------------------------------------------------------------------- /test/test.expected.ml: -------------------------------------------------------------------------------- 1 | let src = Logs.Src.create "mylib" ~doc:"logs for mylib" 2 | module Log = (val (Logs.src_log src : (module Logs.LOG))) 3 | let () = 4 | Log.debug (fun logger-function -> logger-function "Hello %s!" "World") 5 | -------------------------------------------------------------------------------- /test/test.ml: -------------------------------------------------------------------------------- 1 | let src = Logs.Src.create "mylib" ~doc:"logs for mylib" 2 | module Log = (val Logs.src_log src : Logs.LOG) 3 | 4 | let () = [%log debug "Hello %s!" "World"] 5 | --------------------------------------------------------------------------------