diff --git a/Jenkinsfile b/Jenkinsfile index 14a6dd6ce..28b09ae37 100644 --- a/Jenkinsfile +++ b/Jenkinsfile @@ -100,6 +100,8 @@ pipeline { description: "Pass STANCFLAGS to make/local, default none") booleanParam(name:"run_slow_perf_tests", defaultValue: false, description:"Run additional 'slow' performance tests") string(defaultValue: '', name: 'build_multiarch_docker_tag', description: "Docker tag for the multiarch image") + booleanParam(name:"build_multiarch", defaultValue: false, description:"Build multiarch images even when not on 'master'") + } options { parallelsAlwaysFailFast() @@ -113,7 +115,7 @@ pipeline { GIT_AUTHOR_EMAIL = 'mc.stanislaw@gmail.com' GIT_COMMITTER_NAME = 'Stan Jenkins' GIT_COMMITTER_EMAIL = 'mc.stanislaw@gmail.com' - MULTIARCH_DOCKER_TAG = 'multiarch-ocaml-4.14-v2' + MULTIARCH_DOCKER_TAG = 'multiarch-ocaml-4.14-v2-and-cmdliner' } stages { stage('Verify changes') { @@ -749,7 +751,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { @@ -783,7 +785,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { @@ -814,7 +816,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { @@ -845,7 +847,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { @@ -876,7 +878,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { @@ -907,7 +909,7 @@ pipeline { beforeAgent true allOf { expression { !skipRebuildingBinaries } - anyOf { buildingTag(); branch 'master' } + anyOf { buildingTag(); branch 'master'; expression { params.build_multiarch } } } } agent { diff --git a/README.md b/README.md index 5a00d5ce0..1efbd204d 100644 --- a/README.md +++ b/README.md @@ -1,9 +1,10 @@ # A New Stan-to-C++ Compiler, stanc3 + This repo contains a new compiler for Stan, stanc3, written in OCaml. -Since version 2.26, this has been the default compiler for Stan. See [this wiki](https://github.com/stan-dev/stanc3/wiki/changes-from-stanc2) for a list of minor differences between this compiler and the previous Stan compiler. +The latest release (with binaries for many platforms and a JavaScript version) +can be found on the [releases page](https://github.com/stan-dev/stanc3/releases). -To read more about why we built this, see this [introductory blog post](https://statmodeling.stat.columbia.edu/2019/03/13/stanc3-rewriting-the-stan-compiler/). For some discussion as to how we chose OCaml, see [this accidental flamewar](https://discourse.mc-stan.org/t/choosing-the-new-stan-compilers-implementation-language/6203). -We're testing [these models](https://jenkins.flatironinstitute.org/job/Stan/job/Stanc3/job/master/) (listed under Test Results) on every pull request. +Since version 2.26, this has been the default compiler for Stan. [![Build Status](https://jenkins.flatironinstitute.org/job/Stan/job/Stanc3/job/master/badge/icon?style=flat-square)](https://jenkins.flatironinstitute.org/job/Stan/job/Stanc3/job/master/) [![codecov](https://codecov.io/gh/stan-dev/stanc3/branch/master/graph/badge.svg?token=tt76nVXoht)](https://codecov.io/gh/stan-dev/stanc3) @@ -11,12 +12,12 @@ We're testing [these models](https://jenkins.flatironinstitute.org/job/Stan/job/ Documentation for users of stanc3 is in the Stan Users' Guide [here](https://mc-stan.org/docs/stan-users-guide/using-the-stan-compiler.html) -The Stanc3 Developer documentation is available here: https://mc-stan.org/stanc3/stanc - +Developer documentation is available [here](https://mc-stan.org/stanc3/stanc). Want to contribute? See [Getting Started](https://mc-stan.org/stanc3/stanc/getting_started.html) for setup instructions and some useful commands. ## High-level concepts, invariants, and 30,000-ft view + Stanc3 has 4 main src packages: `frontend`, `middle`, `analysis_and_optimization` and `stan_math_backend`. These are pieced together by the `driver` module. @@ -35,11 +36,13 @@ flowchart The goal is to keep as many details about the way Stan is implemented by the core C++ implementation in the Stan Math backend library as possible. The Middle library contains the MIR and currently any types or functions used by the two ends. -The entrypoint for the compiler is in `src/stanc/stanc.ml` which sequences the various components together. +The entrypoint for the compiler is in `src/stanc/stanc.ml`. This parse command line arguments and +calls into `src/driver/Entry.ml`, which sequences the various components together. ### Distinct stanc Phases The phases of stanc are summarized in the following information flowchart and list. + ```mermaid flowchart TB @@ -90,36 +93,35 @@ flowchart TB ``` 1. [Lex](src/frontend/lexer.mll) the Stan language into tokens. -1. [Parse](src/frontend/parser.mly) Stan language into AST that represents the syntax quite closely and aides in development of pretty-printers and linters. `stanc --debug-ast` to print this out. -1. Typecheck & add type information [Typechecker.ml](src/frontend/Typechecker.ml). `stanc --debug-decorated-ast` -1. [Lower](src/frontend/Ast_to_Mir.ml) into [Middle Intermediate Representation](src/middle/Program.ml) (AST -> MIR) `stanc --debug-mir` (or `--debug-mir-pretty`) -1. Backend-specific MIR transform (MIR -> MIR) [Transform_Mir.ml](src/stan_math_backend/Transform_Mir.ml) `stanc --debug-transformed-mir` -1. Analyze & optimize (MIR -> MIR) -1. Code generation (MIR -> [C++](src/stan_math_backend/Stan_math_code_gen.ml)) (or other outputs, like [Tensorflow](https://github.com/stan-dev/stan2tfp/)). +2. [Parse](src/frontend/parser.mly) Stan language into AST that represents the syntax quite closely and aides in development of pretty-printers and linters. `stanc --debug-ast` to print this out. +3. Typecheck & add type information [Typechecker.ml](src/frontend/Typechecker.ml). `stanc --debug-decorated-ast` +4. [Lower](src/frontend/Ast_to_Mir.ml) into [Middle Intermediate Representation](src/middle/Program.ml) (AST -> MIR) `stanc --debug-mir` (or `--debug-mir-pretty`) +5. Backend-specific MIR transform (MIR -> MIR) [Transform_Mir.ml](src/stan_math_backend/Transform_Mir.ml) `stanc --debug-transformed-mir` +6. Analyze & optimize (MIR -> MIR) +7. Code generation (MIR -> [C++](src/stan_math_backend/Lower_program.ml)) (or other outputs, like [Tensorflow](https://github.com/stan-dev/stan2tfp/)). ### The central data structures -1. `src/frontend/Ast.ml` defines the AST. The AST is intended to have a direct 1-1 mapping with the syntax, so there are things like parentheses being kept around. -The pretty-printer in the frontend uses the AST and attempts to keep user syntax the same while just adjusting whitespace. +1. `src/frontend/Ast.ml` defines the AST. The AST is intended to have a direct 1-1 mapping with the syntax, so there are things like parentheses being kept around. The pretty-printer in the frontend uses the AST and attempts to keep user syntax the same while just adjusting whitespace. The AST uses a particular functional programming trick to add metadata to the AST (and its other tree types), sometimes called [the "two-level types" pattern](http://lambda-the-ultimate.org/node/4170#comment-63836). Essentially, many of the tree variant types are parameterized by something that ends up being a placeholder not for just metadata but for the recursive type including metadata, sometimes called the fixed point. So instead of recursively referencing `expression` you would instead reference type parameter `'e`, which will later be filled in with something like `type expr_with_meta = metadata expression`. The AST intends to keep very close to Stan-level semantics and syntax in every way. -2. `src/middle/Program.ml` contains the MIR (Middle Intermediate Language). `src/frontend/Ast_to_Mir.ml` performs the lowering and attempts to strip out as much Stan-specific semantics and syntax as possible, though this is still something of a work-in-progress. +2. `src/middle/Program.ml` contains the MIR (Middle Intermediate Language). `src/frontend/Ast_to_Mir.ml` performs the lowering and attempts to strip out as much Stan-specific semantics and syntax as possible, though this is still something of a work-in-progress. The MIR uses the same two-level types idea to add metadata, notably expression types and autodiff levels as well as locations on many things. The MIR is used as the output data type from the frontend and the input for dataflow analysis, optimization (which also outputs MIR), and code generation. - -3. `src/stan_math_backend/Cpp.ml` defines a minimal representation of C++ used in code generation. +3. `src/stan_math_backend/Cpp.ml` defines a minimal representation of C++ used in code generation. This is intentionally simpler than both the above structures and than a true C++ AST and is tailored pretty specifically to the C++ generated in our model class. ## Design goals -* **Multiple phases** - each with human-readable intermediate representations for easy debugging and optimization design. -* **Optimizing** - takes advantage of info known at the Stan language level. Minimize information we must teach users for them to write performant code. -* **Holistic** - bring as much of the code as possible into the MIR for whole-program optimization. -* **Research platform** - enable a new class of optimizations based on probability theory. -* **Modular** - architect & build in a way that makes it easy to outsource things like symbolic differentiation to external libraries and to use parts of the compiler as the basis for other tools built around the Stan language. -* **Simplicity first** - When making a choice between correct simplicity and a perceived performance benefit, we want to make the choice for simplicity unless we can show significant (> 5%) benchmark improvements to compile times or run times. Premature optimization is the root of all evil. + +- **Multiple phases** - each with human-readable intermediate representations for easy debugging and optimization design. +- **Optimizing** - takes advantage of info known at the Stan language level. Minimize information we must teach users for them to write performant code. +- **Holistic** - bring as much of the code as possible into the MIR for whole-program optimization. +- **Research platform** - enable a new class of optimizations based on probability theory. +- **Modular** - architect & build in a way that makes it easy to outsource things like symbolic differentiation to external libraries and to use parts of the compiler as the basis for other tools built around the Stan language. +- **Simplicity first** - When making a choice between correct simplicity and a perceived performance benefit, we want to make the choice for simplicity unless we can show significant (> 5%) benchmark improvements to compile times or run times. Premature optimization is the root of all evil. diff --git a/docs/dependencies.mld b/docs/dependencies.mld index a91a32b5e..ca2db09a8 100644 --- a/docs/dependencies.mld +++ b/docs/dependencies.mld @@ -16,6 +16,7 @@ These are automatically installed through the [scripts/install_ocaml.sh] and - {{:http://gallium.inria.fr/~fpottier/menhir/manual.html}Menhir} 20230608 (a parser generator and parsing library) - {{:https://github.com/ocaml-ppx/ppx_deriving}ppx_deriving} 5.2.1 (a tool for generating boilerplate code) - {{:https://erratique.ch/software/fmt}fmt} 0.9.0 (a library for pretty-printing of formatted text) +- {{:https://erratique.ch/software/cmdliner}cmdliner} 1.3.0 (a library for command line parsing) - yojson 2.1.0 (a library for producing JSON files) diff --git a/docs/getting_started.mld b/docs/getting_started.mld index f94ecd6b8..cd0fe87ff 100644 --- a/docs/getting_started.mld +++ b/docs/getting_started.mld @@ -38,37 +38,6 @@ for Windows development. Once you have installed and configured WSL, you can proceed through the steps above through the WSL shell. - -{2:nix Alternative: Using Nix} - -{{:https://nixos.org/nix/}Nix} is a declarative package manager with a focus on reproducible builds. -We provide the ability to use Nix to build, test and run Stanc3. We recommend trying the [opam] -instructions first if you are not an existing Nix user, with these as a backup. - -If you have nix installed, you can build Stanc3 by running the following command in the [stanc3] directory: - -[nix-build] - -The binary will be in [result/bin/stanc]. It may take a minute the first time you run it. -Alternatively, the following is sometimes a faster way to build: - -[nix-shell --command "dune build"] - -To run the test suite, run: - -[nix-shell --command "dune build --profile release @runtest"] - -To install Stanc3 to your system, run: - -[nix-env -i -f default.nix] - -To drop into a sandboxed development shell with all of the build dependencies -of Stanc3 plus packages for an OCaml development environment -([dune], [ocp-indent], [ocamlformat], [merlin] and [utop]), run: - -[nix-shell] - - {1 Development} {2 Useful commands} diff --git a/dune-project b/dune-project index 1d07a9c45..5d152b8af 100644 --- a/dune-project +++ b/dune-project @@ -24,6 +24,8 @@ (= 0.9.0)) (yojson (= 2.1.0)) + (cmdliner + (= 1.3.0)) (ocamlformat (and :with-test diff --git a/scripts/build_multiarch_stanc3.sh b/scripts/build_multiarch_stanc3.sh index e89b7a3a9..f99e4fabb 100755 --- a/scripts/build_multiarch_stanc3.sh +++ b/scripts/build_multiarch_stanc3.sh @@ -1,21 +1,27 @@ # Architecture naming isn't consistent between QEMU and Docker, so lookup correct naming if [ $1 = "mips64el" ]; then + export DOCK_PLATFORM="linux/mips64le" export DOCK_ARCH="mips64le" export DOCK_VARIANT="" elif [ $1 = "arm64" ]; then + export DOCK_PLATFORM="linux/arm64" export DOCK_ARCH="arm64" export DOCK_VARIANT="" elif [ $1 = "ppc64el" ]; then + export DOCK_PLATFORM="linux/ppc64le" export DOCK_ARCH="ppc64le" export DOCK_VARIANT="" elif [ $1 = "armhf" ]; then + export DOCK_PLATFORM="linux/arm/v7" export DOCK_ARCH="arm" export DOCK_VARIANT="v7" elif [ $1 = "armel" ]; then + export DOCK_PLATFORM="linux/arm/v6" export DOCK_ARCH="arm" export DOCK_VARIANT="v6" elif [ $1 = "s390x" ]; then + export DOCK_PLATFORM="linux/s390x" export DOCK_ARCH="s390x" export DOCK_VARIANT="" fi @@ -26,6 +32,7 @@ DOCKER_IMAGE_TAG="$2" SHA=$(skopeo inspect --raw docker://stanorg/stanc3:${DOCKER_IMAGE_TAG} | jq '.manifests | .[] | select(.platform.architecture==env.DOCK_ARCH and .platform.variant==(if env.DOCK_VARIANT == "" then null else env.DOCK_VARIANT end)).digest' | tr -d '"') # Register QEMU translation binaries -docker run --rm --privileged multiarch/qemu-user-static --reset +docker run --privileged --rm tonistiigi/binfmt --install all -docker run --group-add=987 --group-add=980 --group-add=988 -v $(pwd):$(pwd):rw,z stanorg/stanc3:${DOCKER_IMAGE_TAG}@$SHA /bin/bash -c "cd $(pwd) && eval \$(opam env) && dune subst && dune build @install --profile static --root=." +docker run --platform=${DOCK_PLATFORM} --group-add=987 --group-add=980 --group-add=988 -v $(pwd):$(pwd):rw,z \ + stanorg/stanc3:${DOCKER_IMAGE_TAG}@$SHA /bin/bash -c "cd $(pwd) && eval \$(opam env) && dune subst && dune clean && dune build @install --profile static --root=." diff --git a/scripts/docker/builder/Dockerfile b/scripts/docker/builder/Dockerfile index d094f5f3d..df95f3ba2 100644 --- a/scripts/docker/builder/Dockerfile +++ b/scripts/docker/builder/Dockerfile @@ -1,5 +1,5 @@ -# Pull the ubuntu:bionic base image -FROM ubuntu:bionic +# Pull the ubuntu:jammy base image +FROM ubuntu:jammy USER root @@ -21,4 +21,4 @@ RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers RUN chown -R jenkins:sudo /usr/local USER jenkins -WORKDIR /home/jenkins \ No newline at end of file +WORKDIR /home/jenkins diff --git a/scripts/docker/debian-windows/Dockerfile b/scripts/docker/debian-windows/Dockerfile index e2611874e..ef8fe8efd 100644 --- a/scripts/docker/debian-windows/Dockerfile +++ b/scripts/docker/debian-windows/Dockerfile @@ -1,5 +1,5 @@ -#Pull the ubuntu:bionic image -FROM ubuntu:bionic +#Pull the ubuntu:jammy image +FROM ubuntu:jammy USER root @@ -35,13 +35,5 @@ RUN printf "\n" | bash -x install_ocaml.sh "stanc" COPY ./scripts/install_build_deps_windows.sh ./ RUN bash -x install_build_deps_windows.sh -#Copy our script and install dev dependencies -COPY ./scripts/install_dev_deps.sh ./ -RUN bash -x install_dev_deps.sh - -# Install Javascript dev environment -COPY ./scripts/install_js_deps.sh ./ -RUN opam update; bash -x install_js_deps.sh - #Specify our entrypoint ENTRYPOINT [ "opam", "config", "exec", "--" ] diff --git a/scripts/docker/debian/Dockerfile b/scripts/docker/debian/Dockerfile index 36d4c4be8..6b19f2c29 100644 --- a/scripts/docker/debian/Dockerfile +++ b/scripts/docker/debian/Dockerfile @@ -1,5 +1,5 @@ -# Pull the ubuntu:bionic base image -FROM ubuntu:bionic +# Pull the ubuntu:jammy base image +FROM ubuntu:jammy USER root @@ -27,6 +27,7 @@ WORKDIR /home/jenkins #Copy our script and install ocaml + init COPY ./scripts/install_opam.sh ./ RUN printf "\n" | bash -x install_opam.sh +RUN opam update # Install and initialize ocaml COPY ./scripts/install_ocaml.sh ./ @@ -34,15 +35,15 @@ RUN printf "\n" | bash -x install_ocaml.sh "stanc" # Install build dependencies COPY ./scripts/install_build_deps.sh ./ -RUN opam update; bash -x install_build_deps.sh +RUN bash -x install_build_deps.sh # Install dev dependencies COPY ./scripts/install_dev_deps.sh ./ -RUN opam update; bash -x install_dev_deps.sh +RUN bash -x install_dev_deps.sh # Install Javascript dev environment (js_of_ocaml 5.4.0) COPY ./scripts/install_js_deps.sh ./ -RUN opam update; bash -x install_js_deps.sh +RUN bash -x install_js_deps.sh # Specify our entrypoint ENTRYPOINT [ "opam", "config", "exec", "--" ] diff --git a/scripts/docker/multiarch/Dockerfile b/scripts/docker/multiarch/Dockerfile index 22663c3ec..ba091e2a9 100644 --- a/scripts/docker/multiarch/Dockerfile +++ b/scripts/docker/multiarch/Dockerfile @@ -1,5 +1,5 @@ # Base image -FROM debian:buster-20220622-slim +FROM debian:bullseye-20241202 USER root @@ -51,18 +51,17 @@ RUN eval $(opam env) && opam update # Native-code compilation not available on MIPS, fall back to bytecode RUN if [ "$(cat /qemu-setup/arch)" = "mips64el" ]; then \ - opam switch create 4.14.1 --packages=ocaml-variants.4.14.1+options,ocaml-option-bytecode-only && opam switch 4.14.1 && opam pin num https://github.com/ocaml/num.git -y; \ + opam switch create 4.14.1 --packages=ocaml-variants.4.14.1+options,ocaml-option-bytecode-only && opam switch 4.14.1; \ else \ opam switch create 4.14.1 && opam switch 4.14.1; \ fi -RUN eval $(opam env) && opam repo add internet https://opam.ocaml.org - -RUN eval $(opam env) && opam install -y dune +RUN eval $(opam env) && opam repository add dune-universe git+https://github.com/dune-universe/opam-overlays.git RUN eval $(opam env) && opam update && opam upgrade +RUN eval $(opam env) && opam install -y dune RUN eval $(opam env) && opam install -y core.v0.16.0 RUN eval $(opam env) && opam install -y menhir.20230608 RUN eval $(opam env) && opam install -y ppx_deriving.5.2.1 RUN eval $(opam env) && opam install -y fmt.0.9.0 RUN eval $(opam env) && opam install -y yojson.2.1.0 -RUN eval $(opam env) \ No newline at end of file +RUN eval $(opam env) && opam install -y cmdliner.1.3.0+dune diff --git a/scripts/docker/publish/Dockerfile b/scripts/docker/publish/Dockerfile index 3669b50b7..49da70cc6 100644 --- a/scripts/docker/publish/Dockerfile +++ b/scripts/docker/publish/Dockerfile @@ -1,5 +1,5 @@ -# Pull the ubuntu:bionic base image -FROM ubuntu:bionic +# Pull the ubuntu:jammy base image +FROM ubuntu:jammy USER root @@ -25,4 +25,4 @@ RUN echo '%sudo ALL=(ALL) NOPASSWD:ALL' >> /etc/sudoers RUN chown -R jenkins:sudo /usr/local USER jenkins -WORKDIR /home/jenkins \ No newline at end of file +WORKDIR /home/jenkins diff --git a/scripts/docker/static/Dockerfile b/scripts/docker/static/Dockerfile index 920031bd3..ded55e7dd 100644 --- a/scripts/docker/static/Dockerfile +++ b/scripts/docker/static/Dockerfile @@ -28,21 +28,20 @@ RUN CONTAINERS_COMMON_VER=$(curl -s https://dl-cdn.alpinelinux.org/alpine/latest apk add cont.apk && \ apk add skopeo.apk -#Switch back to the normal user +# Switch back to the normal user USER jenkins -#Init opam, create and switch to 4.14.0, update shell environment +# Init opam, create and switch to 4.14.0, update shell environment RUN opam init --disable-sandboxing --bare -y +RUN opam update RUN opam switch create 4.14.0 RUN opam switch 4.14.0 RUN eval $(opam env) -RUN opam repo add internet https://opam.ocaml.org - COPY ./scripts/install_build_deps.sh ./ RUN opam update; bash -x install_build_deps.sh RUN opam install odoc -y -#Specify our entrypoint -ENTRYPOINT [ "opam", "config", "exec", "--" ] \ No newline at end of file +# Specify our entrypoint +ENTRYPOINT [ "opam", "config", "exec", "--" ] diff --git a/scripts/install_build_deps.sh b/scripts/install_build_deps.sh index 58f186c23..b90747494 100755 --- a/scripts/install_build_deps.sh +++ b/scripts/install_build_deps.sh @@ -7,6 +7,6 @@ eval $(opam env) opam pin -y core v0.16.1 --no-action -opam install -y dune core.v0.16.1 menhir.20230608 ppx_deriving.5.2.1 fmt.0.9.0 yojson.2.1.0 +opam install -y dune core.v0.16.1 menhir.20230608 ppx_deriving.5.2.1 fmt.0.9.0 yojson.2.1.0 cmdliner.1.3.0 eval $(opam env) diff --git a/scripts/install_build_deps_windows.sh b/scripts/install_build_deps_windows.sh index 97deeaf75..05413d4fa 100755 --- a/scripts/install_build_deps_windows.sh +++ b/scripts/install_build_deps_windows.sh @@ -8,7 +8,6 @@ eval $(opam env) # Add windows repository opam repository add windows http://github.com/ocaml-cross/opam-cross-windows.git -opam update windows # Request the compiler to be built with flambda optimizers opam install -y conf-flambda-windows @@ -17,6 +16,7 @@ opam install -y conf-flambda-windows opam install -y "ocaml-windows64=4.14.1" # Install dependencies -opam install -y core.v0.16.1 core-windows.v0.16.1 menhir.20230608 menhir-windows.20230608 ppx_deriving.5.2.1 ppx_deriving-windows.5.2.1 fmt.0.9.0 fmt-windows.0.9.0 yojson.2.1.0 yojson-windows.2.1.0 +opam install -y core.v0.16.1 core-windows.v0.16.1 menhir.20230608 menhir-windows.20230608 ppx_deriving.5.2.1 ppx_deriving-windows.5.2.1\ + fmt.0.9.0 fmt-windows.0.9.0 yojson.2.1.0 yojson-windows.2.1.0 cmdliner.1.3.0 cmdliner-windows.1.3.0 eval $(opam env) diff --git a/scripts/nix_old/README.md b/scripts/nix_old/README.md new file mode 100644 index 000000000..2f4dc7dbb --- /dev/null +++ b/scripts/nix_old/README.md @@ -0,0 +1,36 @@ +# Nix + +At one time, these scripts allowed you to build with Nix. They are no longer maintained + +The previous documentation of this option: +```odoc +{2:nix Alternative: Using Nix} + +{{:https://nixos.org/nix/}Nix} is a declarative package manager with a focus on reproducible builds. +We provide the ability to use Nix to build, test and run Stanc3. We recommend trying the [opam] +instructions first if you are not an existing Nix user, with these as a backup. + +If you have nix installed, you can build Stanc3 by running the following command in the [stanc3] directory: + +[nix-build] + +The binary will be in [result/bin/stanc]. It may take a minute the first time you run it. +Alternatively, the following is sometimes a faster way to build: + +[nix-shell --command "dune build"] + +To run the test suite, run: + +[nix-shell --command "dune build --profile release @runtest"] + +To install Stanc3 to your system, run: + +[nix-env -i -f default.nix] + +To drop into a sandboxed development shell with all of the build dependencies +of Stanc3 plus packages for an OCaml development environment +([dune], [ocp-indent], [ocamlformat], [merlin] and [utop]), run: + +[nix-shell] + +``` diff --git a/default.nix b/scripts/nix_old/default.nix similarity index 100% rename from default.nix rename to scripts/nix_old/default.nix diff --git a/shell.nix b/scripts/nix_old/shell.nix similarity index 100% rename from shell.nix rename to scripts/nix_old/shell.nix diff --git a/scripts/setup_dev_env.sh b/scripts/setup_dev_env.sh index 16df6222d..a307d61bb 100755 --- a/scripts/setup_dev_env.sh +++ b/scripts/setup_dev_env.sh @@ -9,6 +9,7 @@ then bash -x ./install_opam.sh else echo "OPAM already installed, not re-installing." + opam update fi read -p "Supply a name for the OPAM switch to be created (default: stanc): " opam_switch_name diff --git a/src/driver/Entry.ml b/src/driver/Entry.ml index ae6e24b3f..b82d2ff96 100644 --- a/src/driver/Entry.ml +++ b/src/driver/Entry.ml @@ -92,13 +92,21 @@ let stan2cpp model_name model (flags : Flags.t) (output : other_output -> unit) if flags.warn_pedantic then output (Warnings (Pedantic_analysis.warn_pedantic mir)); debug_output_mir output mir flags.debug_settings.print_mir; - let generation_context = + let* generation_context = match flags.debug_settings.debug_data_json with - | None -> Map.Poly.empty - | Some file -> - Debug_data_generation.json_to_mir - (Ast_to_Mir.gather_declarations typed_ast.datablock) - (Yojson.Basic.from_string file) in + | None -> Ok Map.Poly.empty + | Some string -> ( + try + Ok + (Debug_data_generation.json_to_mir + (Ast_to_Mir.gather_declarations typed_ast.datablock) + (Yojson.Basic.from_string string)) + with Yojson.Json_error reason -> + Error + (Errors.DebugDataError + ( Location_span.empty + , "Failed to parse data JSON for debug generation: " ^ reason + ))) in let* () = if flags.debug_settings.debug_generate_data then let* data = diff --git a/src/frontend/Debugging.ml b/src/frontend/Debugging.ml index 9f6de2bbc..d77b05f76 100644 --- a/src/frontend/Debugging.ml +++ b/src/frontend/Debugging.ml @@ -1,7 +1,5 @@ (** Some helpers for debugging *) -(* For s-expression support *) - (** Controls whether the lexing operations get logged *) let lexer_logging = ref false diff --git a/src/stanc/CLI.ml b/src/stanc/CLI.ml new file mode 100644 index 000000000..a565ef6f0 --- /dev/null +++ b/src/stanc/CLI.ml @@ -0,0 +1,463 @@ +open Core +open Frontend +open Cmdliner + +module Arguments = struct + (** Positional arguments *) + + let model_file = + let doc = + "The Stan model file to compile. This should be a .stan file or \ + .stanfunctions file (the latter automatically sets \ + $(b,--standalone-functions))." in + Arg.( + value & pos 0 (some non_dir_file) None & info [] ~docv:"MODEL_FILE" ~doc) +end + +module Options = struct + (** Normal, user-facing options and flags *) + + let qmark : Manpage.format option Term.t = + Arg.( + value + & opt ~vopt:(Some `Auto) + (some + (enum + [ ("auto", `Auto); ("pager", `Pager); ("plain", `Plain) + ; ("groff", `Groff) ])) + None + & info ["?"] ~doc:"Synonym for $(b,--help)." ~docv:"=FMT") + + let allow_undefined = + let doc = + "Do not fail if a function is declared but not defined. This usually \ + means the definition will be provided later as a C++ function." in + Arg.(value & flag & info ["allow-undefined"] ~doc) + + let auto_format = + let doc = + "Output a formatted version of the Stan program. The output can be \ + tweaked using $(b,--max-line-length) and $(b,--canonicalize)." in + Arg.(value & flag & info ["auto-format"] ~doc) + + let canonicalizer_settings = + let fold_canonicalize_options + (settings : Canonicalize.canonicalizer_settings) = function + | `Deprecations -> {settings with deprecations= true} + | `Parentheses -> {settings with parentheses= true} + | `Braces -> {settings with braces= true} + | `Includes -> {settings with inline_includes= true} + | `Strip_comments -> {settings with strip_comments= true} in + let doc = + "Enable specific canonicalizations in a comma separated list. Options \ + are 'deprecations', 'parentheses', 'braces', 'includes', \ + 'strip-comments'." in + let base_arg = + Arg.enum + [ ("deprecations", `Deprecations); ("parentheses", `Parentheses) + ; ("braces", `Braces); ("includes", `Includes) + ; ("strip-comments", `Strip_comments) ] in + let make_case_insensitive converter = + Arg.( + conv + ( Fn.compose (conv_parser converter) String.lowercase + , conv_printer converter )) in + Term.( + const (List.fold ~f:fold_canonicalize_options ~init:Canonicalize.none) + $ Arg.( + value + & opt (list (make_case_insensitive base_arg)) [] + & info ["canonicalize"] ~doc ~docv:"OPTIONS")) + + let filename_in_msg = + let doc = "Sets the filename used in compiler and runtime errors. " in + Arg.( + value + & opt (some string) None + & info ["filename-in-msg"] ~absent:"$(b,MODEL_FILE)" ~doc ~docv:"FILENAME") + + let include_paths = + let doc = + "A comma-separated list of directories which are searched whenever an \ + #include directive is parsed." in + Term.( + const (fun p -> Frontend.Include_files.FileSystemPaths p) + $ Arg.( + value + & opt (list string) [] + & info ["include-paths"] ~doc ~absent:"\"\"" ~docv:"DIRS")) + + let info = + let doc = "If set, print information about the model." in + Arg.(value & flag & info ["info"] ~doc) + + let max_line_length = + let doc = "Set the maximum line length for the formatter." in + Arg.(value & opt int 78 & info ["max-line-length"] ~doc ~docv:"LENGTH") + + let name = + let doc = + "Take a string to set as the model name. This controls the namespace in \ + the generated C++." in + Arg.( + value + & opt (some string) None + & info ["name"] ~doc ~absent:"$(b,MODEL_FILE_NAME)_model" ~docv:"NAME") + + let output_file = + let doc = + "Output file for generated C++ code (default = \ + \"$(b,MODEL_FILE_NAME).hpp\") or auto-formatting output (default: no \ + file/print to stdout)." in + Arg.(value & opt string "" & info ["o"; "output"] ~doc ~docv:"FILENAME") + + let o0 = + let doc = "$(i,(Default)) Do not apply optimizations to the Stan code." in + Arg.(value & flag & info ["O0"] ~doc) + + let o1 = + let doc = + "Only basic optimizations are applied. Recommended. The deprecated \ + option $(b,--O) is aliased to this starting in Stan 2.37." in + Arg.(value & flag & info ["O1"] ~doc) + + let oexperimental = + let doc = + "$(i,(Experimental)) Apply all compiler optimizations. Some of these are \ + not thorougly tested and may not always improve a programs performance." + in + Arg.(value & flag & info ["Oexperimental"] ~doc) + + let print_canonical = + let doc = + "Prints the canonicalized program. Equivalent to $(b,--auto-format \ + --canonicalize=deprecations,includes,parentheses,braces)." in + Arg.(value & flag & info ["print-canonical"] ~doc) + + let print_cpp = + let doc = "If set, output the generated C++ Stan model class to stdout." in + Arg.(value & flag & info ["print-cpp"] ~doc) + + let standalone_functions = + let doc = + "If set, the generated C++ will only contain the code for the functions \ + in the functions block, not the full Stan model class." in + Arg.(value & flag & info ["standalone-functions"] ~doc) + + let use_opencl = + let doc = + "If set, try to use matrix_cl signatures for supported Stan Math \ + functions." in + Arg.(value & flag & info ["use-opencl"] ~doc) + + let warn_pedantic = + let doc = + "Emit warnings about common mistakes in Stan programs. $(i,Note:) This \ + may produce false positive warnings." in + Arg.(value & flag & info ["warn-pedantic"] ~doc) + + let warn_uninitialized = + let doc = + "$(i,(Experimental)) Emit warnings about uninitialized variables." in + Arg.(value & flag & info ["warn-uninitialized"] ~doc) +end + +module Commands = struct + (** These would be subcommands if we hadn't originally made + them options. They change 'modes' and do not need a model file *) + + let dump_stan_math_signatures = + let doc = + "Dump out the list of supported function signatures the for Stan Math \ + backend." in + Arg.( + value & flag + & info ["dump-stan-math-signatures"] ~doc ~docs:Manpage.s_commands) + + let dump_stan_math_distributions = + let doc = + "Dump out the list of supported probability distributions and their \ + supported suffix types for the Stan Math backend." in + Arg.( + value & flag + & info ["dump-stan-math-distributions"] ~doc ~docs:Manpage.s_commands) +end + +module Debug_Options = struct + (** Options that are developer-facing or for debugging *) + + let section = "EXTRA OPTIONS" + let docs = section + + let debug_ast = + let doc = + "For debugging purposes: print the undecorated AST, before semantic \ + checking." in + Arg.(value & flag & info ["debug-ast"] ~doc ~docs) + + let debug_data_json : string option Term.ret Term.t = + let doc = + "Provide (possibly partially specified) data block values for use with \ + $(b,--debug-generate-data) or $(b,--debug-generate-inits)." in + Term.( + const (function + | None -> `Ok None + | Some file -> ( + try `Ok (Some (In_channel.read_all file)) + with _ -> + `Error (true, "File '" ^ file ^ "' not found or cannot be opened.") + )) + $ Arg.( + value + & opt (some non_dir_file) None + & info ["debug-data-file"] ~doc ~docv:"JSON_FILE" ~docs)) + + let debug_decorated_ast = + let doc = + "For debugging purposes: print the decorated AST, after semantic \ + checking." in + Arg.(value & flag & info ["debug-decorated-ast"] ~doc ~docs) + + let debug_generate_data = + let doc = + "For debugging purposes: generate a mock dataset to run the model on." + in + Arg.(value & flag & info ["debug-generate-data"] ~doc ~docs) + + let debug_generate_inits = + let doc = + "For debugging purposes: generate a mock initial value for each \ + parameter." in + Arg.(value & flag & info ["debug-generate-inits"] ~doc ~docs) + + let debug_lex = + let doc = "For debugging purposes: print the lexer actions." in + Arg.(value & flag & info ["debug-lex"] ~doc ~docs) + + let debug_lir = + let doc = + "For debugging purposes: print the C++ LIR as a s-expression. Mainly for \ + comparison with $(b,--print-cpp)." in + Arg.(value & flag & info ["debug-lir"] ~doc ~docs) + + let debug_mem_patterns = + let doc = + "For debugging purposes: print a list of matrix variables and their \ + memory type, either AoS (array of structs) or the more efficient SoA \ + (struct of arrays). Only has an effect when optimizations are turned \ + on." in + Arg.(value & flag & info ["debug-mem-patterns"] ~doc ~docs) + + (** helper for paired args like --debug-mir and --debug-mir-pretty *) + let debug_basic_or_pretty ~doc flag_name : Driver.Flags.debug_options Term.t = + let doc_pretty = + String.substr_replace_all ~pattern:"print" ~with_:"pretty-print" doc in + Arg.( + value + & vflag Driver.Flags.Off + [ (Basic, info [flag_name] ~doc ~docs) + ; (Pretty, info [flag_name ^ "-pretty"] ~doc:doc_pretty ~docs) ]) + + let debug_mir = + let doc = "For debugging purposes: print the MIR after lowering." in + debug_basic_or_pretty ~doc "debug-mir" + + let debug_optimized_mir = + let doc = + "For debugging purposes: print the MIR after it's been optimized. Only \ + has an effect when optimizations are turned on." in + debug_basic_or_pretty ~doc "debug-optimized-mir" + + let debug_parse = + let doc = "For debugging purposes: print the parser actions." in + Arg.(value & flag & info ["debug-parse"] ~doc ~docs) + + let debug_transformed_mir = + let doc = + "For debugging purposes: print the MIR after the backend has transformed \ + it." in + debug_basic_or_pretty ~doc "debug-transformed-mir" + + let force_soa = + let doc = + "Debugging features. Valid values: $(b,-fsoa) to force on the Struct of \ + Arrays optimization. $(b,-fno-soa) to force it off." in + let soa_conv = + Arg.(conv_parser (enum [("soa", Some true); ("no-soa", Some false)])) + in + let soa_printer pf = function + | Some true -> Fmt.string pf "soa" + | Some false -> Fmt.string pf "no-soa" + | None -> Fmt.string pf "" in + Arg.( + value + & opt (conv (soa_conv, soa_printer)) None + & info ["f"] ~doc ~docv:"SETTING" ~docs) +end + +(** Flags common to all compiler drivers and those specific to the command line *) +type compiler_flags = + { debug_lex: bool + ; debug_parse: bool + ; print_cpp: bool + ; name: string option + ; output_file: string + ; flags: Driver.Flags.t + ; model_file: string } + +(* The result of parsing the command line. Either we're in one of the dump sub-commands, + or we have all the flags we need to proceed *) +type cli_result = DumpMathSigs | DumpMathDists | Compile of compiler_flags + +module Conversion = struct + (** Helper terms to combine the above arguments into useful ocaml values *) + + open Cmdliner.Term.Syntax + + let optimization_level : + Analysis_and_optimization.Optimize.optimization_level Term.t = + let+ o0 = Options.o0 + and+ o1 = Options.o1 + and+ oexperimental = Options.oexperimental in + if o0 then Analysis_and_optimization.Optimize.O0 + else if o1 then O1 + else if oexperimental then Oexperimental + else O0 + + let debug_settings : Driver.Flags.debug_settings Term.t = + let open Debug_Options in + let+ print_ast = debug_ast + and+ print_typed_ast = debug_decorated_ast + and+ print_mir = debug_mir + and+ print_transformed_mir = debug_transformed_mir + and+ print_optimized_mir = debug_optimized_mir + and+ print_mem_patterns = debug_mem_patterns + and+ force_soa = force_soa + and+ print_lir = debug_lir + and+ debug_generate_data = debug_generate_data + and+ debug_generate_inits = debug_generate_inits + and+ debug_data_json = Term.ret debug_data_json in + Driver.Flags. + { print_ast + ; print_typed_ast + ; print_mir + ; print_transformed_mir + ; print_optimized_mir + ; print_mem_patterns + ; force_soa + ; print_lir + ; debug_generate_data + ; debug_generate_inits + ; debug_data_json } + + let flags : Driver.Flags.t Term.t = + let open Options in + let+ optimization_level = optimization_level + and+ allow_undefined = allow_undefined + and+ standalone_functions = standalone_functions + and+ use_opencl = use_opencl + and+ include_source = include_paths + and+ info = info + and+ auto_format = auto_format + and+ line_length = max_line_length + and+ print_canonical = print_canonical + and+ canonicalizer_settings = canonicalizer_settings + and+ warn_pedantic = warn_pedantic + and+ warn_uninitialized = warn_uninitialized + and+ filename_in_msg = filename_in_msg + and+ debug_settings = debug_settings in + Driver.Flags. + { optimization_level + ; allow_undefined + ; functions_only= false + ; standalone_functions + ; use_opencl + ; include_source + ; info + ; version= false + ; auto_format= auto_format || print_canonical + ; line_length + ; canonicalizer_settings= + (if print_canonical then Canonicalize.legacy + else canonicalizer_settings) + ; warn_pedantic + ; warn_uninitialized + ; filename_in_msg + ; debug_settings } + + let cli_result = + let+ qmark = Options.qmark + and+ dump_stan_math_distributions = Commands.dump_stan_math_distributions + and+ dump_stan_math_sigs = Commands.dump_stan_math_signatures + and+ debug_lex = Debug_Options.debug_lex + and+ debug_parse = Debug_Options.debug_parse + and+ print_cpp = Options.print_cpp + and+ name = Options.name + and+ output_file = Options.output_file + and+ model_file = Arguments.model_file + and+ flags = flags in + match qmark with + | Some fmt -> `Help (fmt, None) + | None -> ( + if dump_stan_math_distributions then `Ok DumpMathDists + else if dump_stan_math_sigs then `Ok DumpMathSigs + else + match model_file with + | None -> `Error (true, "No model file provided") + | Some model_file -> + `Ok + (Compile + { debug_lex + ; debug_parse + ; print_cpp + ; name + ; output_file + ; model_file + ; flags })) +end + +let cmd : cli_result Cmd.t = + let doc = "compile Stan programs to C++" in + let man = + [ `S Manpage.s_description + ; `P + "The Stan compiler (also known as $(i,stanc) or $(i,stanc3)) reads a \ + Stan file and compiles it to C++. It also allows for other Stan \ + program manipulation like formatting ($(b,--auto-format)) and \ + introspection ($(b,--info))." + ; `P "For more information on Stan, see https://mc-stan.org." + ; `P + "For more documentation on the compiler for users, see \ + https://mc-stan.org/docs/stan-users-guide/using-stanc.html." + ; `P + "For more information on the compiler for developers, see \ + https://mc-stan.org/stanc3/stanc/."; `S Manpage.s_arguments + ; `S Manpage.s_options; `S Manpage.s_commands + ; `P + "The following flags will cause the compiler to exit after printing \ + information. No $(b,MODEL_FILE) is required."; `S Debug_Options.section + ; `P + "These flags are provided primarily for development and debugging; \ + their exact behavior should not be relied on." + ; `S Manpage.s_exit_status; `S Manpage.s_bugs + ; `P "Please report at https://github.com/stan-dev/stanc3/issues/new." ] + in + let exits = + Cmd.Exit. + [ info ~doc:"on success." 0; info ~doc:"on compilation failure." 1 + ; info ~doc:"on command line parsing errors." 124 + ; info ~doc:"on internal compiler errors. Please file a bug!" 125 ] in + let info = + Cmd.info "%%NAME%%" + ~version:("%%NAME%%3 %%VERSION%%" ^ " (" ^ Sys.os_type ^ ")") + ~sdocs:Manpage.s_options ~doc ~man ~exits in + Cmd.v info (Term.ret Conversion.cli_result) + +let parse_or_exit () : cli_result = + let argv = Sys.get_argv () in + (* workaround the fact that single letter flags must be - in CmdLiner *) + Array.map_inplace argv ~f:(fun s -> + if String.equal s "--O" then "--O1" else s); + match Cmd.eval_value' ~argv ~catch:false cmd with + | `Ok flags -> flags + | `Exit code -> exit code diff --git a/src/stanc/dune b/src/stanc/dune index 148bb4d93..2748ae3c4 100644 --- a/src/stanc/dune +++ b/src/stanc/dune @@ -3,10 +3,10 @@ (modes (byte c) (best exe)) - (libraries driver) + (libraries driver cmdliner) (instrumentation (backend bisect_ppx)) - (modules Stanc) + (modules Stanc CLI) (public_name stanc) (preprocess (pps ppx_jane))) diff --git a/src/stanc/stanc.ml b/src/stanc/stanc.ml index 42a69a70e..1858e2b1a 100644 --- a/src/stanc/stanc.ml +++ b/src/stanc/stanc.ml @@ -2,321 +2,16 @@ open Core open Frontend -module Optimize = Analysis_and_optimization.Optimize module Stan_math_signatures = Middle.Stan_math_signatures -(** The name of the executable. *) -let name = "%%NAME%%" +let write filename data = + try Out_channel.write_all filename ~data + with Sys_error msg -> + Fmt.epr "Error writing to file '%s': %s@." filename msg; + exit 1 -(** The usage message. *) -let usage = "Usage: " ^ name ^ " [option] ... " - -(* some flags aren't available in other drivers / don't make sense there *) -let dump_stan_math_sigs = ref false -let dump_stan_math_distributions = ref false -let model_file = ref "" -let output_file = ref "" -let print_model_cpp = ref false - -(* but most flags are stored here to be handled by the driver *) -let driver_flags = ref Driver.Flags.default - -let parse_canonical_options (settings : Canonicalize.canonicalizer_settings) - string = - match String.lowercase string with - | "deprecations" -> {settings with deprecations= true} - | "parentheses" -> {settings with parentheses= true} - | "braces" -> {settings with braces= true} - | "includes" -> {settings with inline_includes= true} - | "strip-comments" -> {settings with strip_comments= true} - | s -> - raise - @@ Arg.Bad - ("Unrecognized canonicalizer option '" ^ s - ^ "'. \n\ - Should be one of 'deprecations', 'parentheses', 'braces', \ - 'includes', 'strip-comments'") - -(** Some example command-line options here *) -let options = - Arg.align - [ ( "--debug-lex" - , Arg.Set Debugging.lexer_logging - , " For debugging purposes: print the lexer actions" ) - ; ( "--debug-parse" - , Arg.Set Debugging.grammar_logging - , " For debugging purposes: print the parser actions" ) - ; ( "--debug-ast" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_ast= true} }) - , " For debugging purposes: print the undecorated AST, before semantic \ - checking" ) - ; ( "--debug-decorated-ast" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_typed_ast= true} }) - , " For debugging purposes: print the decorated AST, after semantic \ - checking" ) - ; ( "--debug-generate-data" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with debug_generate_data= true} - }) - , " For debugging purposes: generate a mock dataset to run the model on" - ) - ; ( "--debug-generate-inits" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with debug_generate_inits= true} - }) - , " For debugging purposes: generate a mock initial value for each \ - parameter" ) - ; ( "--debug-data-file" - , Arg.String - (fun s -> - driver_flags := - { !driver_flags with - debug_settings= - { !driver_flags.debug_settings with - debug_data_json= Some (In_channel.read_all s) } }) - , " For --debug-generate-data or --debug-generate-inits" ) - ; ( "--debug-mir" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_mir= Basic} }) - , " For debugging purposes: print the MIR as an S-expression." ) - ; ( "--debug-mir-pretty" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_mir= Pretty} }) - , " For debugging purposes: pretty-print the MIR." ) - ; ( "--debug-optimized-mir" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_optimized_mir= Basic} - }) - , " For debugging purposes: print the MIR after it's been optimized. \ - Only has an effect when optimizations are turned on." ) - ; ( "--debug-optimized-mir-pretty" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_optimized_mir= Pretty} - }) - , " For debugging purposes: pretty print the MIR after it's been \ - optimized. Only has an effect when optimizations are turned on." ) - ; ( "--debug-lir" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_lir= true} }) - , " For debugging purposes: print the C++ LIR as a s-expression. Mainly \ - for comparison with --print-cpp" ) - ; ( "--debug-mem-patterns" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with print_mem_patterns= true} - }) - , " For debugging purposes: print a list of matrix variables and their \ - memory type, either AoS (array of structs) or the more efficient SoA \ - (struct of arrays). Only has an effect when optimizations are turned \ - on." ) - ; ( "--debug-transformed-mir" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - { !driver_flags.debug_settings with - print_transformed_mir= Basic } }) - , " For debugging purposes: print the MIR after the backend has \ - transformed it." ) - ; ( "--debug-transformed-mir-pretty" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - { !driver_flags.debug_settings with - print_transformed_mir= Pretty } }) - , " For debugging purposes: pretty print the MIR after the backend has \ - transformed it." ) - ; ( "--dump-stan-math-signatures" - , Arg.Set dump_stan_math_sigs - , " Dump out the list of supported type signatures for Stan Math backend." - ) - ; ( "--dump-stan-math-distributions" - , Arg.Set dump_stan_math_distributions - , " Dump out the list of supported probability distributions and their \ - supported suffix types for the Stan Math backend." ) - ; ( "--warn-uninitialized" - , Arg.Unit - (fun () -> - driver_flags := {!driver_flags with warn_uninitialized= true}) - , " Emit warnings about uninitialized variables to stderr. Currently an \ - experimental feature." ) - ; ( "--warn-pedantic" - , Arg.Unit - (fun () -> driver_flags := {!driver_flags with warn_pedantic= true}) - , " Emit warnings about common mistakes in Stan programs." ) - ; ( "--auto-format" - , Arg.Unit - (fun () -> driver_flags := {!driver_flags with auto_format= true}) - , " Pretty prints a formatted version of the Stan program." ) - ; ( "--canonicalize" - , Arg.String - (fun s -> - let canonicalizer_settings = - List.fold ~f:parse_canonical_options - ~init:!driver_flags.canonicalizer_settings - (String.split s ~on:',') in - driver_flags := {!driver_flags with canonicalizer_settings}) - , " Enable specific canonicalizations in a comma separated list. Options \ - are 'deprecations', 'parentheses', 'braces', 'includes', \ - 'strip-comments'." ) - ; ( "--max-line-length" - , Arg.Int - (fun line_length -> driver_flags := {!driver_flags with line_length}) - , " Set the maximum line length for the formatter. Defaults to 78 \ - characters." ) - ; ( "--print-canonical" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - auto_format= true - ; canonicalizer_settings= Canonicalize.legacy }) - , " Prints the canonicalized program. Equivalent to --auto-format \ - --canonicalize deprecations,includes,parentheses,braces" ) - ; ( "--version" - , Arg.Unit (fun () -> driver_flags := {!driver_flags with version= true}) - , " Display stanc version number" ) - ; ( "--name" - , Arg.Set_string Typechecker.model_name - , " Take a string to set the model name (default = \ - \"$model_filename_model\")" ) - ; ( "--O0" - , Arg.Unit - (fun () -> - driver_flags := {!driver_flags with optimization_level= Optimize.O0}) - , "\t(Default) Do not apply optimizations to the Stan code." ) - ; ( "--O1" - , Arg.Unit - (fun () -> - driver_flags := {!driver_flags with optimization_level= Optimize.O1}) - , "\tApply level 1 compiler optimizations (only basic optimizations)." ) - ; ( "--Oexperimental" - , Arg.Unit - (fun () -> - driver_flags := - {!driver_flags with optimization_level= Optimize.Oexperimental}) - , "\t(Experimental) Apply all compiler optimizations. Some of these are \ - not thorougly tested and may not always improve a programs \ - performance." ) - ; ( "--O" - , Arg.Unit - (fun () -> - driver_flags := {!driver_flags with optimization_level= Optimize.O1}) - , "\tSame as --O1." ) - ; ( "-fno-soa" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with force_soa= Some false} }) - , "\tTurn off the Struct of Arrays optimization" ) - ; ( "-fsoa" - , Arg.Unit - (fun () -> - driver_flags := - { !driver_flags with - debug_settings= - {!driver_flags.debug_settings with force_soa= Some true} }) - , "\tTurn on the Struct of Arrays optimization" ) - ; ( "--o" - , Arg.Set_string output_file - , " Take the path to an output file for generated C++ code (default = \ - \"$name.hpp\") or auto-formatting output (default: no file/print to \ - stdout)" ) - ; ( "--print-cpp" - , Arg.Set print_model_cpp - , " If set, output the generated C++ Stan model class to stdout." ) - ; ( "--allow-undefined" - , Arg.Unit - (fun () -> driver_flags := {!driver_flags with allow_undefined= true}) - , " Do not fail if a function is declared but not defined" ) - ; ( "--include-paths" - , Arg.String - (fun str -> - driver_flags := - { !driver_flags with - include_source= - Include_files.FileSystemPaths - (String.split_on_chars ~on:[','] str) }) - , " Takes a comma-separated list of directories that may contain a file \ - in an #include directive (default = \"\")" ) - ; ( "--use-opencl" - , Arg.Unit - (fun () -> driver_flags := {!driver_flags with use_opencl= true}) - , " If set, try to use matrix_cl signatures." ) - ; ( "--standalone-functions" - , Arg.Unit - (fun () -> - driver_flags := {!driver_flags with standalone_functions= true}) - , " If set, the generated C++ will be the standalone functions C++ code." - ) - ; ( "--filename-in-msg" - , Arg.String - (fun filename_in_msg -> - driver_flags := - {!driver_flags with filename_in_msg= Some filename_in_msg}) - , " Sets the filename used in compiler errors. Uses actual filename by \ - default." ) - ; ( "--info" - , Arg.Unit (fun () -> driver_flags := {!driver_flags with info= true}) - , " If set, print information about the model." ) ] - -let model_file_err () = - Arg.usage options ("Please specify a model_file.\n" ^ usage); - exit 127 - -let add_file filename = - if String.equal !model_file "" then model_file := filename - else raise (Arg.Bad "Please specify only one model_file") - -let print_or_write_and_exit data = - if not (String.equal !output_file "") then - Out_channel.write_all !output_file ~data +let print_or_write_and_exit output_file data = + if not (String.equal output_file "") then write output_file data else print_endline data; exit 0 @@ -324,55 +19,64 @@ let print_and_exit data = print_endline data; exit 0 -let output_callback printed_filename : Driver.Entry.other_output -> unit = - function +let output_callback output_file printed_filename : + Driver.Entry.other_output -> unit = function | Info s -> print_and_exit s - | Version s -> print_and_exit (s ^ " (" ^ Sys.os_type ^ ")") + | Version s -> + (* note: in practice, handled by Cmdliner for this driver *) + print_and_exit (s ^ " (" ^ Sys.os_type ^ ")") | Generated s | Formatted s -> (* these options will use the --o flag if it was passed *) - print_or_write_and_exit s + print_or_write_and_exit output_file s | DebugOutput s | Memory_patterns s -> (* historically, these flags didn't prevent you from continuing *) print_string s | Warnings ws -> Warnings.pp_warnings Fmt.stderr ?printed_filename ws let main () = - (* Parse the arguments. *) - Arg.parse options add_file usage; - (* Deal with multiple modalities *) - if !dump_stan_math_sigs then ( - Stan_math_signatures.pretty_print_all_math_sigs Format.std_formatter (); - exit 0); - if !dump_stan_math_distributions then ( - Stan_math_signatures.pretty_print_all_math_distributions - Format.std_formatter (); - exit 0); - if String.equal !model_file "" && not !driver_flags.version then - model_file_err (); + let CLI. + {debug_lex; debug_parse; print_cpp; name; output_file; model_file; flags} + = + match CLI.parse_or_exit () with + (* Deal with multiple modalities *) + | DumpMathSigs -> + Stan_math_signatures.pretty_print_all_math_sigs Format.std_formatter (); + exit 0 + | DumpMathDists -> + Stan_math_signatures.pretty_print_all_math_distributions + Format.std_formatter (); + exit 0 + | Compile settings -> settings in + Debugging.lexer_logging := debug_lex; + Debugging.grammar_logging := debug_parse; + Typechecker.model_name := Option.value ~default:"" name; Driver.Flags.set_backend_args_list (* remove executable itself from list before passing *) (Sys.get_argv () |> Array.to_list |> List.tl_exn); (* if we only have functions, always compile as standalone *) - if String.is_suffix !model_file ~suffix:".stanfunctions" then - driver_flags := - {!driver_flags with standalone_functions= true; functions_only= true}; + let flags = + if String.is_suffix model_file ~suffix:".stanfunctions" then + {flags with standalone_functions= true; functions_only= true} + else flags in match - Driver.Entry.stan2cpp !model_file (`File !model_file) !driver_flags - (output_callback !driver_flags.filename_in_msg) + Driver.Entry.stan2cpp model_file (`File model_file) flags + (output_callback output_file flags.filename_in_msg) with | Ok cpp_str -> - if String.equal !output_file "" then - output_file := Driver.Flags.remove_dotstan !model_file ^ ".hpp"; - Out_channel.write_all !output_file ~data:cpp_str; - if !print_model_cpp then print_endline cpp_str + if print_cpp then print_endline cpp_str; + let out = + if String.equal output_file "" then + Driver.Flags.remove_dotstan model_file ^ ".hpp" + else output_file in + write out cpp_str | Error (DebugDataError _ as e) -> (* separated out to suggest the possibly-fixing flag *) - Errors.pp Fmt.stderr ?printed_filename:!driver_flags.filename_in_msg e; - if Option.is_none !driver_flags.debug_settings.debug_data_json then + Errors.pp Fmt.stderr ?printed_filename:flags.filename_in_msg e; + if Option.is_none flags.debug_settings.debug_data_json then Fmt.pf Fmt.stderr "Supplying a --debug-data-file may help@;"; exit 1 | Error e -> - Errors.pp Fmt.stderr ?printed_filename:!driver_flags.filename_in_msg e; + Errors.pp Fmt.stderr ?printed_filename:flags.filename_in_msg e; exit 1 let () = @@ -381,4 +85,4 @@ let () = | Error internal_error -> Out_channel.output_string stderr internal_error; Out_channel.flush stderr; - exit 2 + exit 125 diff --git a/stanc.opam b/stanc.opam index 0ccc2106e..ca929f57d 100644 --- a/stanc.opam +++ b/stanc.opam @@ -9,6 +9,7 @@ depends: [ "ppx_deriving" {= "5.2.1"} "fmt" {= "0.9.0"} "yojson" {= "2.1.0"} + "cmdliner" {= "1.3.0"} "ocamlformat" {with-test & = "0.26.1"} "bisect_ppx" {with-test} "merlin" {with-test} diff --git a/test/integration/cli-args/canonicalize/canonicalize.t b/test/integration/cli-args/canonicalize/canonicalize.t index 116017c22..05b8b648e 100644 --- a/test/integration/cli-args/canonicalize/canonicalize.t +++ b/test/integration/cli-args/canonicalize/canonicalize.t @@ -1,94 +1,15 @@ Test that a nonsense argument is caught $ stanc --canonicalize dummy - stanc: Unrecognized canonicalizer option 'dummy'. - Should be one of 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - Usage: %%NAME%% [option] ... - --debug-lex For debugging purposes: print the lexer actions - --debug-parse For debugging purposes: print the parser actions - --debug-ast For debugging purposes: print the undecorated AST, before semantic checking - --debug-decorated-ast For debugging purposes: print the decorated AST, after semantic checking - --debug-generate-data For debugging purposes: generate a mock dataset to run the model on - --debug-generate-inits For debugging purposes: generate a mock initial value for each parameter - --debug-data-file For --debug-generate-data or --debug-generate-inits - --debug-mir For debugging purposes: print the MIR as an S-expression. - --debug-mir-pretty For debugging purposes: pretty-print the MIR. - --debug-optimized-mir For debugging purposes: print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-optimized-mir-pretty For debugging purposes: pretty print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-lir For debugging purposes: print the C++ LIR as a s-expression. Mainly for comparison with --print-cpp - --debug-mem-patterns For debugging purposes: print a list of matrix variables and their memory type, either AoS (array of structs) or the more efficient SoA (struct of arrays). Only has an effect when optimizations are turned on. - --debug-transformed-mir For debugging purposes: print the MIR after the backend has transformed it. - --debug-transformed-mir-pretty For debugging purposes: pretty print the MIR after the backend has transformed it. - --dump-stan-math-signatures Dump out the list of supported type signatures for Stan Math backend. - --dump-stan-math-distributions Dump out the list of supported probability distributions and their supported suffix types for the Stan Math backend. - --warn-uninitialized Emit warnings about uninitialized variables to stderr. Currently an experimental feature. - --warn-pedantic Emit warnings about common mistakes in Stan programs. - --auto-format Pretty prints a formatted version of the Stan program. - --canonicalize Enable specific canonicalizations in a comma separated list. Options are 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - --max-line-length Set the maximum line length for the formatter. Defaults to 78 characters. - --print-canonical Prints the canonicalized program. Equivalent to --auto-format --canonicalize deprecations,includes,parentheses,braces - --version Display stanc version number - --name Take a string to set the model name (default = "$model_filename_model") - --O0 (Default) Do not apply optimizations to the Stan code. - --O1 Apply level 1 compiler optimizations (only basic optimizations). - --Oexperimental (Experimental) Apply all compiler optimizations. Some of these are not thorougly tested and may not always improve a programs performance. - --O Same as --O1. - -fno-soa Turn off the Struct of Arrays optimization - -fsoa Turn on the Struct of Arrays optimization - --o Take the path to an output file for generated C++ code (default = "$name.hpp") or auto-formatting output (default: no file/print to stdout) - --print-cpp If set, output the generated C++ Stan model class to stdout. - --allow-undefined Do not fail if a function is declared but not defined - --include-paths Takes a comma-separated list of directories that may contain a file in an #include directive (default = "") - --use-opencl If set, try to use matrix_cl signatures. - --standalone-functions If set, the generated C++ will be the standalone functions C++ code. - --filename-in-msg Sets the filename used in compiler errors. Uses actual filename by default. - --info If set, print information about the model. - -help Display this list of options - --help Display this list of options - [2] + %%NAME%%: option '--canonicalize': invalid element in list ('dummy'): invalid + value 'dummy', expected one of 'deprecations', 'parentheses', + 'braces', 'includes' or 'strip-comments' + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] Test capitalization - this should fail due to the lack of model_name, not the canonicalizer $ stanc --canonicalize DEPRECATIONS,parentheses,bRaCeS - Please specify a model_file. - Usage: %%NAME%% [option] ... - --debug-lex For debugging purposes: print the lexer actions - --debug-parse For debugging purposes: print the parser actions - --debug-ast For debugging purposes: print the undecorated AST, before semantic checking - --debug-decorated-ast For debugging purposes: print the decorated AST, after semantic checking - --debug-generate-data For debugging purposes: generate a mock dataset to run the model on - --debug-generate-inits For debugging purposes: generate a mock initial value for each parameter - --debug-data-file For --debug-generate-data or --debug-generate-inits - --debug-mir For debugging purposes: print the MIR as an S-expression. - --debug-mir-pretty For debugging purposes: pretty-print the MIR. - --debug-optimized-mir For debugging purposes: print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-optimized-mir-pretty For debugging purposes: pretty print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-lir For debugging purposes: print the C++ LIR as a s-expression. Mainly for comparison with --print-cpp - --debug-mem-patterns For debugging purposes: print a list of matrix variables and their memory type, either AoS (array of structs) or the more efficient SoA (struct of arrays). Only has an effect when optimizations are turned on. - --debug-transformed-mir For debugging purposes: print the MIR after the backend has transformed it. - --debug-transformed-mir-pretty For debugging purposes: pretty print the MIR after the backend has transformed it. - --dump-stan-math-signatures Dump out the list of supported type signatures for Stan Math backend. - --dump-stan-math-distributions Dump out the list of supported probability distributions and their supported suffix types for the Stan Math backend. - --warn-uninitialized Emit warnings about uninitialized variables to stderr. Currently an experimental feature. - --warn-pedantic Emit warnings about common mistakes in Stan programs. - --auto-format Pretty prints a formatted version of the Stan program. - --canonicalize Enable specific canonicalizations in a comma separated list. Options are 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - --max-line-length Set the maximum line length for the formatter. Defaults to 78 characters. - --print-canonical Prints the canonicalized program. Equivalent to --auto-format --canonicalize deprecations,includes,parentheses,braces - --version Display stanc version number - --name Take a string to set the model name (default = "$model_filename_model") - --O0 (Default) Do not apply optimizations to the Stan code. - --O1 Apply level 1 compiler optimizations (only basic optimizations). - --Oexperimental (Experimental) Apply all compiler optimizations. Some of these are not thorougly tested and may not always improve a programs performance. - --O Same as --O1. - -fno-soa Turn off the Struct of Arrays optimization - -fsoa Turn on the Struct of Arrays optimization - --o Take the path to an output file for generated C++ code (default = "$name.hpp") or auto-formatting output (default: no file/print to stdout) - --print-cpp If set, output the generated C++ Stan model class to stdout. - --allow-undefined Do not fail if a function is declared but not defined - --include-paths Takes a comma-separated list of directories that may contain a file in an #include directive (default = "") - --use-opencl If set, try to use matrix_cl signatures. - --standalone-functions If set, the generated C++ will be the standalone functions C++ code. - --filename-in-msg Sets the filename used in compiler errors. Uses actual filename by default. - --info If set, print information about the model. - -help Display this list of options - --help Display this list of options - [127] + %%NAME%%: No model file provided + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] diff --git a/test/integration/cli-args/debug-flags.t/basic.stan b/test/integration/cli-args/debug-flags.t/basic.stan new file mode 100644 index 000000000..3b6099fcc --- /dev/null +++ b/test/integration/cli-args/debug-flags.t/basic.stan @@ -0,0 +1,11 @@ +data { + int N; + array[N] int y; +} +parameters { + real theta; +} +model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); +} diff --git a/test/integration/cli-args/debug-flags.t/run.t b/test/integration/cli-args/debug-flags.t/run.t new file mode 100644 index 000000000..828503554 --- /dev/null +++ b/test/integration/cli-args/debug-flags.t/run.t @@ -0,0 +1,474 @@ +Flags not used elsewhere in the tests + + $ stanc basic.stan --debug-lex + Lexer: data + Lexer: space + Lexer: { + Lexer: newline + {fname=basic.stan; line=1} + Lexer: space + Lexer: space + Lexer: int + Lexer: < + Lexer: lower + Lexer: = + Lexer: int_constant 0 + Lexer: > + Lexer: space + Lexer: identifier N + {fname=basic.stan; line=2} + Lexer: ; + Lexer: newline + {fname=basic.stan; line=2} + Lexer: space + Lexer: space + Lexer: array + Lexer: [ + Lexer: identifier N + {fname=basic.stan; line=3} + Lexer: ] + Lexer: space + Lexer: int + Lexer: < + Lexer: lower + Lexer: = + Lexer: int_constant 0 + Lexer: , + Lexer: space + Lexer: upper + Lexer: = + Lexer: int_constant 1 + Lexer: > + Lexer: space + Lexer: identifier y + {fname=basic.stan; line=3} + Lexer: ; + Lexer: newline + {fname=basic.stan; line=3} + Lexer: } + Lexer: newline + {fname=basic.stan; line=4} + Lexer: parameters + Lexer: space + Lexer: { + Lexer: newline + {fname=basic.stan; line=5} + Lexer: space + Lexer: space + Lexer: real + Lexer: < + Lexer: lower + Lexer: = + Lexer: int_constant 0 + Lexer: , + Lexer: space + Lexer: upper + Lexer: = + Lexer: int_constant 1 + Lexer: > + Lexer: space + Lexer: identifier theta + {fname=basic.stan; line=6} + Lexer: ; + Lexer: newline + {fname=basic.stan; line=6} + Lexer: } + Lexer: newline + {fname=basic.stan; line=7} + Lexer: model + Lexer: space + Lexer: { + Lexer: newline + {fname=basic.stan; line=8} + Lexer: space + Lexer: space + Lexer: identifier theta + {fname=basic.stan; line=9} + Lexer: space + Lexer: ~ + Lexer: space + Lexer: identifier beta + {fname=basic.stan; line=9} + Lexer: ( + Lexer: int_constant 1 + Lexer: , + Lexer: space + Lexer: int_constant 1 + Lexer: ) + Lexer: ; + Lexer: space + Lexer: single comment + {fname=basic.stan; line=9} + Lexer: space + Lexer: space + Lexer: identifier y + {fname=basic.stan; line=10} + Lexer: space + Lexer: ~ + Lexer: space + Lexer: identifier bernoulli + {fname=basic.stan; line=10} + Lexer: ( + Lexer: identifier theta + {fname=basic.stan; line=10} + Lexer: ) + Lexer: ; + Lexer: newline + {fname=basic.stan; line=10} + Lexer: } + Lexer: newline + {fname=basic.stan; line=11} + Lexer: eof + + $ stanc basic.stan --debug-parse + Parser: intnumeral 0 + Parser: constr_expression_common_expr + Parser: lower_range + Parser: range_constraint + Parser: INT_top_var_type + Parser: identifier N + Parser: higher_type + Parser: top_var_decl_no_assign + Parser: identifier N + Parser: identifier_expr + Parser: common_expr + Parser: array dims + Parser: intnumeral 0 + Parser: constr_expression_common_expr + Parser: intnumeral 1 + Parser: constr_expression_common_expr + Parser: lower_upper_range + Parser: range_constraint + Parser: INT_top_var_type + Parser: array_type + Parser: identifier y + Parser: higher_type + Parser: top_var_decl_no_assign + Parser: data_block + Parser: intnumeral 0 + Parser: constr_expression_common_expr + Parser: intnumeral 1 + Parser: constr_expression_common_expr + Parser: lower_upper_range + Parser: range_constraint + Parser: type_constraint_range + Parser: REAL_top_var_type + Parser: identifier theta + Parser: higher_type + Parser: top_var_decl_no_assign + Parser: parameters_block + Parser: identifier theta + Parser: identifier_expr + Parser: common_expr + Parser: identifier beta + Parser: intnumeral 1 + Parser: common_expr + Parser: intnumeral 1 + Parser: common_expr + Parser: tilde_statement + Parser: atomic_statement + Parser: vardecl_or_statement_statement + Parser: identifier y + Parser: identifier_expr + Parser: common_expr + Parser: identifier bernoulli + Parser: identifier theta + Parser: identifier_expr + Parser: common_expr + Parser: tilde_statement + Parser: atomic_statement + Parser: vardecl_or_statement_statement + Parser: model_block + Parser: program + + $ stanc basic.stan --debug-ast + ((functionblock ()) + (datablock + (((stmts + (((stmt + (VarDecl (decl_type SInt) + (transformation (Lower ((expr (IntNumeral 0)) (emeta ((loc )))))) + (is_global true) + (variables (((identifier ((name N) (id_loc ))) (initial_value ())))))) + (smeta ((loc )))) + ((stmt + (VarDecl + (decl_type + (SArray SInt + ((expr (Variable ((name N) (id_loc )))) (emeta ((loc )))))) + (transformation + (LowerUpper ((expr (IntNumeral 0)) (emeta ((loc )))) + ((expr (IntNumeral 1)) (emeta ((loc )))))) + (is_global true) + (variables (((identifier ((name y) (id_loc ))) (initial_value ())))))) + (smeta ((loc )))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 1) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 4) (col_num 1) (included_from ())))))))) + (transformeddatablock ()) + (parametersblock + (((stmts + (((stmt + (VarDecl (decl_type SReal) + (transformation + (LowerUpper ((expr (IntNumeral 0)) (emeta ((loc )))) + ((expr (IntNumeral 1)) (emeta ((loc )))))) + (is_global true) + (variables (((identifier ((name theta) (id_loc ))) (initial_value ())))))) + (smeta ((loc )))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 5) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 7) (col_num 1) (included_from ())))))))) + (transformedparametersblock ()) + (modelblock + (((stmts + (((stmt + (Tilde + (arg + ((expr (Variable ((name theta) (id_loc )))) (emeta ((loc ))))) + (distribution ((name beta) (id_loc ))) (kind ()) + (args + (((expr (IntNumeral 1)) (emeta ((loc )))) + ((expr (IntNumeral 1)) (emeta ((loc )))))) + (truncation NoTruncate))) + (smeta ((loc )))) + ((stmt + (Tilde + (arg ((expr (Variable ((name y) (id_loc )))) (emeta ((loc ))))) + (distribution ((name bernoulli) (id_loc ))) (kind ()) + (args + (((expr (Variable ((name theta) (id_loc )))) (emeta ((loc )))))) + (truncation NoTruncate))) + (smeta ((loc )))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 8) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 11) (col_num 1) (included_from ())))))))) + (generatedquantitiesblock ()) (comments )) + $ stanc basic.stan --debug-decorated-ast + ((functionblock ()) + (datablock + (((stmts + (((stmt + (VarDecl (decl_type SInt) + (transformation + (Lower + ((expr (IntNumeral 0)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))))) + (is_global true) + (variables (((identifier ((name N) (id_loc ))) (initial_value ())))))) + (smeta ((loc ) (return_type Incomplete)))) + ((stmt + (VarDecl + (decl_type + (SArray SInt + ((expr (Variable ((name N) (id_loc )))) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))))) + (transformation + (LowerUpper + ((expr (IntNumeral 0)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))) + ((expr (IntNumeral 1)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))))) + (is_global true) + (variables (((identifier ((name y) (id_loc ))) (initial_value ())))))) + (smeta ((loc ) (return_type Incomplete)))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 1) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 4) (col_num 1) (included_from ())))))))) + (transformeddatablock ()) + (parametersblock + (((stmts + (((stmt + (VarDecl (decl_type SReal) + (transformation + (LowerUpper + ((expr (IntNumeral 0)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))) + ((expr (IntNumeral 1)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))))) + (is_global true) + (variables (((identifier ((name theta) (id_loc ))) (initial_value ())))))) + (smeta ((loc ) (return_type Incomplete)))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 5) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 7) (col_num 1) (included_from ())))))))) + (transformedparametersblock ()) + (modelblock + (((stmts + (((stmt + (Tilde + (arg + ((expr (Variable ((name theta) (id_loc )))) + (emeta ((loc ) (ad_level AutoDiffable) (type_ UReal))))) + (distribution ((name beta) (id_loc ))) (kind (StanLib (FnLpdf true))) + (args + (((expr + (Promotion + ((expr (IntNumeral 1)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))) + UReal DataOnly)) + (emeta ((loc ) (ad_level DataOnly) (type_ UReal)))) + ((expr + (Promotion + ((expr (IntNumeral 1)) + (emeta ((loc ) (ad_level DataOnly) (type_ UInt)))) + UReal DataOnly)) + (emeta ((loc ) (ad_level DataOnly) (type_ UReal)))))) + (truncation NoTruncate))) + (smeta ((loc ) (return_type Incomplete)))) + ((stmt + (Tilde + (arg + ((expr (Variable ((name y) (id_loc )))) + (emeta ((loc ) (ad_level DataOnly) (type_ (UArray UInt)))))) + (distribution ((name bernoulli) (id_loc ))) + (kind (StanLib (FnLpmf true))) + (args + (((expr (Variable ((name theta) (id_loc )))) + (emeta ((loc ) (ad_level AutoDiffable) (type_ UReal)))))) + (truncation NoTruncate))) + (smeta ((loc ) (return_type Incomplete)))))) + (xloc + ((begin_loc ((filename basic.stan) (line_num 8) (col_num 0) (included_from ()))) + (end_loc ((filename basic.stan) (line_num 11) (col_num 1) (included_from ())))))))) + (generatedquantitiesblock ()) (comments )) + + + $ stanc basic.stan --debug-mir-pretty + input_vars { + int N; + array[int, N] y; } + + prepare_data { + data int N; + (FnCheck(trans(Lower 0))(var_name N)(var N))__(0); + FnValidateSize__("y", "N", N); + data array[int, N] y; + (FnCheck(trans(Lower 0))(var_name y)(var y))__(0); + (FnCheck(trans(Upper 1))(var_name y)(var y))__(1); } + + log_prob { + real theta; + { + target += beta_lupdf(theta, promote(1, real, data), + promote(1, real, data)); + target += bernoulli_lupmf(y, theta); + } } + + generate_quantities { + data real theta; + if(PNot__(emit_transformed_parameters__ || emit_generated_quantities__)) return; + if(PNot__(emit_generated_quantities__)) return; } + output_vars { + parameters real theta; //real + } + + $ stanc basic.stan --debug-transformed-mir-pretty + input_vars { + int N; + array[int, N] y; } + + prepare_data { + data int N; + N = FnReadData__("N")[1]; + (FnCheck(trans(Lower 0))(var_name N)(var N))__(0); + FnValidateSize__("y", "N", N); + data array[int, N] y; + y = FnReadData__("y"); + (FnCheck(trans(Lower 0))(var_name y)(var y))__(0); + (FnCheck(trans(Upper 1))(var_name y)(var y))__(1); } + + log_prob { + real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + { + target += beta_lupdf(theta, promote(1, real, data), + promote(1, real, data)); + target += bernoulli_lupmf(y, theta); + } } + + rev_log_prob { + real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + { + target += beta_lupdf(theta, promote(1, real, data), + promote(1, real, data)); + target += bernoulli_lupmf(y, theta); + } } + + generate_quantities { + data real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + (FnWriteParam(unconstrain_opt())(var theta))__(); + if(PNot__(emit_transformed_parameters__ || emit_generated_quantities__)) { + return; + } + if(PNot__(emit_generated_quantities__)) { + return; + } } + + transform_inits { + real theta; + theta = FnReadData__("theta")[1]; + (FnWriteParam(unconstrain_opt((LowerUpper 0 1)))(var theta))__(); } + output_vars { + parameters real theta; //real + } + + $ stanc basic.stan --debug-optimized-mir-pretty + input_vars { + int N; + array[int, N] y; } + + prepare_data { + data int N; + N = FnReadData__("N")[1]; + (FnCheck(trans(Lower 0))(var_name N)(var N))__(0); + FnValidateSize__("y", "N", N); + data array[int, N] y; + y = FnReadData__("y"); + (FnCheck(trans(Lower 0))(var_name y)(var y))__(0); + (FnCheck(trans(Upper 1))(var_name y)(var y))__(1); } + + log_prob { + real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + { + target += beta_lupdf(theta, promote(1, real, data), + promote(1, real, data)); + target += bernoulli_lupmf(y, theta); + } } + + rev_log_prob { + real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + { + target += beta_lupdf(theta, promote(1, real, data), + promote(1, real, data)); + target += bernoulli_lupmf(y, theta); + } } + + generate_quantities { + data real + theta = (FnReadParam(constrain(LowerUpper 0 1))(dims())(mem_pattern AoS))__( + ); + (FnWriteParam(unconstrain_opt())(var theta))__(); + if(PNot__(emit_transformed_parameters__ || emit_generated_quantities__)) { + return; + } + if(PNot__(emit_generated_quantities__)) { + return; + } } + + transform_inits { + real theta; + theta = FnReadData__("theta")[1]; + (FnWriteParam(unconstrain_opt((LowerUpper 0 1)))(var theta))__(); } + output_vars { + parameters real theta; //real + } diff --git a/test/integration/cli-args/debug-generation.t/bad.json b/test/integration/cli-args/debug-generation.t/bad.json new file mode 100644 index 000000000..259baa2dd --- /dev/null +++ b/test/integration/cli-args/debug-generation.t/bad.json @@ -0,0 +1,3 @@ +{ + "N": 10, + "K": 1a, diff --git a/test/integration/cli-args/debug-generation.t/run.t b/test/integration/cli-args/debug-generation.t/run.t index d3cc0cae1..79e9e9642 100644 --- a/test/integration/cli-args/debug-generation.t/run.t +++ b/test/integration/cli-args/debug-generation.t/run.t @@ -5,6 +5,15 @@ Provide partial data file $ stanc --debug-generate-inits debug.stan --debug-data-file partial_data.json | python3 -c "import sys, json; data=json.loads(sys.stdin.read()); print(len(data['theta']))" 29 +Output file works + + $ stanc --debug-generate-data debug.stan --o output.json && ls *.json && rm output.json + bad.json + incomplete_data.json + output.json + partial-div0.json + partial_data.json + Don't provide any data $ stanc --debug-generate-inits debug.stan Error in 'debug.stan', line 8, column 10 to column 25: @@ -18,8 +27,28 @@ Don't provide _enough_ data Cannot evaluate expression: (((14 + N) + 1) + x.1) [1] -Bad data block, cannot be partially evaluated +Provide a non-existant file + $ stanc --debug-generate-inits debug.stan --debug-data-file non_existant.json + %%NAME%%: option '--debug-data-file': no 'non_existant.json' file + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] +Provide an invalid JSON file + $ stanc --debug-generate-inits debug.stan --debug-data-file bad.json + Error: Failed to parse data JSON for debug generation: Line 3, bytes 10-13: + Expected ',' or '}' but found 'a, + ' + [1] + +Provide an unreadable JSON file + $ touch unreadable.json && chmod -r unreadable.json && stanc --debug-generate-inits debug.stan --debug-data-file unreadable.json + %%NAME%%: File 'unreadable.json' not found or cannot be opened. + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] + +Bad data block, cannot be partially evaluated $ stanc --debug-generate-data div0.stan --debug-data-file partial-div0.json Error in 'div0.stan', line 4, column 9 to column 16: Integer division by zero diff --git a/test/integration/cli-args/notfound.expected b/test/integration/cli-args/notfound.expected index b305aeda6..dee569433 100644 --- a/test/integration/cli-args/notfound.expected +++ b/test/integration/cli-args/notfound.expected @@ -1,3 +1,5 @@ $ ../../../../install/default/bin/stanc notfound.stan -Error: file 'notfound.stan' not found or cannot be opened -[exit 1] +%%NAME%%: MODEL_FILE argument: no 'notfound.stan' file +Usage: %%NAME%% [OPTION]… [MODEL_FILE] +Try '%%NAME%% --help' for more information. +[exit 124] diff --git a/test/integration/cli-args/output-files.t/basic.stan b/test/integration/cli-args/output-files.t/basic.stan new file mode 100644 index 000000000..3b6099fcc --- /dev/null +++ b/test/integration/cli-args/output-files.t/basic.stan @@ -0,0 +1,11 @@ +data { + int N; + array[N] int y; +} +parameters { + real theta; +} +model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); +} diff --git a/test/integration/cli-args/output-files.t/run.t b/test/integration/cli-args/output-files.t/run.t new file mode 100644 index 000000000..8bf07eaa8 --- /dev/null +++ b/test/integration/cli-args/output-files.t/run.t @@ -0,0 +1,34 @@ +Default is filename.hpp + $ stanc basic.stan && ls && rm basic.hpp + basic.hpp + basic.stan + +Output file is respected + $ stanc --o=foo.cpp basic.stan && ls && rm foo.cpp + basic.stan + foo.cpp + +Output file for formatting prevents cpp generation + $ stanc --auto-format --o=basic_fmt.stan basic.stan && ls && cat basic_fmt.stan && rm basic_fmt.stan + basic.stan + basic_fmt.stan + data { + int N; + array[N] int y; + } + parameters { + real theta; + } + model { + theta ~ beta(1, 1); // uniform prior on interval 0,1 + y ~ bernoulli(theta); + } + +Output file isn't present in C++ args array + $ stanc --O -fno-soa --o=foo.cpp basic.stan && grep "stancflags" foo.cpp && rm foo.cpp + "stancflags = --O1 -fno-soa"}; + +Error on un-writable output file + $ touch basic.hpp && chmod -w basic.hpp && stanc --o=basic.hpp basic.stan + Error writing to file 'basic.hpp': basic.hpp: Permission denied + [1] diff --git a/test/integration/cli-args/stanc.t b/test/integration/cli-args/stanc.t index 329b4f534..7cfd91db5 100644 --- a/test/integration/cli-args/stanc.t +++ b/test/integration/cli-args/stanc.t @@ -1,141 +1,254 @@ Show help - $ stanc --help - Usage: %%NAME%% [option] ... - --debug-lex For debugging purposes: print the lexer actions - --debug-parse For debugging purposes: print the parser actions - --debug-ast For debugging purposes: print the undecorated AST, before semantic checking - --debug-decorated-ast For debugging purposes: print the decorated AST, after semantic checking - --debug-generate-data For debugging purposes: generate a mock dataset to run the model on - --debug-generate-inits For debugging purposes: generate a mock initial value for each parameter - --debug-data-file For --debug-generate-data or --debug-generate-inits - --debug-mir For debugging purposes: print the MIR as an S-expression. - --debug-mir-pretty For debugging purposes: pretty-print the MIR. - --debug-optimized-mir For debugging purposes: print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-optimized-mir-pretty For debugging purposes: pretty print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-lir For debugging purposes: print the C++ LIR as a s-expression. Mainly for comparison with --print-cpp - --debug-mem-patterns For debugging purposes: print a list of matrix variables and their memory type, either AoS (array of structs) or the more efficient SoA (struct of arrays). Only has an effect when optimizations are turned on. - --debug-transformed-mir For debugging purposes: print the MIR after the backend has transformed it. - --debug-transformed-mir-pretty For debugging purposes: pretty print the MIR after the backend has transformed it. - --dump-stan-math-signatures Dump out the list of supported type signatures for Stan Math backend. - --dump-stan-math-distributions Dump out the list of supported probability distributions and their supported suffix types for the Stan Math backend. - --warn-uninitialized Emit warnings about uninitialized variables to stderr. Currently an experimental feature. - --warn-pedantic Emit warnings about common mistakes in Stan programs. - --auto-format Pretty prints a formatted version of the Stan program. - --canonicalize Enable specific canonicalizations in a comma separated list. Options are 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - --max-line-length Set the maximum line length for the formatter. Defaults to 78 characters. - --print-canonical Prints the canonicalized program. Equivalent to --auto-format --canonicalize deprecations,includes,parentheses,braces - --version Display stanc version number - --name Take a string to set the model name (default = "$model_filename_model") - --O0 (Default) Do not apply optimizations to the Stan code. - --O1 Apply level 1 compiler optimizations (only basic optimizations). - --Oexperimental (Experimental) Apply all compiler optimizations. Some of these are not thorougly tested and may not always improve a programs performance. - --O Same as --O1. - -fno-soa Turn off the Struct of Arrays optimization - -fsoa Turn on the Struct of Arrays optimization - --o Take the path to an output file for generated C++ code (default = "$name.hpp") or auto-formatting output (default: no file/print to stdout) - --print-cpp If set, output the generated C++ Stan model class to stdout. - --allow-undefined Do not fail if a function is declared but not defined - --include-paths Takes a comma-separated list of directories that may contain a file in an #include directive (default = "") - --use-opencl If set, try to use matrix_cl signatures. - --standalone-functions If set, the generated C++ will be the standalone functions C++ code. - --filename-in-msg Sets the filename used in compiler errors. Uses actual filename by default. - --info If set, print information about the model. - -help Display this list of options - --help Display this list of options + $ stanc --help=plain + NAME + %%NAME%% - compile Stan programs to C++ + + SYNOPSIS + %%NAME%% [OPTION]… [MODEL_FILE] + + DESCRIPTION + The Stan compiler (also known as stanc or stanc3) reads a Stan file + and compiles it to C++. It also allows for other Stan program + manipulation like formatting (--auto-format) and introspection + (--info). + + For more information on Stan, see https://mc-stan.org. + + For more documentation on the compiler for users, see + https://mc-stan.org/docs/stan-users-guide/using-stanc.html. + + For more information on the compiler for developers, see + https://mc-stan.org/stanc3/stanc/. + + ARGUMENTS + MODEL_FILE + The Stan model file to compile. This should be a .stan file or + .stanfunctions file (the latter automatically sets + --standalone-functions). + + OPTIONS + -? [=FMT] (default=auto) + Synonym for --help. + + --allow-undefined + Do not fail if a function is declared but not defined. This + usually means the definition will be provided later as a C++ + function. + + --auto-format + Output a formatted version of the Stan program. The output can be + tweaked using --max-line-length and --canonicalize. + + --canonicalize=OPTIONS + Enable specific canonicalizations in a comma separated list. + Options are 'deprecations', 'parentheses', 'braces', 'includes', + 'strip-comments'. + + --filename-in-msg=FILENAME (absent=MODEL_FILE) + Sets the filename used in compiler and runtime errors. + + --help[=FMT] (default=auto) + Show this help in format FMT. The value FMT must be one of auto, + pager, groff or plain. With auto, the format is pager or plain + whenever the TERM env var is dumb or undefined. + + --include-paths=DIRS (absent="") + A comma-separated list of directories which are searched whenever + an #include directive is parsed. + + --info + If set, print information about the model. + + --max-line-length=LENGTH (absent=78) + Set the maximum line length for the formatter. + + --name=NAME (absent=MODEL_FILE_NAME_model) + Take a string to set as the model name. This controls the + namespace in the generated C++. + + -o FILENAME, --output=FILENAME + Output file for generated C++ code (default = + "MODEL_FILE_NAME.hpp") or auto-formatting output (default: no + file/print to stdout). + + --O0 + (Default) Do not apply optimizations to the Stan code. + + --O1 + Only basic optimizations are applied. Recommended. The deprecated + option --O is aliased to this starting in Stan 2.37. + + --Oexperimental + (Experimental) Apply all compiler optimizations. Some of these are + not thorougly tested and may not always improve a programs + performance. + + --print-canonical + Prints the canonicalized program. Equivalent to --auto-format + --canonicalize=deprecations,includes,parentheses,braces. + + --print-cpp + If set, output the generated C++ Stan model class to stdout. + + --standalone-functions + If set, the generated C++ will only contain the code for the + functions in the functions block, not the full Stan model class. + + --use-opencl + If set, try to use matrix_cl signatures for supported Stan Math + functions. + + --version + Show version information. + + --warn-pedantic + Emit warnings about common mistakes in Stan programs. Note: This + may produce false positive warnings. + + --warn-uninitialized + (Experimental) Emit warnings about uninitialized variables. + + COMMANDS + The following flags will cause the compiler to exit after printing + information. No MODEL_FILE is required. + + --dump-stan-math-distributions + Dump out the list of supported probability distributions and their + supported suffix types for the Stan Math backend. + + --dump-stan-math-signatures + Dump out the list of supported function signatures the for Stan + Math backend. + + EXTRA OPTIONS + These flags are provided primarily for development and debugging; + their exact behavior should not be relied on. + + --debug-ast + For debugging purposes: print the undecorated AST, before semantic + checking. + + --debug-data-file=JSON_FILE + Provide (possibly partially specified) data block values for use + with --debug-generate-data or --debug-generate-inits. + + --debug-decorated-ast + For debugging purposes: print the decorated AST, after semantic + checking. + + --debug-generate-data + For debugging purposes: generate a mock dataset to run the model + on. + + --debug-generate-inits + For debugging purposes: generate a mock initial value for each + parameter. + + --debug-lex + For debugging purposes: print the lexer actions. + + --debug-lir + For debugging purposes: print the C++ LIR as a s-expression. + Mainly for comparison with --print-cpp. + + --debug-mem-patterns + For debugging purposes: print a list of matrix variables and their + memory type, either AoS (array of structs) or the more efficient + SoA (struct of arrays). Only has an effect when optimizations are + turned on. + + --debug-mir + For debugging purposes: print the MIR after lowering. + + --debug-mir-pretty + For debugging purposes: pretty-print the MIR after lowering. + + --debug-optimized-mir + For debugging purposes: print the MIR after it's been optimized. + Only has an effect when optimizations are turned on. + + --debug-optimized-mir-pretty + For debugging purposes: pretty-print the MIR after it's been + optimized. Only has an effect when optimizations are turned on. + + --debug-parse + For debugging purposes: print the parser actions. + + --debug-transformed-mir + For debugging purposes: print the MIR after the backend has + transformed it. + + --debug-transformed-mir-pretty + For debugging purposes: pretty-print the MIR after the backend has + transformed it. + + -f SETTING + Debugging features. Valid values: -fsoa to force on the Struct of + Arrays optimization. -fno-soa to force it off. + + EXIT STATUS + 0 on success. + + 1 on compilation failure. + + 124 on command line parsing errors. + + 125 on internal compiler errors. Please file a bug! + + BUGS + Please report at https://github.com/stan-dev/stanc3/issues/new. + + + + + +Qmark alias + $ stanc -? plain | head + NAME + %%NAME%% - compile Stan programs to C++ + + SYNOPSIS + %%NAME%% [OPTION]… [MODEL_FILE] + + DESCRIPTION + The Stan compiler (also known as stanc or stanc3) reads a Stan file + and compiles it to C++. It also allows for other Stan program + manipulation like formatting (--auto-format) and introspection + Show version $ stanc --version %%NAME%%3 %%VERSION%% (Unix) + Error when no file passed $ stanc - Please specify a model_file. - Usage: %%NAME%% [option] ... - --debug-lex For debugging purposes: print the lexer actions - --debug-parse For debugging purposes: print the parser actions - --debug-ast For debugging purposes: print the undecorated AST, before semantic checking - --debug-decorated-ast For debugging purposes: print the decorated AST, after semantic checking - --debug-generate-data For debugging purposes: generate a mock dataset to run the model on - --debug-generate-inits For debugging purposes: generate a mock initial value for each parameter - --debug-data-file For --debug-generate-data or --debug-generate-inits - --debug-mir For debugging purposes: print the MIR as an S-expression. - --debug-mir-pretty For debugging purposes: pretty-print the MIR. - --debug-optimized-mir For debugging purposes: print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-optimized-mir-pretty For debugging purposes: pretty print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-lir For debugging purposes: print the C++ LIR as a s-expression. Mainly for comparison with --print-cpp - --debug-mem-patterns For debugging purposes: print a list of matrix variables and their memory type, either AoS (array of structs) or the more efficient SoA (struct of arrays). Only has an effect when optimizations are turned on. - --debug-transformed-mir For debugging purposes: print the MIR after the backend has transformed it. - --debug-transformed-mir-pretty For debugging purposes: pretty print the MIR after the backend has transformed it. - --dump-stan-math-signatures Dump out the list of supported type signatures for Stan Math backend. - --dump-stan-math-distributions Dump out the list of supported probability distributions and their supported suffix types for the Stan Math backend. - --warn-uninitialized Emit warnings about uninitialized variables to stderr. Currently an experimental feature. - --warn-pedantic Emit warnings about common mistakes in Stan programs. - --auto-format Pretty prints a formatted version of the Stan program. - --canonicalize Enable specific canonicalizations in a comma separated list. Options are 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - --max-line-length Set the maximum line length for the formatter. Defaults to 78 characters. - --print-canonical Prints the canonicalized program. Equivalent to --auto-format --canonicalize deprecations,includes,parentheses,braces - --version Display stanc version number - --name Take a string to set the model name (default = "$model_filename_model") - --O0 (Default) Do not apply optimizations to the Stan code. - --O1 Apply level 1 compiler optimizations (only basic optimizations). - --Oexperimental (Experimental) Apply all compiler optimizations. Some of these are not thorougly tested and may not always improve a programs performance. - --O Same as --O1. - -fno-soa Turn off the Struct of Arrays optimization - -fsoa Turn on the Struct of Arrays optimization - --o Take the path to an output file for generated C++ code (default = "$name.hpp") or auto-formatting output (default: no file/print to stdout) - --print-cpp If set, output the generated C++ Stan model class to stdout. - --allow-undefined Do not fail if a function is declared but not defined - --include-paths Takes a comma-separated list of directories that may contain a file in an #include directive (default = "") - --use-opencl If set, try to use matrix_cl signatures. - --standalone-functions If set, the generated C++ will be the standalone functions C++ code. - --filename-in-msg Sets the filename used in compiler errors. Uses actual filename by default. - --info If set, print information about the model. - -help Display this list of options - --help Display this list of options - [127] + %%NAME%%: No model file provided + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] + Error when multiple files passed $ stanc foo.stan foo2.stan - stanc: Please specify only one model_file. - Usage: %%NAME%% [option] ... - --debug-lex For debugging purposes: print the lexer actions - --debug-parse For debugging purposes: print the parser actions - --debug-ast For debugging purposes: print the undecorated AST, before semantic checking - --debug-decorated-ast For debugging purposes: print the decorated AST, after semantic checking - --debug-generate-data For debugging purposes: generate a mock dataset to run the model on - --debug-generate-inits For debugging purposes: generate a mock initial value for each parameter - --debug-data-file For --debug-generate-data or --debug-generate-inits - --debug-mir For debugging purposes: print the MIR as an S-expression. - --debug-mir-pretty For debugging purposes: pretty-print the MIR. - --debug-optimized-mir For debugging purposes: print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-optimized-mir-pretty For debugging purposes: pretty print the MIR after it's been optimized. Only has an effect when optimizations are turned on. - --debug-lir For debugging purposes: print the C++ LIR as a s-expression. Mainly for comparison with --print-cpp - --debug-mem-patterns For debugging purposes: print a list of matrix variables and their memory type, either AoS (array of structs) or the more efficient SoA (struct of arrays). Only has an effect when optimizations are turned on. - --debug-transformed-mir For debugging purposes: print the MIR after the backend has transformed it. - --debug-transformed-mir-pretty For debugging purposes: pretty print the MIR after the backend has transformed it. - --dump-stan-math-signatures Dump out the list of supported type signatures for Stan Math backend. - --dump-stan-math-distributions Dump out the list of supported probability distributions and their supported suffix types for the Stan Math backend. - --warn-uninitialized Emit warnings about uninitialized variables to stderr. Currently an experimental feature. - --warn-pedantic Emit warnings about common mistakes in Stan programs. - --auto-format Pretty prints a formatted version of the Stan program. - --canonicalize Enable specific canonicalizations in a comma separated list. Options are 'deprecations', 'parentheses', 'braces', 'includes', 'strip-comments'. - --max-line-length Set the maximum line length for the formatter. Defaults to 78 characters. - --print-canonical Prints the canonicalized program. Equivalent to --auto-format --canonicalize deprecations,includes,parentheses,braces - --version Display stanc version number - --name Take a string to set the model name (default = "$model_filename_model") - --O0 (Default) Do not apply optimizations to the Stan code. - --O1 Apply level 1 compiler optimizations (only basic optimizations). - --Oexperimental (Experimental) Apply all compiler optimizations. Some of these are not thorougly tested and may not always improve a programs performance. - --O Same as --O1. - -fno-soa Turn off the Struct of Arrays optimization - -fsoa Turn on the Struct of Arrays optimization - --o Take the path to an output file for generated C++ code (default = "$name.hpp") or auto-formatting output (default: no file/print to stdout) - --print-cpp If set, output the generated C++ Stan model class to stdout. - --allow-undefined Do not fail if a function is declared but not defined - --include-paths Takes a comma-separated list of directories that may contain a file in an #include directive (default = "") - --use-opencl If set, try to use matrix_cl signatures. - --standalone-functions If set, the generated C++ will be the standalone functions C++ code. - --filename-in-msg Sets the filename used in compiler errors. Uses actual filename by default. - --info If set, print information about the model. - -help Display this list of options - --help Display this list of options - [2] + %%NAME%%: too many arguments, don't know what to do with 'foo2.stan' + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] + +Error when a folder is passed + $ mkdir foo.d && stanc foo.d + %%NAME%%: MODEL_FILE argument: 'foo.d' is a directory + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] +Error when nonsense argument is passed + $ stanc -fno-generated-quantities + %%NAME%%: option '-f': invalid value 'no-generated-quantities', expected + either 'soa' or 'no-soa' + Usage: %%NAME%% [OPTION]… [MODEL_FILE] + Try '%%NAME%% --help' for more information. + [124] +Error when unreadable file is passed + $ touch unreadable.stan && chmod -r unreadable.stan && stanc unreadable.stan + Error: file 'unreadable.stan' not found or cannot be opened + [1] diff --git a/test/integration/cli-args/warn-uninitialized/dune b/test/integration/cli-args/warn-uninitialized/dune new file mode 100644 index 000000000..52b573603 --- /dev/null +++ b/test/integration/cli-args/warn-uninitialized/dune @@ -0,0 +1,18 @@ +(rule + (targets stanc.output) + (deps + (package stanc) + (:stanfiles + (glob_files *.stan))) + (action + (with-stdout-to + %{targets} + (run + %{bin:run_bin_on_args} + "%{bin:stanc} --warn-uninitialized " + %{stanfiles})))) + +(rule + (alias runtest) + (action + (diff stanc.expected stanc.output))) diff --git a/test/integration/cli-args/warn-uninitialized/simple.stan b/test/integration/cli-args/warn-uninitialized/simple.stan new file mode 100644 index 000000000..ff5540318 --- /dev/null +++ b/test/integration/cli-args/warn-uninitialized/simple.stan @@ -0,0 +1,6 @@ +model { + real theta; + real zero; + theta ~ normal(zero, 1); + zero = 0; +} diff --git a/test/integration/cli-args/warn-uninitialized/stanc.expected b/test/integration/cli-args/warn-uninitialized/stanc.expected new file mode 100644 index 000000000..e71448866 --- /dev/null +++ b/test/integration/cli-args/warn-uninitialized/stanc.expected @@ -0,0 +1,6 @@ + $ ../../../../../install/default/bin/stanc --warn-uninitialized simple.stan +Warning in 'simple.stan', line 4, column 17: The variable zero may not have + been assigned a value before its use. +Warning in 'simple.stan', line 4, column 2: The variable theta may not have + been assigned a value before its use. +[exit 0]