diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml new file mode 100644 index 000000000..2a5ddb59d --- /dev/null +++ b/.github/workflows/ci.yml @@ -0,0 +1,58 @@ +name: CI + +on: + push: + branches: [ master ] + pull_request: + branches: [ master ] + +jobs: + test: + strategy: + fail-fast: false + matrix: + os: [macos-latest, ubuntu-latest, windows-latest] + + runs-on: ${{matrix.os}} + + steps: + - uses: actions/checkout@v2.3.4 + + - name: Cache OCaml's opam + uses: actions/cache@v2.1.5 + with: + path: ~/.opam + key: ${{matrix.os}}-latest-ocaml-4.06.1 + + - name: Use OCaml + uses: avsm/setup-ocaml@v1.1.10 + with: + ocaml-version: 4.06.1 + + - name: Use Node.js + uses: actions/setup-node@v2.1.5 + with: + node-version: 14.4.0 + + - run: npm ci + + # These 2 runs (or just the second?) are for when you have opam dependencies. We don't. + # Don't add deps. But if you ever do, un-comment these + # - run: opam pin add rescript-editor-support.dev . --no-action + # - run: opam install . --deps-only --with-doc --with-test + + - run: eval $(opam env) && cd analysis && make test + if: matrix.os != 'windows-latest' + # CI windows running the binary somehow stucks. Not sure why. Disable for now. + - run: "cd analysis && & $env:CYGWIN_ROOT\\bin\\ocaml-env exec -- make" + if: matrix.os == 'windows-latest' + + # Also avoids artifacts upload permission loss: + # https://github.com/actions/upload-artifact/tree/ee69f02b3dfdecd58bb31b4d133da38ba6fe3700#permission-loss + - name: Compress files + run: tar -cvf binary.tar -C server/analysis_binaries current-platform.exe + + - uses: actions/upload-artifact@v2 + with: + name: ${{matrix.os}}.exe + path: binary.tar diff --git a/.gitignore b/.gitignore index 19aab5094..365560786 100644 --- a/.gitignore +++ b/.gitignore @@ -2,4 +2,5 @@ out node_modules client/server -.vscode-test \ No newline at end of file +.vscode-test +*.exe diff --git a/analysis/.depend b/analysis/.depend new file mode 100644 index 000000000..43f7cdcb8 --- /dev/null +++ b/analysis/.depend @@ -0,0 +1,144 @@ +src/BuildSystem.cmx : src/ModuleResolution.cmx src/Log.cmx src/Infix.cmx \ + src/Files.cmx +src/EditorSupportCommands.cmx : src/Utils.cmx src/Uri2.cmx src/TopTypes.cmx \ + src/State.cmx src/SharedTypes.cmx src/Shared.cmx src/References.cmx \ + src/Protocol.cmx src/NewCompletions.cmx src/Hover.cmx src/Files.cmx +src/Files.cmx : +src/FindFiles.cmx : src/Utils.cmx src/SharedTypes.cmx \ + src/ModuleResolution.cmx src/Log.cmx src/vendor/Json.cmx src/Infix.cmx \ + src/Files.cmx src/BuildSystem.cmx +src/Hover.cmx : src/Utils.cmx src/SharedTypes.cmx src/Shared.cmx \ + src/References.cmx src/Query.cmx +src/Infix.cmx : src/Log.cmx src/Files.cmx +src/Log.cmx : +src/MarkdownOfOCamldoc.cmx : src/vendor/odoc_parser/root.cmx \ + src/vendor/odoc_parser/paths.cmx src/vendor/odoc_parser/parser_.cmx \ + src/vendor/omd/omd.cmx src/Log.cmx src/vendor/odoc_parser/location_.cmx \ + src/vendor/odoc_parser/error.cmx src/vendor/odoc_parser/comment.cmx +src/ModuleResolution.cmx : src/Infix.cmx src/Files.cmx +src/NewCompletions.cmx : src/Utils.cmx src/Uri2.cmx src/TopTypes.cmx \ + src/State.cmx src/SharedTypes.cmx src/Shared.cmx src/Query.cmx \ + src/Protocol.cmx src/PartialParser.cmx src/Log.cmx src/Infix.cmx \ + src/Hover.cmx +src/Packages.cmx : src/Uri2.cmx src/TopTypes.cmx src/SharedTypes.cmx \ + src/Log.cmx src/vendor/Json.cmx src/Infix.cmx src/FindFiles.cmx \ + src/Files.cmx src/BuildSystem.cmx +src/PartialParser.cmx : src/SharedTypes.cmx src/Infix.cmx +src/PrepareUtils.cmx : +src/PrintType.cmx : src/vendor/res_outcome_printer/res_outcome_printer.cmx \ + src/vendor/res_outcome_printer/res_doc.cmx +src/ProcessAttributes.cmx : src/SharedTypes.cmx src/PrepareUtils.cmx +src/ProcessCmt.cmx : src/Utils.cmx src/SharedTypes.cmx \ + src/ProcessAttributes.cmx src/Infix.cmx +src/ProcessExtra.cmx : src/Utils.cmx src/SharedTypes.cmx src/Shared.cmx \ + src/Query.cmx src/ProcessCmt.cmx src/ProcessAttributes.cmx src/Log.cmx +src/Process_406.cmx : src/SharedTypes.cmx src/Shared.cmx \ + src/ProcessExtra.cmx src/ProcessCmt.cmx src/PrintType.cmx \ + src/Process_406.cmi +src/Process_406.cmi : src/Uri2.cmx src/SharedTypes.cmx +src/Protocol.cmx : +src/Query.cmx : src/SharedTypes.cmx src/Log.cmx src/Infix.cmx +src/References.cmx : src/Utils.cmx src/Uri2.cmx src/SharedTypes.cmx \ + src/Query.cmx src/Log.cmx src/Infix.cmx +src/RescriptEditorSupport.cmx : src/EditorSupportCommands.cmx +src/Shared.cmx : src/PrintType.cmx src/Files.cmx +src/SharedTypes.cmx : src/Utils.cmx src/Uri2.cmx src/Shared.cmx \ + src/Infix.cmx +src/State.cmx : src/Utils.cmx src/Uri2.cmx src/TopTypes.cmx \ + src/SharedTypes.cmx src/Process_406.cmx src/Packages.cmx \ + src/vendor/omd/omd.cmx src/MarkdownOfOCamldoc.cmx src/Log.cmx \ + src/Infix.cmx src/FindFiles.cmx src/Files.cmx src/BuildSystem.cmx +src/TopTypes.cmx : src/Uri2.cmx src/SharedTypes.cmx +src/Uri2.cmx : +src/Utils.cmx : src/Protocol.cmx +src/vendor/Json.cmx : +src/vendor/odoc_parser/ast.cmx : src/vendor/odoc_parser/paths.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/comment.cmx +src/vendor/odoc_parser/comment.cmx : src/vendor/odoc_parser/paths.cmx \ + src/vendor/odoc_parser/location_.cmx +src/vendor/odoc_parser/error.cmx : src/vendor/odoc_parser/location_.cmx +src/vendor/odoc_parser/helpers.cmx : src/vendor/odoc_parser/paths.cmx +src/vendor/odoc_parser/lang.cmx : src/vendor/odoc_parser/root.cmx \ + src/vendor/odoc_parser/paths.cmx src/vendor/odoc_parser/comment.cmx +src/vendor/odoc_parser/location_.cmx : +src/vendor/odoc_parser/odoc_lexer.cmx : src/vendor/odoc_parser/token.cmx \ + src/vendor/odoc_parser/parse_error.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/error.cmx \ + src/vendor/odoc_parser/odoc_lexer.cmi +src/vendor/odoc_parser/odoc_lexer.cmi : src/vendor/odoc_parser/token.cmx \ + src/vendor/odoc_parser/location_.cmx +src/vendor/odoc_parser/parse_error.cmx : \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/error.cmx +src/vendor/odoc_parser/parser_.cmx : src/vendor/odoc_parser/syntax.cmx \ + src/vendor/odoc_parser/semantics.cmx \ + src/vendor/odoc_parser/odoc_lexer.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/error.cmx \ + src/vendor/odoc_parser/ast.cmx src/vendor/odoc_parser/parser_.cmi +src/vendor/odoc_parser/parser_.cmi : src/vendor/odoc_parser/paths.cmi \ + src/vendor/odoc_parser/error.cmx src/vendor/odoc_parser/comment.cmx \ + src/vendor/odoc_parser/ast.cmx +src/vendor/odoc_parser/paths.cmx : src/vendor/odoc_parser/root.cmx \ + src/vendor/odoc_parser/paths_types.cmx src/vendor/odoc_parser/paths.cmi +src/vendor/odoc_parser/paths.cmi : src/vendor/odoc_parser/root.cmi \ + src/vendor/odoc_parser/paths_types.cmx +src/vendor/odoc_parser/paths_types.cmx : src/vendor/odoc_parser/root.cmx +src/vendor/odoc_parser/root.cmx : src/vendor/odoc_parser/root.cmi +src/vendor/odoc_parser/root.cmi : +src/vendor/odoc_parser/semantics.cmx : src/vendor/odoc_parser/token.cmx \ + src/vendor/odoc_parser/paths.cmx src/vendor/odoc_parser/parse_error.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/error.cmx \ + src/vendor/odoc_parser/comment.cmx src/vendor/odoc_parser/ast.cmx \ + src/vendor/odoc_parser/semantics.cmi +src/vendor/odoc_parser/semantics.cmi : src/vendor/odoc_parser/paths.cmi \ + src/vendor/odoc_parser/error.cmx src/vendor/odoc_parser/comment.cmx \ + src/vendor/odoc_parser/ast.cmx +src/vendor/odoc_parser/syntax.cmx : src/vendor/odoc_parser/token.cmx \ + src/vendor/odoc_parser/parse_error.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/helpers.cmx \ + src/vendor/odoc_parser/error.cmx src/vendor/odoc_parser/comment.cmx \ + src/vendor/odoc_parser/ast.cmx src/vendor/odoc_parser/syntax.cmi +src/vendor/odoc_parser/syntax.cmi : src/vendor/odoc_parser/token.cmx \ + src/vendor/odoc_parser/location_.cmx src/vendor/odoc_parser/error.cmx \ + src/vendor/odoc_parser/ast.cmx +src/vendor/odoc_parser/token.cmx : src/vendor/odoc_parser/comment.cmx +src/vendor/omd/html_characters.cmx : +src/vendor/omd/omd.cmx : src/vendor/omd/omd_representation.cmx \ + src/vendor/omd/omd_parser.cmx src/vendor/omd/omd_lexer.cmx \ + src/vendor/omd/omd_backend.cmx src/vendor/omd/omd.cmi +src/vendor/omd/omd.cmi : src/vendor/omd/omd_representation.cmi +src/vendor/omd/omd_backend.cmx : src/vendor/omd/omd_utils.cmx \ + src/vendor/omd/omd_representation.cmx src/vendor/omd/omd_backend.cmi +src/vendor/omd/omd_backend.cmi : src/vendor/omd/omd_utils.cmi \ + src/vendor/omd/omd_representation.cmi +src/vendor/omd/omd_html.cmx : +src/vendor/omd/omd_lexer.cmx : src/vendor/omd/omd_utils.cmx \ + src/vendor/omd/omd_representation.cmx src/vendor/omd/omd_lexer.cmi +src/vendor/omd/omd_lexer.cmi : src/vendor/omd/omd_representation.cmi +src/vendor/omd/omd_parser.cmx : src/vendor/omd/omd_utils.cmx \ + src/vendor/omd/omd_representation.cmx src/vendor/omd/omd_lexer.cmx \ + src/vendor/omd/omd_backend.cmx src/vendor/omd/omd_parser.cmi +src/vendor/omd/omd_parser.cmi : src/vendor/omd/omd_utils.cmi \ + src/vendor/omd/omd_representation.cmi +src/vendor/omd/omd_representation.cmx : src/vendor/omd/omd_utils.cmx \ + src/vendor/omd/omd_representation.cmi +src/vendor/omd/omd_representation.cmi : +src/vendor/omd/omd_types.cmx : +src/vendor/omd/omd_utils.cmx : src/vendor/omd/omd_utils.cmi +src/vendor/omd/omd_utils.cmi : +src/vendor/omd/omd_xtxt.cmx : src/vendor/omd/omd_xtxt.cmi +src/vendor/omd/omd_xtxt.cmi : +src/vendor/res_outcome_printer/res_comment.cmx : \ + src/vendor/res_outcome_printer/res_comment.cmi +src/vendor/res_outcome_printer/res_comment.cmi : +src/vendor/res_outcome_printer/res_doc.cmx : \ + src/vendor/res_outcome_printer/res_minibuffer.cmx \ + src/vendor/res_outcome_printer/res_doc.cmi +src/vendor/res_outcome_printer/res_doc.cmi : +src/vendor/res_outcome_printer/res_minibuffer.cmx : \ + src/vendor/res_outcome_printer/res_minibuffer.cmi +src/vendor/res_outcome_printer/res_minibuffer.cmi : +src/vendor/res_outcome_printer/res_outcome_printer.cmx : \ + src/vendor/res_outcome_printer/res_token.cmx \ + src/vendor/res_outcome_printer/res_doc.cmx +src/vendor/res_outcome_printer/res_token.cmx : \ + src/vendor/res_outcome_printer/res_comment.cmx diff --git a/analysis/.gitignore b/analysis/.gitignore new file mode 100644 index 000000000..bd4fcb222 --- /dev/null +++ b/analysis/.gitignore @@ -0,0 +1,11 @@ +.merlin +!/.merlin +*.install +examples/*/lib +tests/lib +node_modules +*.cmi +*.cmt +*.cmti +*.cmx +*.o diff --git a/analysis/.merlin b/analysis/.merlin new file mode 100644 index 000000000..618916e48 --- /dev/null +++ b/analysis/.merlin @@ -0,0 +1,15 @@ +B src +B src/vendor +B src/vendor/odoc_parser +B src/vendor/omd +B src/vendor/res_outcome_printer +B ../../.opam/4.06.1/lib/ocaml/compiler-libs/ + +S src +S src/vendor +S src/vendor/odoc_parser +S src/vendor/omd +S src/vendor/res_outcome_printer +S ../../.opam/4.06.1/lib/ocaml/compiler-libs/ + +FLG -w +26+27+32+33+39 diff --git a/analysis/.ocamlformat b/analysis/.ocamlformat new file mode 100644 index 000000000..d52f3a47e --- /dev/null +++ b/analysis/.ocamlformat @@ -0,0 +1,5 @@ +cases-exp-indent = 2 +space-around-arrays = false +space-around-lists = false +space-around-records = false +space-around-variants = false diff --git a/analysis/Changes.md b/analysis/Changes.md new file mode 100644 index 000000000..549dccbb7 --- /dev/null +++ b/analysis/Changes.md @@ -0,0 +1,59 @@ +# master +- Fix issue in jump-to-definition on Windows. (See https://github.com/rescript-lang/rescript-vscode/issues/98) where the wrong URI was generated. +- Don't show file path on hover. +- Add autocomplete for props in JSX components. +- Autocomplete: fix issue where `->` autocomplete was overruling `.`. See https://github.com/rescript-lang/rescript-editor-support/issues/99. +- Add pipe autocomplete for builtin list, array, string, option types. And for string and array literals. +- Fix hover on labels in component functions with compiler version 9.1, and labels with type annotation. + +## Release 1.0.6 of rescript-vscode +This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/03ee0d97b250474028d4fb08eac81ddb21ccb082) is vendored in [rescript-vscode 1.0.6](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.6). + +#### New features +- Add support for autocomplete for pipe-first `foo->`: the type of `foo` is used to determine the module to take completions from. +- Add support for autocomplete for decorators such as `@module` and `@val`. +- Add support for autocomplete of labelled arguments `foo(~label... )`. +- Add support for @deprecated attributes in autocomplete. +- Support for upcoming `rescript` npm package for the compiler. Looks for `rescript` in addition to `bs-platform` in `node_modules`. + +#### Fixes + +- Fix issue for uncurried functions where the internal definition of `Js.Fn.arity` is shown on hover. (See https://github.com/rescript-lang/rescript-editor-support/issues/62). +- Fix type hint when hovering over labeled arguments of components (See https://github.com/rescript-lang/rescript-editor-support/issues/63). +- Fix issue where values declared with type annotation would not show up in autocomplete, and would show no doc comment on hover. (See https://github.com/rescript-lang/rescript-vscode/issues/72). +- Fix hover on definitions inside a react component module, or whenever multiple definitins for the same value exist in the module (See https://github.com/rescript-lang/rescript-editor-support/issues/67). +- Fix autocomplete issue where multiple open's were considered in the wrong priority order (See https://github.com/rescript-lang/rescript-editor-support/issues/72). +- Autocomplete: add support for `open!` in addition to `open`. + +## Release 1.0.5 of rescript-vscode +This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/6bdd10f6af259edc5f9cbe5b9df06836de3ab865) is vendored in [rescript-vscode 1.0.5](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.5). + +- Add support for doc strings when hovering on modules. +- Add support for printing uncurried function types in hover. +- Fix autocomplete issue where `open Foo` would be picked up inside line comments (see https://github.com/rescript-lang/rescript-editor-support/issues/15). +- Don't print parens as in `A()` for 0-ary variants. +- Fix infinite loop in autocomplete that can cause `rescript-editor-support.exe` processes to use up 100% cpu. +- Fix jump to type definition for types defined in an inner module. + +## Release 1.0.3 of rescript-vscode +This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/214d220d8573f9f0c8d54e623c163e01617bf124) is vendored in [rescript-vscode 1.0.3](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.3). + +- Fix type shown when hovering on record fields (see https://github.com/rescript-lang/rescript-vscode/issues/52), and doc comments for records. +- Fix issue where type variables are printed with global renaming when hovering or autocompleting a module (see https://github.com/rescript-lang/rescript-editor-support/issues/38). +- Fix issue where a log file was always created (see https://github.com/rescript-lang/rescript-vscode/issues/47). +- Add support for hover on the id of toplevel module definitions (```module Id = ...```). + +## Release 1.0.1 of rescript-vscode +This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/232ad609766c415048750c5cc828973a9995f382) is vendored in [rescript-vscode 1.0.1](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.1). + +- Support printing inline records. +- Add typedef hover support. +- Always output valid json, even in case of internal error. +- Remove semicolon in module top level preview. +- Support syntax highlight in hover fenced blocks. +- Fix printing of variant arguments. +- Use outcome printer from the syntax to print type declarations. +- Fix issue in command-line parsing on Windows with paths of the form `c:/...:line:column`. + +## Release 1.0.0 of rescript-vscode +This [commit](https://github.com/rescript-lang/rescript-editor-support/commit/d45f45793a307a3bb87dcac0542fd412669f1b6e) is vendored in [rescript-vscode 1.0.0](https://github.com/rescript-lang/rescript-vscode/releases/tag/1.0.0). diff --git a/analysis/Makefile b/analysis/Makefile new file mode 100644 index 000000000..19b7b78c6 --- /dev/null +++ b/analysis/Makefile @@ -0,0 +1,49 @@ +SHELL = /bin/bash +MAKEFLAGS += --jobs 4 +INCLUDES = -I src -I src/vendor/odoc_parser -I src/vendor/omd -I src/vendor/res_outcome_printer -I src/vendor + +OCAMLOPT = ocamlopt.opt +OCAMLFLAGS = -g -w +26+27+32+33+39 -bin-annot -I +compiler-libs $(INCLUDES) +OCAMLDEP = ocamldep.opt + +OUTPUT = ../server/analysis_binaries/current-platform.exe + +%.cmi : %.mli + @echo Building $@ + @$(OCAMLOPT) $(OCAMLFLAGS) -c $< +%.cmx : %.ml + @echo Building $@ + @$(OCAMLOPT) $(OCAMLFLAGS) -c $< + +include .depend +depend: + @$(OCAMLDEP) -native $(INCLUDES) `find src -name "*.ml" -o -name "*.mli"` > .depend + +SOURCE_FILES = $(shell $(OCAMLDEP) -sort `find src -name "*.ml"` | sed -E "s/\.ml/.cmx/g") + +$(OUTPUT): $(SOURCE_FILES) + @echo Linking... + @$(OCAMLOPT) $(OCAMLFLAGS) -O2 -o $(OUTPUT) \ + -I +compiler-libs unix.cmxa str.cmxa ocamlcommon.cmxa $(INCLUDES) $(SOURCE_FILES) + @echo Done! + +build-native: $(OUTPUT) depend + +dce: build-native + ../node_modules/.bin/reanalyze -dce-cmt src -suppress src/vendor + +tests/node_modules/.bin/rescript: + @cd tests && npm install + +tests/lib/.compiler.log: tests/node_modules/.bin/rescript + @cd tests && node_modules/.bin/rescript build -with-deps + +test: dce tests/lib/.compiler.log + ./test.sh + +clean: + git clean -dfx src + +.DEFAULT_GOAL := build-native + +.PHONY: depend clean build-native dce test diff --git a/analysis/Readme.md b/analysis/Readme.md new file mode 100644 index 000000000..180467011 --- /dev/null +++ b/analysis/Readme.md @@ -0,0 +1,39 @@ +# Rescript Editor Support + +This is a private command line binary used by [rescript-vscode](https://github.com/rescript-lang/rescript-vscode) to power a few functionalities such as jump to definition, hover and autocomplete. + +The binary reads the `.cmt` and `.cmti` files and analyses them. + +## Install + +``` +opam switch 4.06.1 +``` + +## Build + +``` +make +``` + +The built artifact is in `lib/rescript-editor-support.exe` + +## Test + +``` +make test +``` + +## Usage + +Run: + +```sh +lib/rescript-editor-support.exe --help +``` + +## History + +This project is based on a fork of [Reason Language Server](https://github.com/jaredly/reason-language-server). + +Distributed under the MIT License (see [LICENSE](./LICENSE)). diff --git a/analysis/examples/example-project/bsconfig.json b/analysis/examples/example-project/bsconfig.json new file mode 100644 index 000000000..8cbede0a1 --- /dev/null +++ b/analysis/examples/example-project/bsconfig.json @@ -0,0 +1,12 @@ +{ + "name": "tryit", + "sources": "src", + "bsc-flags": ["-bs-super-errors", "-open Belt"], + "warnings": { + "number": "-32-26-27-33" + }, + "bs-dependencies": ["reason-react"], + "reason": { "react-jsx": 3 }, + "namespace": "try-it", + "refmt": 3 +} \ No newline at end of file diff --git a/analysis/examples/example-project/package-lock.json b/analysis/examples/example-project/package-lock.json new file mode 100644 index 000000000..39f9ced65 --- /dev/null +++ b/analysis/examples/example-project/package-lock.json @@ -0,0 +1,42 @@ +{ + "name": "example-project", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "bs-platform": "9.0.2", + "reason-react": "^0.9.1" + } + }, + "node_modules/bs-platform": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-9.0.2.tgz", + "integrity": "sha512-Ye9JqJ4Oa7mcjjoOVRYI8Uc2Cf8N7jQLWDcdUplY7996d/YErSR7WitmV7XnSwr4EvdrbwjEsg1NxNjUQv3ChA==", + "hasInstallScript": true, + "bin": { + "bsb": "bsb", + "bsc": "bsc", + "bsrefmt": "bsrefmt", + "bstracing": "lib/bstracing" + } + }, + "node_modules/reason-react": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/reason-react/-/reason-react-0.9.1.tgz", + "integrity": "sha512-nlH0O2TDy9KzOLOW+vlEQk4ExHOeciyzFdoLcsmmiit6hx6H5+CVDrwJ+8aiaLT/kqK5xFOjy4PS7PftWz4plA==" + } + }, + "dependencies": { + "bs-platform": { + "version": "9.0.2", + "resolved": "https://registry.npmjs.org/bs-platform/-/bs-platform-9.0.2.tgz", + "integrity": "sha512-Ye9JqJ4Oa7mcjjoOVRYI8Uc2Cf8N7jQLWDcdUplY7996d/YErSR7WitmV7XnSwr4EvdrbwjEsg1NxNjUQv3ChA==" + }, + "reason-react": { + "version": "0.9.1", + "resolved": "https://registry.npmjs.org/reason-react/-/reason-react-0.9.1.tgz", + "integrity": "sha512-nlH0O2TDy9KzOLOW+vlEQk4ExHOeciyzFdoLcsmmiit6hx6H5+CVDrwJ+8aiaLT/kqK5xFOjy4PS7PftWz4plA==" + } + } +} diff --git a/analysis/examples/example-project/package.json b/analysis/examples/example-project/package.json new file mode 100644 index 000000000..818017cfe --- /dev/null +++ b/analysis/examples/example-project/package.json @@ -0,0 +1,11 @@ +{ + "dependencies": { + "bs-platform": "9.0.2", + "reason-react": "^0.9.1" + }, + "scripts": { + "build": "bsb -make-world", + "start": "bsb -make-world -w", + "clean": "bsb -clean" + } +} diff --git a/analysis/examples/example-project/src/B.re b/analysis/examples/example-project/src/B.re new file mode 100644 index 000000000..2ad1ee830 --- /dev/null +++ b/analysis/examples/example-project/src/B.re @@ -0,0 +1,9 @@ + + +let x = 12 + + +let y = 44 + + +let z = 123 diff --git a/analysis/examples/example-project/src/Embeded.md b/analysis/examples/example-project/src/Embeded.md new file mode 100644 index 000000000..ede126c0f --- /dev/null +++ b/analysis/examples/example-project/src/Embeded.md @@ -0,0 +1,55 @@ +# Markdown Embedded Fenced Code Regression Test + +```re +module Something = { + open Other; + + let m = {name: "Me", age: 0}; + let animal = Things(10); + let other = Things(2); + let me: animals = People("Hie"); + let x = something + 10; + let r = m.name; + + let awesome = 20; + if (true) { + () + } +}; +``` + +```reason +module Something = { + open Other; + + let m = {name: "Me", age: 0}; + let animal = Things(10); + let other = Things(2); + let me: animals = People("Hie"); + let x = something + 10; + let r = m.name; + + let awesome = 20; + if (true) { + () + } +}; +``` + +```reasonml +module Something = { + open Other; + + let m = {name: "Me", age: 0}; + let animal = Things(10); + let other = Things(2); + let me: animals = People("Hie"); + let x = something + 10; + let r = m.name; + + let awesome = 20; + if (true) { + () + } +}; +``` \ No newline at end of file diff --git a/analysis/examples/example-project/src/Hello.re b/analysis/examples/example-project/src/Hello.re new file mode 100644 index 000000000..04e8a6f3f --- /dev/null +++ b/analysis/examples/example-project/src/Hello.re @@ -0,0 +1,188 @@ +let someLongName = 10; + +let otherLongName = "string"; + +let x = [%bs.obj {a: 3}]; + +let r = Other.something; + +let l = More.inner + More.n + Other.inner; + +let n = More.n; + +let _ = More.party; +let _ = string_of_bool; + +/* let m = {More.a: 2, b: 32.}; */ + +module Something = { + open Other; + + let m = {name: "Me", age: 0}; + let animal = Things(10); + let other = Things(2); + let me: animals = People("Hie"); + let x = something + 10; + let r = m.name; + + let awesome = 20; + if (true) { + () + } +}; + +open! Something; + +let y = x + 10; + +switch me { +| Things(n) => () +| _ => () +}; + + +let z = x * x; + +let aThing = 10 + Other.something; + +/** Some docs about this **awesome** thing. */ +let awesome = 100 + m.age; + +let thing = "thing"; + +let transform = (x, y) => x ++ Js.Float.toString(y); + +let z = transform("hello ", 5.); + +let zzz = 1; + +let more = 20; + +/** Something here */ +let added = 10 + awesome; + +open Other; + +open Hashtbl; + +/** Some more documentation about this */ +let awesome = x => x + 2; + +let a = [ + "hello", + "my fine" ++ "folks", + "in boonville" +]; + +let div = (~x, ~y, ~children, ()) => 10; + +let m =
; + + let something = animal => switch animal { + | blank => () + }; + + something(animal); + +let someFunction = (memorableName, {contents}) => { + let innerMemorable = 20; + memorableName + innerMemorable; +}; + +/* let awesome = 10000; */ + +/* let awesome = 111; */ + +let z = 10; + +let z = find; + +let z = later; + +let m = Other.later; + +for (_index in 0 to 10) { + print_endline("hellO"); +}; + +module OneOneOneOne = { + module TwoTwoTwoTwo = { + let xxxxxxxxxx = 10; + }; +}; +let r = OneOneOneOne.TwoTwoTwoTwo.xxxxxxxxxx; + +type awesome = { + one: string, + two: float, +}; + +open OneOneOneOne.TwoTwoTwoTwo; + +include OneOneOneOne.TwoTwoTwoTwo; + +include More; + +let _ = Other.oo.person.name; + +type lots = +| Parties +| Plutocrats(int, float) +| Possums +| Oppossums; + +let y = Some(10 + awesome(3)); + +let z = {contents: 30}; +let party = {one: "one", two: 2.}; + +let {one, two} = party; + +let thing = () => { + 34 + 43; +}; + +type more = awesome; + +let {contents} = z; + +switch (y) { +| Some(u) => () +| None => () +}; + +/* let x = [%raw " hello"]; */ + +let awesome = "hello"; + + +type shortReference = (string, list(string), string); + +type reference = { + uri: string, + moduleName: string, + modulePath: list(string), + name: string, +}; + +type typeSource = + | Builtin(string) + | Public(reference) + | NotFound; + +type lockfile = { + version: int, + pastVersions: Belt.HashMap.Int.t( + list(( + shortReference, + int + )) + ), + current: list(( + shortReference, + int + )) +}; \ No newline at end of file diff --git a/analysis/examples/example-project/src/Json.re b/analysis/examples/example-project/src/Json.re new file mode 100644 index 000000000..514597428 --- /dev/null +++ b/analysis/examples/example-project/src/Json.re @@ -0,0 +1,599 @@ +/** # Json parser + * + * Works with bucklescript and bsb-native + * + * ## Basics + * + * ``` + * open Json.Infix; /* for the nice infix operators */ + * let raw = {|{"hello": "folks"}|}; + * let who = Json.parse(raw) |> Json.get("hello") |?> Json.string; + * Js.log(who); + * ``` + * + * ## Parse & stringify + * + * @doc parse, stringify + * + * ## Accessing descendents + * + * @doc get, nth, getPath + * + * ## Coercing to types + * + * @doc string, number, array, obj, bool, null + * + * ## The JSON type + * + * @doc t + * + * ## Infix operators for easier working + * + * @doc Infix + */; + +type t = + | String(string) + | Number(float) + | Array(list(t)) + | Object(list((string, t))) + | True + | False + | Null; + +let string_of_number = (f) => { + let s = Js.Float.toString(f); + if (s.[String.length(s) - 1] == '.') { + String.sub(s, 0, String.length(s) - 1) + } else { + s + } +}; + +/** + * This module is provided for easier working with optional values. + */ +module Infix = { + /** The "force unwrap" operator + * + * If you're sure there's a value, you can force it. + * ``` + * open Json.Infix; + * let x: int = Some(10) |! "Expected this to be present"; + * Js.log(x); + * ``` + * + * But you gotta be sure, otherwise it will throw. + * ```reason;raises + * open Json.Infix; + * let x: int = None |! "This will throw"; + * ``` + */ + let (|!) = (o, d) => + switch o { + | None => failwith(d) + | Some(v) => v + }; + /** The "upwrap with default" operator + * ``` + * open Json.Infix; + * let x: int = Some(10) |? 4; + * let y: int = None |? 5; + * Js.log2(x, y); + * ``` + */ + let (|?) = (o, d) => + switch o { + | None => d + | Some(v) => v + }; + /** The "transform contents into new optional" operator + * ``` + * open Json.Infix; + * let maybeInc = x => x > 5 ? Some(x + 1) : None; + * let x: option(int) = Some(14) |?> maybeInc; + * let y: option(int) = None |?> maybeInc; + * ``` + */ + let (|?>) = (o, fn) => + switch o { + | None => None + | Some(v) => fn(v) + }; + /** The "transform contents into new value & then re-wrap" operator + * ``` + * open Json.Infix; + * let inc = x => x + 1; + * let x: option(int) = Some(7) |?>> inc; + * let y: option(int) = None |?>> inc; + * Js.log2(x, y); + * ``` + */ + let (|?>>) = (o, fn) => + switch o { + | None => None + | Some(v) => Some(fn(v)) + }; + /** "handle the value if present, otherwise here's the default" + * + * It's called fold because that's what people call it :?. It's the same as "transform contents to new value" + "unwrap with default". + * + * ``` + * open Json.Infix; + * let inc = x => x + 1; + * let x: int = fold(Some(4), 10, inc); + * let y: int = fold(None, 2, inc); + * Js.log2(x, y); + * ``` + */ + let fold = (o, d, f) => + switch o { + | None => d + | Some(v) => f(v) + }; +}; + +let escape = (text) => { + let ln = String.length(text); + let buf = Buffer.create(ln); + let rec loop = (i) => + if (i < ln) { + switch text.[i] { + | '\012' => Buffer.add_string(buf, "\\f") + | '\\' => Buffer.add_string(buf, "\\\\") + | '"' => Buffer.add_string(buf, "\\\"") + | '\n' => Buffer.add_string(buf, "\\n") + | '\b' => Buffer.add_string(buf, "\\b") + | '\r' => Buffer.add_string(buf, "\\r") + | '\t' => Buffer.add_string(buf, "\\t") + | c => Buffer.add_char(buf, c) + }; + loop(i + 1) + }; + loop(0); + Buffer.contents(buf) +}; + +/** ``` + * let text = {|{"hello": "folks", "aa": [2, 3, "four"]}|}; + * let result = Json.stringify(Json.parse(text)); + * Js.log(result); + * assert(text == result); + * ``` + */ +let rec stringify = (t) => + switch t { + | String(value) => "\"" ++ escape(value) ++ "\"" + | Number(num) => string_of_number(num) + | Array(items) => "[" ++ String.concat(", ", List.map(items, stringify)) ++ "]" + | Object(items) => + "{" + ++ String.concat( + ", ", + List.map(items, ((k, v)) => "\"" ++ String.escaped(k) ++ "\": " ++ stringify(v)) + ) + ++ "}" + | True => "true" + | False => "false" + | Null => "null" + }; + +let white = n => { + let buffer = Buffer.create(n); + for (_ in 0 to n - 1) { + Buffer.add_char(buffer, ' ') + }; + Buffer.contents(buffer) +}; + +let rec stringifyPretty = (~indent=0, t) => + switch t { + | String(value) => "\"" ++ escape(value) ++ "\"" + | Number(num) => string_of_number(num) + | Array([]) => "[]" + | Array(items) => "[\n" ++ white(indent) ++ String.concat(",\n" ++ white(indent), List.map(items, stringifyPretty(~indent=indent + 2))) ++ "\n" ++ white(indent) ++ "]" + | Object([]) => "{}" + | Object(items) => + "{\n" ++ white(indent) + ++ String.concat( + ",\n" ++ white(indent), + List.map(items, ((k, v)) => "\"" ++ String.escaped(k) ++ "\": " ++ stringifyPretty(~indent=indent + 2, v)) + ) + ++ "\n" ++ white(indent) ++ "}" + | True => "true" + | False => "false" + | Null => "null" + }; + + +let unwrap = (message, t) => + switch t { + | Some(v) => v + | None => failwith(message) + }; + +[@nodoc] +module Parser = { + let split_by = (~keep_empty=false, is_delim, str) => { + let len = String.length(str); + let rec loop = (acc, last_pos, pos) => + if (pos == (-1)) { + if (last_pos == 0 && ! keep_empty) { + acc + } else { + [String.sub(str, 0, last_pos), ...acc] + } + } else if (is_delim(str.[pos])) { + let new_len = last_pos - pos - 1; + if (new_len != 0 || keep_empty) { + let v = String.sub(str, pos + 1, new_len); + loop([v, ...acc], pos, pos - 1) + } else { + loop(acc, pos, pos - 1) + } + } else { + loop(acc, last_pos, pos - 1) + }; + loop([], len, len - 1) + }; + let fail = (text, pos, message) => { + let pre = String.sub(text, 0, pos); + let lines = split_by((c) => c == '\n', pre); + let count = List.length(lines); + let last = count > 0 ? List.getExn(lines, count - 1) : ""; + let col = String.length(last) + 1; + let line = List.length(lines); + let string = Printf.sprintf("Error \"%s\" at %d:%d -> %s\n", message, line, col, last); + failwith(string) + }; + let rec skipToNewline = (text, pos) => + if (pos >= String.length(text)) { + pos + } else if (text.[pos] == '\n') { + pos + 1 + } else { + skipToNewline(text, pos + 1) + }; + let stringTail = (text) => { + let len = String.length(text); + if (len > 1) { + String.sub(text, 1, len - 1) + } else { + "" + } + }; + let rec skipToCloseMultilineComment = (text, pos) => + if (pos + 1 >= String.length(text)) { + failwith("Unterminated comment") + } else if (text.[pos] == '*' && text.[pos + 1] == '/') { + pos + 2 + } else { + skipToCloseMultilineComment(text, pos + 1) + }; + let rec skipWhite = (text, pos) => + if (pos < String.length(text) + && (text.[pos] == ' ' || text.[pos] == '\t' || text.[pos] == '\n' || text.[pos] == '\r')) { + skipWhite(text, pos + 1) + } else { + pos + }; + let parseString = (text, pos) => { + /* let i = ref(pos); */ + let buffer = Buffer.create(String.length(text)); + let ln = String.length(text); + let rec loop = (i) => + i >= ln ? + fail(text, i, "Unterminated string") : + ( + switch text.[i] { + | '"' => i + 1 + | '\\' => + i + 1 >= ln ? + fail(text, i, "Unterminated string") : + ( + switch text.[i + 1] { + | '/' => + Buffer.add_char(buffer, '/'); + loop(i + 2) + | 'f' => + Buffer.add_char(buffer, '\012'); + loop(i + 2) + | _ => + Buffer.add_string(buffer, Scanf.unescaped(String.sub(text, i, 2))); + loop(i + 2) + } + ) + | c => + Buffer.add_char(buffer, c); + loop(i + 1) + } + ); + let final = loop(pos); + (Buffer.contents(buffer), final) + }; + let parseDigits = (text, pos) => { + let len = String.length(text); + let rec loop = (i) => + if (i >= len) { + i + } else { + switch text.[i] { + | '0'..'9' => loop(i + 1) + | _ => i + } + }; + loop(pos + 1) + }; + let parseWithDecimal = (text, pos) => { + let pos = parseDigits(text, pos); + if (pos < String.length(text) && text.[pos] == '.') { + let pos = parseDigits(text, pos + 1); + pos + } else { + pos + } + }; + let parseNumber = (text, pos) => { + let pos = parseWithDecimal(text, pos); + let ln = String.length(text); + if (pos < ln - 1 && (text.[pos] == 'E' || text.[pos] == 'e')) { + let pos = + switch text.[pos + 1] { + | '-' + | '+' => pos + 2 + | _ => pos + 1 + }; + parseDigits(text, pos) + } else { + pos + } + }; + let parseNegativeNumber = (text, pos) => { + let final = + if (text.[pos] == '-') { + parseNumber(text, pos + 1) + } else { + parseNumber(text, pos) + }; + (Number(float_of_string(String.sub(text, pos, final - pos))), final) + }; + let expect = (char, text, pos, message) => + if (text.[pos] != char) { + fail(text, pos, "Expected: " ++ message) + } else { + pos + 1 + }; + let parseComment: 'a .(string, int, (string, int) => 'a) => 'a = + (text, pos, next) => + if (text.[pos] != '/') { + if (text.[pos] == '*') { + next(text, skipToCloseMultilineComment(text, pos + 1)) + } else { + failwith("Invalid syntax") + } + } else { + next(text, skipToNewline(text, pos + 1)) + }; + let maybeSkipComment = (text, pos) => + if (pos < String.length(text) && text.[pos] == '/') { + if (pos + 1 < String.length(text) && text.[pos + 1] == '/') { + skipToNewline(text, pos + 1) + } else if (pos + 1 < String.length(text) && text.[pos + 1] == '*') { + skipToCloseMultilineComment(text, pos + 1) + } else { + fail(text, pos, "Invalid synatx") + } + } else { + pos + }; + let rec skip = (text, pos) => + if (pos == String.length(text)) { + pos + } else { + let n = skipWhite(text, pos) |> maybeSkipComment(text); + if (n > pos) { + skip(text, n) + } else { + n + } + }; + let rec parse = (text, pos) => + if (pos >= String.length(text)) { + fail(text, pos, "Reached end of file without being done parsing") + } else { + switch text.[pos] { + | '/' => parseComment(text, pos + 1, parse) + | '[' => parseArray(text, pos + 1) + | '{' => parseObject(text, pos + 1) + | 'n' => + if (String.sub(text, pos, 4) == "null") { + (Null, pos + 4) + } else { + fail(text, pos, "unexpected character") + } + | 't' => + if (String.sub(text, pos, 4) == "true") { + (True, pos + 4) + } else { + fail(text, pos, "unexpected character") + } + | 'f' => + if (String.sub(text, pos, 5) == "false") { + (False, pos + 5) + } else { + fail(text, pos, "unexpected character") + } + | '\n' + | '\t' + | ' ' + | '\r' => parse(text, skipWhite(text, pos)) + | '"' => + let (s, pos) = parseString(text, pos + 1); + (String(s), pos) + | '-' + | '0'..'9' => parseNegativeNumber(text, pos) + | _ => fail(text, pos, "unexpected character") + } + } + and parseArrayValue = (text, pos) => { + let pos = skip(text, pos); + let (value, pos) = parse(text, pos); + let pos = skip(text, pos); + switch text.[pos] { + | ',' => + let pos = skip(text, pos + 1); + if (text.[pos] == ']') { + ([value], pos + 1) + } else { + let (rest, pos) = parseArrayValue(text, pos); + ([value, ...rest], pos) + } + | ']' => ([value], pos + 1) + | _ => fail(text, pos, "unexpected character") + } + } + and parseArray = (text, pos) => { + let pos = skip(text, pos); + switch text.[pos] { + | ']' => (Array([]), pos + 1) + | _ => + let (items, pos) = parseArrayValue(text, pos); + (Array(items), pos) + } + } + and parseObjectValue = (text, pos) => { + let pos = skip(text, pos); + if (text.[pos] != '"') { + fail(text, pos, "Expected string") + } else { + let (key, pos) = parseString(text, pos + 1); + let pos = skip(text, pos); + let pos = expect(':', text, pos, "Colon"); + let (value, pos) = parse(text, pos); + let pos = skip(text, pos); + switch text.[pos] { + | ',' => + let pos = skip(text, pos + 1); + if (text.[pos] == '}') { + ([(key, value)], pos + 1) + } else { + let (rest, pos) = parseObjectValue(text, pos); + ([(key, value), ...rest], pos) + } + | '}' => ([(key, value)], pos + 1) + | _ => + let (rest, pos) = parseObjectValue(text, pos); + ([(key, value), ...rest], pos) + } + } + } + and parseObject = (text, pos) => { + let pos = skip(text, pos); + if (text.[pos] == '}') { + (Object([]), pos + 1) + } else { + let (pairs, pos) = parseObjectValue(text, pos); + (Object(pairs), pos) + } + }; +}; + +/** Turns some text into a json object. throws on failure */ +let parse = (text) => { + let (item, pos) = Parser.parse(text, 0); + let pos = Parser.skip(text, pos); + if (pos < String.length(text)) { + failwith( + "Extra data after parse finished: " ++ String.sub(text, pos, String.length(text) - pos) + ) + } else { + item + } +}; + +/* Accessor helpers */ +let bind = (v, fn) => + switch v { + | None => None + | Some(v) => fn(v) + }; + +/** If `t` is an object, get the value associated with the given string key */ +let get = (key, t) => + switch t { + | Object(items) => + List.getAssoc(items, key, (==)) + | _ => None + }; + +/** If `t` is an array, get the value associated with the given index */ +let nth = (n, t) => + switch t { + | Array(items) => + if (n < List.length(items)) { + Some(List.getExn(items, n)) + } else { + None + } + | _ => None + }; + +let string = (t) => + switch t { + | String(s) => Some(s) + | _ => None + }; + +let number = (t) => + switch t { + | Number(s) => Some(s) + | _ => None + }; + +let array = (t) => + switch t { + | Array(s) => Some(s) + | _ => None + }; + +let obj = (t) => + switch t { + | Object(s) => Some(s) + | _ => None + }; + +let bool = (t) => + switch t { + | True => Some(true) + | False => Some(false) + | _ => None + }; + +let null = (t) => + switch t { + | Null => Some() + | _ => None + }; + +let rec parsePath = (keyList, t) => + switch keyList { + | [] => Some(t) + | [head, ...rest] => + switch (get(head, t)) { + | None => None + | Some(value) => parsePath(rest, value) + } + }; + +/** Get a deeply nested value from an object `t`. + * ``` + * open Json.Infix; + * let json = Json.parse({|{"a": {"b": {"c": 2}}}|}); + * let num = Json.getPath("a.b.c", json) |?> Json.number; + * assert(num == Some(2.)) + * ``` + */ +let getPath = (path, t) => { + let keys = Parser.split_by((c) => c == '.', path); + parsePath(keys, t) +}; \ No newline at end of file diff --git a/analysis/examples/example-project/src/ModuleWithDocComment.res b/analysis/examples/example-project/src/ModuleWithDocComment.res new file mode 100644 index 000000000..a09e2692f --- /dev/null +++ b/analysis/examples/example-project/src/ModuleWithDocComment.res @@ -0,0 +1,13 @@ +@@ocaml.doc("This comment is for the **toplevel** module.") + +@ocaml.doc("This comment is for the first **nested** module.") +module Nested = { + let x = "123" + + @ocaml.doc("This comment is for the inner **nested-again** module.") + module NestedAgain = { + let y = 123 + } +} + +module M = Nested.NestedAgain diff --git a/analysis/examples/example-project/src/More.re b/analysis/examples/example-project/src/More.re new file mode 100644 index 000000000..94eb86f28 --- /dev/null +++ b/analysis/examples/example-project/src/More.re @@ -0,0 +1,12 @@ +/** Toplevel docs */; + +/** Some contents */ +let contnets = "here"; + +let inner = 20; + +let n = 10; + +let party = 30; + +let awesome = 200; \ No newline at end of file diff --git a/analysis/examples/example-project/src/More.rei b/analysis/examples/example-project/src/More.rei new file mode 100644 index 000000000..4bda491db --- /dev/null +++ b/analysis/examples/example-project/src/More.rei @@ -0,0 +1,5 @@ + +let contnets: string; +let inner: int; +let n: int; +let party: int \ No newline at end of file diff --git a/analysis/examples/example-project/src/Other.re b/analysis/examples/example-project/src/Other.re new file mode 100644 index 000000000..a6f1d8e86 --- /dev/null +++ b/analysis/examples/example-project/src/Other.re @@ -0,0 +1,32 @@ + +/* let later = 10; */ + +/* Ok testing things */ + +let something = 10; + +type person = {name: string, age: int}; + +type animals = Things(int) | People(string) | Mouse; + +let inner = 10; +/* More.outer; */ + + +let m = Things(1); + +/* working on things. */ + + +let z = {name: "hi", age: 20}; + +let later = 20; + +let concat = (~first, ~second) => first + second; + +type other = {person, height: float}; +let oo = {person: z, height: 34.2}; + +let show = o => { + let m = o.height; +}; diff --git a/analysis/examples/example-project/src/Serde.ml b/analysis/examples/example-project/src/Serde.ml new file mode 100644 index 000000000..a1e19fa74 --- /dev/null +++ b/analysis/examples/example-project/src/Serde.ml @@ -0,0 +1,348 @@ +let rec (deserialize_Hello__TryIt____lockfile : + Json.t -> (TryIt.Hello.lockfile, string) Belt.Result.t) = + fun record -> + match record with + | ((Json.Object (items))[@explicit_arity ]) -> + (match Belt.List.getAssoc items "current" (=) with + | None -> + ((Belt.Result.Error + (((("No attribute ")[@reason.raw_literal "No attribute "]) ^ + "current"))) + [@explicit_arity ]) + | ((Some (json))[@explicit_arity ]) -> + (match (fun list -> + match list with + | ((Json.Array (items))[@explicit_arity ]) -> + let transformer json = + match json with + | ((Json.Array + (arg0::arg1::[]))[@explicit_arity ]) -> + (match (fun number -> + match number with + | ((Json.Number + (number))[@explicit_arity ]) + -> + ((Belt.Result.Ok + ((int_of_float number))) + [@explicit_arity ]) + | _ -> + ((Error + ((("Expected a float") + [@reason.raw_literal + "Expected a float"]))) + [@explicit_arity ])) arg1 + with + | Belt.Result.Ok arg1 -> + (match deserialize_Hello__TryIt____shortReference + arg0 + with + | Belt.Result.Ok arg0 -> + Belt.Result.Ok (arg0, arg1) + | Error error -> Error error) + | Error error -> Error error) + | _ -> + ((Belt.Result.Error + ((("Expected array") + [@reason.raw_literal "Expected array"]))) + [@explicit_arity ]) in + let rec loop items = + match items with + | [] -> ((Belt.Result.Ok ([])) + [@explicit_arity ]) + | one::rest -> + (match transformer one with + | ((Belt.Result.Error + (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (value))[@explicit_arity ]) -> + (match loop rest with + | ((Belt.Result.Error + (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (rest))[@explicit_arity ]) -> + ((Belt.Result.Ok ((value :: rest))) + [@explicit_arity ]))) in + loop items + | _ -> + ((Belt.Result.Error + ((("expected an array") + [@reason.raw_literal "expected an array"]))) + [@explicit_arity ])) json + with + | ((Belt.Result.Error (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error))[@explicit_arity ]) + | ((Belt.Result.Ok (attr_current))[@explicit_arity ]) -> + (match Belt.List.getAssoc items "pastVersions" (=) with + | None -> + ((Belt.Result.Error + (((("No attribute ") + [@reason.raw_literal "No attribute "]) ^ + "pastVersions"))) + [@explicit_arity ]) + | ((Some (json))[@explicit_arity ]) -> + (match (deserialize_Belt_HashMapInt____t + (fun list -> + match list with + | ((Json.Array + (items))[@explicit_arity ]) -> + let transformer json = + match json with + | ((Json.Array + (arg0::arg1::[]))[@explicit_arity + ]) + -> + (match (fun number -> + match number with + | ((Json.Number + (number)) + [@explicit_arity + ]) + -> + ((Belt.Result.Ok + ((int_of_float + number))) + [@explicit_arity + ]) + | _ -> + ((Error + ((("Expected a float") + [@reason.raw_literal + "Expected a float"]))) + [@explicit_arity + ])) arg1 + with + | Belt.Result.Ok arg1 -> + (match deserialize_Hello__TryIt____shortReference + arg0 + with + | Belt.Result.Ok arg0 -> + Belt.Result.Ok + (arg0, arg1) + | Error error -> + Error error) + | Error error -> Error error) + | _ -> + ((Belt.Result.Error + ((("Expected array") + [@reason.raw_literal + "Expected array"]))) + [@explicit_arity ]) in + let rec loop items = + match items with + | [] -> ((Belt.Result.Ok ([])) + [@explicit_arity ]) + | one::rest -> + (match transformer one with + | ((Belt.Result.Error + (error))[@explicit_arity ]) + -> + ((Belt.Result.Error + (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (value))[@explicit_arity ]) + -> + (match loop rest with + | ((Belt.Result.Error + (error))[@explicit_arity + ]) + -> + ((Belt.Result.Error + (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (rest))[@explicit_arity + ]) + -> + ((Belt.Result.Ok + ((value :: rest))) + [@explicit_arity ]))) in + loop items + | _ -> + ((Belt.Result.Error + ((("expected an array") + [@reason.raw_literal + "expected an array"]))) + [@explicit_arity ]))) json + with + | ((Belt.Result.Error (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error))[@explicit_arity ]) + | ((Belt.Result.Ok + (attr_pastVersions))[@explicit_arity ]) -> + (match Belt.List.getAssoc items "version" (=) + with + | None -> + ((Belt.Result.Error + (((("No attribute ") + [@reason.raw_literal + "No attribute "]) + ^ "version"))) + [@explicit_arity ]) + | ((Some (json))[@explicit_arity ]) -> + (match (fun number -> + match number with + | ((Json.Number + (number))[@explicit_arity ]) + -> + ((Belt.Result.Ok + ((int_of_float number))) + [@explicit_arity ]) + | _ -> + ((Error + ((("Expected a float") + [@reason.raw_literal + "Expected a float"]))) + [@explicit_arity ])) json + with + | ((Belt.Result.Error + (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (attr_version))[@explicit_arity ]) -> + Belt.Result.Ok + { + version = attr_version; + pastVersions = attr_pastVersions; + current = attr_current + })))))) + | _ -> + ((Belt.Result.Error + ((("Expected an object") + [@reason.raw_literal "Expected an object"]))) + [@explicit_arity ]) +and (deserialize_Hello__TryIt____shortReference : + Json.t -> (TryIt.Hello.shortReference, string) Belt.Result.t) = + fun value -> + (fun json -> + match json with + | ((Json.Array (arg0::arg1::arg2::[]))[@explicit_arity ]) -> + (match (fun string -> + match string with + | ((Json.String (string))[@explicit_arity ]) -> + ((Belt.Result.Ok (string))[@explicit_arity ]) + | _ -> + ((Error + ((("epected a string") + [@reason.raw_literal "epected a string"]))) + [@explicit_arity ])) arg2 + with + | Belt.Result.Ok arg2 -> + (match (fun list -> + match list with + | ((Json.Array (items))[@explicit_arity ]) -> + let transformer string = + match string with + | ((Json.String (string))[@explicit_arity ]) + -> ((Belt.Result.Ok (string)) + [@explicit_arity ]) + | _ -> + ((Error + ((("epected a string") + [@reason.raw_literal + "epected a string"]))) + [@explicit_arity ]) in + let rec loop items = + match items with + | [] -> ((Belt.Result.Ok ([])) + [@explicit_arity ]) + | one::rest -> + (match transformer one with + | ((Belt.Result.Error + (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (value))[@explicit_arity ]) -> + (match loop rest with + | ((Belt.Result.Error + (error))[@explicit_arity ]) -> + ((Belt.Result.Error (error)) + [@explicit_arity ]) + | ((Belt.Result.Ok + (rest))[@explicit_arity ]) -> + ((Belt.Result.Ok + ((value :: rest))) + [@explicit_arity ]))) in + loop items + | _ -> + ((Belt.Result.Error + ((("expected an array") + [@reason.raw_literal "expected an array"]))) + [@explicit_arity ])) arg1 + with + | Belt.Result.Ok arg1 -> + (match (fun string -> + match string with + | ((Json.String (string))[@explicit_arity ]) + -> ((Belt.Result.Ok (string)) + [@explicit_arity ]) + | _ -> + ((Error + ((("epected a string") + [@reason.raw_literal + "epected a string"]))) + [@explicit_arity ])) arg0 + with + | Belt.Result.Ok arg0 -> + Belt.Result.Ok (arg0, arg1, arg2) + | Error error -> Error error) + | Error error -> Error error) + | Error error -> Error error) + | _ -> + ((Belt.Result.Error + ((("Expected array")[@reason.raw_literal "Expected array"]))) + [@explicit_arity ])) value +and deserialize_Belt_HashMapInt____t : + 'arg0 . + (Json.t -> ('arg0, string) Belt.Result.t) -> + Json.t -> ('arg0 Belt_HashMapInt.t, string) Belt.Result.t + = + fun bTransformer -> + TransformHelpers.deserialize_Belt_HashMapInt____t bTransformer +let rec (serialize_Hello__TryIt____lockfile : TryIt.Hello.lockfile -> Json.t) + = + fun record -> + Json.Object + [("version", + (((fun i -> ((Json.Number ((float_of_int i)))[@explicit_arity ]))) + record.version)); + ("pastVersions", + ((serialize_Belt_HashMapInt____t + (fun list -> + Json.Array + (Belt.List.map list + (fun (arg0, arg1) -> + Json.Array + [serialize_Hello__TryIt____shortReference arg0; + ((fun i -> ((Json.Number ((float_of_int i))) + [@explicit_arity ]))) arg1])))) + record.pastVersions)); + ("current", + (((fun list -> + Json.Array + (Belt.List.map list + (fun (arg0, arg1) -> + Json.Array + [serialize_Hello__TryIt____shortReference arg0; + ((fun i -> ((Json.Number ((float_of_int i))) + [@explicit_arity ]))) arg1])))) record.current))] +and (serialize_Hello__TryIt____shortReference : + TryIt.Hello.shortReference -> Json.t) = + fun value -> + (fun (arg0, arg1, arg2) -> + Json.Array + [((fun s -> ((Json.String (s))[@explicit_arity ]))) arg0; + ((fun list -> + Json.Array + (Belt.List.map list + (fun s -> ((Json.String (s))[@explicit_arity ]))))) arg1; + ((fun s -> ((Json.String (s))[@explicit_arity ]))) arg2]) value +and serialize_Belt_HashMapInt____t : + 'arg0 . ('arg0 -> Json.t) -> 'arg0 Belt_HashMapInt.t -> Json.t = + fun bTransformer -> + TransformHelpers.serialize_Belt_HashMapInt____t bTransformer diff --git a/analysis/examples/example-project/src/SomeFile.ml b/analysis/examples/example-project/src/SomeFile.ml new file mode 100644 index 000000000..82babab72 --- /dev/null +++ b/analysis/examples/example-project/src/SomeFile.ml @@ -0,0 +1,7 @@ +let x = 10 + +let y = 20 + +let m x y = + let z = x + y in + z diff --git a/analysis/examples/example-project/src/TransformHelpers.re b/analysis/examples/example-project/src/TransformHelpers.re new file mode 100644 index 000000000..b3d5a21cb --- /dev/null +++ b/analysis/examples/example-project/src/TransformHelpers.re @@ -0,0 +1,16 @@ + +let deserialize_Belt__HashMapInt__t = (transformer, t) => { + assert(false) +}; + +let deserialize_Belt_HashMapInt____t = (a, b) => assert(false); + +let deserialize_Belt__HashMap__Int__t = (a, b) => assert(false); + +let serialize_Belt_HashMapInt____t = (a, b) => assert(false); + +let serialize_Belt__HashMap__Int__t = (a, b) => assert(false); + +let serialize_Belt_HashMapInt____t = (transformer, t) => { + assert(false) +}; diff --git a/analysis/examples/example-project/src/ZZ.res b/analysis/examples/example-project/src/ZZ.res new file mode 100644 index 000000000..6a3c9ddcd --- /dev/null +++ b/analysis/examples/example-project/src/ZZ.res @@ -0,0 +1,145 @@ +let a = 12 + +let b = [1, 2, 3, a] + +let c =
+ +let s = React.string + +module M = { + @react.component + let make = (~x) => React.string(x) +} + +let d = + +module J = { + @react.component + export make = (~children: React.element) => React.null +} + +let z = {React.string("")} {React.string("")} + +type inline = + | A({x: int, y: string}) + | B({x: int, y: string}) + | C({ + x: int, + y: string, + z: string, + w: string, + x0: string, + q1: string, + q2: string, + q3: string, + q4: string, + }) + | D({x: int, y: string}) + | E({x: int, y: string}) + | F + +module MSig: { + type rec t = A(list) + and s = list + + let x: int +} = { + type rec t = A(list) + and s = list + + let x = 14 +} + +module Impl = { + type rec t = A(list) + and s = list + + type w = int + + let x = 14 +} + +module Impl2 = { + include Impl +} + +module D = MSig +module E = Impl +module F = Impl2 + +@ocaml.doc("str docstring") +type str = string + +@ocaml.doc("gr docstring") +type gr = {x: int, s: str} + +let testRecordFields = (gr: gr) => { + let str = gr.s + str +} + +@ocaml.doc("vr docstring") +type vr = V1 | V2 + +let v1 = V1 + +module DoubleNested = ModuleWithDocComment.Nested.NestedAgain + +let uncurried = (. x) => x + 1 + +module Inner = { + type tInner = int + let vInner = 34 +} + +type typeInner = Inner.tInner + +let valueInner = Inner.vInner + +@ocaml.doc("Doc comment for functionWithTypeAnnotation") +let functionWithTypeAnnotation: unit => int = () => 1 + +module HoverInsideModuleWithComponent = { + let x = 2 // check that hover on x works + + @react.component + let make = () => React.null +} + +module Lib = { + let foo = (~age, ~name) => name ++ string_of_int(age) + let next = (~number=0, ~year) => number + year +} + +@ocaml.doc("This module is commented") @deprecated("This module is deprecated") +module Dep: { + @ocaml.doc("Some doc comment") @deprecated("Use customDouble instead") + let customDouble: int => int + + let customDouble2: int => int +} = { + let customDouble = foo => foo * 2 + let customDouble2 = foo => foo * 2 +} + +let cc = Dep.customDouble(11) + +module O = { + module Comp = { + @react.component + let make = (~first="", ~kas=11, ~foo=3, ~second, ~v) => + React.string(first ++ second ++ string_of_int(foo)) + } +} + +let comp = + +let lll = List.make(3, 4) + +let abc = "abc" + +let arr = [1, 2, 3] + +let some7 = Some(7) + + diff --git a/analysis/examples/example-project/types.json b/analysis/examples/example-project/types.json new file mode 100644 index 000000000..904cdf616 --- /dev/null +++ b/analysis/examples/example-project/types.json @@ -0,0 +1,13 @@ +{ + "output": "src/Serde.ml", + "engine": "rex-json", + "entries": [ + { + "file": "src/Hello.re", + "type": "lockfile" + } + ], + "custom": [ + {"module": "Belt_HashMapInt", "path": [], "name": "t", "args": 1} + ] +} \ No newline at end of file diff --git a/analysis/package-lock.json b/analysis/package-lock.json new file mode 100644 index 000000000..3d168c8c1 --- /dev/null +++ b/analysis/package-lock.json @@ -0,0 +1,33 @@ +{ + "name": "rescript-editor-support", + "version": "1.0.0", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "version": "1.0.0", + "license": "MIT", + "devDependencies": { + "reanalyze": "^2.15.0" + } + }, + "node_modules/reanalyze": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/reanalyze/-/reanalyze-2.15.0.tgz", + "integrity": "sha512-FUN/pqgTKs5i+kzi9Mje5deahZHKniOQDyig5UseozDiK81eW77A4iRyN+3UsnontG6K6mAdUcXCU9NpEqZFug==", + "dev": true, + "hasInstallScript": true, + "bin": { + "reanalyze": "reanalyze.exe" + } + } + }, + "dependencies": { + "reanalyze": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/reanalyze/-/reanalyze-2.15.0.tgz", + "integrity": "sha512-FUN/pqgTKs5i+kzi9Mje5deahZHKniOQDyig5UseozDiK81eW77A4iRyN+3UsnontG6K6mAdUcXCU9NpEqZFug==", + "dev": true + } + } +} diff --git a/analysis/package.json b/analysis/package.json new file mode 100644 index 000000000..26a2352b0 --- /dev/null +++ b/analysis/package.json @@ -0,0 +1,20 @@ +{ + "name": "rescript-editor-support", + "version": "1.0.0", + "keywords": [ + "rescript", + "lsp", + "ide" + ], + "private": true, + "repository": { + "url": "https://github.com/rescript-lang/rescript-editor-support", + "type": "git" + }, + "description": "Core editor analysis for ReScript's editor plugins", + "author": "Cristiano Calcagno", + "license": "MIT", + "devDependencies": { + "reanalyze": "^2.15.0" + } +} diff --git a/analysis/src/BuildSystem.ml b/analysis/src/BuildSystem.ml new file mode 100644 index 000000000..e94a2ce77 --- /dev/null +++ b/analysis/src/BuildSystem.ml @@ -0,0 +1,29 @@ +let namespacedName namespace name = + match namespace with + | None -> name + | Some namespace -> name ^ "-" ^ namespace + +open Infix + +let getBsPlatformDir rootPath = + let result = + ModuleResolution.resolveNodeModulePath ~startPath:rootPath "bs-platform" + in + let result = + if result = None then + ModuleResolution.resolveNodeModulePath ~startPath:rootPath "rescript" + else result + in + match result with + | Some path -> Ok path + | None -> + let message = "bs-platform could not be found" in + Log.log message; + Error message + +let getCompiledBase root = Files.ifExists (root /+ "lib" /+ "bs") + +let getStdlib base = + match getBsPlatformDir base with + | Error e -> Error e + | Ok bsPlatformDir -> Ok (bsPlatformDir /+ "lib" /+ "ocaml") diff --git a/analysis/src/Complete.current b/analysis/src/Complete.current new file mode 100644 index 000000000..e69de29bb diff --git a/analysis/src/EditorSupportCommands.ml b/analysis/src/EditorSupportCommands.ml new file mode 100644 index 000000000..643f7c6db --- /dev/null +++ b/analysis/src/EditorSupportCommands.ml @@ -0,0 +1,214 @@ +let dumpLocations state ~package ~file ~extra = + let locations = + extra.SharedTypes.locations + |> List.filter (fun (l, _) -> not l.Location.loc_ghost) + in + locations + |> List.map (fun ((location : Location.t), loc) -> + let hoverText = + Hover.newHover ~file + ~getModule:(State.fileForModule state ~package) + loc + in + let hover = + match hoverText with None -> "" | Some s -> String.escaped s + in + let uriLocOpt = + References.definitionForLoc ~pathsForModule:package.pathsForModule + ~file ~getUri:(State.fileForUri state) + ~getModule:(State.fileForModule state ~package) + loc + in + let def = + match uriLocOpt with + | None -> Protocol.null + | Some (uri2, loc) -> + Protocol.stringifyLocation + {uri = Uri2.toString uri2; range = Utils.cmtLocToRange loc} + in + Protocol.stringifyRange (Utils.cmtLocToRange location) + ^ "\n Hover: " ^ hover ^ "\n Definition: " ^ def) + |> String.concat "\n\n" + +let dump files = + Shared.cacheTypeToString := true; + let state = TopTypes.empty () in + files + |> List.iter (fun path -> + let filePath = Files.maybeConcat (Unix.getcwd ()) path in + let uri = Uri2.fromPath filePath in + let result = + match State.getFullFromCmt ~state ~uri with + | Error message -> + prerr_endline message; + "[]" + | Ok (package, {file; extra}) -> + dumpLocations state ~package ~file ~extra + in + print_endline result) + +let complete ~path ~line ~col ~currentFile = + let state = TopTypes.empty () in + let filePath = Files.maybeConcat (Unix.getcwd ()) path in + let uri = Uri2.fromPath filePath in + let result = + match State.getFullFromCmt ~state ~uri with + | Error message -> + prerr_endline message; + "[]" + | Ok (package, full) -> + let maybeText = Files.readFile currentFile in + NewCompletions.computeCompletions ~full ~maybeText ~package + ~pos:(line, col) ~state + |> List.map Protocol.stringifyCompletionItem + |> Protocol.array + in + print_endline result + +let hover state ~file ~line ~col ~extra ~package = + let open TopTypes in + let locations = + extra.SharedTypes.locations + |> List.filter (fun (l, _) -> not l.Location.loc_ghost) + in + let pos = Utils.protocolLineColToCmtLoc ~line ~col in + match References.locForPos ~extra:{extra with locations} pos with + | None -> Protocol.null + | Some (_, loc) -> ( + let locIsModule = + match loc with + | SharedTypes.LModule _ | TopLevelModule _ -> true + | TypeDefinition _ | Typed _ | Constant _ | Explanation _ -> false + in + let uriLocOpt = + References.definitionForLoc ~pathsForModule:package.pathsForModule ~file + ~getUri:(State.fileForUri state) + ~getModule:(State.fileForModule state ~package) + loc + in + let skipZero = + match uriLocOpt with + | None -> false + | Some (_, loc) -> + let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = + pos_lnum = 1 && pos_cnum - pos_bol = 0 + in + (* Skip if range is all zero, unless it's a module *) + (not locIsModule) && posIsZero loc.loc_start && posIsZero loc.loc_end + in + if skipZero then Protocol.null + else + let hoverText = + Hover.newHover ~file ~getModule:(State.fileForModule state ~package) loc + in + match hoverText with + | None -> Protocol.null + | Some s -> Protocol.stringifyHover {contents = s} ) + +let hover ~path ~line ~col = + let state = TopTypes.empty () in + let filePath = Files.maybeConcat (Unix.getcwd ()) path in + let uri = Uri2.fromPath filePath in + let result = + match State.getFullFromCmt ~state ~uri with + | Error message -> Protocol.stringifyHover {contents = message} + | Ok (package, {file; extra}) -> + hover state ~file ~line ~col ~extra ~package + in + print_endline result + +let definition state ~file ~line ~col ~extra ~package = + let open TopTypes in + let locations = + extra.SharedTypes.locations + |> List.filter (fun (l, _) -> not l.Location.loc_ghost) + in + let pos = Utils.protocolLineColToCmtLoc ~line ~col in + match References.locForPos ~extra:{extra with locations} pos with + | None -> Protocol.null + | Some (_, loc) -> ( + let locIsModule = + match loc with + | SharedTypes.LModule _ | TopLevelModule _ -> true + | TypeDefinition _ | Typed _ | Constant _ | Explanation _ -> false + in + let uriLocOpt = + References.definitionForLoc ~pathsForModule:package.pathsForModule ~file + ~getUri:(State.fileForUri state) + ~getModule:(State.fileForModule state ~package) + loc + in + match uriLocOpt with + | None -> Protocol.null + | Some (uri2, loc) -> + let posIsZero {Lexing.pos_lnum; pos_bol; pos_cnum} = + pos_lnum = 1 && pos_cnum - pos_bol = 0 + in + (* Skip if range is all zero, unless it's a module *) + let skipZero = + (not locIsModule) && posIsZero loc.loc_start && posIsZero loc.loc_end + in + if skipZero then Protocol.null + else + Protocol.stringifyLocation + {uri = Uri2.toString uri2; range = Utils.cmtLocToRange loc} ) + +let definition ~path ~line ~col = + let state = TopTypes.empty () in + let filePath = Files.maybeConcat (Unix.getcwd ()) path in + let uri = Uri2.fromPath filePath in + let result = + match State.getFullFromCmt ~state ~uri with + | Error _message -> Protocol.null + | Ok (package, {file; extra}) -> + definition state ~file ~line ~col ~extra ~package + in + print_endline result + +let test ~path = + Uri2.stripPath := true; + match Files.readFile path with + | None -> assert false + | Some text -> + let lines = text |> String.split_on_char '\n' in + let processLine i line = + if Str.string_match (Str.regexp "^//[ ]*\\^") line 0 then + let matched = Str.matched_string line in + let len = line |> String.length in + let mlen = String.length matched in + let rest = String.sub line mlen (len - mlen) in + let line = i - 1 in + let col = mlen - 1 in + if mlen >= 3 then ( + ( match String.sub rest 0 3 with + | "def" -> + print_endline + ( "Definition " ^ path ^ " " ^ string_of_int line ^ ":" + ^ string_of_int col ); + definition ~path ~line ~col + | "hov" -> + print_endline + ( "Hover " ^ path ^ " " ^ string_of_int line ^ ":" + ^ string_of_int col ); + + hover ~path ~line ~col + | "com" -> + print_endline + ( "Complete " ^ path ^ " " ^ string_of_int line ^ ":" + ^ string_of_int col ); + let currentFile, cout = Filename.open_temp_file "def" "txt" in + lines + |> List.iteri (fun j l -> + let lineToOutput = + if j == i then String.sub rest 3 (len - mlen - 3) else l + in + Printf.fprintf cout "%s\n" lineToOutput); + let line = line + 1 in + let col = len - mlen - 3 in + close_out cout; + complete ~path ~line ~col ~currentFile; + Sys.remove currentFile + | _ -> () ); + print_newline () ) + in + lines |> List.iteri processLine diff --git a/analysis/src/Files.ml b/analysis/src/Files.ml new file mode 100644 index 000000000..e11979533 --- /dev/null +++ b/analysis/src/Files.ml @@ -0,0 +1,120 @@ +let split str string = Str.split (Str.regexp_string str) string + +let removeExtraDots path = + Str.global_replace (Str.regexp_string "/./") "/" path + |> Str.global_replace (Str.regexp {|^\./\.\./|}) "../" + +(* Win32 & MacOS are case-insensitive *) +let pathEq = + match Sys.os_type = "Linux" with + | true -> fun a b -> a = b + | false -> fun a b -> String.lowercase_ascii a = String.lowercase_ascii b + +let pathStartsWith text prefix = + String.length prefix <= String.length text + && pathEq (String.sub text 0 (String.length prefix)) prefix + +let sliceToEnd str pos = String.sub str pos (String.length str - pos) + +let relpath base path = + if pathStartsWith path base then + let baselen = String.length base in + let rest = String.sub path baselen (String.length path - baselen) in + if rest = "" then "." ^ Filename.dir_sep + else if rest.[0] = Filename.dir_sep.[0] then + if String.length rest > 1 && rest.[1] = '.' then sliceToEnd rest 1 + else "." ^ rest + else if rest.[0] = '.' then rest + else "." ^ Filename.dir_sep ^ rest + else + let rec loop bp pp = + match (bp, pp) with + | "." :: ra, _ -> loop ra pp + | _, "." :: rb -> loop bp rb + | a :: ra, b :: rb when pathEq a b -> loop ra rb + | _ -> (bp, pp) + in + let base, path = + loop (split Filename.dir_sep base) (split Filename.dir_sep path) + in + String.concat Filename.dir_sep + ( ( match base = [] with + | true -> ["."] + | false -> List.map (fun _ -> "..") base ) + @ path ) + |> removeExtraDots + +let maybeStat path = + try Some (Unix.stat path) with Unix.Unix_error (Unix.ENOENT, _, _) -> None + +let getMtime path = + match maybeStat path with Some {Unix.st_mtime} -> Some st_mtime | _ -> None + +let readFile ~filename = + try + (* windows can't use open_in *) + let chan = open_in_bin filename in + let content = really_input_string chan (in_channel_length chan) in + close_in_noerr chan; + Some content + with + | _ -> None + +let exists path = match maybeStat path with None -> false | Some _ -> true + +let ifExists path = match exists path with true -> Some path | false -> None + +let readDirectory dir = + let maybeGet handle = + try Some (Unix.readdir handle) with End_of_file -> None + in + let rec loop handle = + match maybeGet handle with + | None -> + Unix.closedir handle; + [] + | Some name + when name = Filename.current_dir_name || name = Filename.parent_dir_name + -> + loop handle + | Some name -> name :: loop handle + in + match Unix.opendir dir with + | exception Unix.Unix_error (Unix.ENOENT, "opendir", _dir) -> [] + | handle -> loop handle + +let rec collectDirs path = + match maybeStat path with + | None -> [] + | Some {Unix.st_kind = Unix.S_DIR} -> + path + :: ( readDirectory path + |> List.map (fun name -> collectDirs (Filename.concat path name)) + |> List.concat ) + | _ -> [] + +let rec collect ?(checkDir = fun _ -> true) path test = + match maybeStat path with + | None -> [] + | Some {Unix.st_kind = Unix.S_DIR} -> + if checkDir path then + readDirectory path + |> List.map (fun name -> + collect ~checkDir (Filename.concat path name) test) + |> List.concat + else [] + | _ -> ( match test path with true -> [path] | false -> [] ) + +let fileConcat a b = + if + b <> "" + && b.[0] = '.' + && String.length b >= 2 + && b.[1] = Filename.dir_sep.[0] + then Filename.concat a (String.sub b 2 (String.length b - 2)) + else Filename.concat a b + +let isFullPath b = + b.[0] = '/' || (Sys.win32 && String.length b > 1 && b.[1] = ':') + +let maybeConcat a b = if b <> "" && isFullPath b then b else fileConcat a b diff --git a/analysis/src/FindFiles.ml b/analysis/src/FindFiles.ml new file mode 100644 index 000000000..ce0021db2 --- /dev/null +++ b/analysis/src/FindFiles.ml @@ -0,0 +1,293 @@ +open Infix + +let ifDebug debug name fn v = + if debug then Log.log (name ^ ": " ^ fn v); + v + +(* Returns a list of paths, relative to the provided `base` *) +let getSourceDirectories ~includeDev base config = + let rec handleItem current item = + match item with + | Json.Array contents -> + List.map (handleItem current) contents |> List.concat + | Json.String text -> [current /+ text] + | Json.Object _ -> ( + let dir = + Json.get "dir" item |?> Json.string |? "Must specify directory" + in + let typ = + match includeDev with + | true -> "lib" + | false -> item |> Json.get "type" |?> Json.string |? "lib" + in + if typ = "dev" then [] + else + match item |> Json.get "subdirs" with + | None | Some Json.False -> [current /+ dir] + | Some Json.True -> + Files.collectDirs (base /+ current /+ dir) + (* |> ifDebug(true, "Subdirs", String.concat(" - ")) *) + |> List.filter (fun name -> name <> Filename.current_dir_name) + |> List.map (Files.relpath base) + | Some item -> (current /+ dir) :: handleItem (current /+ dir) item ) + | _ -> failwith "Invalid subdirs entry" + in + config |> Json.get "sources" |?>> handleItem "" |? [] + +let isCompiledFile name = + Filename.check_suffix name ".cmt" || Filename.check_suffix name ".cmti" + +let isSourceFile name = + Filename.check_suffix name ".re" + || Filename.check_suffix name ".rei" + || Filename.check_suffix name ".res" + || Filename.check_suffix name ".resi" + || Filename.check_suffix name ".ml" + || Filename.check_suffix name ".mli" + +let compiledNameSpace name = + String.split_on_char '-' name + |> List.map String.capitalize_ascii + |> String.concat "" + (* Remove underscores??? Whyyy bucklescript, whyyyy *) + |> String.split_on_char '_' + |> String.concat "" + +let compiledBaseName ~namespace name = + Filename.chop_extension name + ^ match namespace with None -> "" | Some n -> "-" ^ compiledNameSpace n + +let getName x = + Filename.basename x |> Filename.chop_extension |> String.capitalize_ascii + +let filterDuplicates cmts = + (* Remove .cmt's that have .cmti's *) + let intfs = Hashtbl.create 100 in + cmts + |> List.iter (fun path -> + if + Filename.check_suffix path ".rei" + || Filename.check_suffix path ".mli" + || Filename.check_suffix path ".cmti" + then Hashtbl.add intfs (getName path) true); + cmts + |> List.filter (fun path -> + not + ( ( Filename.check_suffix path ".re" + || Filename.check_suffix path ".ml" + || Filename.check_suffix path ".cmt" ) + && Hashtbl.mem intfs (getName path) )) + +let nameSpaceToName n = + n + |> Str.split (Str.regexp "[-/@]") + |> List.map String.capitalize_ascii + |> String.concat "" + +let getNamespace config = + let ns = Json.get "namespace" config in + let isNamespaced = + ns |?> Json.bool |? (ns |?> Json.string |?> (fun _ -> Some true) |? false) + in + match isNamespaced with + | true -> + ns |?> Json.string + |?? (Json.get "name" config |?> Json.string) + |! "name is required if namespace is true" |> nameSpaceToName + |> fun s -> Some s + | false -> None + +let collectFiles directory = + let allFiles = Files.readDirectory directory in + let compileds = allFiles |> List.filter isCompiledFile |> filterDuplicates in + let sources = allFiles |> List.filter isSourceFile |> filterDuplicates in + compileds + |> List.map (fun path -> + let modName = getName path in + let compiled = directory /+ path in + let source = + Utils.find + (fun name -> + match getName name = modName with + | true -> Some (directory /+ name) + | false -> None) + sources + in + (modName, SharedTypes.Impl (compiled, source))) + +(* returns a list of (absolute path to cmt(i), relative path from base to source file) *) +let findProjectFiles ~debug namespace root sourceDirectories compiledBase = + let files = + sourceDirectories + |> List.map (Files.fileConcat root) + |> ifDebug debug "Source directories" (String.concat " - ") + |> List.map (fun name -> Files.collect name isSourceFile) + |> List.concat |> Utils.dedup + |> ifDebug debug "Source files found" (String.concat " : ") + (* + |> filterDuplicates + |> Utils.filterMap(path => { + let rel = Files.relpath(root, path); + ifOneExists([ + compiledBase /+ cmtName(~namespace, rel), + compiledBase /+ cmiName(~namespace, rel), + ]) |?>> cm => (cm, path) + }) + |> ifDebug(debug, "With compiled base", (items) => String.concat("\n", List.map(((a, b)) => a ++ " : " ++ b, items))) + |> List.filter(((full, rel)) => Files.exists(full)) + /* TODO more than just Impl() */ + |> List.map(((cmt, src)) => (getName(src), SharedTypes.Impl(cmt, Some(src)))) + *) + in + let interfaces = Hashtbl.create 100 in + files + |> List.iter (fun path -> + if + Filename.check_suffix path ".rei" + || Filename.check_suffix path ".resi" + || Filename.check_suffix path ".mli" + then ( + Log.log ("Adding intf " ^ path); + Hashtbl.replace interfaces (getName path) path )); + let normals = + files + |> Utils.filterMap (fun path -> + if + Filename.check_suffix path ".re" + || Filename.check_suffix path ".res" + || Filename.check_suffix path ".ml" + then ( + let mname = getName path in + let intf = Hashtbl.find_opt interfaces mname in + Hashtbl.remove interfaces mname; + let base = compiledBaseName ~namespace (Files.relpath root path) in + match intf with + | Some intf -> + let cmti = (compiledBase /+ base) ^ ".cmti" in + let cmt = (compiledBase /+ base) ^ ".cmt" in + if Files.exists cmti then + if Files.exists cmt then + (* Log.log("Intf and impl " ++ cmti ++ " " ++ cmt) *) + Some (mname, SharedTypes.IntfAndImpl (cmti, intf, cmt, path)) + else Some (mname, Intf (cmti, intf)) + else ( + (* Log.log("Just intf " ++ cmti) *) + Log.log ("Bad source file (no cmt/cmti/cmi) " ^ (compiledBase /+ base)); + None + ) + | None -> + let cmt = (compiledBase /+ base) ^ ".cmt" in + if Files.exists cmt then Some (mname, Impl (cmt, Some path)) + else ( + Log.log ("Bad source file (no cmt/cmi) " ^ (compiledBase /+ base)); + None + ) + ) else ( + Log.log ("Bad source file (extension) " ^ path); + None + ) + ) + in + let result = + List.append normals + (Hashtbl.fold + (fun mname intf res -> + let base = compiledBaseName ~namespace (Files.relpath root intf) in + Log.log ("Extra intf " ^ intf); + let cmti = (compiledBase /+ base) ^ ".cmti" in + if Files.exists cmti then + (mname, SharedTypes.Intf (cmti, intf)) :: res + else res) + interfaces []) + |> List.map (fun (name, paths) -> + match namespace with + | None -> (name, paths) + | Some namespace -> (name ^ "-" ^ namespace, paths)) + in + match namespace with + | None -> result + | Some namespace -> + let mname = nameSpaceToName namespace in + let cmt = (compiledBase /+ namespace) ^ ".cmt" in + Log.log ("adding namespace " ^ namespace ^ " : " ^ mname ^ " : " ^ cmt); + (mname, Impl (cmt, None)) :: result + +(* +let loadStdlib = stdlib => { + collectFiles(stdlib) + |> List.filter(((_, (cmt, src))) => Files.exists(cmt)) +}; +*) + +let findDependencyFiles ~debug base config = + let deps = + config |> Json.get "bs-dependencies" |?> Json.array |? [] + |> optMap Json.string + in + let devDeps = + config + |> Json.get "bs-dev-dependencies" + |?> Json.array |? [] |> optMap Json.string + in + let deps = deps @ devDeps in + Log.log ("Deps " ^ String.concat ", " deps); + let depFiles = + deps + |> List.map (fun name -> + let result = + ModuleResolution.resolveNodeModulePath ~startPath:base name + |?> fun loc -> + let innerPath = loc /+ "bsconfig.json" in + Log.log ("Dep loc " ^ innerPath); + match Files.readFile innerPath with + | Some text -> ( + let inner = Json.parse text in + let namespace = getNamespace inner in + let directories = + getSourceDirectories ~includeDev:false loc inner + in + match BuildSystem.getCompiledBase loc with + | None -> None + | Some compiledBase -> + if debug then Log.log ("Compiled base: " ^ compiledBase); + let compiledDirectories = + directories |> List.map (Files.fileConcat compiledBase) + in + let compiledDirectories = + match namespace = None with + | true -> compiledDirectories + | false -> compiledBase :: compiledDirectories + in + let files = + findProjectFiles ~debug namespace loc directories + compiledBase + in + (* + let files = switch (namespace) { + | None => + files + | Some(namespace) => + files + |> List.map(((name, paths)) => + (namespace ++ "-" ++ name, paths) + ) + }; + *) + Some (compiledDirectories, files) ) + | None -> None + in + match result with + | Some dependency -> dependency + | None -> + Log.log ("Skipping nonexistent dependency: " ^ name); + ([], []) + ) + in + let directories, files = List.split depFiles in + let files = List.concat files in + match BuildSystem.getStdlib base with + | Error e -> Error e + | Ok stdlibDirectory -> + let directories = stdlibDirectory :: List.concat directories in + let results = files @ collectFiles stdlibDirectory in + Ok (directories, results) diff --git a/analysis/src/Hover.ml b/analysis/src/Hover.ml new file mode 100644 index 000000000..0c4ad8a8b --- /dev/null +++ b/analysis/src/Hover.ml @@ -0,0 +1,160 @@ +let digConstructor ~env ~getModule path = + match Query.resolveFromCompilerPath ~env ~getModule path with + | `Not_found -> None + | `Stamp stamp -> ( + match Hashtbl.find_opt env.file.stamps.types stamp with + | None -> None + | Some t -> Some (env, t) ) + | `Exported (env, name) -> ( + match Hashtbl.find_opt env.exported.types name with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.types stamp with + | None -> None + | Some t -> Some (env, t) ) ) + | _ -> None + +let codeBlock code = Printf.sprintf "```rescript\n%s\n```" code + +let showModuleTopLevel ~docstring ~name + (topLevel : SharedTypes.moduleItem SharedTypes.declared list) = + let contents = + topLevel + |> List.map (fun item -> + match item.SharedTypes.item with + (* TODO pretty print module contents *) + | SharedTypes.MType ({decl}, recStatus) -> + " " ^ (decl |> Shared.declToString ~recStatus item.name.txt) + | Module _ -> " module " ^ item.name.txt + | MValue typ -> + " let " ^ item.name.txt ^ ": " ^ (typ |> Shared.typeToString)) (* TODO indent *) + |> String.concat "\n" + in + let full = "module " ^ name ^ " = {" ^ "\n" ^ contents ^ "\n}" in + let doc = + match docstring with + | [] -> "" + | _ :: _ -> "\n" ^ (docstring |> String.concat "\n") ^ "\n" + in + Some (doc ^ codeBlock full) + +let showModule ~docstring ~(file : SharedTypes.file) ~name + (declared : SharedTypes.moduleKind SharedTypes.declared option) = + match declared with + | None -> showModuleTopLevel ~docstring ~name file.contents.topLevel + | Some {item = Structure {topLevel}} -> + showModuleTopLevel ~docstring ~name topLevel + | Some {item = Ident _} -> Some "Unable to resolve module reference" + +let newHover ~(file : SharedTypes.file) ~getModule loc = + match loc with + | SharedTypes.Explanation text -> Some text + | TypeDefinition (name, decl, _stamp) -> + let typeDef = Shared.declToString name decl in + Some (codeBlock typeDef) + | LModule (Definition (stamp, _tip)) | LModule (LocalReference (stamp, _tip)) + -> ( + match Hashtbl.find_opt file.stamps.modules stamp with + | None -> None + | Some md -> ( + match References.resolveModuleReference ~file ~getModule md with + | None -> None + | Some (file, declared) -> + let name, docstring = + match declared with + | Some d -> (d.name.txt, d.docstring) + | None -> (file.moduleName, file.contents.docstring) + in + showModule ~docstring ~name ~file declared ) ) + | LModule (GlobalReference (moduleName, path, tip)) -> ( + match getModule moduleName with + | None -> None + | Some file -> ( + let env = Query.fileEnv file in + match Query.resolvePath ~env ~path ~getModule with + | None -> None + | Some (env, name) -> ( + match Query.exportedForTip ~env name tip with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt file.stamps.modules stamp with + | None -> None + | Some md -> ( + match References.resolveModuleReference ~file ~getModule md with + | None -> None + | Some (file, declared) -> + let name, docstring = + match declared with + | Some d -> (d.name.txt, d.docstring) + | None -> (file.moduleName, file.contents.docstring) + in + showModule ~docstring ~name ~file declared ) ) ) ) ) + | LModule NotFound -> None + | TopLevelModule name -> ( + match getModule name with + | None -> None + | Some file -> + showModule ~docstring:file.contents.docstring ~name:file.moduleName ~file + None ) + | Typed (_, Definition (_, (Field _ | Constructor _))) -> None + | Constant t -> + Some + ( match t with + | Const_int _ -> "int" + | Const_char _ -> "char" + | Const_string _ -> "string" + | Const_float _ -> "float" + | Const_int32 _ -> "int32" + | Const_int64 _ -> "int64" + | Const_nativeint _ -> "int" ) + | Typed (t, locKind) -> + let fromType ~docstring typ = + let typeString = codeBlock (typ |> Shared.typeToString) in + let extraTypeInfo = + let env = Query.fileEnv file in + match typ |> Shared.digConstructor with + | None -> None + | Some path -> ( + match digConstructor ~env ~getModule path with + | None -> None + | Some (_env, {docstring; name = {txt}; item = {decl}}) -> + let isUncurriedInternal = + Utils.startsWith (Path.name path) "Js.Fn.arity" + in + if isUncurriedInternal then None + else Some (decl |> Shared.declToString txt, docstring) ) + in + let typeString, docstring = + match extraTypeInfo with + | None -> (typeString, docstring) + | Some (extra, extraDocstring) -> + (typeString ^ "\n\n" ^ codeBlock extra, extraDocstring) + in + (typeString, docstring) + in + let parts = + match References.definedForLoc ~file ~getModule locKind with + | None -> + let typeString, docstring = t |> fromType ~docstring:[] in + typeString :: docstring + | Some (docstring, res) -> ( + match res with + | `Declared -> + let typeString, docstring = t |> fromType ~docstring in + typeString :: docstring + | `Constructor {cname = {txt}; args} -> + let typeString, docstring = t |> fromType ~docstring in + let argsString = + match args with + | [] -> "" + | _ -> + args + |> List.map (fun (t, _) -> Shared.typeToString t) + |> String.concat ", " |> Printf.sprintf "(%s)" + in + typeString :: codeBlock (txt ^ argsString) :: docstring + | `Field {typ} -> + let typeString, docstring = typ |> fromType ~docstring in + typeString :: docstring ) + in + Some (String.concat "\n\n" parts) diff --git a/analysis/src/Infix.ml b/analysis/src/Infix.ml new file mode 100644 index 000000000..85b1aa12f --- /dev/null +++ b/analysis/src/Infix.ml @@ -0,0 +1,31 @@ +(** + * This combines a filter and a map. + * You provide a function that turns an element into an optional of another element, + * and you get a list of all of the present results. + *) +let optMap : ('a -> 'b option) -> 'a list -> 'b list = fun fn items -> + List.fold_left + (fun result item -> + match fn item with None -> result | Some res -> res :: result) + [] items + +let ( |! ) o d = match o with None -> failwith d | Some v -> v + +let ( |? ) o d = match o with None -> d | Some v -> v + +let ( |?? ) o d = match o with None -> d | Some v -> Some v + +let ( |?> ) o fn = match o with None -> None | Some v -> fn v + +let ( |?>> ) o fn = match o with None -> None | Some v -> Some (fn v) + +let fold o d f = match o with None -> d | Some v -> f v + +let logIfAbsent message x = + match x with + | None -> + Log.log message; + None + | _ -> x + +let ( /+ ) = Files.fileConcat diff --git a/analysis/src/Log.ml b/analysis/src/Log.ml new file mode 100644 index 000000000..cd63d875f --- /dev/null +++ b/analysis/src/Log.ml @@ -0,0 +1,6 @@ +let spamError = ref false + +let log msg = + if !spamError then ( + output_string stderr (msg ^ "\n"); + flush stderr ) diff --git a/analysis/src/MarkdownOfOCamldoc.ml b/analysis/src/MarkdownOfOCamldoc.ml new file mode 100644 index 000000000..24a64077a --- /dev/null +++ b/analysis/src/MarkdownOfOCamldoc.ml @@ -0,0 +1,181 @@ +open Comment + +let withStyle style contents = + match style with + | `Bold -> Omd.Bold contents + | `Italic -> Omd.Emph contents + | `Emphasis -> Omd.Emph contents + | `Superscript -> Omd.Raw "Superscript" + | `Subscript -> Omd.Raw "Subscript" + +let stripLoc fn item = fn item.Location_.value + +let whiteLeft text = + let ln = String.length text in + let rec loop i = + match i >= ln with + | true -> i - 1 + | false -> ( match text.[i] = ' ' with true -> loop (i + 1) | false -> i ) + in + loop 0 + +let sliceToEnd text num = + let ln = String.length text in + if ln <= num then "" else String.sub text num (ln - num) + +let stripLeft text = + let lines = Str.split (Str.regexp_string "\n") text in + let rec loop lines = + match lines with + | [] -> 0 + | [one] -> whiteLeft one + | one :: more -> min (whiteLeft one) (loop more) + in + let min = loop (lines |> List.filter (fun x -> String.trim x <> "")) in + String.concat "\n" (List.map (fun line -> sliceToEnd line min) lines) + +let makeHeader level content = + match level with + | `Title -> Omd.H1 content + | `Section -> Omd.H2 content + | `Subsection -> Omd.H3 content + | `Subsubsection -> Omd.H4 content + +(* [ `Module | `ModuleType | `Type + | `Constructor | `Field | `Extension + | `Exception | `Value | `Class | `ClassType + | `Method | `InstanceVariable | `Label | `Page ] *) +let handleRef reference = + match reference with + | Paths.Reference.Root (name, _tag) -> name + | Paths.Reference.Resolved _ -> "resolved..." + | Paths.Reference.Dot (_, name) -> name + | Paths.Reference.Module (_, name) -> name + | Paths.Reference.ModuleType (_, name) -> name + | Paths.Reference.Type (_, name) -> name + | Paths.Reference.Constructor (_, name) -> name + | Paths.Reference.Field (_, name) -> name + | Paths.Reference.Extension (_, name) -> name + | Paths.Reference.Exception (_, name) -> name + | Paths.Reference.Value (_, name) -> name + | Paths.Reference.Class (_, name) -> name + | Paths.Reference.ClassType (_, name) -> name + | Paths.Reference.Method (_, name) -> name + | _ -> "(unhandled reference)" + +let rec showPath (path : Path.module_) = + match path with + | Path.Resolved _resolved -> "" + | Path.Root name -> name + | Path.Forward name -> name + | Path.Dot (inner, name) -> showPath inner ^ "." ^ name + | Path.Apply (one, two) -> showPath one ^ "(" ^ showPath two ^ ")" + +let convertItem item = + let rec convertItem item = + match item.Location_.value with + | `Heading (level, _label, content) -> + makeHeader level (List.map convertLink content) + | `Tag (`Author string) -> Omd.Text ("Author: " ^ string) + | `Tag (`Deprecated contents) -> + Omd.Paragraph + (Omd.Text "Deprecated: " :: List.map (stripLoc convertNestable) contents) + | `Tag (`Param (name, contents)) -> + Omd.Paragraph + ( Omd.Text ("Param: " ^ name) + :: List.map (stripLoc convertNestable) contents ) + | `Tag (`Raise (name, contents)) -> + Omd.Paragraph + ( Omd.Text ("Raises: " ^ name) + :: List.map (stripLoc convertNestable) contents ) + | `Tag (`Before (version, contents)) -> + Omd.Paragraph + ( Omd.Text ("Before: " ^ version) + :: List.map (stripLoc convertNestable) contents ) + | `Tag (`Return contents) -> + Omd.Paragraph + (Omd.Text "Returns: " :: List.map (stripLoc convertNestable) contents) + | `Tag (`See (_, link, contents)) -> + Omd.Paragraph + [ + Omd.Text "See: "; + Omd.Url (link, List.map (stripLoc convertNestable) contents, ""); + ] + | `Tag (`Since versionString) -> Omd.Text ("Since: " ^ versionString) + | `Tag (`Version versionString) -> Omd.Text ("Version: " ^ versionString) + | `Tag `Open -> Omd.Text "Open" + | `Tag `Closed -> Omd.Text "Closed" + | `Tag `Inline -> Omd.Text "Inline" + | `Tag (`Canonical (path, _reference)) -> + (* output_string(stderr, "Warning: Unhandled tag 'Canonical' in ocamldoc (please tell the rescript-editor-support maintainers)\n"); *) + Omd.Text (showPath path) (* ++ ", " ++ handleRef(reference)) *) + | `Tag _ -> + output_string stderr + "Warning: Unhandled tag in ocamldoc (please tell the \ + rescript-editor-support maintainers)\n"; + Omd.Text "Unhandled tag" + | #nestable_block_element as item -> convertNestable item + and convertNestable item = + match item with + | `Example (lang, contents) -> + let newLang = + match String.trim lang = "" with + | true -> "ml" + | false -> + let parts = Str.split (Str.regexp_string ";") (String.trim lang) in + if + List.mem "ml" parts || List.mem "ocaml" parts || List.mem "re" parts + || List.mem "reason" parts + then lang + else lang ^ ";ml" + in + Omd.Code_block (newLang, stripLeft contents) + | `Doc contents -> Omd.Paragraph [Omd.Text ("@doc " ^ contents)] + | `Paragraph inline -> Omd.Paragraph (List.map convertInline inline) + | `Code_block text -> Omd.Code_block ("ml", stripLeft text) + | `Verbatim text -> Omd.Raw text (* TODO *) + | `Modules _ -> + Log.log "Unhandled modules"; + Omd.Raw "!!!! Modules please" + | `List (`Ordered, children) -> + Omd.Ol (List.map (List.map (stripLoc convertNestable)) children) + | `List (`Unordered, children) -> + Omd.Ul (List.map (List.map (stripLoc convertNestable)) children) + and convertInline item = + match item.Location_.value with + | `Link (href, content) -> Omd.Url (href, List.map convertLink content, "") + | `Styled (style, contents) -> + withStyle style (List.map convertInline contents) + | `Reference (someref, _link) -> + let text = handleRef someref in + Omd.Text text + (* Omd.Url("#TODO-ref", [Omd.Text("REFERENCE"), ...List.map(convertLink, link)], "") *) + | #leaf_inline_element as rest -> convertLeaf rest + and convertLink item = + match item.Location_.value with + | `Styled (style, contents) -> + withStyle style (List.map convertLink contents) + | #leaf_inline_element as rest -> convertLeaf rest + and convertLeaf (item : Comment.leaf_inline_element) = + match item with + | `Space -> Omd.Text " " + | `Word text -> Omd.Text text + | `Code_span text -> Omd.Code ("", text) + in + convertItem item + +let convert text = + try + let res = + Parser_.parse_comment ~permissive:true ~sections_allowed:`All + ~containing_definition: + (Paths.Identifier.Root + ({Root.package = "hi"; file = Page "hi"; digest = "hi"}, "What")) + ~location:Lexing.dummy_pos ~text + in + match res.result with + | Error.Ok docs -> List.map convertItem docs + | Error message -> + [Omd.Text ("failed to parse: " ^ Error.to_string message)] + with exn -> + [Omd.Text ("Error (invalid syntax?) while parsing ocamldoc: " ^ Printexc.to_string exn)] diff --git a/analysis/src/ModuleResolution.ml b/analysis/src/ModuleResolution.ml new file mode 100644 index 000000000..1aac4ee7e --- /dev/null +++ b/analysis/src/ModuleResolution.ml @@ -0,0 +1,11 @@ +open Infix + +let rec resolveNodeModulePath ~startPath name = + let path = startPath /+ "node_modules" /+ name in + match startPath with + | "/" -> ( match Files.exists path with true -> Some path | false -> None ) + | _ -> ( + match Files.exists path with + | true -> Some path + | false -> + resolveNodeModulePath ~startPath:(Filename.dirname startPath) name ) diff --git a/analysis/src/NewCompletions.ml b/analysis/src/NewCompletions.ml new file mode 100644 index 000000000..f98c532ce --- /dev/null +++ b/analysis/src/NewCompletions.ml @@ -0,0 +1,697 @@ +open SharedTypes + +let showConstructor {cname = {txt}; args; res} = + let open Infix in + txt + ^ (match args = [] with + | true -> "" + | false -> + "(" + ^ String.concat ", " + (args |> List.map (fun (typ, _) -> typ |> Shared.typeToString)) + ^ ")") + ^ (res |?>> (fun typ -> "\n" ^ (typ |> Shared.typeToString)) |? "") + +(* TODO: local opens *) +let resolveOpens ~env ~previous opens ~getModule = + List.fold_left + (fun previous path -> + (* Finding an open, first trying to find it in previoulsly resolved opens *) + let rec loop prev = + match prev with + | [] -> ( + match path with + | Tip _ -> previous + | Nested (name, path) -> ( + match getModule name with + | None -> + Log.log ("Could not get module " ^ name); + previous (* TODO: warn? *) + | Some file -> ( + match + Query.resolvePath ~env:(Query.fileEnv file) ~getModule ~path + with + | None -> + Log.log ("Could not resolve in " ^ name); + previous + | Some (env, _placeholder) -> previous @ [env]))) + | env :: rest -> ( + match Query.resolvePath ~env ~getModule ~path with + | None -> loop rest + | Some (env, _placeholder) -> previous @ [env]) + in + Log.log ("resolving open " ^ pathToString path); + match Query.resolvePath ~env ~getModule ~path with + | None -> + Log.log "Not local"; + loop previous + | Some (env, _) -> + Log.log "Was local"; + previous @ [env]) + (* loop(previous) *) + previous opens + +let completionForDeclareds ~pos declareds prefix transformContents = + (* Log.log("complete for declares " ++ prefix); *) + Hashtbl.fold + (fun _stamp declared results -> + if + Utils.startsWith declared.name.txt prefix + && Utils.locationContainsFuzzy declared.scopeLoc pos + then {declared with item = transformContents declared.item} :: results + else + (* Log.log("Nope doesn't count " ++ Utils.showLocation(declared.scopeLoc) ++ " " ++ m); *) + results) + declareds [] + +let completionForExporteds exporteds + (stamps : (int, 'a SharedTypes.declared) Hashtbl.t) prefix transformContents + = + Hashtbl.fold + (fun name stamp results -> + (* Log.log("checking exported: " ++ name); *) + if Utils.startsWith name prefix then + let declared = Hashtbl.find stamps stamp in + {declared with item = transformContents declared.item} :: results + else results) + exporteds [] + +let completionForConstructors exportedTypes + (stamps : (int, SharedTypes.Type.t SharedTypes.declared) Hashtbl.t) prefix = + Hashtbl.fold + (fun _name stamp results -> + let t = Hashtbl.find stamps stamp in + match t.item.kind with + | SharedTypes.Type.Variant constructors -> + (constructors + |> List.filter (fun c -> Utils.startsWith c.cname.txt prefix) + |> List.map (fun c -> (c, t))) + @ results + | _ -> results) + exportedTypes [] + +let completionForFields exportedTypes + (stamps : (int, SharedTypes.Type.t SharedTypes.declared) Hashtbl.t) prefix = + Hashtbl.fold + (fun _name stamp results -> + let t = Hashtbl.find stamps stamp in + match t.item.kind with + | Record fields -> + (fields + |> List.filter (fun f -> Utils.startsWith f.fname.txt prefix) + |> List.map (fun f -> (f, t))) + @ results + | _ -> results) + exportedTypes [] + +let isCapitalized name = + if name = "" then false + else + let c = name.[0] in + match c with 'A' .. 'Z' -> true | _ -> false + +let determineCompletion items = + let rec loop offset items = + match items with + | [] -> assert false + | [one] -> `Normal (Tip one) + | [one; two] when not (isCapitalized one) -> `Attribute ([one], two) + | [one; two] -> `Normal (Nested (one, Tip two)) + | one :: rest -> ( + if isCapitalized one then + match loop (offset + String.length one + 1) rest with + | `Normal path -> `Normal (Nested (one, path)) + | x -> x + else + match loop (offset + String.length one + 1) rest with + | `Normal path -> `AbsAttribute path + | `Attribute (path, suffix) -> `Attribute (one :: path, suffix) + | x -> x) + in + loop 0 items + +(* Note: This is a hack. It will be wrong some times if you have a local thing + that overrides an open. + + Maybe the way to fix it is to make note of what things in an open override + locally defined things... +*) +let getEnvWithOpens ~pos ~(env : Query.queryEnv) ~getModule + ~(opens : Query.queryEnv list) path = + (* Query.resolvePath(~env, ~path, ~getModule) *) + match Query.resolveFromStamps ~env ~path ~getModule ~pos with + | Some x -> Some x + | None -> + let rec loop opens = + match opens with + | env :: rest -> ( + Log.log ("Looking for env in " ^ Uri2.toString env.Query.file.uri); + match Query.resolvePath ~env ~getModule ~path with + | Some x -> Some x + | None -> loop rest) + | [] -> ( + match path with + | Tip _ -> None + | Nested (top, path) -> ( + Log.log ("Getting module " ^ top); + match getModule top with + | None -> None + | Some file -> + Log.log "got it"; + let env = Query.fileEnv file in + Query.resolvePath ~env ~getModule ~path + |> Infix.logIfAbsent "Unable to resolve the path")) + in + loop opens + +type k = + | Module of moduleKind + | Value of Types.type_expr + | Type of Type.t + | Constructor of constructor * Type.t declared + | Field of field * Type.t declared + | FileModule of string + +let kindToInt k = + match k with + | Module _ -> 9 + | FileModule _ -> 9 + | Constructor (_, _) -> 4 + | Field (_, _) -> 5 + | Type _ -> 22 + | Value _ -> 12 + +let detail name contents = + match contents with + | Type {decl} -> decl |> Shared.declToString name + | Value typ -> typ |> Shared.typeToString + | Module _ -> "module" + | FileModule _ -> "file module" + | Field ({typ}, t) -> + name ^ ": " + ^ (typ |> Shared.typeToString) + ^ "\n\n" + ^ (t.item.decl |> Shared.declToString t.name.txt) + | Constructor (c, t) -> + showConstructor c ^ "\n\n" ^ (t.item.decl |> Shared.declToString t.name.txt) + +let localValueCompletions ~pos ~(env : Query.queryEnv) suffix = + let results = [] in + Log.log "---------------- LOCAL VAL"; + let results = + if suffix = "" || isCapitalized suffix then + results + @ completionForDeclareds ~pos env.file.stamps.modules suffix (fun m -> + Module m) + @ (completionForConstructors env.exported.types env.file.stamps.types + (* TODO declared thingsz *) + suffix + |> List.map (fun (c, t) -> + {(emptyDeclared c.cname.txt) with item = Constructor (c, t)})) + else results + in + let results = + if suffix = "" || not (isCapitalized suffix) then + results + @ completionForDeclareds ~pos env.file.stamps.values suffix (fun v -> + Value v) + @ completionForDeclareds ~pos env.file.stamps.types suffix (fun t -> + Type t) + @ (completionForFields env.exported.types env.file.stamps.types suffix + |> List.map (fun (f, t) -> + {(emptyDeclared f.fname.txt) with item = Field (f, t)})) + else results + in + results |> List.map (fun x -> (env.file.uri, x)) + +let valueCompletions ~(env : Query.queryEnv) suffix = + Log.log (" - Completing in " ^ Uri2.toString env.file.uri); + let results = [] in + let results = + if suffix = "" || isCapitalized suffix then ( + (* Get rid of lowercase modules (#417) *) + env.exported.modules + |> Hashtbl.filter_map_inplace (fun name key -> + match isCapitalized name with true -> Some key | false -> None); + let moduleCompletions = + completionForExporteds env.exported.modules env.file.stamps.modules + suffix (fun m -> Module m) + in + (* Log.log(" -- capitalized " ++ string_of_int(Hashtbl.length(env.exported.types)) ++ " exported types"); *) + (* env.exported.types |> Hashtbl.iter((name, _) => Log.log(" > " ++ name)); *) + results @ moduleCompletions + @ ((* TODO declared thingsz *) + completionForConstructors env.exported.types env.file.stamps.types + suffix + |> List.map (fun (c, t) -> + {(emptyDeclared c.cname.txt) with item = Constructor (c, t)}))) + else results + in + let results = + if suffix = "" || not (isCapitalized suffix) then ( + Log.log " -- not capitalized"; + results + @ completionForExporteds env.exported.values env.file.stamps.values suffix + (fun v -> Value v) + @ completionForExporteds env.exported.types env.file.stamps.types suffix + (fun t -> Type t) + @ (completionForFields env.exported.types env.file.stamps.types suffix + |> List.map (fun (f, t) -> + {(emptyDeclared f.fname.txt) with item = Field (f, t)}))) + else results + in + (* Log.log("Getting value completions " ++ env.file.uri); + Log.log(String.concat(", ", results |. Belt.List.map(x => x.name.txt))); *) + results |> List.map (fun x -> (env.file.uri, x)) + +let attributeCompletions ~(env : Query.queryEnv) ~suffix = + let results = [] in + let results = + if suffix = "" || isCapitalized suffix then + results + @ completionForExporteds env.exported.modules env.file.stamps.modules + suffix (fun m -> Module m) + else results + in + let results = + if suffix = "" || not (isCapitalized suffix) then + results + @ completionForExporteds env.exported.values env.file.stamps.values suffix + (fun v -> Value v) + (* completionForExporteds(env.exported.types, env.file.stamps.types, suffix, t => Type(t)) @ *) + @ (completionForFields env.exported.types env.file.stamps.types suffix + |> List.map (fun (f, t) -> + {(emptyDeclared f.fname.txt) with item = Field (f, t)})) + else results + in + results |> List.map (fun x -> (env.file.uri, x)) + +(* TODO filter out things that are defined after the current position *) +let resolveRawOpens ~env ~getModule ~rawOpens ~package = + (* TODO Stdlib instead of Pervasives *) + let packageOpens = "Pervasives" :: package.TopTypes.opens in + Log.log ("Package opens " ^ String.concat " " packageOpens); + let opens = + resolveOpens ~env + ~previous: + (List.map Query.fileEnv (packageOpens |> Utils.filterMap getModule)) + rawOpens ~getModule + in + opens + +let getItems ~full ~package ~rawOpens ~getModule ~allModules ~pos ~parts = + Log.log + ("Opens folkz > " + ^ string_of_int (List.length rawOpens) + ^ " " + ^ String.concat " ... " (rawOpens |> List.map pathToString)); + let env = Query.fileEnv full.file in + let packageOpens = "Pervasives" :: package.TopTypes.opens in + Log.log ("Package opens " ^ String.concat " " packageOpens); + let resolvedOpens = resolveRawOpens ~env ~getModule ~rawOpens ~package in + Log.log + ("Opens nows " + ^ string_of_int (List.length resolvedOpens) + ^ " " + ^ String.concat " " + (resolvedOpens |> List.map (fun e -> Uri2.toString e.Query.file.uri))); + (* Last open takes priority *) + let opens = List.rev resolvedOpens in + match parts with + | [] -> [] + | [suffix] -> + let locallyDefinedValues = localValueCompletions ~pos ~env suffix in + let alreadyUsedIdentifiers = Hashtbl.create 10 in + let valuesFromOpens = + opens + |> List.fold_left + (fun results env -> + let completionsFromThisOpen = valueCompletions ~env suffix in + List.filter + (fun (_uri, declared) -> + if not (Hashtbl.mem alreadyUsedIdentifiers declared.name.txt) + then ( + Hashtbl.add alreadyUsedIdentifiers declared.name.txt true; + true) + else false) + completionsFromThisOpen + @ results) + [] + in + (* TODO complete the namespaced name too *) + let localModuleNames = + allModules + |> Utils.filterMap (fun name -> + match + Utils.startsWith name suffix && not (String.contains name '-') + with + | true -> + Some + ( env.file.uri, + {(emptyDeclared name) with item = FileModule name} ) + | false -> None) + in + locallyDefinedValues @ valuesFromOpens @ localModuleNames + | multiple -> ( + Log.log ("Completing for " ^ String.concat "<.>" multiple); + match determineCompletion multiple with + | `Normal path -> ( + Log.log ("normal " ^ pathToString path); + match getEnvWithOpens ~pos ~env ~getModule ~opens path with + | Some (env, suffix) -> + Log.log "Got the env"; + valueCompletions ~env suffix + | None -> []) + | `Attribute (target, suffix) -> ( + Log.log ("suffix :" ^ suffix); + match target with + | [] -> [] + | first :: rest -> ( + Log.log ("-------------- Looking for " ^ first); + match Query.findInScope pos first env.file.stamps.values with + | None -> [] + | Some declared -> ( + Log.log ("Found it! " ^ declared.name.txt); + match declared.item |> Shared.digConstructor with + | None -> [] + | Some path -> ( + match Hover.digConstructor ~env ~getModule path with + | None -> [] + | Some (env, typ) -> ( + match + rest + |> List.fold_left + (fun current name -> + match current with + | None -> None + | Some (env, typ) -> ( + match typ.item.SharedTypes.Type.kind with + | Record fields -> ( + match + fields + |> List.find_opt (fun f -> f.fname.txt = name) + with + | None -> None + | Some attr -> ( + Log.log ("Found attr " ^ name); + match attr.typ |> Shared.digConstructor with + | None -> None + | Some path -> + Hover.digConstructor ~env ~getModule path)) + | _ -> None)) + (Some (env, typ)) + with + | None -> [] + | Some (env, typ) -> ( + match typ.item.kind with + | Record fields -> + fields + |> Utils.filterMap (fun f -> + if Utils.startsWith f.fname.txt suffix then + Some + ( env.file.uri, + { + (emptyDeclared f.fname.txt) with + item = Field (f, typ); + } ) + else None) + | _ -> [])))))) + | `AbsAttribute path -> ( + match getEnvWithOpens ~pos ~env ~getModule ~opens path with + | None -> [] + | Some (env, suffix) -> + attributeCompletions ~env ~suffix + @ List.concat + (opens |> List.map (fun env -> attributeCompletions ~env ~suffix)))) + +let mkItem ~name ~kind ~detail ~deprecated ~docstring ~uri ~pos_lnum = + let valueMessage = + (match deprecated with None -> "" | Some s -> "Deprecated: " ^ s ^ "\n\n") + ^ (match docstring with + | [] -> "" + | _ :: _ -> (docstring |> String.concat "\n") ^ "\n\n") + ^ "\n" ^ Uri2.toString uri ^ ":" ^ string_of_int pos_lnum + in + let tags = + match deprecated = None with + | true -> [] + | false -> [1 (* deprecated *)] + in + Protocol.{ + label = name; + kind = kind; + tags = tags; + detail = detail; + documentation = { + kind = "markdown"; + value = valueMessage; + }; + } + +let processCompletable ~findItems ~full ~package ~pos ~rawOpens + (completable : PartialParser.completable) = + match completable with + | Cjsx (componentPath, prefix) -> + let items = findItems ~exact:true (componentPath @ ["make"]) in + let labels = + match items with + | (_uri, {SharedTypes.item = Value typ}) :: _ -> + let rec getFields (texp : Types.type_expr) = + match texp.desc with + | Tfield (name, _, t1, t2) -> + let fields = t2 |> getFields in + (name, t1) :: fields + | Tlink te -> te |> getFields + | Tvar None -> [] + | _ -> [] + in + let rec getLabels (t : Types.type_expr) = + match t.desc with + | Tlink t1 | Tsubst t1 -> getLabels t1 + | Tarrow + ( Nolabel, + { + desc = + ( Tconstr (* Js.t *) (_, [{desc = Tobject (tObj, _)}], _) + | Tobject (tObj, _) ); + }, + _, + _ ) -> + getFields tObj + | _ -> [] + in + typ |> getLabels + | _ -> [] + in + let mkLabel_ name typString = + mkItem ~name ~kind:4 ~deprecated:None ~detail:typString ~docstring:[] + ~uri:full.file.uri ~pos_lnum:(fst pos) + in + let mkLabel (name, typ) = mkLabel_ name (typ |> Shared.typeToString) in + let keyLabel = mkLabel_ "key" "string" in + if labels = [] then [] + else + keyLabel + :: (labels + |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) + |> List.map mkLabel) + | Cpath parts -> + let items = parts |> findItems ~exact:false in + (* TODO(#107): figure out why we're getting duplicates. *) + items |> Utils.dedup + |> List.map + (fun + ( uri, + { + SharedTypes.name = {txt = name; loc = {loc_start = {pos_lnum}}}; + deprecated; + docstring; + item; + } ) + -> + mkItem ~name ~kind:(kindToInt item) ~deprecated + ~detail:(detail name item) ~docstring ~uri ~pos_lnum) + | Cpipe (pipe, partialName) -> ( + let arrayModulePath = ["Js"; "Array2"] in + let listModulePath = ["Belt"; "List"] in + let optionModulePath = ["Belt"; "Option"] in + let stringModulePath = ["Js"; "String2"] in + let getModulePath path = + let rec loop (path : Path.t) = + match path with + | Pident id -> [Ident.name id] + | Pdot (p, s, _) -> s :: loop p + | Papply _ -> [] + in + match path with + | Path.Pident id when Ident.name id = "array" -> arrayModulePath + | Path.Pident id when Ident.name id = "list" -> listModulePath + | Path.Pident id when Ident.name id = "option" -> optionModulePath + | Path.Pident id when Ident.name id = "string" -> stringModulePath + | _ -> ( match loop path with _ :: rest -> List.rev rest | [] -> []) + in + let getLhsPath ~pipeId ~partialName = + match [pipeId] |> findItems ~exact:true with + | (_uri, {SharedTypes.item = Value t}) :: _ -> + let modulePath = + match t.desc with + | Tconstr (path, _, _) -> getModulePath path + | Tlink {desc = Tconstr (path, _, _)} -> getModulePath path + | _ -> [] + in + Some (modulePath, partialName) + | _ -> None + in + let lhsPath = + match pipe with + | PipeId pipeId -> getLhsPath ~pipeId ~partialName + | PipeString -> Some (stringModulePath, partialName) + | PipeArray -> Some (arrayModulePath, partialName) + in + let removePackageOpens modulePath = + match modulePath with + | toplevel :: rest when package.TopTypes.opens |> List.mem toplevel -> + rest + | _ -> modulePath + in + let rec removeRawOpen rawOpen modulePath = + match (rawOpen, modulePath) with + | Tip _, _ -> Some modulePath + | Nested (s, inner), first :: restPath when s = first -> + removeRawOpen inner restPath + | _ -> None + in + let rec removeRawOpens rawOpens modulePath = + match rawOpens with + | rawOpen :: restOpens -> + let newModulePath = + match removeRawOpen rawOpen modulePath with + | None -> modulePath + | Some newModulePath -> newModulePath + in + removeRawOpens restOpens newModulePath + | [] -> modulePath + in + match lhsPath with + | Some (modulePath, partialName) -> ( + match modulePath with + | _ :: _ -> + let modulePathMinusOpens = + modulePath |> removePackageOpens |> removeRawOpens rawOpens + |> String.concat "." + in + let completionName name = + match modulePathMinusOpens = "" with + | true -> name + | false -> modulePathMinusOpens ^ "." ^ name + in + let parts = modulePath @ [partialName] in + let items = parts |> findItems ~exact:false in + items + |> List.filter (fun (_, {item}) -> + match item with Value _ -> true | _ -> false) + |> List.map + (fun + ( uri, + { + SharedTypes.name = + {txt = name; loc = {loc_start = {pos_lnum}}}; + deprecated; + docstring; + item; + } ) + -> + mkItem ~name:(completionName name) ~kind:(kindToInt item) + ~detail:(detail name item) ~deprecated ~docstring ~uri + ~pos_lnum) + | _ -> []) + | None -> []) + | Cdecorator prefix -> + let mkDecorator name = + mkItem ~name ~kind:4 ~deprecated:None ~detail:"" ~docstring:[] + ~uri:full.file.uri ~pos_lnum:(fst pos) + in + [ + "as"; + "deriving"; + "genType"; + "genType.as"; + "genType.import"; + "genType.opaque"; + "get"; + "get_index"; + "inline"; + "int"; + "meth"; + "module"; + "new"; + "obj"; + "react.component"; + "return"; + "scope"; + "send"; + "set"; + "set_index"; + "string"; + "this"; + "unboxed"; + "uncurry"; + "unwrap"; + "val"; + "variadic"; + ] + |> List.filter (fun decorator -> Utils.startsWith decorator prefix) + |> List.map mkDecorator + | Clabel (funPath, prefix) -> + let labels = + match funPath |> findItems ~exact:true with + | (_uri, {SharedTypes.item = Value typ}) :: _ -> + let rec getLabels (t : Types.type_expr) = + match t.desc with + | Tlink t1 | Tsubst t1 -> getLabels t1 + | Tarrow ((Labelled l | Optional l), tArg, tRet, _) -> + (l, tArg) :: getLabels tRet + | Tarrow (Nolabel, _, tRet, _) -> getLabels tRet + | _ -> [] + in + typ |> getLabels + | _ -> [] + in + let mkLabel (name, typ) = + mkItem ~name ~kind:4 ~deprecated:None + ~detail:(typ |> Shared.typeToString) + ~docstring:[] ~uri:full.file.uri ~pos_lnum:(fst pos) + in + labels + |> List.filter (fun (name, _t) -> Utils.startsWith name prefix) + |> List.map mkLabel + +let computeCompletions ~full ~maybeText ~package ~pos ~state = + match maybeText with + | None -> [] + | Some text -> ( + match PartialParser.positionToOffset text pos with + | None -> [] + | Some offset -> ( + match PartialParser.findCompletable text offset with + | None -> [] + | Some completable -> + let rawOpens = PartialParser.findOpens text offset in + let allModules = + package.TopTypes.localModules @ package.dependencyModules + in + let findItems ~exact parts = + let items = + getItems ~full ~package ~rawOpens + ~getModule:(State.fileForModule state ~package) + ~allModules ~pos ~parts + in + match parts |> List.rev with + | last :: _ when exact -> + items + |> List.filter (fun (_uri, {SharedTypes.name = {txt}}) -> + txt = last) + | _ -> items + in + completable + |> processCompletable ~findItems ~full ~package ~pos ~rawOpens)) diff --git a/analysis/src/Packages.ml b/analysis/src/Packages.ml new file mode 100644 index 000000000..d772400fb --- /dev/null +++ b/analysis/src/Packages.ml @@ -0,0 +1,139 @@ +open Infix +open TopTypes + +(* Creates the `pathsForModule` hashtbl, which maps a `moduleName` to it's `paths` (the ml/re, mli/rei, cmt, and cmti files) *) +let makePathsForModule (localModules : (string * SharedTypes.paths) list) + (dependencyModules : (string * SharedTypes.paths) list) = + let pathsForModule = Hashtbl.create 30 in + dependencyModules + |> List.iter (fun (modName, paths) -> + Hashtbl.replace pathsForModule modName paths); + localModules + |> List.iter (fun (modName, paths) -> + Hashtbl.replace pathsForModule modName paths); + pathsForModule + +let newBsPackage rootPath = + let path = rootPath /+ "bsconfig.json" in + match Files.readFile path with + | None -> Error ("Unable to read " ^ path) + | Some raw -> ( + let config = Json.parse raw in + Log.log {|📣 📣 NEW BSB PACKAGE 📣 📣|}; + (* failwith("Wat"); *) + Log.log ("- location: " ^ rootPath); + let compiledBase = BuildSystem.getCompiledBase rootPath in + match FindFiles.findDependencyFiles ~debug:true rootPath config with + | Error e -> Error e + | Ok (dependencyDirectories, dependencyModules) -> ( + match compiledBase with + | None -> + Error + "You need to run bsb first so that rescript-editor-support can \ + access the compiled artifacts.\n\ + Once you've run bsb, restart the language server." + | Some compiledBase -> + Ok + (let namespace = FindFiles.getNamespace config in + let localSourceDirs = + FindFiles.getSourceDirectories ~includeDev:true rootPath config + in + Log.log + ("Got source directories " ^ String.concat " - " localSourceDirs); + let localModules = + FindFiles.findProjectFiles ~debug:true namespace rootPath + localSourceDirs compiledBase + (* + |> List.map(((name, paths)) => (switch (namespace) { + | None => name + | Some(n) => name ++ "-" ++ n }, paths)); *) + in + Log.log + ("-- All local modules found: " + ^ string_of_int (List.length localModules)); + localModules + |> List.iter (fun (name, paths) -> + Log.log name; + match paths with + | SharedTypes.Impl (cmt, _) -> Log.log ("impl " ^ cmt) + | Intf (cmi, _) -> Log.log ("intf " ^ cmi) + | _ -> Log.log "Both"); + let pathsForModule = + makePathsForModule localModules dependencyModules + in + let opens_from_namespace = + match namespace with + | None -> [] + | Some namespace -> + let cmt = (compiledBase /+ namespace) ^ ".cmt" in + Log.log ("############ Namespaced as " ^ namespace ^ " at " ^ cmt); + Hashtbl.add pathsForModule namespace (Impl (cmt, None)); + [FindFiles.nameSpaceToName namespace] + in + Log.log ("Dependency dirs " ^ String.concat " " dependencyDirectories); + let opens_from_bsc_flags = + match Json.get "bsc-flags" config |?> Json.array with + | Some l -> + List.fold_left + (fun opens item -> + match item |> Json.string with + | None -> opens + | Some s -> ( + let parts = String.split_on_char ' ' s in + match parts with + | "-open" :: name :: _ -> name :: opens + | _ -> opens)) + [] l + | None -> [] + in + let opens = + List.rev_append opens_from_bsc_flags opens_from_namespace + in + Log.log ("Opens from bsconfig: " ^ (opens |> String.concat " ")); + let interModuleDependencies = + Hashtbl.create (List.length localModules) + in + { + rootPath; + localModules = localModules |> List.map fst; + dependencyModules = dependencyModules |> List.map fst; + pathsForModule; + opens; + namespace; + interModuleDependencies; + }))) + +let findRoot ~uri packagesByRoot = + let path = Uri2.toPath uri in + let rec loop path = + if path = "/" then None + else if Hashtbl.mem packagesByRoot path then Some (`Root path) + else if Files.exists (path /+ "bsconfig.json") then Some (`Bs path) + else loop (Filename.dirname path) + in + loop (Filename.dirname path) + +let getPackage ~uri state = + if Hashtbl.mem state.rootForUri uri then + Ok (Hashtbl.find state.packagesByRoot (Hashtbl.find state.rootForUri uri)) + else + match findRoot ~uri state.packagesByRoot with + | None -> Error "No root directory found" + | Some root -> ( + match + match root with + | `Root rootPath -> + Hashtbl.replace state.rootForUri uri rootPath; + Ok + (Hashtbl.find state.packagesByRoot + (Hashtbl.find state.rootForUri uri)) + | `Bs rootPath -> ( + match newBsPackage rootPath with + | Error e -> Error e + | Ok package -> + Hashtbl.replace state.rootForUri uri package.rootPath; + Hashtbl.replace state.packagesByRoot package.rootPath package; + Ok package) + with + | Error e -> Error e + | Ok package -> Ok package) diff --git a/analysis/src/PartialParser.ml b/analysis/src/PartialParser.ml new file mode 100644 index 000000000..121448009 --- /dev/null +++ b/analysis/src/PartialParser.ml @@ -0,0 +1,274 @@ +let rec findBack text char i = + if i < 0 then i + else if text.[i] = char && (i = 0 || text.[i - 1] <> '/') then i - 1 + else findBack text char (i - 1) + +let rec findOpenComment text i = + if i < 1 then 0 + else if text.[i] = '*' && text.[i - 1] = '/' then i - 2 + else findOpenComment text (i - 1) + +let rec findBackSkippingCommentsAndStrings text char pair i level = + let loop = findBackSkippingCommentsAndStrings text char pair in + if i < 0 then 0 + else if text.[i] = char then + if level = 0 then i - 1 else loop (i - 1) (level - 1) + else if text.[i] = pair then loop (i - 1) (level + 1) + else + match text.[i] with + | '"' -> loop (findBack text '"' (i - 1)) level + | '/' when i >= 1 && text.[i - 1] = '*' -> + loop (findOpenComment text (i - 2)) level + | _ -> loop (i - 1) level + +let rec skipWhite text i = + if i < 0 then 0 + else + match text.[i] with ' ' | '\n' | '\t' -> skipWhite text (i - 1) | _ -> i + +let rec startOfLident text i = + if i < 0 then 0 + else + match text.[i] with + | 'a' .. 'z' | 'A' .. 'Z' | '.' | '_' | '0' .. '9' -> + startOfLident text (i - 1) + | _ -> i + 1 + +(* foo(... ~arg) from ~arg find foo *) +let findCallFromArgument text offset = + let rec loop ~i ~nClosed = + if i > 0 then + match text.[i] with + | '(' when nClosed > 0 -> loop ~i:(i - 1) ~nClosed:(nClosed - 1) + | '(' -> + let i1 = skipWhite text (i - 1) in + let i0 = startOfLident text i1 in + let funLident = String.sub text i0 (i1 - i0 + 1) in + Str.split (Str.regexp_string ".") funLident + | ')' -> loop ~i:(i - 1) ~nClosed:(nClosed + 1) + | _ -> loop ~i:(i - 1) ~nClosed + else [] + in + loop ~i:offset ~nClosed:0 + +(* Figure out whether id should be autocompleted as component prop. *) +(* Find JSX context ctx for component M to autocomplete id (already parsed) as a prop. *) +(* ctx ::= 0 then + match text.[i] with + | '}' -> ( + let i1 = findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0 in + match i1 > 0 with true -> beforeValue i1 | false -> None) + | ')' -> ( + let i1 = findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0 in + match i1 > 0 with true -> beforeValue i1 | false -> None) + | ']' -> ( + let i1 = findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0 in + match i1 > 0 with true -> beforeValue i1 | false -> None) + | '"' -> ( + let i1 = findBack text '"' (i - 1) in + match i1 > 0 with true -> beforeValue i1 | false -> None) + | _ -> + let i1 = startOfLident text i in + let ident = String.sub text i1 (i - i1 + 1) in + if i1 >= 1 && ident <> "" then + match ident.[0] with + | 'A' .. 'Z' when i1 >= 1 && text.[i1 - 1] = '<' -> Some ident + | _ -> beforeIdent (i1 - 1) + else None + else None + and beforeIdent i = + let i = skipWhite text i in + if i > 0 then + match text.[i] with + | '?' -> fromEquals (i - 1) + | '=' -> fromEquals i + | _ -> loop (i - 1) + else None + and beforeValue i = + let i = skipWhite text i in + if i > 0 then + match text.[i] with '?' -> fromEquals (i - 1) | _ -> fromEquals i + else None + and fromEquals i = + let i = skipWhite text i in + if i > 0 then + match text.[i] with + | '=' -> ( + let i = skipWhite text (i - 1) in + let i1 = startOfLident text i in + let ident = String.sub text i1 (i - i1 + 1) in + match ident = "" with true -> None | false -> loop (i1 - 1)) + | _ -> None + else None + in + loop offset + +type pipe = PipeId of string | PipeArray | PipeString + +type completable = + | Cdecorator of string (** e.g. @module *) + | Clabel of string list * string + (** e.g. (["M", "foo"], "label") for M.foo(...~label...) *) + | Cpath of string list (** e.g. ["M", "foo"] for M.foo *) + | Cjsx of string list * string + (** E.g. (["M", "Comp"], "id") for foo" *) + +let isLowercaseIdent id = + let rec loop i = + if i < 0 then true + else + match id.[i] with + | ('a' .. 'z' | '_') when i = 0 -> true + | ('a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '_') when i > 0 -> loop (i - 1) + | _ -> false + in + loop (String.length id - 1) + +let findCompletable text offset = + let mkPath s = + let len = String.length s in + let parts = Str.split (Str.regexp_string ".") s in + let parts = + match s.[len - 1] = '.' with true -> parts @ [""] | false -> parts + in + match parts with + | [id] when String.lowercase_ascii id = id -> ( + match findJsxContext text (offset - len - 1) with + | None -> Cpath parts + | Some componentName -> + Cjsx (Str.split (Str.regexp_string ".") componentName, id)) + | _ -> Cpath parts + in + let mkPipe off partialName = + let off = skipWhite text off in + let rec loop i = + match i < 0 with + | true -> Some (PipeId (String.sub text 0 (i - 1))) + | false -> ( + match text.[i] with + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) + | '"' when i == off -> Some PipeString + | ']' when i == off -> Some PipeArray + | _ -> Some (PipeId (String.sub text (i + 1) (off - i)))) + in + match loop off with + | None -> None + | Some lhs -> Some (Cpipe (lhs, partialName)) + in + + let suffix i = String.sub text (i + 1) (offset - (i + 1)) in + let rec loop i = + match i < 0 with + | true -> Some (mkPath (suffix i)) + | false -> ( + match text.[i] with + | '>' when i > 0 && text.[i - 1] = '-' -> + let rest = suffix i in + if isLowercaseIdent rest then mkPipe (i - 2) rest + else Some (mkPath rest) + | '~' -> + let labelPrefix = suffix i in + let funPath = findCallFromArgument text (i - 1) in + Some (Clabel (funPath, labelPrefix)) + | '@' -> Some (Cdecorator (suffix i)) + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' | '.' | '_' -> loop (i - 1) + | _ -> ( + match i = offset - 1 with + | true -> None + | false -> Some (mkPath (suffix i)))) + in + if offset > String.length text || offset = 0 then None else loop (offset - 1) + +(* Check if the position is inside a `//` comment *) +let rec insideLineComment text offset = + if offset <= 0 || text.[offset] = '\n' then false + else if offset > 0 && text.[offset] = '/' && text.[offset - 1] = '/' then true + else insideLineComment text (offset - 1) + +let findOpens text offset = + let opens = ref [] in + let pathOfModuleOpen o = + let rec loop items = + match items with + | [] -> SharedTypes.Tip "place holder" + | one :: rest -> Nested (one, loop rest) + in + loop (o |> Str.split (Str.regexp_string ".")) + in + let add o = opens := (o |> pathOfModuleOpen) :: !opens in + let maybeOpen i0 = + let rec loop i = + if i < 4 then 0 + else + match text.[i] with + | 'a' .. 'z' | 'A' .. 'Z' | '.' | '_' | '0' .. '9' -> loop (i - 1) + | ' ' | '!' -> + let at = skipWhite text (i - 1) in + let at = + if at >= 0 && text.[at] = '!' then + (* handle open! *) + skipWhite text (at - 1) + else at + in + if + at >= 3 + && text.[at - 3] = 'o' + && text.[at - 2] = 'p' + && text.[at - 1] = 'e' + && text.[at] = 'n' + && not (insideLineComment text (at - 4)) + then ( + add (String.sub text (i + 1) (i0 + 1 - (i + 1))); + at - 4) + else at + | _ -> i + in + loop (i0 - 1) + in + let rec loop i = + if i > 1 then + match text.[i] with + | '}' -> loop (findBackSkippingCommentsAndStrings text '{' '}' (i - 1) 0) + | ']' -> loop (findBackSkippingCommentsAndStrings text '[' ']' (i - 1) 0) + | ')' -> loop (findBackSkippingCommentsAndStrings text '(' ')' (i - 1) 0) + | '"' -> loop (findBack text '"' (i - 1)) + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> loop (maybeOpen i) + | '(' when text.[i - 1] = '.' -> ( + match text.[i - 2] with + | 'a' .. 'z' | 'A' .. 'Z' | '_' | '0' .. '9' -> + let i0 = startOfLident text (i - 3) in + add (String.sub text i0 (i - i0 - 1)) + | _ -> loop (i - 1)) + | _ -> + if i > 1 && text.[i] = '/' && text.[i - 1] = '*' then + loop (findOpenComment text (i - 2)) + else loop (i - 1) + in + loop (offset - 1) |> ignore; + !opens + +let offsetOfLine text line = + let ln = String.length text in + let rec loop i lno = + match i >= ln with + | true -> None + | false -> ( + match text.[i] with + | '\n' -> ( + match lno = line - 1 with + | true -> Some (i + 1) + | false -> loop (i + 1) (lno + 1)) + | _ -> loop (i + 1) lno) + in + match line = 0 with true -> Some 0 | false -> loop 0 0 + +let positionToOffset text (line, character) = + let open Infix in + offsetOfLine text line |?>> fun bol -> bol + character diff --git a/analysis/src/PrepareUtils.ml b/analysis/src/PrepareUtils.ml new file mode 100644 index 000000000..80da5040e --- /dev/null +++ b/analysis/src/PrepareUtils.ml @@ -0,0 +1,45 @@ +let findStars line = + let l = String.length line in + let rec loop i = + if i >= l - 1 then None + else if line.[i] = '*' && line.[i + 1] = ' ' then Some (i + 2) + else if line.[i] <> ' ' then None + else loop (i + 1) + in + loop 0 + +let combine one two = + match (one, two) with + | None, None -> None + | Some a, None -> Some a + | None, Some b -> Some b + | Some a, Some b -> ( match a = b with true -> Some a | false -> Some 0 ) + +let trimFirst num string = + let length = String.length string in + match length > num with + | true -> String.sub string num (length - num) + | false -> "" + +let cleanOffStars doc = + let lines = Str.split (Str.regexp_string "\n") doc in + let rec loop lines = + match lines with + | [] -> None + | [one] -> ( + match String.trim one = "" with true -> None | false -> findStars one ) + | one :: rest -> ( + match String.trim one = "" with + | true -> loop rest + | false -> combine (findStars one) (loop rest) ) + in + let num = loop lines in + match num with + | None | Some 0 -> doc + | Some num -> ( + match lines with + | [] | [_] -> doc + | one :: rest -> + (if findStars one <> None then trimFirst num one else String.trim one) + ^ "\n" + ^ String.concat "\n" (rest |> List.map (trimFirst num)) ) diff --git a/analysis/src/PrintType.ml b/analysis/src/PrintType.ml new file mode 100644 index 000000000..b146430fb --- /dev/null +++ b/analysis/src/PrintType.ml @@ -0,0 +1,10 @@ +let printExpr typ = + Printtyp.reset_names (); + Res_doc.toString ~width:60 + (Res_outcome_printer.printOutTypeDoc (Printtyp.tree_of_typexp false typ)) + +let printDecl ~recStatus name decl = + Printtyp.reset_names (); + Res_doc.toString ~width:60 + (Res_outcome_printer.printOutSigItemDoc + (Printtyp.tree_of_type_declaration (Ident.create name) decl recStatus)) diff --git a/analysis/src/ProcessAttributes.ml b/analysis/src/ProcessAttributes.ml new file mode 100644 index 000000000..ce177c023 --- /dev/null +++ b/analysis/src/ProcessAttributes.ml @@ -0,0 +1,54 @@ +open SharedTypes + +(* TODO should I hang on to location? *) +let rec findDocAttribute attributes = + let open Parsetree in + match attributes with + | [] -> None + | ( {Asttypes.txt = "ocaml.doc"}, + PStr + [ + { + pstr_desc = + Pstr_eval ({pexp_desc = Pexp_constant (Pconst_string (doc, _))}, _); + }; + ] ) + :: _ -> + Some (PrepareUtils.cleanOffStars doc) + | _ :: rest -> findDocAttribute rest + +let rec findDeprecatedAttribute attributes = + let open Parsetree in + match attributes with + | [] -> None + | ( {Asttypes.txt = "deprecated"}, + PStr + [ + { + pstr_desc = + Pstr_eval ({pexp_desc = Pexp_constant (Pconst_string (msg, _))}, _); + }; + ] ) + :: _ -> + Some msg + | ({Asttypes.txt = "deprecated"}, _) :: _ -> Some "" + | _ :: rest -> findDeprecatedAttribute rest + +let newDeclared ~item ~scope ~extent ~name ~stamp ~modulePath ~processDoc + exported attributes = + { + name; + stamp; + extentLoc = extent; + scopeLoc = scope; + exported; + modulePath; + deprecated = findDeprecatedAttribute attributes; + docstring = + ( match findDocAttribute attributes with + | None -> [] + | Some d -> processDoc d ); + item; + (* scopeType = Let; *) + (* scopeStart = env.scopeStart; *) + } diff --git a/analysis/src/ProcessCmt.ml b/analysis/src/ProcessCmt.ml new file mode 100644 index 000000000..9f53943dd --- /dev/null +++ b/analysis/src/ProcessCmt.ml @@ -0,0 +1,525 @@ +open Typedtree +open SharedTypes +open Infix + +let itemsExtent items = + let open Typedtree in + match items with + | [] -> Location.none + | first :: _ -> + let last = List.nth items (List.length items - 1) in + let first, last = + if first.str_loc.loc_start.pos_cnum < last.str_loc.loc_start.pos_cnum then + (first, last) + else (last, first) + in + { + loc_ghost = true; + loc_start = first.str_loc.loc_start; + loc_end = last.str_loc.loc_end; + } + +let sigItemsExtent items = + let open Typedtree in + match items with + | [] -> Location.none + | first :: _ -> + let last = List.nth items (List.length items - 1) in + { + Location.loc_ghost = true; + loc_start = first.sig_loc.loc_start; + loc_end = last.sig_loc.loc_end; + } + +type env = { + stamps : stamps; + processDoc : string -> string list; + modulePath : visibilityPath; + scope : Location.t; +} + +let addItem ~name ~extent ~stamp ~env ~item attributes exported stamps = + let declared = + ProcessAttributes.newDeclared ~item + ~scope: + { + Location.loc_start = extent.Location.loc_end; + loc_end = env.scope.loc_end; + loc_ghost = false; + } + ~extent ~name ~stamp ~modulePath:env.modulePath ~processDoc:env.processDoc + (not (Hashtbl.mem exported name.txt)) + attributes + in + if not (Hashtbl.mem exported name.txt) then + Hashtbl.add exported name.txt stamp; + Hashtbl.add stamps stamp declared; + declared + +let rec forSignatureTypeItem env (exported : SharedTypes.exported) item = + let open Types in + match item with + | Sig_value (ident, {val_type; val_attributes; val_loc = loc}) -> + let item = val_type in + let declared = + addItem + ~name:(Location.mknoloc (Ident.name ident)) + ~extent:loc ~stamp:(Ident.binding_time ident) ~env ~item val_attributes + exported.values env.stamps.values + in + [{declared with item = MValue declared.item}] + | Sig_type + ( ident, + ({type_loc; type_kind; type_manifest; type_attributes} as decl), + recStatus ) -> + let declared = + addItem ~extent:type_loc + ~item: + { + Type.decl; + kind = + (match type_kind with + | Type_abstract -> ( + match type_manifest with + | Some {desc = Tconstr (path, args, _)} -> + Abstract (Some (path, args)) + | Some {desc = Ttuple items} -> Tuple items + (* TODO dig *) + | _ -> Abstract None) + | Type_open -> Open + | Type_variant constructors -> + Variant + (constructors + |> List.map + (fun {cd_loc; cd_id; cd_args; cd_res; cd_attributes} -> + let name = Ident.name cd_id in + let stamp = Ident.binding_time cd_id in + let item = + { + stamp; + cname = Location.mknoloc name; + args = + (match cd_args with + | Cstr_tuple args -> + args |> List.map (fun t -> (t, Location.none)) + (* TODO(406): constructor record args support *) + | Cstr_record _ -> []); + res = cd_res; + } + in + let declared = + ProcessAttributes.newDeclared ~item ~extent:cd_loc + ~scope: + { + Location.loc_start = type_loc.Location.loc_end; + loc_end = env.scope.loc_end; + loc_ghost = false; + } + ~name:(Location.mknoloc name) + ~stamp (* TODO maybe this needs another child *) + ~modulePath:env.modulePath + ~processDoc:env.processDoc true cd_attributes + in + Hashtbl.add env.stamps.constructors stamp declared; + item)) + | Type_record (fields, _) -> + Record + (fields + |> List.map (fun {ld_id; ld_type} -> + let astamp = Ident.binding_time ld_id in + let name = Ident.name ld_id in + { + stamp = astamp; + fname = Location.mknoloc name; + typ = ld_type; + }))); + } + ~name:(Location.mknoloc (Ident.name ident)) + ~stamp:(Ident.binding_time ident) ~env type_attributes exported.types + env.stamps.types + in + [{declared with item = MType (declared.item, recStatus)}] + (* | Sig_module({stamp, name}, {md_type: Mty_ident(path) | Mty_alias(path), md_attributes, md_loc}, _) => + let declared = addItem(~contents=Module.Ident(path), ~name=Location.mknoloc(name), ~stamp, ~env, md_attributes, exported.modules, env.stamps.modules); + [{...declared, contents: Module.Module(declared.contents)}, ...items] *) + | Sig_module (ident, {md_type; md_attributes; md_loc}, _) -> + let declared = + addItem ~extent:md_loc + ~item:(forModuleType env md_type) + ~name:(Location.mknoloc (Ident.name ident)) + ~stamp:(Ident.binding_time ident) ~env md_attributes exported.modules + env.stamps.modules + in + [{declared with item = Module declared.item}] + | _ -> [] + +and forSignatureType env signature = + let exported = initExported () in + let topLevel = + List.fold_right + (fun item items -> forSignatureTypeItem env exported item @ items) + signature [] + in + {docstring = []; exported; topLevel} + +and forModuleType env moduleType = + match moduleType with + | Types.Mty_ident path -> Ident path + | Mty_alias (_ (* 402 *), path) -> Ident path + | Mty_signature signature -> Structure (forSignatureType env signature) + | Mty_functor (_argIdent, _argType, resultType) -> + forModuleType env resultType + +let getModuleTypePath mod_desc = + match mod_desc with + | Tmty_ident (path, _) | Tmty_alias (path, _) -> Some path + | Tmty_signature _ | Tmty_functor _ | Tmty_with _ | Tmty_typeof _ -> None + +let forTypeDeclaration ~env ~(exported : exported) + { + typ_id; + typ_loc; + typ_name = name; + typ_attributes; + typ_type; + typ_kind; + typ_manifest; + } ~recStatus = + let stamp = Ident.binding_time typ_id in + let declared = + addItem ~extent:typ_loc + ~item: + { + Type.decl = typ_type; + kind = + (match typ_kind with + | Ttype_abstract -> ( + match typ_manifest with + | Some {ctyp_desc = Ttyp_constr (path, _lident, args)} -> + Abstract (Some (path, args |> List.map (fun t -> t.ctyp_type))) + | Some {ctyp_desc = Ttyp_tuple items} -> + Tuple (items |> List.map (fun t -> t.ctyp_type)) + (* TODO dig *) + | _ -> Abstract None) + | Ttype_open -> Open + | Ttype_variant constructors -> + Variant + (constructors + |> List.map (fun {cd_id; cd_name = cname; cd_args; cd_res} -> + let stamp = Ident.binding_time cd_id in + { + stamp; + cname; + args = + (match cd_args with + | Cstr_tuple args -> + args + |> List.map (fun t -> (t.ctyp_type, t.ctyp_loc)) + (* TODO(406) *) + | Cstr_record _ -> []); + res = + (match cd_res with + | None -> None + | Some t -> Some t.ctyp_type); + })) + | Ttype_record fields -> + Record + (fields + |> List.map + (fun {ld_id; ld_name = fname; ld_type = {ctyp_type}} -> + let fstamp = Ident.binding_time ld_id in + {stamp = fstamp; fname; typ = ctyp_type}))); + } + ~name ~stamp ~env typ_attributes exported.types env.stamps.types + in + {declared with item = MType (declared.item, recStatus)} + +let forSignatureItem ~env ~(exported : exported) item = + match item.sig_desc with + | Tsig_value {val_id; val_loc; val_name = name; val_desc; val_attributes} -> + let declared = + addItem ~name + ~stamp:(Ident.binding_time val_id) + ~extent:val_loc ~item:val_desc.ctyp_type ~env val_attributes + exported.values env.stamps.values + in + [{declared with item = MValue declared.item}] + | Tsig_type (recFlag, decls) -> + decls + |> List.mapi (fun i decl -> + let recStatus = + match recFlag with + | Recursive when i = 0 -> Types.Trec_first + | Nonrecursive when i = 0 -> Types.Trec_not + | _ -> Types.Trec_next + in + decl |> forTypeDeclaration ~env ~exported ~recStatus) + | Tsig_module + {md_id; md_attributes; md_loc; md_name = name; md_type = {mty_type}} -> + let item = forModuleType env mty_type in + let declared = + addItem ~item ~name ~extent:md_loc ~stamp:(Ident.binding_time md_id) ~env + md_attributes exported.modules env.stamps.modules + in + [{declared with item = Module declared.item}] + | Tsig_include {incl_mod; incl_type} -> + let env = + match getModuleTypePath incl_mod.mty_desc with + | None -> env + | Some path -> + {env with modulePath = IncludedModule (path, env.modulePath)} + in + let topLevel = + List.fold_right + (fun item items -> forSignatureTypeItem env exported item @ items) + incl_type [] + in + topLevel + (* TODO: process other things here *) + | _ -> [] + +let forSignature ~env items = + let exported = initExported () in + let topLevel = + items |> List.map (forSignatureItem ~env ~exported) |> List.flatten + in + let attributes = + match items with + | {sig_desc = Tsig_attribute attribute} :: _ -> [attribute] + | _ -> [] + in + let docstring = + match ProcessAttributes.findDocAttribute attributes with + | None -> [] + | Some d -> env.processDoc d + in + {docstring; exported; topLevel} + +let forTreeModuleType ~env {mty_desc} = + match mty_desc with + | Tmty_ident _ -> None + | Tmty_signature {sig_items} -> + let contents = forSignature ~env sig_items in + Some (Structure contents) + | _ -> None + +let rec getModulePath mod_desc = + match mod_desc with + | Tmod_ident (path, _lident) -> Some path + | Tmod_structure _ -> None + | Tmod_functor (_ident, _argName, _maybeType, _resultExpr) -> None + | Tmod_apply (functor_, _arg, _coercion) -> getModulePath functor_.mod_desc + | Tmod_unpack (_expr, _moduleType) -> None + | Tmod_constraint (expr, _typ, _constraint, _coercion) -> + getModulePath expr.mod_desc + +let rec forItem ~env ~(exported : exported) item = + match item.str_desc with + | Tstr_value (_isRec, bindings) -> + optMap + (fun {vb_loc; vb_pat = {pat_desc; pat_type}; vb_attributes} -> + (* TODO get all the things out of the var. *) + match pat_desc with + | Tpat_var (ident, name) + | Tpat_alias ({pat_desc = Tpat_any}, ident, name) (* let x : t = ... *) + -> + let item = pat_type in + let declared = + addItem ~name ~stamp:(Ident.binding_time ident) ~env ~extent:vb_loc + ~item vb_attributes exported.values env.stamps.values + in + Some {declared with item = MValue declared.item} + | _ -> None) + bindings + | Tstr_module + {mb_id; mb_attributes; mb_loc; mb_name = name; mb_expr = {mod_desc}} -> + let item = forModule env mod_desc name.txt in + let declared = + addItem ~item ~name ~extent:mb_loc ~stamp:(Ident.binding_time mb_id) ~env + mb_attributes exported.modules env.stamps.modules + in + [{declared with item = Module declared.item}] + | Tstr_include {incl_mod; incl_type} -> + let env = + match getModulePath incl_mod.mod_desc with + | None -> env + | Some path -> + {env with modulePath = IncludedModule (path, env.modulePath)} + in + let topLevel = + List.fold_right + (fun item items -> forSignatureTypeItem env exported item @ items) + incl_type [] + in + topLevel + | Tstr_primitive + {val_id; val_name = name; val_loc; val_attributes; val_val = {val_type}} + -> + let declared = + addItem ~extent:val_loc ~item:val_type ~name + ~stamp:(Ident.binding_time val_id) + ~env val_attributes exported.values env.stamps.values + in + [{declared with item = MValue declared.item}] + | Tstr_type (recFlag, decls) -> + decls + |> List.mapi (fun i decl -> + let recStatus = + match recFlag with + | Recursive when i = 0 -> Types.Trec_first + | Nonrecursive when i = 0 -> Types.Trec_not + | _ -> Types.Trec_next + in + decl |> forTypeDeclaration ~env ~exported ~recStatus) + | _ -> [] + +and forModule env mod_desc moduleName = + match mod_desc with + | Tmod_ident (path, _lident) -> Ident path + | Tmod_structure structure -> + let env = + { + env with + scope = itemsExtent structure.str_items; + modulePath = ExportedModule (moduleName, env.modulePath); + } + in + let contents = forStructure ~env structure.str_items in + Structure contents + | Tmod_functor (ident, argName, maybeType, resultExpr) -> + (match maybeType with + | None -> () + | Some t -> ( + match forTreeModuleType ~env t with + | None -> () + | Some kind -> + let stamp = Ident.binding_time ident in + let declared = + ProcessAttributes.newDeclared ~item:kind ~name:argName + ~scope: + { + Location.loc_start = t.mty_loc.loc_end; + loc_end = env.scope.loc_end; + loc_ghost = false; + } + ~extent:t.Typedtree.mty_loc ~stamp ~modulePath:NotVisible + ~processDoc:env.processDoc false [] + in + Hashtbl.add env.stamps.modules stamp declared)); + forModule env resultExpr.mod_desc moduleName + | Tmod_apply (functor_, _arg, _coercion) -> + forModule env functor_.mod_desc moduleName + | Tmod_unpack (_expr, moduleType) -> + let env = + {env with modulePath = ExportedModule (moduleName, env.modulePath)} + in + forModuleType env moduleType + | Tmod_constraint (expr, _typ, Tmodtype_implicit, Tcoerce_structure _) -> + (* implicit contraint synthesized during typechecking *) + (* e.g. when the same id is defined twice (e.g. make with @react.component) *) + (* skip the constraint and use the original module definition *) + forModule env expr.mod_desc moduleName + | Tmod_constraint (_expr, typ, _constraint, _coercion) -> + (* TODO do this better I think *) + let env = + {env with modulePath = ExportedModule (moduleName, env.modulePath)} + in + forModuleType env typ + +and forStructure ~env items = + let exported = initExported () in + let topLevel = + List.fold_right + (fun item results -> forItem ~env ~exported item @ results) + items [] + in + let attributes = + match items with + | {str_desc = Tstr_attribute attribute} :: _ -> [attribute] + | _ -> [] + in + let docstring = + match ProcessAttributes.findDocAttribute attributes with + | None -> [] + | Some d -> env.processDoc d + in + {docstring; exported; topLevel} + +let forCmt ~moduleName ~uri processDoc + ({cmt_modname; cmt_annots} : Cmt_format.cmt_infos) = + match cmt_annots with + | Partial_implementation parts -> + let items = + parts |> Array.to_list + |> Utils.filterMap (fun p -> + match (p : Cmt_format.binary_part) with + | Partial_structure str -> Some str.str_items + | Partial_structure_item str -> Some [str] + | _ -> None) + |> List.concat + in + let extent = itemsExtent items in + let extent = + { + extent with + loc_end = + { + extent.loc_end with + pos_lnum = extent.loc_end.pos_lnum + 1000000; + pos_cnum = extent.loc_end.pos_cnum + 100000000; + }; + } + in + let env = + { + scope = extent; + stamps = initStamps (); + processDoc; + modulePath = File (uri, moduleName); + } + in + let contents = forStructure ~env items in + {uri; moduleName = cmt_modname; stamps = env.stamps; contents} + | Partial_interface parts -> + let items = + parts |> Array.to_list + |> Utils.filterMap (fun (p : Cmt_format.binary_part) -> + match p with + | Partial_signature str -> Some str.sig_items + | Partial_signature_item str -> Some [str] + | _ -> None) + |> List.concat + in + let env = + { + scope = sigItemsExtent items; + stamps = initStamps (); + processDoc; + modulePath = File (uri, moduleName); + } + in + let contents = forSignature ~env items in + {uri; moduleName = cmt_modname; stamps = env.stamps; contents} + | Implementation structure -> + let env = + { + scope = itemsExtent structure.str_items; + stamps = initStamps (); + processDoc; + modulePath = File (uri, moduleName); + } + in + let contents = forStructure ~env structure.str_items in + {uri; moduleName = cmt_modname; stamps = env.stamps; contents} + | Interface signature -> + let env = + { + scope = sigItemsExtent signature.sig_items; + stamps = initStamps (); + processDoc; + modulePath = File (uri, moduleName); + } + in + let contents = forSignature ~env signature.sig_items in + {uri; moduleName = cmt_modname; stamps = env.stamps; contents} + | _ -> SharedTypes.emptyFile moduleName uri diff --git a/analysis/src/ProcessExtra.ml b/analysis/src/ProcessExtra.ml new file mode 100644 index 000000000..d4119da41 --- /dev/null +++ b/analysis/src/ProcessExtra.ml @@ -0,0 +1,546 @@ +open Typedtree +open SharedTypes + +let handleConstructor path txt = + let typeName = + match path with + | Path.Pdot (_path, typename, _) -> typename + | Pident ident -> Ident.name ident + | _ -> assert false + in + let open Longident in + match txt with + | Longident.Lident name -> (name, Lident typeName) + | Ldot (left, name) -> (name, Ldot (left, typeName)) + | Lapply (_, _) -> assert false + +let rec relative ident path = + match (ident, path) with + | Longident.Lident name, Path.Pdot (path, pname, _) when pname = name -> + Some path + | Longident.Ldot (ident, name), Path.Pdot (path, pname, _) when pname = name + -> + relative ident path + (* | (Ldot(Lident("*predef*" | "exn"), _), Pident(_)) => None *) + | _ -> None + +let findClosestMatchingOpen opens path ident loc = + match relative ident path with + | None -> None + | Some openNeedle -> ( + let matching = + Hashtbl.fold + (fun _ op res -> + if Utils.locWithinLoc loc op.extent && Path.same op.path openNeedle + then op :: res + else res) + opens [] + |> List.sort (fun (a : SharedTypes.openTracker) b -> + b.loc.loc_start.pos_cnum - a.loc.loc_start.pos_cnum) + in + match matching with [] -> None | first :: _ -> Some first ) + +let getTypeAtPath ~env path = + match Query.fromCompilerPath ~env path with + | `GlobalMod _ -> `Not_found + | `Global (moduleName, path) -> `Global (moduleName, path) + | `Not_found -> `Not_found + | `Exported (env, name) -> ( + match Hashtbl.find_opt env.exported.types name with + | None -> `Not_found + | Some stamp -> ( + let declaredType = Hashtbl.find_opt env.file.stamps.types stamp in + match declaredType with + | Some declaredType -> `Local declaredType + | None -> `Not_found ) ) + | `Stamp stamp -> ( + let declaredType = Hashtbl.find_opt env.file.stamps.types stamp in + match declaredType with + | Some declaredType -> `Local declaredType + | None -> `Not_found ) + +module F (Collector : sig + val extra : extra + + val file : file + + val scopeExtent : Location.t list ref +end) = +struct + let extra = Collector.extra + + let maybeAddUse path ident loc tip = + match findClosestMatchingOpen extra.opens path ident loc with + | None -> () + | Some tracker -> ( + match Query.makeRelativePath tracker.path path with + | None -> () + | Some relpath -> tracker.used <- (relpath, tip, loc) :: tracker.used ) + + let addLocation loc ident = extra.locations <- (loc, ident) :: extra.locations + + let addReference stamp loc = + Hashtbl.replace extra.internalReferences stamp + ( loc + :: + ( match Hashtbl.mem extra.internalReferences stamp with + | true -> Hashtbl.find extra.internalReferences stamp + | false -> [] ) ) + + let addExternalReference moduleName path tip loc = + (* TODO need to follow the path, and be able to load the files to follow module references... *) + Hashtbl.replace extra.externalReferences moduleName + ( (path, tip, loc) + :: + ( match Hashtbl.mem extra.externalReferences moduleName with + | true -> Hashtbl.find extra.externalReferences moduleName + | false -> [] ) ) + + let env = Query.fileEnv Collector.file + + let getTypeAtPath = getTypeAtPath ~env + + let addForPath path lident loc typ tip = + maybeAddUse path lident loc tip; + let identName = Longident.last lident in + let identLoc = Utils.endOfLocation loc (String.length identName) in + let locType = + match Query.fromCompilerPath ~env path with + | `Stamp stamp -> + addReference stamp identLoc; + LocalReference (stamp, tip) + | `Not_found -> NotFound + | `Global (moduleName, path) -> + addExternalReference moduleName path tip identLoc; + GlobalReference (moduleName, path, tip) + | `Exported (env, name) -> ( + match + Hashtbl.find_opt + ( match tip = Type with + | true -> env.exported.types + | false -> env.exported.values ) + name + with + | Some stamp -> + addReference stamp identLoc; + LocalReference (stamp, tip) + | None -> NotFound ) + | `GlobalMod _ -> NotFound + in + addLocation loc (Typed (typ, locType)) + + let addForPathParent path loc = + let locType = + match Query.fromCompilerPath ~env path with + | `GlobalMod name -> + (* TODO track external references to filenames to handle renames well *) + TopLevelModule name + | `Stamp stamp -> + addReference stamp loc; + LModule (LocalReference (stamp, Module)) + | `Not_found -> LModule NotFound + | `Global (moduleName, path) -> + addExternalReference moduleName path Module loc; + LModule (GlobalReference (moduleName, path, Module)) + | `Exported (env, name) -> ( + match Hashtbl.find_opt env.exported.modules name with + | Some stamp -> + addReference stamp loc; + LModule (LocalReference (stamp, Module)) + | None -> LModule NotFound ) + in + addLocation loc locType + + let addForField recordType item {Asttypes.txt; loc} = + match (Shared.dig recordType).desc with + | Tconstr (path, _args, _memo) -> + let t = getTypeAtPath path in + let {Types.lbl_res} = item in + let name, typeLident = handleConstructor path txt in + maybeAddUse path typeLident loc (Field name); + let nameLoc = Utils.endOfLocation loc (String.length name) in + let locType = + match t with + | `Local {stamp; item = {kind = Record fields}} -> ( + match fields |> List.find_opt (fun f -> f.fname.txt = name) with + | Some {stamp = astamp} -> + addReference astamp nameLoc; + LocalReference (stamp, Field name) + | None -> NotFound ) + | `Global (moduleName, path) -> + addExternalReference moduleName path (Field name) nameLoc; + GlobalReference (moduleName, path, Field name) + | _ -> NotFound + in + addLocation nameLoc (Typed (lbl_res, locType)) + | _ -> () + + let addForRecord recordType items = + match (Shared.dig recordType).desc with + | Tconstr (path, _args, _memo) -> + let t = getTypeAtPath path in + items + |> List.iter (fun ({Asttypes.txt; loc}, {Types.lbl_res}, _) -> + (* let name = Longident.last(txt); *) + let name, typeLident = handleConstructor path txt in + maybeAddUse path typeLident loc (Field name); + let nameLoc = Utils.endOfLocation loc (String.length name) in + let locType = + match t with + | `Local {stamp; item = {kind = Record fields}} -> ( + match + fields |> List.find_opt (fun f -> f.fname.txt = name) + with + | Some {stamp = astamp} -> + addReference astamp nameLoc; + LocalReference (stamp, Field name) + | None -> NotFound ) + | `Global (moduleName, path) -> + addExternalReference moduleName path (Field name) nameLoc; + GlobalReference (moduleName, path, Field name) + | _ -> NotFound + in + addLocation nameLoc (Typed (lbl_res, locType))) + | _ -> () + + let addForConstructor constructorType {Asttypes.txt; loc} {Types.cstr_name} = + match (Shared.dig constructorType).desc with + | Tconstr (path, _args, _memo) -> + (* let name = Longident.last(txt); *) + let name, typeLident = handleConstructor path txt in + maybeAddUse path typeLident loc (Constructor name); + let nameLoc = Utils.endOfLocation loc (String.length name) in + let t = getTypeAtPath path in + let locType = + match t with + | `Local {stamp; item = {kind = Variant constructors}} -> ( + match + constructors |> List.find_opt (fun c -> c.cname.txt = cstr_name) + with + | Some {stamp = cstamp} -> + addReference cstamp nameLoc; + LocalReference (stamp, Constructor name) + | None -> NotFound ) + | `Global (moduleName, path) -> + addExternalReference moduleName path (Constructor name) nameLoc; + GlobalReference (moduleName, path, Constructor name) + | _ -> NotFound + in + addLocation nameLoc (Typed (constructorType, locType)) + | _ -> () + + let currentScopeExtent () = + if !Collector.scopeExtent = [] then Location.none + else List.hd !Collector.scopeExtent + + let addScopeExtent loc = + Collector.scopeExtent := loc :: !Collector.scopeExtent + + let popScopeExtent () = + if List.length !Collector.scopeExtent > 1 then + Collector.scopeExtent := List.tl !Collector.scopeExtent + + let rec addForLongident top (path : Path.t) (txt : Longident.t) loc = + if not loc.Location.loc_ghost then ( + let idLength = + String.length (String.concat "." (Longident.flatten txt)) + in + let reportedLength = loc.loc_end.pos_cnum - loc.loc_start.pos_cnum in + let isPpx = idLength <> reportedLength in + if isPpx then + match top with + | Some (t, tip) -> addForPath path txt loc t tip + | None -> addForPathParent path loc + else + let l = Utils.endOfLocation loc (String.length (Longident.last txt)) in + ( match top with + | Some (t, tip) -> addForPath path txt l t tip + | None -> addForPathParent path l ); + match (path, txt) with + | Pdot (pinner, _pname, _), Ldot (inner, name) -> + addForLongident None pinner inner + (Utils.chopLocationEnd loc (String.length name + 1)) + | Pident _, Lident _ -> () + | _ -> () ) + + let rec handle_module_expr expr = + match expr with + | Tmod_constraint (expr, _, _, _) -> handle_module_expr expr.mod_desc + | Tmod_ident (path, {txt; loc}) -> + Log.log ("Ident!! " ^ String.concat "." (Longident.flatten txt)); + maybeAddUse path txt loc Module; + addForLongident None path txt loc + | Tmod_functor (_ident, _argName, _maybeType, resultExpr) -> + handle_module_expr resultExpr.mod_desc + | Tmod_apply (obj, arg, _) -> + handle_module_expr obj.mod_desc; + handle_module_expr arg.mod_desc + | _ -> () + + open Typedtree + include TypedtreeIter.DefaultIteratorArgument + + let enter_structure_item item = + match item.str_desc with + | Tstr_attribute + ( {Asttypes.txt = "ocaml.explanation"; loc}, + PStr + [ + { + pstr_desc = + Pstr_eval + ({pexp_desc = Pexp_constant (Pconst_string (doc, _))}, _); + }; + ] ) -> + addLocation loc (Explanation doc) + | Tstr_include {incl_mod = expr} -> handle_module_expr expr.mod_desc + | Tstr_module {mb_expr} -> handle_module_expr mb_expr.mod_desc + | Tstr_open {open_path; open_txt = {txt; loc}} -> + (* Log.log("Have an open here"); *) + maybeAddUse open_path txt loc Module; + let tracker = + { + path = open_path; + loc; + used = []; + extent = + { + loc_ghost = true; + loc_start = loc.loc_end; + loc_end = (currentScopeExtent ()).loc_end; + }; + } + in + addForLongident None open_path txt loc; + Hashtbl.replace Collector.extra.opens loc tracker + | _ -> () + + let enter_structure {str_items} = + if str_items <> [] then + let first = List.hd str_items in + let last = List.nth str_items (List.length str_items - 1) in + let extent = + { + Location.loc_ghost = true; + loc_start = first.str_loc.loc_start; + loc_end = last.str_loc.loc_end; + } + in + addScopeExtent extent + + let leave_structure str = if str.str_items <> [] then popScopeExtent () + + let enter_signature_item item = + match item.sig_desc with + | Tsig_value {val_id; val_loc; val_name = name; val_desc; val_attributes} -> + let stamp = Ident.binding_time val_id in + if not (Hashtbl.mem Collector.file.stamps.values stamp) then ( + let declared = + ProcessAttributes.newDeclared ~name ~stamp ~extent:val_loc + ~scope: + { + loc_ghost = true; + loc_start = val_loc.loc_end; + loc_end = (currentScopeExtent ()).loc_end; + } + ~modulePath:NotVisible + ~processDoc:(fun x -> [x]) + ~item:val_desc.ctyp_type false val_attributes + in + Hashtbl.add Collector.file.stamps.values stamp declared; + addReference stamp name.loc; + addLocation name.loc + (Typed (val_desc.ctyp_type, Definition (stamp, Value))) ) + | _ -> () + + let enter_core_type {ctyp_type; ctyp_desc} = + match ctyp_desc with + | Ttyp_constr (path, {txt; loc}, _args) -> + (* addForPath(path, txt, loc, Shared.makeFlexible(ctyp_type), Type) *) + addForLongident (Some (ctyp_type, Type)) path txt loc + | _ -> () + + let enter_pattern {pat_desc; pat_loc; pat_type; pat_attributes} = + let addForPattern stamp name = + if not (Hashtbl.mem Collector.file.stamps.values stamp) then ( + let declared = + ProcessAttributes.newDeclared ~name ~stamp + ~scope: + { + loc_ghost = true; + loc_start = pat_loc.loc_end; + loc_end = (currentScopeExtent ()).loc_end; + } + ~modulePath:NotVisible ~extent:pat_loc + ~processDoc:(fun x -> [x]) + ~item:pat_type false pat_attributes + in + Hashtbl.add Collector.file.stamps.values stamp declared; + addReference stamp name.loc; + addLocation name.loc (Typed (pat_type, Definition (stamp, Value))) ) + in + (* Log.log("Entering pattern " ++ Utils.showLocation(pat_loc)); *) + match pat_desc with + | Tpat_record (items, _) -> addForRecord pat_type items + | Tpat_construct (lident, constructor, _) -> + addForConstructor pat_type lident constructor + | Tpat_alias (_inner, ident, name) -> + let stamp = Ident.binding_time ident in + addForPattern stamp name + | Tpat_var (ident, name) -> + (* Log.log("Pattern " ++ name.txt); *) + let stamp = Ident.binding_time ident in + addForPattern stamp name + | _ -> () + + let enter_expression expression = + expression.exp_extra + |> List.iter (fun (e, eloc, _) -> + match e with + | Texp_open (_, path, _ident, _) -> + Hashtbl.add extra.opens eloc + {path; loc = eloc; extent = expression.exp_loc; used = []} + | _ -> ()); + match expression.exp_desc with + | Texp_ident (path, {txt; loc}, {val_type}) -> + addForLongident (Some (val_type, Value)) path txt loc + | Texp_record {fields} -> + addForRecord expression.exp_type + ( fields |> Array.to_list + |> Utils.filterMap (fun (desc, item) -> + match item with + | Overridden (loc, _) -> Some (loc, desc, ()) + | _ -> None) ) + | Texp_constant constant -> + addLocation expression.exp_loc (Constant constant) + (* Skip unit and list literals *) + | Texp_construct ({txt = Lident ("()" | "::"); loc}, _, _args) + when loc.loc_end.pos_cnum - loc.loc_start.pos_cnum <> 2 -> + () + | Texp_construct (lident, constructor, _args) -> + addForConstructor expression.exp_type lident constructor + | Texp_field (inner, lident, label_description) -> + addForField inner.exp_type label_description lident + | Texp_let (_, _, _) -> + (* TODO this scope tracking won't work for recursive *) + addScopeExtent expression.exp_loc + | Texp_function {cases} -> ( + match cases with [{c_rhs}] -> addScopeExtent c_rhs.exp_loc | _ -> () ) + | _ -> () + + let leave_expression expression = + match expression.exp_desc with + | Texp_let (_isrec, _bindings, _expr) -> popScopeExtent () + | Texp_function {cases} -> ( + match cases with [_] -> popScopeExtent () | _ -> () ) + | _ -> () +end + +let forFile ~file = + let extra = initExtra () in + let addLocation loc ident = + extra.locations <- (loc, ident) :: extra.locations + in + let addReference stamp loc = + Hashtbl.replace extra.internalReferences stamp + ( loc + :: + ( match Hashtbl.mem extra.internalReferences stamp with + | true -> Hashtbl.find extra.internalReferences stamp + | false -> [] ) ) + in + file.stamps.modules + |> Hashtbl.iter (fun stamp d -> + addLocation d.name.loc (LModule (Definition (stamp, Module))); + addReference stamp d.name.loc); + file.stamps.values + |> Hashtbl.iter (fun stamp d -> + addLocation d.name.loc (Typed (d.item, Definition (stamp, Value))); + addReference stamp d.name.loc); + file.stamps.types + |> Hashtbl.iter (fun stamp d -> + addLocation d.name.loc (TypeDefinition (d.name.txt, d.item.Type.decl, stamp)); + addReference stamp d.name.loc; + match d.item.Type.kind with + | Record labels -> + labels + |> List.iter (fun {stamp; fname; typ} -> + addReference stamp fname.loc; + addLocation fname.loc + (Typed (typ, Definition (d.stamp, Field fname.txt)))) + | Variant constructos -> + constructos + |> List.iter (fun {stamp; cname} -> + addReference stamp cname.loc; + let t = + { + Types.id = 0; + level = 0; + desc = + Tconstr + ( Path.Pident + {Ident.stamp; name = d.name.txt; flags = 0}, + [], + ref Types.Mnil ); + } + in + addLocation cname.loc + (Typed (t, Definition (d.stamp, Constructor cname.txt)))) + | _ -> ()); + extra + +let forItems ~file items parts = + let extra = forFile ~file in + let extent = ProcessCmt.itemsExtent items in + let extent = + { + extent with + loc_end = + { + extent.loc_end with + pos_lnum = extent.loc_end.pos_lnum + 1000000; + pos_cnum = extent.loc_end.pos_cnum + 100000000; + }; + } + in + (* TODO look through parts and extend the extent *) + let module Iter = TypedtreeIter.MakeIterator (F (struct + let scopeExtent = ref [extent] + + let extra = extra + + let file = file + end)) in + List.iter Iter.iter_structure_item items; + (* Log.log("Parts " ++ string_of_int(Array.length(parts))); *) + parts + |> Array.iter (fun part -> + match part with + | Cmt_format.Partial_signature str -> Iter.iter_signature str + | Partial_signature_item str -> Iter.iter_signature_item str + | Partial_expression expression -> Iter.iter_expression expression + | Partial_pattern pattern -> Iter.iter_pattern pattern + | Partial_class_expr class_expr -> Iter.iter_class_expr class_expr + | Partial_module_type module_type -> Iter.iter_module_type module_type + | Partial_structure _ | Partial_structure_item _ -> ()); + extra + +let forCmt ~file ({cmt_annots} : Cmt_format.cmt_infos) = + match cmt_annots with + | Partial_implementation parts -> + let items = + parts |> Array.to_list + |> Utils.filterMap (fun (p : Cmt_format.binary_part) -> + match p with + | Partial_structure str -> Some str.str_items + | Partial_structure_item str -> Some [str] + (* | Partial_expression(exp) => Some([ str]) *) + | _ -> None) + |> List.concat + in + forItems ~file items parts + | Implementation structure -> forItems ~file structure.str_items [||] + | Partial_interface _ | Interface _ -> + (** TODO actually process signature items *) + forItems ~file [] [||] + | _ -> forItems ~file [] [||] diff --git a/analysis/src/Process_406.ml b/analysis/src/Process_406.ml new file mode 100644 index 000000000..d8821d59d --- /dev/null +++ b/analysis/src/Process_406.ml @@ -0,0 +1,16 @@ +open SharedTypes + +let fileForCmt ~moduleName ~uri cmt processDoc = + match Shared.tryReadCmt cmt with + | Error e -> Error e + | Ok infos -> Ok (ProcessCmt.forCmt ~moduleName ~uri processDoc infos) + +let fullForCmt ~moduleName ~uri cmt processDoc = + match Shared.tryReadCmt cmt with + | Error e -> Error e + | Ok infos -> + let file = ProcessCmt.forCmt ~moduleName ~uri processDoc infos in + let extra = ProcessExtra.forCmt ~file infos in + Ok {file; extra} + +module PrintType = PrintType diff --git a/analysis/src/Process_406.mli b/analysis/src/Process_406.mli new file mode 100644 index 000000000..075aacc44 --- /dev/null +++ b/analysis/src/Process_406.mli @@ -0,0 +1,13 @@ +val fileForCmt : + moduleName:string -> + uri:Uri2.t -> + string -> + (string -> string list) -> + (SharedTypes.file, string) result + +val fullForCmt : + moduleName:string -> + uri:Uri2.t -> + string -> + (string -> string list) -> + (SharedTypes.full, string) result diff --git a/analysis/src/Protocol.ml b/analysis/src/Protocol.ml new file mode 100644 index 000000000..3153a5104 --- /dev/null +++ b/analysis/src/Protocol.ml @@ -0,0 +1,70 @@ +let array l = "[" ^ (String.concat ", " l) ^ "]" + +type position = { + line: int; + character: int; +} + +type range = { + start: position; + end_: position; +} + +type markupContent = { + kind: string; + value: string; +} + +type completionItem = { + label: string; + kind: int; + tags: int list; + detail: string; + documentation: markupContent; +} + +type hover = { + contents: string; +} + +type location = { + uri: string; + range: range; +} + +let stringifyPosition p = + Printf.sprintf {|{"line": %i, "character": %i}|} p.line p.character + +let stringifyRange r = + Printf.sprintf {|{"start": %s, "end": %s}|} + (stringifyPosition r.start) + (stringifyPosition r.end_) + +let stringifyMarkupContent (m: markupContent) = + Printf.sprintf {|{"kind": "%s", "value": "%s"}|} + m.kind (String.escaped m.value) + +let stringifyCompletionItem c = + Printf.sprintf {|{ + "label": "%s", + "kind": %i, + "tags": %s, + "detail": "%s", + "documentation": %s + }|} + (String.escaped c.label) + c.kind + (c.tags |> List.map string_of_int |> array) + (String.escaped c.detail) + (stringifyMarkupContent c.documentation) + +let stringifyHover h = + Printf.sprintf {|{"contents": "%s"}|} + (String.escaped h.contents) + +let stringifyLocation h = + Printf.sprintf {|{"uri": "%s", "range": %s}|} + (String.escaped h.uri) + (stringifyRange h.range) + +let null = "null" diff --git a/analysis/src/Query.ml b/analysis/src/Query.ml new file mode 100644 index 000000000..e3e4f7056 --- /dev/null +++ b/analysis/src/Query.ml @@ -0,0 +1,245 @@ +open SharedTypes + +type queryEnv = {file : file; exported : exported} + +let fileEnv file = {file; exported = file.contents.exported} + +let tupleOfLexing {Lexing.pos_lnum; pos_cnum; pos_bol} = + (pos_lnum - 1, pos_cnum - pos_bol) + +let locationIsBefore {Location.loc_start} pos = tupleOfLexing loc_start <= pos + +let findInScope pos name stamps = + (* Log.log("Find " ++ name ++ " with " ++ string_of_int(Hashtbl.length(stamps)) ++ " stamps"); *) + Hashtbl.fold + (fun _stamp declared result -> + if declared.name.txt = name then + (* Log.log("a stamp " ++ Utils.showLocation(declared.scopeLoc) ++ " " ++ string_of_int(l) ++ "," ++ string_of_int(c)); *) + if locationIsBefore declared.scopeLoc pos then + match result with + | None -> Some declared + | Some current -> + if + current.name.loc.loc_start.pos_cnum + < declared.name.loc.loc_start.pos_cnum + then Some declared + else result + else result + else + (* Log.log("wrong name " ++ declared.name.txt); *) + result + ) + stamps None + +let rec joinPaths modulePath path = + match modulePath with + | Path.Pident ident -> (ident.stamp, ident.name, path) + | Papply (fnPath, _argPath) -> joinPaths fnPath path + | Pdot (inner, name, _) -> joinPaths inner (Nested (name, path)) + +let rec makePath modulePath = + match modulePath with + | Path.Pident ident when ident.stamp == 0 -> `GlobalMod ident.name + | Pident ident -> `Stamp ident.stamp + | Papply (fnPath, _argPath) -> makePath fnPath + | Pdot (inner, name, _) -> `Path (joinPaths inner (Tip name)) + +let makeRelativePath basePath otherPath = + let rec loop base other tip = + if Path.same base other then Some tip + else + match other with + | Pdot (inner, name, _) -> loop basePath inner (Nested (name, tip)) + | _ -> None + in + match otherPath with + | Path.Pdot (inner, name, _) -> loop basePath inner (Tip name) + | _ -> None + +let rec resolvePathInner ~env ~path = + match path with + | Tip name -> Some (`Local (env, name)) + | Nested (subName, subPath) -> ( + match Hashtbl.find_opt env.exported.modules subName with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some {item = kind} -> findInModule ~env kind subPath ) ) + +and findInModule ~env kind path = + match kind with + | Structure {exported} -> resolvePathInner ~env:{env with exported} ~path + | Ident modulePath -> ( + let stamp, moduleName, fullPath = joinPaths modulePath path in + if stamp = 0 then Some (`Global (moduleName, fullPath)) + else + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some {item = kind} -> findInModule ~env kind fullPath ) + +(* let rec findSubModule = (~env, ~getModule) *) + +let rec resolvePath ~env ~path ~getModule = + match resolvePathInner ~env ~path with + | None -> None + | Some result -> ( + match result with + | `Local (env, name) -> Some (env, name) + | `Global (moduleName, fullPath) -> ( + match getModule moduleName with + | None -> None + | Some file -> resolvePath ~env:(fileEnv file) ~path:fullPath ~getModule ) + ) + +let resolveFromStamps ~env ~path ~getModule ~pos = + match path with + | Tip name -> Some (env, name) + | Nested (name, inner) -> ( + (* Log.log("Finding from stamps " ++ name); *) + match findInScope pos name env.file.stamps.modules with + | None -> None + | Some declared -> ( + (* Log.log("found it"); *) + match findInModule ~env declared.item inner with + | None -> None + | Some res -> ( + match res with + | `Local (env, name) -> Some (env, name) + | `Global (moduleName, fullPath) -> ( + match getModule moduleName with + | None -> None + | Some file -> + resolvePath ~env:(fileEnv file) ~path:fullPath ~getModule ) ) ) ) + +open Infix + +let fromCompilerPath ~env path = + match makePath path with + | `Stamp stamp -> `Stamp stamp + | `Path (0, moduleName, path) -> `Global (moduleName, path) + | `GlobalMod name -> `GlobalMod name + | `Path (stamp, _moduleName, path) -> ( + let res = + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some {item = kind} -> findInModule ~env kind path + in + match res with + | None -> `Not_found + | Some (`Local (env, name)) -> `Exported (env, name) + | Some (`Global (moduleName, fullPath)) -> `Global (moduleName, fullPath) ) + +let resolveModuleFromCompilerPath ~env ~getModule path = + match fromCompilerPath ~env path with + | `Global (moduleName, path) -> ( + match getModule moduleName with + | None -> None + | Some file -> ( + let env = fileEnv file in + match resolvePath ~env ~getModule ~path with + | None -> None + | Some (env, name) -> ( + match Hashtbl.find_opt env.exported.modules name with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some declared -> Some (env, Some declared) ) ) ) ) + | `Stamp stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some declared -> Some (env, Some declared) ) + | `GlobalMod moduleName -> ( + match getModule moduleName with + | None -> None + | Some file -> + let env = fileEnv file in + Some (env, None) ) + | `Not_found -> None + | `Exported (env, name) -> ( + match Hashtbl.find_opt env.exported.modules name with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some declared -> Some (env, Some declared) ) ) + +let resolveFromCompilerPath ~env ~getModule path = + match fromCompilerPath ~env path with + | `Global (moduleName, path) -> ( + let res = + match getModule moduleName with + | None -> None + | Some file -> + let env = fileEnv file in + resolvePath ~env ~getModule ~path + in + match res with + | None -> `Not_found + | Some (env, name) -> `Exported (env, name) ) + | `Stamp stamp -> `Stamp stamp + | `GlobalMod _ -> `Not_found + | `Not_found -> `Not_found + | `Exported (env, name) -> `Exported (env, name) + +let declaredForExportedTip ~(stamps : stamps) ~(exported : exported) name tip = + match tip with + | Value -> + Hashtbl.find_opt exported.values name |?> fun stamp -> + Hashtbl.find_opt stamps.values stamp |?>> fun x -> {x with item = ()} + | Field _ | Constructor _ | Type -> + Hashtbl.find_opt exported.types name |?> fun stamp -> + Hashtbl.find_opt stamps.types stamp |?>> fun x -> {x with item = ()} + | Module -> + Hashtbl.find_opt exported.modules name |?> fun stamp -> + Hashtbl.find_opt stamps.modules stamp |?>> fun x -> {x with item = ()} + +let declaredForTip ~stamps stamp tip = + match tip with + | Value -> + Hashtbl.find_opt stamps.values stamp |?>> fun x -> {x with item = ()} + | Field _ | Constructor _ | Type -> + Hashtbl.find_opt stamps.types stamp |?>> fun x -> {x with item = ()} + | Module -> + Hashtbl.find_opt stamps.modules stamp |?>> fun x -> {x with item = ()} + +let getField file stamp name = + match Hashtbl.find_opt file.stamps.types stamp with + | None -> None + | Some {item = {kind}} -> ( + match kind with + | Record fields -> fields |> List.find_opt (fun f -> f.fname.txt = name) + | _ -> None ) + +let getConstructor file stamp name = + match Hashtbl.find_opt file.stamps.types stamp with + | None -> None + | Some {item = {kind}} -> ( + match kind with + | Variant constructors -> ( + match + constructors |> List.find_opt (fun const -> const.cname.txt = name) + with + | None -> None + | Some const -> Some const ) + | _ -> None ) + +let exportedForTip ~env name tip = + match tip with + | Value -> Hashtbl.find_opt env.exported.values name + | Field _ | Constructor _ | Type -> Hashtbl.find_opt env.exported.types name + | Module -> Hashtbl.find_opt env.exported.modules name + +let rec getSourceUri ~env ~getModule path = + match path with + | File (uri, _moduleName) -> uri + | NotVisible -> env.file.uri + | IncludedModule (path, inner) -> ( + Log.log "INCLUDED MODULE"; + match resolveModuleFromCompilerPath ~env ~getModule path with + | None -> + Log.log "NOT FOUND"; + getSourceUri ~env ~getModule inner + | Some (env, _declared) -> env.file.uri ) + | ExportedModule (_, inner) -> getSourceUri ~env ~getModule inner diff --git a/analysis/src/References.ml b/analysis/src/References.ml new file mode 100644 index 000000000..0ccfa5ddc --- /dev/null +++ b/analysis/src/References.ml @@ -0,0 +1,303 @@ +open SharedTypes + +let debugReferences = ref true + +let maybeLog m = if !debugReferences then Log.log ("[ref] " ^ m) + +let checkPos (line, char) + {Location.loc_start = {pos_lnum; pos_bol; pos_cnum}; loc_end} = + if line < pos_lnum || (line = pos_lnum && char < pos_cnum - pos_bol) then + false + else if + line > loc_end.pos_lnum + || (line = loc_end.pos_lnum && char > loc_end.pos_cnum - loc_end.pos_bol) + then false + else true + +let locsForPos ~extra pos = + extra.locations |> List.filter (fun (loc, _l) -> checkPos pos loc) + +let locForPos ~extra pos = + let locs = locsForPos ~extra pos in + match locs with + | [(loc1, Typed (_, LocalReference _)); ((loc3, _) as l3)] when loc1 = loc3 -> + (* JSX and compiler combined: + ~x becomes Props#x + heuristic for: [Props, x], give loc of `x` *) + Some l3 + | [ + (loc1, Typed (_, LocalReference _)); + (loc2, Typed (_, GlobalReference ("Js_OO", Tip "unsafe_downgrade", _))); + ((loc3, _) as l3); + ] + (* For older compiler 9.0 or earlier *) + when loc1 = loc2 && loc2 = loc3 -> + (* JSX and compiler combined: + ~x becomes Js_OO.unsafe_downgrade(Props)#x + heuristic for: [Props, unsafe_downgrade, x], give loc of `x` *) + Some l3 + | [ + ((_, Typed (_, LocalReference (_, Value))) as _l1); + ((_, Typed (_, Definition (_, Value))) as l2); + ] -> + (* JSX on type-annotated labeled (~arg:t): + (~arg:t) becomes Props#arg + Props has the location range of arg:t + arg has the location range of arg + heuristic for: [Props, arg], give loc of `arg` *) + (* Printf.eprintf "l1 %s\nl2 %s\n" + (SharedTypes.locationToString _l1) + (SharedTypes.locationToString l2); *) + Some l2 + | [(loc1, _); ((loc2, _) as l); (loc3, _)] when loc1 = loc2 && loc2 = loc3 -> + (* JSX with at most one child + heuristic for: [makeProps, make, createElement], give the loc of `make` *) + Some l + | [(loc1, _); (loc2, _); ((loc3, _) as l); (loc4, _)] + when loc1 = loc2 && loc2 = loc3 && loc3 = loc4 -> + (* JSX variadic, e.g. {x} {y} + heuristic for: [makeProps , React.null, make, createElementVariadic], give the loc of `make` *) + Some l + | l :: _ -> Some l + | _ -> None + +let definedForLoc ~file ~getModule locKind = + let inner ~file stamp tip = + match tip with + | Constructor name -> ( + match Query.getConstructor file stamp name with + | None -> None + | Some constructor -> Some ([], `Constructor constructor)) + | Field name -> ( + match Query.getField file stamp name with + | None -> None + | Some field -> Some ([], `Field field)) + | _ -> ( + maybeLog + ("Trying for declared " ^ tipToString tip ^ " " ^ string_of_int stamp + ^ " in file " ^ Uri2.toString file.uri); + match Query.declaredForTip ~stamps:file.stamps stamp tip with + | None -> None + | Some declared -> Some (declared.docstring, `Declared)) + in + match locKind with + | NotFound -> None + | LocalReference (stamp, tip) | Definition (stamp, tip) -> + inner ~file stamp tip + | GlobalReference (moduleName, path, tip) -> ( + maybeLog ("Getting global " ^ moduleName); + match getModule moduleName with + | None -> + Log.log ("Cannot get module " ^ moduleName); + None + | Some file -> ( + let env = Query.fileEnv file in + match Query.resolvePath ~env ~path ~getModule with + | None -> + Log.log ("Cannot resolve path " ^ pathToString path); + None + | Some (env, name) -> ( + match Query.exportedForTip ~env name tip with + | None -> + Log.log + ("Exported not found for tip " ^ name ^ " > " ^ tipToString tip); + None + | Some stamp -> ( + maybeLog ("Getting for " ^ string_of_int stamp ^ " in " ^ name); + match inner ~file:env.file stamp tip with + | None -> + Log.log "could not get defined"; + None + | Some res -> + maybeLog "Yes!! got it"; + Some res)))) + +let alternateDeclared ~file ~pathsForModule ~getUri declared tip = + match Hashtbl.find_opt pathsForModule file.moduleName with + | None -> None + | Some paths -> ( + maybeLog ("paths for " ^ file.moduleName); + match paths with + | IntfAndImpl (_, intf, _, impl) -> ( + maybeLog "Have both!!"; + let intfUri = Uri2.fromPath intf in + let implUri = Uri2.fromPath impl in + if intfUri = file.uri then + match getUri implUri with + | Error e -> + Log.log e; + None + | Ok (file, extra) -> ( + match + Query.declaredForExportedTip ~stamps:file.stamps + ~exported:file.contents.exported declared.name.txt tip + with + | None -> None + | Some declared -> Some (file, extra, declared)) + else + match getUri intfUri with + | Error e -> + Log.log e; + None + | Ok (file, extra) -> ( + match + Query.declaredForExportedTip ~stamps:file.stamps + ~exported:file.contents.exported declared.name.txt tip + with + | None -> None + | Some declared -> Some (file, extra, declared))) + | _ -> None) + +let resolveModuleReference ~file ~getModule (declared : moduleKind declared) = + match declared.item with + | Structure _ -> Some (file, Some declared) + | Ident path -> ( + let env = Query.fileEnv file in + match Query.fromCompilerPath ~env path with + | `Not_found -> None + | `Exported (env, name) -> ( + match Hashtbl.find_opt env.exported.modules name with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some md -> + Some (env.file, Some md) + (* Some((env.file.uri, validateLoc(md.name.loc, md.extentLoc))) *))) + | `Global (moduleName, path) -> ( + match getModule moduleName with + | None -> None + | Some file -> ( + let env = Query.fileEnv file in + match Query.resolvePath ~env ~getModule ~path with + | None -> None + | Some (env, name) -> ( + match Hashtbl.find_opt env.exported.modules name with + | None -> None + | Some stamp -> ( + match Hashtbl.find_opt env.file.stamps.modules stamp with + | None -> None + | Some md -> + Some (env.file, Some md) + (* Some((env.file.uri, validateLoc(md.name.loc, md.extentLoc))) *) + )))) + | `Stamp stamp -> ( + match Hashtbl.find_opt file.stamps.modules stamp with + | None -> None + | Some md -> + Some (file, Some md) + (* Some((file.uri, validateLoc(md.name.loc, md.extentLoc))) *)) + | `GlobalMod name -> ( + match getModule name with + | None -> None + | Some file -> + (* maybeLog("Congrats, found a global mod"); *) + Some (file, None)) + | _ -> None) + +let validateLoc (loc : Location.t) (backup : Location.t) = + if loc.loc_start.pos_cnum = -1 then + if backup.loc_start.pos_cnum = -1 then + { + Location.loc_ghost = true; + loc_start = {pos_cnum = 0; pos_lnum = 1; pos_bol = 0; pos_fname = ""}; + loc_end = {pos_cnum = 0; pos_lnum = 1; pos_bol = 0; pos_fname = ""}; + } + else backup + else loc + +let resolveModuleDefinition ~file ~getModule stamp = + match Hashtbl.find_opt file.stamps.modules stamp with + | None -> None + | Some md -> ( + match resolveModuleReference ~file ~getModule md with + | None -> None + | Some (file, declared) -> + let loc = + match declared with + | None -> Utils.topLoc (Uri2.toPath file.uri) + | Some declared -> validateLoc declared.name.loc declared.extentLoc + in + Some (file.uri, loc)) + +let definition ~file ~getModule stamp tip = + match tip with + | Constructor name -> ( + match Query.getConstructor file stamp name with + | None -> None + | Some constructor -> Some (file.uri, constructor.cname.loc)) + | Field name -> ( + match Query.getField file stamp name with + | None -> None + | Some field -> Some (file.uri, field.fname.loc)) + | Module -> resolveModuleDefinition ~file ~getModule stamp + | _ -> ( + match Query.declaredForTip ~stamps:file.stamps stamp tip with + | None -> None + | Some declared -> + let loc = validateLoc declared.name.loc declared.extentLoc in + let env = Query.fileEnv file in + let uri = Query.getSourceUri ~env ~getModule declared.modulePath in + maybeLog ("Inner uri " ^ Uri2.toString uri); + Some (uri, loc)) + +let orLog message v = + match v with + | None -> + maybeLog message; + None + | _ -> v + +let definitionForLoc ~pathsForModule ~file ~getUri ~getModule loc = + match loc with + | Typed (_, Definition (stamp, tip)) -> ( + maybeLog "Trying to find a defintion for a definition"; + match Query.declaredForTip ~stamps:file.stamps stamp tip with + | None -> None + | Some declared -> + maybeLog "Declared"; + if declared.exported then ( + maybeLog ("exported, looking for alternate " ^ file.moduleName); + match alternateDeclared ~pathsForModule ~file ~getUri declared tip with + | None -> None + | Some (file, _extra, declared) -> + let loc = validateLoc declared.name.loc declared.extentLoc in + Some (file.uri, loc)) + else None) + | Explanation _ + | Typed (_, NotFound) + | LModule (NotFound | Definition (_, _)) + | TypeDefinition (_, _, _) + | Constant _ -> + None + | TopLevelModule name -> ( + maybeLog ("Toplevel " ^ name); + let open Infix in + match + Hashtbl.find_opt pathsForModule name + |> orLog "No paths found" |?> getSrc |> orLog "No src found" + with + | None -> None + | Some src -> Some (Uri2.fromPath src, Utils.topLoc src)) + | LModule (LocalReference (stamp, tip)) + | Typed (_, LocalReference (stamp, tip)) -> + maybeLog ("Local defn " ^ tipToString tip); + definition ~file ~getModule stamp tip + | LModule (GlobalReference (moduleName, path, tip)) + | Typed (_, GlobalReference (moduleName, path, tip)) -> ( + maybeLog + ("Global defn " ^ moduleName ^ " " ^ pathToString path ^ " : " + ^ tipToString tip); + match getModule moduleName with + | None -> None + | Some file -> ( + let env = Query.fileEnv file in + match Query.resolvePath ~env ~path ~getModule with + | None -> None + | Some (env, name) -> ( + match Query.exportedForTip ~env name tip with + | None -> None + | Some stamp -> + (* oooh wht do I do if the stamp is inside a pseudo-file? *) + maybeLog ("Got stamp " ^ string_of_int stamp); + definition ~file:env.file ~getModule stamp tip))) diff --git a/analysis/src/RescriptEditorSupport.ml b/analysis/src/RescriptEditorSupport.ml new file mode 100644 index 000000000..0657445fd --- /dev/null +++ b/analysis/src/RescriptEditorSupport.ml @@ -0,0 +1,71 @@ +module StringSet = Set.Make (String) + +let parseArgs args = + match args with + | [] -> assert false + | _ :: args -> + let opts, pos = + args |> List.rev + |> List.fold_left + (fun (set, pos) arg -> + if arg <> "" && arg.[0] = '-' then (set |> StringSet.add arg, pos) + else (set, arg :: pos)) + (StringSet.empty, []) + in + (opts, pos) + +let hasOpt opts name = opts |> StringSet.mem name + +let hasOpts opts names = names |> List.exists (opts |> hasOpt) + +let help = + {| +**Private CLI For rescript-vscode usage only** + +Examples: + rescript-editor-support.exe dump src/MyFile.res src/MyFile2.res + rescript-editor-support.exe complete src/MyFile.res 0 4 currentContent.res + rescript-editor-support.exe hover src/MyFile.res 10 2 + rescript-editor-support.exe definition src/MyFile.res 9 3 + +Options: + dump: debugging. definition and hover for Foo.res and Foo2.res: + + rescript-editor-support.exe dump src/Foo.res src/Foo2.res + + complete: compute autocomplete for Foo.res at line 0 and column 4, + where Foo.res is being edited and the editor content is in file current.res. + + rescript-editor-support.exe complete src/Foo.res 0 4 current.res + + hover: get inferred type for Foo.res at line 10 column 2: + + rescript-editor-support.exe hover src/Foo.res 10 2 + + definition: get inferred type for Foo.res at line 10 column 2: + + rescript-editor-support.exe definition src/Foo.res 10 2 +|} + +let showHelp () = prerr_endline help + +let main () = + match parseArgs (Sys.argv |> Array.to_list) with + | opts, _ when hasOpts opts ["-h"; "--help"] -> showHelp () + | _opts, "dump" :: files -> EditorSupportCommands.dump files + | _opts, ["complete"; path; line; col; currentFile] -> + EditorSupportCommands.complete ~path ~line:(int_of_string line) + ~col:(int_of_string col) ~currentFile + | _opts, ["hover"; path; line; col] -> + EditorSupportCommands.hover ~path ~line:(int_of_string line) + ~col:(int_of_string col) + | _opts, ["definition"; path; line; col] -> + EditorSupportCommands.definition ~path ~line:(int_of_string line) + ~col:(int_of_string col) + | _opts, ["test"; path] -> EditorSupportCommands.test ~path + | _ -> + showHelp (); + exit 1 + +;; +main () diff --git a/analysis/src/Shared.ml b/analysis/src/Shared.ml new file mode 100644 index 000000000..b9fee0ccd --- /dev/null +++ b/analysis/src/Shared.ml @@ -0,0 +1,50 @@ +let tryReadCmt cmt = + if not (Files.exists cmt) then Error ("Cmt file does not exist " ^ cmt) + else + match Cmt_format.read_cmt cmt with + | exception Cmi_format.Error err -> + Error + ( "Failed to load " ^ cmt ^ " as a cmt w/ ocaml version " ^ "406" + ^ ", error: " + ^ + ( Cmi_format.report_error Format.str_formatter err; + Format.flush_str_formatter () ) ) + | exception err -> + Error + ( "Invalid cmt format " ^ cmt + ^ " - probably wrong ocaml version, expected " ^ Config.version ^ " : " + ^ Printexc.to_string err ) + | x -> Ok x + +(** TODO move to the Process_ stuff *) +let rec dig typ = + match typ.Types.desc with + | Types.Tlink inner -> dig inner + | Types.Tsubst inner -> dig inner + | Types.Tpoly (inner, _) -> dig inner + | _ -> typ + +let digConstructor expr = + let expr = dig expr in + match expr.desc with + | Tconstr (path, _args, _memo) -> Some path + | _ -> None + +let declToString ?(recStatus = Types.Trec_not) name t = + PrintType.printDecl ~recStatus name t + +let cacheTypeToString = ref false + +let typeTbl = Hashtbl.create 1 + +let typeToString (t : Types.type_expr) = + match + match !cacheTypeToString with + | true -> Hashtbl.find_opt typeTbl (t.id, t) + | false -> None + with + | None -> + let s = PrintType.printExpr t in + Hashtbl.replace typeTbl (t.id, t) s; + s + | Some s -> s diff --git a/analysis/src/SharedTypes.ml b/analysis/src/SharedTypes.ml new file mode 100644 index 000000000..f9c5ec083 --- /dev/null +++ b/analysis/src/SharedTypes.ml @@ -0,0 +1,238 @@ +type filePath = string + +type paths = + | Impl of filePath * filePath option + | Intf of filePath * filePath + (* .cm(t)i, .mli, .cmt, .rei *) + | IntfAndImpl of filePath * filePath * filePath * filePath + +open Infix + +let showPaths paths = + match paths with + | Impl (cmt, src) -> Printf.sprintf "Impl(%s, %s)" cmt (src |? "nil") + | Intf (cmti, src) -> Printf.sprintf "Intf(%s, %s)" cmti src + | IntfAndImpl (cmti, srci, cmt, src) -> + Printf.sprintf "IntfAndImpl(%s, %s, %s, %s)" cmti srci cmt src + +let getSrc p = + match p with + | Impl (_, s) -> s + | Intf (_, s) | IntfAndImpl (_, s, _, _) -> Some s + +let getCmt ?(interface = true) p = + match p with + | Impl (c, _) | Intf (c, _) -> c + | IntfAndImpl (cint, _, cimpl, _) -> ( + match interface with true -> cint | false -> cimpl) + +type visibilityPath = + | File of Uri2.t * string + | NotVisible + | IncludedModule of Path.t * visibilityPath + | ExportedModule of string * visibilityPath + +type 't declared = { + name : string Location.loc; + extentLoc : Location.t; + scopeLoc : Location.t; + stamp : int; + modulePath : visibilityPath; + exported : bool; + deprecated : string option; + docstring : string list; + item : 't; + (* TODO: maybe add a uri? *) + (* scopeType: scope, *) + (* scopeStart: (int, int), *) +} + +let emptyDeclared name = + { + name = Location.mknoloc name; + extentLoc = Location.none; + scopeLoc = Location.none; + stamp = 0; + modulePath = NotVisible; + exported = false; + deprecated = None; + docstring = []; + item = (); + } + +type field = {stamp : int; fname : string Location.loc; typ : Types.type_expr} + +type constructor = { + stamp : int; + cname : string Location.loc; + args : (Types.type_expr * Location.t) list; + res : Types.type_expr option; +} + +module Type = struct + type kind = + | Abstract of (Path.t * Types.type_expr list) option + | Open + | Tuple of Types.type_expr list + | Record of field list + | Variant of constructor list + + type t = {kind : kind; decl : Types.type_declaration} +end + +(* type scope = + | File + | Switch + | Module + | Let + | LetRec; *) + +type 't namedMap = (string, 't) Hashtbl.t + +type namedStampMap = int namedMap + +type exported = { + types : namedStampMap; + values : namedStampMap; + modules : namedStampMap; + (* constructors: namedStampMap, *) + (* classes: namedStampMap, + classTypes: namedStampMap, *) +} + +let initExported () = + { + types = Hashtbl.create 10; + values = Hashtbl.create 10; + modules = Hashtbl.create 10 (* constructors: Hashtbl.create(10), *); + } + +type moduleItem = + | MValue of Types.type_expr + | MType of Type.t * Types.rec_status + | Module of moduleKind + +and moduleContents = { + docstring : string list; + exported : exported; + topLevel : moduleItem declared list; +} + +and moduleKind = Ident of Path.t | Structure of moduleContents + +type 't stampMap = (int, 't) Hashtbl.t + +type stamps = { + types : Type.t declared stampMap; + values : Types.type_expr declared stampMap; + modules : moduleKind declared stampMap; + constructors : constructor declared stampMap; +} + +let initStamps () = + { + types = Hashtbl.create 10; + values = Hashtbl.create 10; + modules = Hashtbl.create 10; + constructors = Hashtbl.create 10; + } + +type file = { + uri : Uri2.t; + stamps : stamps; + moduleName : string; + contents : moduleContents; +} + +let emptyFile moduleName uri = + { + uri; + stamps = initStamps (); + moduleName; + contents = {docstring = []; exported = initExported (); topLevel = []}; + } + +type tip = Value | Type | Field of string | Constructor of string | Module + +let tipToString tip = + match tip with + | Value -> "Value" + | Type -> "Type" + | Field f -> "Field(" ^ f ^ ")" + | Constructor a -> "Constructor(" ^ a ^ ")" + | Module -> "Module" + +type path = Tip of string | Nested of string * path + +let rec pathToString path = + match path with + | Tip name -> name + | Nested (name, inner) -> name ^ "." ^ pathToString inner + +type locKind = + | LocalReference of int * tip + | GlobalReference of string * path * tip + | NotFound + | Definition of int * tip + +type loc = + | Typed of Types.type_expr * locKind + | Constant of Asttypes.constant + | LModule of locKind + | TopLevelModule of string + | TypeDefinition of string * Types.type_declaration * int + | Explanation of string + +type openTracker = { + path : Path.t; + loc : Location.t; + extent : Location.t; + mutable used : (path * tip * Location.t) list; +} + +type extra = { + internalReferences : (int, Location.t list) Hashtbl.t; + externalReferences : (string, (path * tip * Location.t) list) Hashtbl.t; + mutable locations : (Location.t * loc) list; + (* This is the "open location", like the location... + or maybe the >> location of the open ident maybe *) + (* OPTIMIZE: using a stack to come up with this would cut the computation time of this considerably. *) + opens : (Location.t, openTracker) Hashtbl.t; +} +(** These are the bits of info that we need to make in-app stuff awesome *) + +type full = {extra : extra; file : file} + +let initExtra () = + { + internalReferences = Hashtbl.create 10; + externalReferences = Hashtbl.create 10; + locations = []; + opens = Hashtbl.create 10; + } + +let hashList h = Hashtbl.fold (fun a b c -> (a, b) :: c) h [] + +let locKindToString = function + | LocalReference (_, tip) -> "(LocalReference " ^ tipToString tip ^ ")" + | GlobalReference _ -> "GlobalReference" + | NotFound -> "NotFound" + | Definition (_, tip) -> "(Definition " ^ tipToString tip ^ ")" + +let locToString = function + | Typed (e, locKind) -> + "Typed " ^ Shared.typeToString e ^ " " ^ locKindToString locKind + | Constant _ -> "Constant" + | LModule _ -> "LModule" + | TopLevelModule _ -> "TopLevelModule" + | TypeDefinition _ -> "TypeDefinition" + | Explanation _ -> "Explanation" + +let locationToString ({Location.loc_start; loc_end}, loc) = + let pos1 = Utils.cmtPosToPosition loc_start in + let pos2 = Utils.cmtPosToPosition loc_end in + Printf.sprintf "%d:%d-%d:%d %s" pos1.line pos1.character pos2.line + pos2.character (locToString loc) + +(* for debugging *) +let _ = locationToString \ No newline at end of file diff --git a/analysis/src/State.ml b/analysis/src/State.ml new file mode 100644 index 000000000..a29a032b2 --- /dev/null +++ b/analysis/src/State.ml @@ -0,0 +1,91 @@ +open Infix +open TopTypes + +let isMl path = + Filename.check_suffix path ".ml" || Filename.check_suffix path ".mli" + +let odocToMd text = MarkdownOfOCamldoc.convert text + +let compose fn1 fn2 arg = fn1 arg |> fn2 + +let converter src = + let mlToOutput s = [compose odocToMd Omd.to_markdown s] in + fold src mlToOutput (fun src -> + match isMl src with true -> mlToOutput | false -> fun x -> [x]) + +let newDocsForCmt ~moduleName cmtCache changed cmt src = + let uri = Uri2.fromPath (src |? cmt) in + match Process_406.fileForCmt ~moduleName ~uri cmt (converter src) with + | Error e -> + Log.log e; + None + | Ok file -> + Hashtbl.replace cmtCache cmt (changed, file); + Some file + +let docsForCmt ~moduleName cmt src state = + if Hashtbl.mem state.cmtCache cmt then + let mtime, docs = Hashtbl.find state.cmtCache cmt in + (* TODO: I should really throttle this mtime checking to like every 50 ms or so *) + match Files.getMtime cmt with + | None -> + Log.log + ("\226\154\160\239\184\143 cannot get docs for nonexistant cmt " ^ cmt); + None + | Some changed -> + if changed > mtime then + newDocsForCmt ~moduleName state.cmtCache changed cmt src + else Some docs + else + match Files.getMtime cmt with + | None -> + Log.log + ("\226\154\160\239\184\143 cannot get docs for nonexistant cmt " ^ cmt); + None + | Some changed -> newDocsForCmt ~moduleName state.cmtCache changed cmt src + +open Infix + +let getFullFromCmt ~state ~uri = + let path = Uri2.toPath uri in + match Packages.getPackage uri state with + | Error e -> Error e + | Ok package -> ( + let moduleName = + BuildSystem.namespacedName package.namespace (FindFiles.getName path) + in + match Hashtbl.find_opt package.pathsForModule moduleName with + | Some paths -> ( + let cmt = SharedTypes.getCmt ~interface:(Utils.endsWith path "i") paths in + match Process_406.fullForCmt ~moduleName ~uri cmt (fun x -> [x]) with + | Error e -> Error e + | Ok full -> + Hashtbl.replace package.interModuleDependencies moduleName + (SharedTypes.hashList full.extra.externalReferences |> List.map fst); + Ok (package, full)) + | None -> Error ("can't find module " ^ moduleName)) + +let docsForModule modname state ~package = + if Hashtbl.mem package.pathsForModule modname then ( + let paths = Hashtbl.find package.pathsForModule modname in + (* TODO: do better *) + let cmt = SharedTypes.getCmt paths in + let src = SharedTypes.getSrc paths in + Log.log ("FINDING docs for module " ^ SharedTypes.showPaths paths); + Log.log ("FINDING " ^ cmt ^ " src " ^ (src |? "")); + match docsForCmt ~moduleName:modname cmt src state with + | None -> None + | Some docs -> Some (docs, src)) + else ( + Log.log ("No path for module " ^ modname); + None) + +let fileForUri state uri = + match getFullFromCmt ~state ~uri with + | Error e -> Error e + | Ok (_package, {extra; file}) -> Ok (file, extra) + +let fileForModule state ~package modname = + match docsForModule modname state ~package with + | None -> None + | Some (file, _) -> Some file diff --git a/analysis/src/TopTypes.ml b/analysis/src/TopTypes.ml new file mode 100644 index 000000000..1f200514f --- /dev/null +++ b/analysis/src/TopTypes.ml @@ -0,0 +1,31 @@ +(* Aliases to make the intents clearer *) +type uri = Uri2.t + +type filePath = string + +type moduleName = string + +(* Here are the things that will be different between jbuilder things *) +type package = { + rootPath : filePath; + (* Depend on bsb having already run *) + localModules : moduleName list; + interModuleDependencies : (moduleName, moduleName list) Hashtbl.t; + dependencyModules : moduleName list; + pathsForModule : (moduleName, SharedTypes.paths) Hashtbl.t; + namespace : string option; + opens : string list; +} + +type state = { + packagesByRoot : (string, package) Hashtbl.t; + rootForUri : (uri, string) Hashtbl.t; + cmtCache : (filePath, float * SharedTypes.file) Hashtbl.t; +} + +let empty () = + { + packagesByRoot = Hashtbl.create 1; + rootForUri = Hashtbl.create 30; + cmtCache = Hashtbl.create 30; + } diff --git a/analysis/src/Uri2.ml b/analysis/src/Uri2.ml new file mode 100644 index 000000000..e90558d29 --- /dev/null +++ b/analysis/src/Uri2.ml @@ -0,0 +1,32 @@ +module Uri : sig + type t + + val fromPath : string -> t + + val stripPath : bool ref + + val toPath : t -> string + + val toString : t -> string +end = struct + type t = {path : string; uri : string} + + let stripPath = ref false (* for use in tests *) + + let pathToUri path = + if Sys.os_type = "Unix" then "file://" ^ path + else + "file://" + ^ ( Str.global_replace (Str.regexp_string "\\") "/" path + |> Str.substitute_first (Str.regexp "^\\([a-zA-Z]\\):") (fun text -> + let name = Str.matched_group 1 text in + "/" ^ String.lowercase_ascii name ^ "%3A") ) + + let fromPath path = {path; uri = pathToUri path} + + let toPath {path} = path + + let toString {uri} = if !stripPath then Filename.basename uri else uri +end + +include Uri diff --git a/analysis/src/Utils.ml b/analysis/src/Utils.ml new file mode 100644 index 000000000..d56632619 --- /dev/null +++ b/analysis/src/Utils.ml @@ -0,0 +1,90 @@ +let topLoc fname = + { + Location.loc_start = + {Lexing.pos_fname = fname; pos_lnum = 1; pos_bol = 0; pos_cnum = 0}; + Location.loc_end = + {Lexing.pos_fname = fname; pos_lnum = 1; pos_bol = 0; pos_cnum = 0}; + loc_ghost = false; + } + +(** + * `startsWith(string, prefix)` + * true if the string starts with the prefix + *) +let startsWith s prefix = + if prefix = "" then true + else + let p = String.length prefix in + p <= String.length s && String.sub s 0 p = prefix + +let endsWith s suffix = + if suffix = "" then true + else + let p = String.length suffix in + let l = String.length s in + p <= String.length s && String.sub s (l - p) p = suffix + +let protocolLineColToCmtLoc ~line ~col = (line + 1, col) + +let cmtPosToPosition {Lexing.pos_lnum; pos_cnum; pos_bol} = Protocol.{ + line = pos_lnum - 1; + character = pos_cnum - pos_bol; +} + +let cmtLocToRange {Location.loc_start; loc_end} = Protocol.{ + start = cmtPosToPosition loc_start; + end_ = cmtPosToPosition loc_end; +} + +let locWithinLoc inner outer = + let open Location in + inner.loc_start.pos_cnum >= outer.loc_start.pos_cnum + && inner.loc_end.pos_cnum <= outer.loc_end.pos_cnum + +let endOfLocation loc length = + let open Location in + { + loc with + loc_start = {loc.loc_end with pos_cnum = loc.loc_end.pos_cnum - length}; + } + +let chopLocationEnd loc length = + let open Location in + { + loc with + loc_end = {loc.loc_end with pos_cnum = loc.loc_end.pos_cnum - length}; + } + +(** An optional List.find *) +let rec find fn items = + match items with + | [] -> None + | one :: rest -> ( + match fn one with None -> find fn rest | Some x -> Some x) + +let dedup items = + let m = Hashtbl.create (List.length items) in + items + |> List.filter (fun a -> + if Hashtbl.mem m a then false + else ( + Hashtbl.add m a (); + true)) + +let tupleOfLexing {Lexing.pos_lnum; pos_cnum; pos_bol} = + (pos_lnum - 1, pos_cnum - pos_bol) + +(** + Check if pos is within the location, but be fuzzy about when the location ends. + If it's within 5 lines, go with it. +*) +let locationContainsFuzzy {Location.loc_start; loc_end} (l, c) = + tupleOfLexing loc_start <= (l, c) && tupleOfLexing loc_end >= (l - 5, c) + +let filterMap f = + let rec aux accu = function + | [] -> List.rev accu + | x :: l -> ( + match f x with None -> aux accu l | Some v -> aux (v :: accu) l) + in + aux [] diff --git a/analysis/src/vendor/Json.ml b/analysis/src/vendor/Json.ml new file mode 100644 index 000000000..830857e87 --- /dev/null +++ b/analysis/src/vendor/Json.ml @@ -0,0 +1,490 @@ +(** # Json parser + * + * Works with bucklescript and bsb-native + * + * ## Basics + * + * ``` + * open Json.Infix; /* for the nice infix operators */ + * let raw = {|{"hello": "folks"}|}; + * let who = Json.parse(raw) |> Json.get("hello") |?> Json.string; + * Js.log(who); + * ``` + * + * ## Parse & stringify + * + * @doc parse, stringify + * + * ## Accessing descendents + * + * @doc get, nth, getPath + * + * ## Coercing to types + * + * @doc string, number, array, obj, bool, null + * + * ## The JSON type + * + * @doc t + * + * ## Infix operators for easier working + * + * @doc Infix + *) + +type t = + | String of string + | Number of float + | Array of t list + | Object of (string * t) list + | True + | False + | Null + +let string_of_number f = + let s = string_of_float f in + if s.[String.length s - 1] = '.' then String.sub s 0 (String.length s - 1) + else s + +(** + * This module is provided for easier working with optional values. + *) +module Infix = struct + (** The "force unwrap" operator + * + * If you're sure there's a value, you can force it. + * ``` + * open Json.Infix; + * let x: int = Some(10) |! "Expected this to be present"; + * Js.log(x); + * ``` + * + * But you gotta be sure, otherwise it will throw. + * ```reason;raises + * open Json.Infix; + * let x: int = None |! "This will throw"; + * ``` + *) + let ( |! ) o d = match o with None -> failwith d | Some v -> v + + (** The "upwrap with default" operator + * ``` + * open Json.Infix; + * let x: int = Some(10) |? 4; + * let y: int = None |? 5; + * Js.log2(x, y); + * ``` + *) + let ( |? ) o d = match o with None -> d | Some v -> v + + (** The "transform contents into new optional" operator + * ``` + * open Json.Infix; + * let maybeInc = x => x > 5 ? Some(x + 1) : None; + * let x: option(int) = Some(14) |?> maybeInc; + * let y: option(int) = None |?> maybeInc; + * ``` + *) + let ( |?> ) o fn = match o with None -> None | Some v -> fn v + + (** The "transform contents into new value & then re-wrap" operator + * ``` + * open Json.Infix; + * let inc = x => x + 1; + * let x: option(int) = Some(7) |?>> inc; + * let y: option(int) = None |?>> inc; + * Js.log2(x, y); + * ``` + *) + let ( |?>> ) o fn = match o with None -> None | Some v -> Some (fn v) + + (** "handle the value if present, otherwise here's the default" + * + * It's called fold because that's what people call it :?. It's the same as "transform contents to new value" + "unwrap with default". + * + * ``` + * open Json.Infix; + * let inc = x => x + 1; + * let x: int = fold(Some(4), 10, inc); + * let y: int = fold(None, 2, inc); + * Js.log2(x, y); + * ``` + *) + let fold o d f = match o with None -> d | Some v -> f v +end + +let escape text = + let ln = String.length text in + let buf = Buffer.create ln in + let rec loop i = + if i < ln then ( + ( match text.[i] with + | '\012' -> Buffer.add_string buf "\\f" + | '\\' -> Buffer.add_string buf "\\\\" + | '"' -> Buffer.add_string buf "\\\"" + | '\n' -> Buffer.add_string buf "\\n" + | '\b' -> Buffer.add_string buf "\\b" + | '\r' -> Buffer.add_string buf "\\r" + | '\t' -> Buffer.add_string buf "\\t" + | c -> Buffer.add_char buf c ); + loop (i + 1) ) + in + loop 0; + Buffer.contents buf + +(** + * ``` + * let text = {|{"hello": "folks", "aa": [2, 3, "four"]}|}; + * let result = Json.stringify(Json.parse(text)); + * Js.log(result); + * assert(text == result); + * ``` + *) +let rec stringify t = + match t with + | String value -> "\"" ^ escape value ^ "\"" + | Number num -> string_of_number num + | Array items -> "[" ^ String.concat ", " (List.map stringify items) ^ "]" + | Object items -> + "{" + ^ String.concat ", " + (List.map + (fun (k, v) -> "\"" ^ String.escaped k ^ "\": " ^ stringify v) + items) + ^ "}" + | True -> "true" + | False -> "false" + | Null -> "null" + +let white n = + let buffer = Buffer.create n in + for i = 0 to n - 1 do + Buffer.add_char buffer ' ' + done; + Buffer.contents buffer + +let rec stringifyPretty ?(indent = 0) t = + match t with + | String value -> "\"" ^ escape value ^ "\"" + | Number num -> string_of_number num + | Array [] -> "[]" + | Array [ (String _ as contents) ] -> "[" ^ stringifyPretty contents ^ "]" + | Array items -> + "[\n" ^ white indent + ^ String.concat + (",\n" ^ white indent) + (List.map (stringifyPretty ~indent:(indent + 2)) items) + ^ "\n" + ^ white (indent - 2) + ^ "]" + | Object [] -> "{}" + | Object items -> + "{\n" ^ white indent + ^ String.concat + (",\n" ^ white indent) + (List.map + (fun (k, v) -> + "\"" ^ String.escaped k ^ "\": " + ^ stringifyPretty ~indent:(indent + 2) v) + items) + ^ "\n" + ^ white (indent - 2) + ^ "}" + | True -> "true" + | False -> "false" + | Null -> "null" + +let unwrap message t = match t with Some v -> v | None -> failwith message + +module Parser = struct + let split_by ?(keep_empty = false) is_delim str = + let len = String.length str in + let rec loop acc last_pos pos = + if pos = -1 then + if last_pos = 0 && not keep_empty then acc + else String.sub str 0 last_pos :: acc + else if is_delim str.[pos] then + let new_len = last_pos - pos - 1 in + if new_len <> 0 || keep_empty then + let v = String.sub str (pos + 1) new_len in + loop (v :: acc) pos (pos - 1) + else loop acc pos (pos - 1) + else loop acc last_pos (pos - 1) + in + loop [] len (len - 1) + + let fail text pos message = + let pre = String.sub text 0 pos in + let lines = split_by (fun c -> c = '\n') pre in + let count = List.length lines in + let last = + match count > 0 with true -> List.nth lines (count - 1) | false -> "" + in + let col = String.length last + 1 in + let line = List.length lines in + let string = + Printf.sprintf "Error \"%s\" at %d:%d -> %s\n" message line col last + in + failwith string + + let rec skipToNewline text pos = + if pos >= String.length text then pos + else if text.[pos] = '\n' then pos + 1 + else skipToNewline text (pos + 1) + + let stringTail text = + let len = String.length text in + if len > 1 then String.sub text 1 (len - 1) else "" + + let rec skipToCloseMultilineComment text pos = + if pos + 1 >= String.length text then failwith "Unterminated comment" + else if text.[pos] = '*' && text.[pos + 1] = '/' then pos + 2 + else skipToCloseMultilineComment text (pos + 1) + + let rec skipWhite text pos = + if + pos < String.length text + && ( text.[pos] = ' ' + || text.[pos] = '\t' + || text.[pos] = '\n' + || text.[pos] = '\r' ) + then skipWhite text (pos + 1) + else pos + + (* from https://stackoverflow.com/a/42431362 *) + let utf8encode s = + let prefs = [| 0; 192; 224 |] in + let s1 n = String.make 1 (Char.chr n) in + let rec ienc k sofar resid = + let bct = if k = 0 then 7 else 6 - k in + if resid < 1 lsl bct then s1 (prefs.(k) + resid) ^ sofar + else ienc (k + 1) (s1 (128 + (resid mod 64)) ^ sofar) (resid / 64) + in + ienc 0 "" (int_of_string ("0x" ^ s)) + + let parseString text pos = + (* let i = ref(pos); *) + let buffer = Buffer.create (String.length text) in + let ln = String.length text in + let rec loop i = + match i >= ln with + | true -> fail text i "Unterminated string" + | false -> ( + match text.[i] with + | '"' -> i + 1 + | '\\' -> ( + match i + 1 >= ln with + | true -> fail text i "Unterminated string" + | false -> ( + match text.[i + 1] with + | '/' -> + Buffer.add_char buffer '/'; + loop (i + 2) + | 'f' -> + Buffer.add_char buffer '\012'; + loop (i + 2) + | 'u' when i + 6 < ln -> + Buffer.add_string buffer + (utf8encode (String.sub text (i + 2) 4)); + loop (i + 7) + | _ -> + Buffer.add_string buffer + (Scanf.unescaped (String.sub text i 2)); + loop (i + 2) ) ) + | c -> + Buffer.add_char buffer c; + loop (i + 1) ) + in + let final = loop pos in + (Buffer.contents buffer, final) + + let parseDigits text pos = + let len = String.length text in + let rec loop i = + if i >= len then i + else match text.[i] with '0' .. '9' -> loop (i + 1) | _ -> i + in + loop (pos + 1) + + let parseWithDecimal text pos = + let pos = parseDigits text pos in + if pos < String.length text && text.[pos] = '.' then + let pos = parseDigits text (pos + 1) in + pos + else pos + + let parseNumber text pos = + let pos = parseWithDecimal text pos in + let ln = String.length text in + if pos < ln - 1 && (text.[pos] = 'E' || text.[pos] = 'e') then + let pos = + match text.[pos + 1] with '-' | '+' -> pos + 2 | _ -> pos + 1 + in + parseDigits text pos + else pos + + let parseNegativeNumber text pos = + let final = + if text.[pos] = '-' then parseNumber text (pos + 1) + else parseNumber text pos + in + (Number (float_of_string (String.sub text pos (final - pos))), final) + + let expect char text pos message = + if text.[pos] <> char then fail text pos ("Expected: " ^ message) + else pos + 1 + + let parseComment : 'a. string -> int -> (string -> int -> 'a) -> 'a = + fun text pos next -> + if text.[pos] <> '/' then + if text.[pos] = '*' then + next text (skipToCloseMultilineComment text (pos + 1)) + else failwith "Invalid syntax" + else next text (skipToNewline text (pos + 1)) + + let maybeSkipComment text pos = + if pos < String.length text && text.[pos] = '/' then + if pos + 1 < String.length text && text.[pos + 1] = '/' then + skipToNewline text (pos + 1) + else if pos + 1 < String.length text && text.[pos + 1] = '*' then + skipToCloseMultilineComment text (pos + 1) + else fail text pos "Invalid synatx" + else pos + + let rec skip text pos = + if pos = String.length text then pos + else + let n = skipWhite text pos |> maybeSkipComment text in + if n > pos then skip text n else n + + let rec parse text pos = + if pos >= String.length text then + fail text pos "Reached end of file without being done parsing" + else + match text.[pos] with + | '/' -> parseComment text (pos + 1) parse + | '[' -> parseArray text (pos + 1) + | '{' -> parseObject text (pos + 1) + | 'n' -> + if String.sub text pos 4 = "null" then (Null, pos + 4) + else fail text pos "unexpected character" + | 't' -> + if String.sub text pos 4 = "true" then (True, pos + 4) + else fail text pos "unexpected character" + | 'f' -> + if String.sub text pos 5 = "false" then (False, pos + 5) + else fail text pos "unexpected character" + | '\n' | '\t' | ' ' | '\r' -> parse text (skipWhite text pos) + | '"' -> + let s, pos = parseString text (pos + 1) in + (String s, pos) + | '-' | '0' .. '9' -> parseNegativeNumber text pos + | _ -> fail text pos "unexpected character" + + and parseArrayValue text pos = + let pos = skip text pos in + let value, pos = parse text pos in + let pos = skip text pos in + match text.[pos] with + | ',' -> + let pos = skip text (pos + 1) in + if text.[pos] = ']' then ([ value ], pos + 1) + else + let rest, pos = parseArrayValue text pos in + (value :: rest, pos) + | ']' -> ([ value ], pos + 1) + | _ -> fail text pos "unexpected character" + + and parseArray text pos = + let pos = skip text pos in + match text.[pos] with + | ']' -> (Array [], pos + 1) + | _ -> + let items, pos = parseArrayValue text pos in + (Array items, pos) + + and parseObjectValue text pos = + let pos = skip text pos in + if text.[pos] <> '"' then fail text pos "Expected string" + else + let key, pos = parseString text (pos + 1) in + let pos = skip text pos in + let pos = expect ':' text pos "Colon" in + let value, pos = parse text pos in + let pos = skip text pos in + match text.[pos] with + | ',' -> + let pos = skip text (pos + 1) in + if text.[pos] = '}' then ([ (key, value) ], pos + 1) + else + let rest, pos = parseObjectValue text pos in + ((key, value) :: rest, pos) + | '}' -> ([ (key, value) ], pos + 1) + | _ -> + let rest, pos = parseObjectValue text pos in + ((key, value) :: rest, pos) + + and parseObject text pos = + let pos = skip text pos in + if text.[pos] = '}' then (Object [], pos + 1) + else + let pairs, pos = parseObjectValue text pos in + (Object pairs, pos) +end +[@@nodoc] + +(** Turns some text into a json object. throws on failure *) +let parse text = + let item, pos = Parser.parse text 0 in + let pos = Parser.skip text pos in + if pos < String.length text then + failwith + ( "Extra data after parse finished: " + ^ String.sub text pos (String.length text - pos) ) + else item + +(* Accessor helpers *) +let bind v fn = match v with None -> None | Some v -> fn v + +(** If `t` is an object, get the value associated with the given string key *) +let get key t = + match t with + | Object items -> ( try Some (List.assoc key items) with Not_found -> None ) + | _ -> None + +(** If `t` is an array, get the value associated with the given index *) +let nth n t = + match t with + | Array items -> + if n < List.length items then Some (List.nth items n) else None + | _ -> None + +let string t = match t with String s -> Some s | _ -> None + +let number t = match t with Number s -> Some s | _ -> None + +let array t = match t with Array s -> Some s | _ -> None + +let obj t = match t with Object s -> Some s | _ -> None + +let bool t = match t with True -> Some true | False -> Some false | _ -> None + +let null t = match t with Null -> Some () | _ -> None + +let rec parsePath keyList t = + match keyList with + | [] -> Some t + | head :: rest -> ( + match get head t with None -> None | Some value -> parsePath rest value ) + +(** Get a deeply nested value from an object `t`. + * ``` + * open Json.Infix; + * let json = Json.parse({|{"a": {"b": {"c": 2}}}|}); + * let num = Json.getPath("a.b.c", json) |?> Json.number; + * assert(num == Some(2.)) + * ``` + *) +let getPath path t = + let keys = Parser.split_by (fun c -> c = '.') path in + parsePath keys t diff --git a/analysis/src/vendor/odoc_parser/LICENSE.md b/analysis/src/vendor/odoc_parser/LICENSE.md new file mode 100644 index 000000000..f6adb3e6a --- /dev/null +++ b/analysis/src/vendor/odoc_parser/LICENSE.md @@ -0,0 +1,5 @@ +Copyright (c) 2016 Thomas Refis trefis@janestreet.com Copyright (c) 2014, 2015 Leo White leo@lpw25.net Copyright (c) 2015 David Sheets sheets@alum.mit.edu + +Permission to use, copy, modify, and distribute this software for any purpose with or without fee is hereby granted, provided that the above copyright notice and this permission notice appear in all copies. + +THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. \ No newline at end of file diff --git a/analysis/src/vendor/odoc_parser/Readme.md b/analysis/src/vendor/odoc_parser/Readme.md new file mode 100644 index 000000000..b98fbbf3c --- /dev/null +++ b/analysis/src/vendor/odoc_parser/Readme.md @@ -0,0 +1,3 @@ +The source code in this directory was taken, with some modifications from the `odoc` project (https://github.com/ocaml/odoc/tree/master/src/parser). + +It is under the ISC license. \ No newline at end of file diff --git a/analysis/src/vendor/odoc_parser/ast.ml b/analysis/src/vendor/odoc_parser/ast.ml new file mode 100644 index 000000000..bcb8775bb --- /dev/null +++ b/analysis/src/vendor/odoc_parser/ast.ml @@ -0,0 +1,63 @@ +module Path = Paths.Path +module Reference = Paths.Reference +module Identifier = Paths.Identifier +module Comment = Comment + +type 'a with_location = 'a Location_.with_location + + + +type reference_kind = [ `Simple | `With_text ] + +type inline_element = [ + | `Space + | `Word of string + | `Code_span of string + | `Styled of Comment.style * (inline_element with_location) list + | `Reference of + reference_kind * Reference.any * (inline_element with_location) list + | `Link of string * (inline_element with_location) list +] + +type nestable_block_element = [ + | `Paragraph of (inline_element with_location) list + | `Code_block of string + | `Example of string * string + | `Doc of string + | `Verbatim of string + | `Modules of Reference.module_ list + | `List of + [ `Unordered | `Ordered ] * + ((nestable_block_element with_location) list) list +] + +type tag = [ + | `Author of string + | `Deprecated of (nestable_block_element with_location) list + | `Param of string * (nestable_block_element with_location) list + | `Raise of string * (nestable_block_element with_location) list + | `Return of (nestable_block_element with_location) list + | `See of + [ `Url | `File | `Document ] * + string * + (nestable_block_element with_location) list + | `Since of string + | `Before of string * (nestable_block_element with_location) list + | `Version of string + | `Canonical of Path.module_ * Reference.module_ + | `Inline + | `Open + | `Closed +] + +type block_element = [ + | nestable_block_element + | `Heading of int * string option * (inline_element with_location) list + | `Tag of tag +] + +type docs = (block_element with_location) list + + + +type sections_allowed = [ `All | `No_titles | `None ] diff --git a/analysis/src/vendor/odoc_parser/comment.ml b/analysis/src/vendor/odoc_parser/comment.ml new file mode 100644 index 000000000..71f20bdf6 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/comment.ml @@ -0,0 +1,89 @@ +module Path = Paths.Path +module Reference = Paths.Reference +module Identifier = Paths.Identifier + +type 'a with_location = 'a Location_.with_location + + + +type style = [ + | `Bold + | `Italic + | `Emphasis + | `Superscript + | `Subscript +] + +type leaf_inline_element = [ + | `Space + | `Word of string + | `Code_span of string +] + +type non_link_inline_element = [ + | leaf_inline_element + | `Styled of style * (non_link_inline_element with_location) list +] + +(* The cross-referencer stores section heading text, and sometimes pastes it + into link contents. This type alias is provided for use by the + cross-referencer. *) +type link_content = (non_link_inline_element with_location) list + +type inline_element = [ + | leaf_inline_element + | `Styled of style * (inline_element with_location) list + | `Reference of Reference.any * link_content + | `Link of string * link_content +] + +type nestable_block_element = [ + | `Paragraph of (inline_element with_location) list + | `Code_block of string + | `Example of string * string + | `Doc of string + | `Verbatim of string + | `Modules of Reference.module_ list + | `List of + [ `Unordered | `Ordered ] * + ((nestable_block_element with_location) list) list +] + +type tag = [ + | `Author of string + | `Deprecated of (nestable_block_element with_location) list + | `Param of string * (nestable_block_element with_location) list + | `Raise of string * (nestable_block_element with_location) list + | `Return of (nestable_block_element with_location) list + | `See of + [ `Url | `File | `Document ] * + string * + (nestable_block_element with_location) list + | `Since of string + | `Before of string * (nestable_block_element with_location) list + | `Version of string + | `Canonical of Path.module_ * Reference.module_ + | `Inline + | `Open + | `Closed +] + +type heading_level = [ + | `Title + | `Section + | `Subsection + | `Subsubsection +] + +type block_element = [ + | nestable_block_element + | `Heading of heading_level * Identifier.label * link_content + | `Tag of tag +] + +type docs = (block_element with_location) list + +type docs_or_stop = [ + | `Docs of docs + | `Stop +] diff --git a/analysis/src/vendor/odoc_parser/error.ml b/analysis/src/vendor/odoc_parser/error.ml new file mode 100644 index 000000000..a58a5b2dd --- /dev/null +++ b/analysis/src/vendor/odoc_parser/error.ml @@ -0,0 +1,70 @@ +type full_location_payload = { + location : Location_.span; + message : string; +} + +type filename_only_payload = { + file : string; + message : string; +} + +type t = [ + | `With_full_location of full_location_payload + | `With_filename_only of filename_only_payload +] + +type 'a with_warnings = { + result : 'a; + warnings : t list; +} + +let make : string -> Location_.span -> t = fun message location -> + `With_full_location {location; message} + +let filename_only : string -> string -> t = fun message file -> + `With_filename_only {file; message} + +let format = fun format -> + (Printf.ksprintf make) format + +let to_string : t -> string = function + | `With_full_location {location; message} -> + let location_string = + if location.start.line = location.end_.line then + Printf.sprintf "line %i, characters %i-%i" + location.start.line + location.start.column + location.end_.column + else + Printf.sprintf "line %i, character %i to line %i, character %i" + location.start.line + location.start.column + location.end_.line + location.end_.column + in + Printf.sprintf "File \"%s\", %s:\n%s" location.file location_string message + + | `With_filename_only {file; message} -> + Printf.sprintf "File \"%s\":\n%s" file message + +exception Conveyed_by_exception of t + +type ('a, 'b) result = | Ok of 'a | Error of 'b + +let raise_exception : t -> _ = fun error -> + raise (Conveyed_by_exception error) + +let to_exception : ('a, t) result -> 'a = function + | Ok v -> v + | Error error -> raise_exception error + +let catch : (unit -> 'a) -> ('a, t) result = fun f -> + try Ok (f ()) + with Conveyed_by_exception error -> Error error + +(* TODO This is a temporary measure until odoc is ported to handle warnings + throughout. *) +let shed_warnings : 'a with_warnings -> 'a = fun with_warnings -> + with_warnings.warnings + |> List.iter (fun warning -> warning |> to_string |> prerr_endline); + with_warnings.result diff --git a/analysis/src/vendor/odoc_parser/helpers.ml b/analysis/src/vendor/odoc_parser/helpers.ml new file mode 100644 index 000000000..865a03f96 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/helpers.ml @@ -0,0 +1,381 @@ +(* This file contains mostly functions from the former [model/attrs.ml]. It + should be reorganized in the future. *) + +module Paths = Paths + +(* This should be merged into [Parse_error] above. *) +exception InvalidReference of string + +let read_qualifier : + string option -> + [< Paths.Reference.kind ] Paths.Reference.tag + = function + | None -> TUnknown + | Some "module" -> TModule + | Some "module-type" -> TModuleType + | Some "type" -> TType + | Some ("const" | "constructor") -> TConstructor + | Some ("recfield" | "field") -> TField + | Some "extension" -> TExtension + | Some ("exn" | "exception") -> TException + | Some ("val" | "value") -> TValue + | Some "class" -> TClass + | Some ("classtype" | "class-type") -> TClassType + | Some "method" -> TMethod + | Some "instance-variable" -> TInstanceVariable + | Some ("section" | "label") -> TLabel + | Some ("page") -> TPage + | Some s -> raise (InvalidReference ("unknown qualifier `" ^ s ^ "'")) + +let read_longident s = + let open Paths.Reference in + let split_qualifier str = + match String.rindex str '-' with + | exception Not_found -> (None, str) + | idx -> + let qualifier = String.sub str 0 idx in + let name = String.sub str (idx + 1) (String.length str - idx - 1) in + (Some qualifier, name) + in + let rec loop_datatype : string -> int -> datatype option = + fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + begin match read_qualifier kind with + | TUnknown | TType as tag -> Some (Root(name, tag)) + | _ -> None + end + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(label_parent_of_parent parent, name)) + end + | TType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Type(parent, name)) + end + | _ -> None + and loop_signature : string -> int -> signature option = fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + begin match read_qualifier kind with + | TUnknown | TModule | TModuleType as tag -> Some (Root(name, tag)) + | _ -> None + end + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(label_parent_of_parent parent, name)) + end + | TModule -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Module(parent, name)) + end + | TModuleType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ModuleType(parent, name)) + end + | _ -> None + and loop_class_signature : string -> int -> class_signature option = + fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + begin match read_qualifier kind with + | TUnknown | TClass | TClassType as tag -> Some (Root(name, tag)) + | _ -> None + end + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(label_parent_of_parent parent, name)) + end + | TClass -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Class(parent, name)) + end + | TClassType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ClassType(parent, name)) + end + | _ -> None + and loop_label_parent : string -> int -> label_parent option = + fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + begin match read_qualifier kind with + | TUnknown | TModule | TModuleType + | TType | TClass | TClassType | TPage as tag -> + Some (Root(name, tag)) + | _ -> None + end + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_label_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(parent, name)) + end + | TModule -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Module(parent, name)) + end + | TModuleType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ModuleType(parent, name)) + end + | TType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Type(parent, name)) + end + | TClass -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Class(parent, name)) + end + | TClassType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ClassType(parent, name)) + end + | _ -> None + and loop_parent : string -> int -> parent option = + fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + begin match read_qualifier kind with + | TUnknown + | TModule | TModuleType | TType | TClass | TClassType as tag -> + Some (Root(name, tag)) + | _ -> None + end + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(label_parent_of_parent parent, name)) + end + | TModule -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Module(parent, name)) + end + | TModuleType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ModuleType(parent, name)) + end + | TType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Type(parent, name)) + end + | TClass -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Class(parent, name)) + end + | TClassType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ClassType(parent, name)) + end + | _ -> None + in + let loop : 'k. string -> int -> kind t option = + fun s pos -> + match String.rindex_from s pos '.' with + | exception Not_found -> + let maybe_qualified = String.sub s 0 (pos + 1) in + if String.length maybe_qualified = 0 then + None + else + let (kind, name) = split_qualifier maybe_qualified in + Some (Root (name, read_qualifier kind)) + | idx -> + let maybe_qualified = String.sub s (idx + 1) (pos - idx) in + if String.length maybe_qualified = 0 then + None + else + let (qualifier, name) = split_qualifier maybe_qualified in + match read_qualifier qualifier with + | TUnknown -> begin + match loop_label_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(parent, name)) + end + | TModule -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Module(parent, name)) + end + | TModuleType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ModuleType(parent, name)) + end + | TType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Type(parent, name)) + end + | TConstructor -> begin + match loop_datatype s (idx - 1) with + | None -> None + | Some parent -> Some (Constructor(parent, name)) + end + | TField -> begin + match loop_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Field(parent, name)) + end + | TExtension -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Extension(parent, name)) + end + | TException -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Exception(parent, name)) + end + | TValue -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Value(parent, name)) + end + | TClass -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Class(parent, name)) + end + | TClassType -> begin + match loop_signature s (idx - 1) with + | None -> None + | Some parent -> Some (ClassType(parent, name)) + end + | TMethod -> begin + match loop_class_signature s (idx - 1) with + | None -> None + | Some parent -> Some (Method(parent, name)) + end + | TInstanceVariable -> begin + match loop_class_signature s (idx - 1) with + | None -> None + | Some parent -> Some (InstanceVariable(parent, name)) + end + | TLabel -> begin + match loop_label_parent s (idx - 1) with + | None -> None + | Some parent -> Some (Label(parent, name)) + end + | TPage -> None + in + match loop s (String.length s - 1) with + | None -> raise (InvalidReference s) + | Some r -> r + +let read_reference (s : string) : Paths.Reference.any = + let s = + match String.rindex s ':' with + | index -> String.sub s (index + 1) (String.length s - (index + 1)) + | exception Not_found -> s + in + read_longident s + +let read_path_longident s = + let open Paths.Path in + let rec loop : 'k. string -> int -> ([< kind > `Module ] as 'k) t option = + fun s pos -> + try + let idx = String.rindex_from s pos '.' in + let name = String.sub s (idx + 1) (pos - idx) in + if String.length name = 0 then None + else + match loop s (idx - 1) with + | None -> None + | Some parent -> Some (Dot(parent, name)) + with Not_found -> + let name = String.sub s 0 (pos + 1) in + if String.length name = 0 then None + else Some (Root name) + in + match loop s (String.length s - 1) with + | None -> raise (InvalidReference s) + | Some r -> r + +exception Expected_reference_to_a_module_but_got of string + +let read_mod_longident lid : Paths.Reference.module_ = + let open Paths.Reference in + match read_longident lid with + | Root (_, (TUnknown | TModule)) + | Dot (_, _) + | Module (_,_) as r -> r + | _ -> + (* FIXME: propagate location *) + raise (Expected_reference_to_a_module_but_got lid) diff --git a/analysis/src/vendor/odoc_parser/lang.ml b/analysis/src/vendor/odoc_parser/lang.ml new file mode 100644 index 000000000..83a285f48 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/lang.ml @@ -0,0 +1,442 @@ +(* + * Copyright (c) 2014 Leo White + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +open Paths + +(** {3 Modules} *) + +module rec Module : sig + + type expansion = + | AlreadyASig + | Signature of Signature.t + | Functor of FunctorArgument.t option list * Signature.t + + type decl = + | Alias of Path.module_ + | ModuleType of ModuleType.expr + + type t = + { id: Identifier.module_; + doc: Comment.docs; + type_: decl; + canonical : (Path.module_ * Reference.module_) option; + hidden : bool; + display_type : decl option; + expansion: expansion option; + } + + module Equation : sig + + type t = decl + + end + +end = Module + +and FunctorArgument : sig + type t = { + id : Identifier.module_; + expr : ModuleType.expr; + expansion: Module.expansion option; + } +end = FunctorArgument + +(** {3 Modules Types} *) + +and ModuleType : sig + + type substitution = + | ModuleEq of Fragment.module_ * Module.Equation.t + | TypeEq of Fragment.type_ * TypeDecl.Equation.t + | ModuleSubst of Fragment.module_ * Path.module_ + | TypeSubst of Fragment.type_ * TypeDecl.Equation.t + + type expr = + | Path of Path.module_type + | Signature of Signature.t + | Functor of FunctorArgument.t option * expr + | With of expr * substitution list + | TypeOf of Module.decl + + type t = + { id: Identifier.module_type; + doc: Comment.docs; + expr: expr option; + expansion: Module.expansion option; + } + +end = ModuleType + +(** {3 Signatures} *) + +and Signature : sig + + type item = + | Module of Module.t + | ModuleType of ModuleType.t + | Type of TypeDecl.t + | TypExt of Extension.t + | Exception of Exception.t + | Value of Value.t + | External of External.t + | Class of Class.t + | ClassType of ClassType.t + | Include of Include.t + | Comment of Comment.docs_or_stop + + type t = item list + +end = Signature + +(** {3 Includes} *) + +and Include : sig + type expansion = { + resolved: bool; + content: Signature.t; + } + + type t = + { parent: Identifier.signature; + doc: Comment.docs; + decl: Module.decl; + expansion: expansion; } + +end = Include + +(** {3 Type Declarations} *) + +and TypeDecl : sig + + module Field : sig + + type t = + { id: Identifier.field; + doc: Comment.docs; + mutable_ : bool; + type_: TypeExpr.t; } + + end + + module Constructor : sig + type argument = + | Tuple of TypeExpr.t list + | Record of Field.t list + + type t = + { id: Identifier.constructor; + doc: Comment.docs; + args: argument; + res: TypeExpr.t option; } + + end + + + module Representation : sig + + type t = + | Variant of Constructor.t list + | Record of Field.t list + | Extensible + + end + + type variance = + | Pos + | Neg + + type param_desc = + | Any + | Var of string + + type param = param_desc * variance option + + module Equation : sig + + type t = + { params: param list; + private_: bool; + manifest: TypeExpr.t option; + constraints: (TypeExpr.t * TypeExpr.t) list; } + + end + + type t = + { id: Identifier.type_; + doc: Comment.docs; + equation: Equation.t; + representation: Representation.t option; } + +end = TypeDecl + +(** {3 Type extensions} *) + +and Extension : sig + + module Constructor : sig + + type t = + { id: Identifier.extension; + doc: Comment.docs; + args: TypeDecl.Constructor.argument; + res: TypeExpr.t option; } + + end + + type t = + { type_path: Path.type_; + doc: Comment.docs; + type_params: TypeDecl.param list; + private_: bool; + constructors: Constructor.t list; } + +end = Extension + +(** {3 Exception} *) +and Exception : sig + + type t = + { id: Identifier.exception_; + doc: Comment.docs; + args: TypeDecl.Constructor.argument; + res: TypeExpr.t option; } + +end = Exception + + +(** {3 Values} *) + +and Value : sig + + type t = + { id: Identifier.value; + doc: Comment.docs; + type_: TypeExpr.t; } + +end = Value + +(** {3 External values} *) + +and External : sig + + type t = + { id: Identifier.value; + doc: Comment.docs; + type_: TypeExpr.t; + primitives: string list; } + +end = External + +(** {3 Classes} *) + +and Class : sig + + type decl = + | ClassType of ClassType.expr + | Arrow of TypeExpr.label option * TypeExpr.t * decl + + type t = + { id: Identifier.class_; + doc: Comment.docs; + virtual_: bool; + params: TypeDecl.param list; + type_: decl; + expansion: ClassSignature.t option; } + +end = Class + +(** {3 Class Types} *) + +and ClassType : sig + + type expr = + | Constr of Path.class_type * TypeExpr.t list + | Signature of ClassSignature.t + + type t = + { id: Identifier.class_type; + doc: Comment.docs; + virtual_: bool; + params: TypeDecl.param list; + expr: expr; + expansion: ClassSignature.t option; } + +end = ClassType + +(** {3 Class Signatures} *) + +and ClassSignature : sig + + type item = + | Method of Method.t + | InstanceVariable of InstanceVariable.t + | Constraint of TypeExpr.t * TypeExpr.t + | Inherit of ClassType.expr + | Comment of Comment.docs_or_stop + + type t = + { self: TypeExpr.t option; + items: item list; } + +end = ClassSignature + +(** {3 Methods} *) + +and Method : sig + + type t = + { id: Identifier.method_; + doc: Comment.docs; + private_: bool; + virtual_: bool; + type_: TypeExpr.t; } + +end = Method + +(** {3 Instance variables} *) + +and InstanceVariable : sig + + type t = + { id: Identifier.instance_variable; + doc: Comment.docs; + mutable_: bool; + virtual_: bool; + type_: TypeExpr.t; } + +end = InstanceVariable + +(** {3 Type expressions} *) + +and TypeExpr : sig + + module Variant : sig + + type kind = + | Fixed + | Closed of string list + | Open + + type element = + | Type of TypeExpr.t + | Constructor of string * bool * TypeExpr.t list + + type t = + { kind: kind; + elements: element list;} + + end + + module Object : sig + + type method_ = + { name: string; + type_: TypeExpr.t; } + + type field = + | Method of method_ + | Inherit of TypeExpr.t + + type t = + { fields: field list; + open_ : bool; } + + end + + module Package : sig + + type substitution = Fragment.type_ * TypeExpr.t + + type t = + { path: Path.module_type; + substitutions: substitution list; } + + end + + type label = + | Label of string + | Optional of string + + type t = + | Var of string + | Any + | Alias of t * string + | Arrow of label option * t * t + | Tuple of t list + | Constr of Path.type_ * t list + | Variant of TypeExpr.Variant.t + | Object of TypeExpr.Object.t + | Class of Path.class_type * t list + | Poly of string list * t + | Package of TypeExpr.Package.t + +end = TypeExpr + +(** {3 Compilation units} *) + +module rec Compilation_unit : sig + + module Import : sig + + type t = + | Unresolved of string * Digest.t option + | Resolved of Root.t + + end + + module Source : sig + + type t = + { file: string; + build_dir: string; + digest: Digest.t; } + + end + + module Packed : sig + + type item = + { id: Identifier.module_; + path: Path.module_; } + + type t = item list + + end + + type content = + | Module of Signature.t + | Pack of Packed.t + + type t = + { id: Identifier.module_; + doc: Comment.docs; + digest: Digest.t; + imports: Import.t list; + source: Source.t option; + interface: bool; + hidden: bool; + content: content; + expansion: Signature.t option; } + +end = Compilation_unit + +module rec Page : sig + type t = + { name: Identifier.page; + content: Comment.docs; + digest: Digest.t; } +end = Page diff --git a/analysis/src/vendor/odoc_parser/location_.ml b/analysis/src/vendor/odoc_parser/location_.ml new file mode 100644 index 000000000..3de7f26b3 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/location_.ml @@ -0,0 +1,53 @@ +type point = { + line : int; + column : int; +} + +type span = { + file : string; + start : point; + end_ : point; +} + +type +'a with_location = { + location : span; + value : 'a; +} + +let at : span -> 'a -> 'a with_location = fun location value -> + {location; value} + +let location : 'a with_location -> span = fun {location; _} -> + location + +let value : 'a with_location -> 'a = fun {value; _} -> + value + +let map : ('a -> 'b) -> 'a with_location -> 'b with_location = + fun f annotated -> + {annotated with value = f annotated.value} + +let same : _ with_location -> 'b -> 'b with_location = fun annotated value -> + {annotated with value} + +let span : span list -> span = fun spans -> + match spans with + | [] -> + { + file = "_none_"; + start = { + line = 1; + column = 0; + }; + end_ = { + line = 1; + column = 0; + }; + } + | first::spans -> + let last = List.fold_left (fun _ span -> span) first spans in + { + file = first.file; + start = first.start; + end_ = last.end_; + } diff --git a/analysis/src/vendor/odoc_parser/odoc_lexer.ml b/analysis/src/vendor/odoc_parser/odoc_lexer.ml new file mode 100644 index 000000000..2fb9d942e --- /dev/null +++ b/analysis/src/vendor/odoc_parser/odoc_lexer.ml @@ -0,0 +1,2504 @@ +# 1 "odoc_parser/odoc_lexer.mll" + + +let unescape_word : string -> string = fun s -> + (* The common case is that there are no escape sequences. *) + match String.index s '\\' with + | exception Not_found -> s + | _ -> + let buffer = Buffer.create (String.length s) in + let rec scan_word index = + if index >= String.length s then + () + else + let c = s.[index] in + let c, increment = + match c with + | '\\' -> + if index + 1 < String.length s then + match s.[index + 1] with + | '{' | '}' | '[' | ']' | '@' as c -> c, 2 + | _ -> c, 1 + else c, 1 + | _ -> c, 1 + in + Buffer.add_char buffer c; + scan_word (index + increment) + in + scan_word 0; + Buffer.contents buffer + + + +(* This is used for code and verbatim blocks. It can be done with a regular + expression, but the regexp gets quite ugly, so a function is easier to + understand. *) +let trim_leading_blank_lines : string -> string = fun s -> + let rec scan_for_last_newline : int -> int -> int = + fun index trim_until -> + if index >= String.length s then + String.length s + else + match s.[index] with + | ' ' | '\t' | '\r' -> scan_for_last_newline (index + 1) trim_until + | '\n' -> scan_for_last_newline (index + 1) (index + 1) + | _ -> trim_until + in + let trim_until = scan_for_last_newline 0 0 in + String.sub s trim_until (String.length s - trim_until) + +let trim_trailing_blank_lines : string -> string = fun s -> + let rec scan_for_last_newline : int -> int option -> int option = + fun index trim_from -> + if index < 0 then + Some 0 + else + match s.[index] with + | ' ' | '\t' | '\r' -> scan_for_last_newline (index - 1) trim_from + | '\n' -> scan_for_last_newline (index - 1) (Some index) + | _ -> trim_from + in + let last = String.length s - 1 in + match scan_for_last_newline last None with + | None -> + s + | Some trim_from -> + let trim_from = + if trim_from > 0 && s.[trim_from - 1] = '\r' then + trim_from - 1 + else + trim_from + in + String.sub s 0 trim_from + + + +module Location = Location_ +module Error = Error + + + +(* Assuming an ASCII-compatible input encoding here. *) +let heading_level level = + Char.code level - Char.code '0' + + + +type input = { + file : string; + offset_to_location : int -> Location.point; + lexbuf : Lexing.lexbuf; +} + +let offset_span_to_location + ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by input = + let start = + match start_offset with + | None -> Lexing.lexeme_start input.lexbuf + | Some s -> s + in + let start = + match adjust_start_by with + | None -> start + | Some s -> start + String.length s + in + let end_ = + match end_offset with + | None -> Lexing.lexeme_end input.lexbuf + | Some e -> e + in + let end_ = + match adjust_end_by with + | None -> end_ + | Some s -> end_ - String.length s + in + { + Location_.file = input.file; + start = input.offset_to_location start; + end_ = input.offset_to_location end_; + } + +let emit input ?start_offset ?adjust_start_by ?adjust_end_by token = + let location = + offset_span_to_location + ?start_offset ?adjust_start_by ?adjust_end_by input + in + Location.at location token + +let raise_error + input ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by error = + let location = + offset_span_to_location + ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by input + in + Error.raise_exception (error location) + +let reference_token start target = + match start with + | "{!" -> `Simple_reference target + | "{{!" -> `Begin_reference_with_replacement_text target + | "{:" -> `Simple_link target + | "{{:" -> `Begin_link_with_replacement_text target + | _ -> assert false + +let emit_reference input start target = + let target = String.trim target in + let token = reference_token start target in + if target = "" then + raise_error input (Parse_error.cannot_be_empty ~what:(Token.describe token)) + else + emit input token + + + +let trim_leading_space_or_accept_whitespace input text = + match text.[0] with + | ' ' -> String.sub text 1 (String.length text - 1) + | '\t' | '\r' | '\n' -> text + | exception Invalid_argument _ -> "" + | _ -> + raise_error + input + ~end_offset:(Lexing.lexeme_start input.lexbuf + 2) + Parse_error.no_leading_whitespace_in_verbatim + +let trim_trailing_space_or_accept_whitespace input text = + match text.[String.length text - 1] with + | ' ' -> String.sub text 0 (String.length text - 1) + | '\t' | '\r' | '\n' -> text + | exception Invalid_argument _ -> "" + | _ -> + raise_error + input + ~start_offset:(Lexing.lexeme_end input.lexbuf - 2) + Parse_error.no_trailing_whitespace_in_verbatim + + +# 178 "odoc_parser/odoc_lexer.ml" +let __ocaml_lex_tables = { + Lexing.lex_base = + "\000\000\211\255\000\000\091\000\249\255\119\000\124\000\144\000\ + \002\000\251\255\004\000\010\000\255\255\027\000\006\000\007\000\ + \012\000\149\000\159\000\151\000\212\255\000\000\234\255\021\000\ + \019\000\022\000\020\000\003\000\006\000\002\000\041\000\242\255\ + \243\255\244\255\245\255\246\255\201\255\240\255\030\000\052\000\ + \065\000\050\000\062\000\070\000\059\000\115\000\053\000\202\255\ + \241\255\200\255\095\000\086\000\239\255\199\255\075\000\076\000\ + \238\255\237\255\236\255\235\255\173\000\005\001\012\001\095\001\ + \204\001\006\002\064\002\122\002\180\002\238\002\040\003\098\003\ + \156\003\214\003\016\004\074\004\132\004\190\004\248\004\050\005\ + \164\005\004\000\057\000\223\005\222\000\104\000\108\000\231\255\ + \026\006\100\006\159\006\217\006\075\007\255\000\016\000\133\007\ + \191\007\249\007\051\008\109\008\167\008\225\008\027\009\085\009\ + \143\009\001\010\097\001\017\000\059\010\117\010\175\010\033\011\ + \214\001\216\001\091\011\149\011\207\011\009\012\123\012\191\005\ + \193\005\181\012\239\012\041\013\099\013\157\013\215\013\073\014\ + \232\000\218\000\136\000\024\001\224\255\236\000\245\000\131\014\ + \189\014\047\015\084\007\235\000\105\015\163\015\221\015\023\016\ + \137\016\086\007\010\010\195\016\253\016\055\017\113\017\171\017\ + \029\018\012\010\250\000\087\018\145\018\203\018\005\019\063\019\ + \121\019\179\019\237\019\095\020\042\011\099\001\153\020\211\020\ + \013\021\071\021\129\021\187\021\245\021\047\022\105\022\163\022\ + \221\022\023\023\073\014\250\255\251\255\008\000\018\000\112\000\ + \254\255\255\255\253\255\014\000\252\255\028\000"; + Lexing.lex_backtrk = + "\255\255\255\255\051\000\043\000\255\255\008\000\007\000\005\000\ + \005\000\255\255\052\000\002\000\255\255\003\000\255\255\255\255\ + \001\000\005\000\005\000\005\000\255\255\023\000\255\255\043\000\ + \043\000\043\000\043\000\043\000\043\000\043\000\043\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\041\000\042\000\022\000\050\000\ + \050\000\050\000\050\000\050\000\050\000\050\000\050\000\050\000\ + \050\000\050\000\050\000\050\000\050\000\050\000\050\000\050\000\ + \050\000\255\255\255\255\050\000\255\255\255\255\255\255\255\255\ + \050\000\050\000\050\000\050\000\045\000\025\000\025\000\050\000\ + \050\000\050\000\050\000\050\000\050\000\050\000\050\000\050\000\ + \026\000\050\000\027\000\027\000\050\000\050\000\050\000\046\000\ + \255\255\028\000\050\000\050\000\050\000\050\000\047\000\255\255\ + \029\000\050\000\050\000\050\000\030\000\050\000\050\000\049\000\ + \255\255\255\255\255\255\255\255\255\255\032\000\033\000\050\000\ + \050\000\045\000\034\000\034\000\050\000\050\000\050\000\050\000\ + \048\000\255\255\035\000\050\000\050\000\050\000\050\000\050\000\ + \045\000\036\000\036\000\050\000\050\000\050\000\050\000\050\000\ + \050\000\050\000\050\000\045\000\037\000\037\000\050\000\050\000\ + \050\000\040\000\050\000\050\000\050\000\050\000\038\000\050\000\ + \050\000\039\000\255\255\255\255\255\255\005\000\005\000\005\000\ + \255\255\255\255\255\255\255\255\255\255\255\255"; + Lexing.lex_default = + "\007\000\000\000\255\255\020\000\000\000\018\000\018\000\007\000\ + \007\000\000\000\255\255\255\255\000\000\255\255\255\255\255\255\ + \255\255\007\000\018\000\018\000\000\000\255\255\000\000\255\255\ + \255\255\255\255\055\000\051\000\038\000\255\255\038\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\038\000\038\000\ + \038\000\038\000\038\000\038\000\038\000\038\000\046\000\000\000\ + \000\000\000\000\051\000\051\000\000\000\000\000\055\000\055\000\ + \000\000\000\000\000\000\000\000\255\255\062\000\062\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \082\000\255\255\082\000\082\000\082\000\085\000\085\000\000\000\ + \255\255\255\255\255\255\255\255\255\255\094\000\094\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\107\000\107\000\255\255\255\255\255\255\255\255\ + \113\000\113\000\255\255\255\255\255\255\255\255\255\255\120\000\ + \120\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \128\000\129\000\130\000\255\255\000\000\129\000\128\000\255\255\ + \255\255\255\255\139\000\139\000\255\255\255\255\255\255\255\255\ + \255\255\146\000\146\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\154\000\154\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\165\000\165\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\179\000\000\000\000\000\255\255\255\255\255\255\ + \000\000\000\000\000\000\255\255\000\000\255\255"; + Lexing.lex_trans = + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\013\000\011\000\255\255\255\255\010\000\011\000\255\255\ + \011\000\016\000\189\000\011\000\016\000\016\000\016\000\015\000\ + \188\000\015\000\255\255\255\255\188\000\255\255\255\255\187\000\ + \013\000\000\000\255\255\038\000\013\000\011\000\188\000\000\000\ + \014\000\187\000\011\000\005\000\016\000\006\000\000\000\000\000\ + \060\000\060\000\060\000\060\000\060\000\060\000\060\000\060\000\ + \060\000\060\000\061\000\013\000\038\000\000\000\000\000\000\000\ + \002\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\004\000\008\000\001\000\017\000\085\000\ + \050\000\073\000\068\000\066\000\072\000\074\000\063\000\063\000\ + \063\000\065\000\063\000\063\000\063\000\063\000\063\000\064\000\ + \071\000\063\000\070\000\069\000\063\000\063\000\067\000\063\000\ + \063\000\063\000\063\000\003\000\030\000\009\000\059\000\058\000\ + \255\255\255\255\057\000\037\000\255\255\255\255\255\255\009\000\ + \022\000\255\255\054\000\021\000\021\000\021\000\021\000\021\000\ + \021\000\021\000\021\000\021\000\021\000\028\000\039\000\255\255\ + \009\000\255\255\255\255\037\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\040\000\255\255\041\000\037\000\042\000\ + \255\255\255\255\043\000\044\000\255\255\046\000\045\000\037\000\ + \255\255\037\000\048\000\050\000\081\000\255\255\027\000\255\255\ + \037\000\032\000\031\000\037\000\050\000\035\000\037\000\255\255\ + \033\000\054\000\054\000\037\000\034\000\086\000\132\000\023\000\ + \056\000\086\000\024\000\186\000\000\000\186\000\000\000\000\000\ + \025\000\026\000\255\255\019\000\255\255\000\000\029\000\255\255\ + \019\000\255\255\000\000\000\000\052\000\060\000\060\000\060\000\ + \060\000\060\000\060\000\060\000\060\000\060\000\060\000\084\000\ + \000\000\087\000\000\000\255\255\017\000\255\255\000\000\000\000\ + \037\000\017\000\255\255\019\000\255\255\255\255\000\000\255\255\ + \255\255\255\255\255\255\019\000\255\255\000\000\084\000\000\000\ + \012\000\133\000\255\255\049\000\255\255\000\000\036\000\255\255\ + \093\000\255\255\134\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\133\000\053\000\255\255\255\255\134\000\ + \255\255\255\255\255\255\012\000\255\255\000\000\036\000\093\000\ + \000\000\131\000\000\000\000\000\000\000\255\255\255\255\000\000\ + \000\000\036\000\255\255\000\000\255\255\000\000\000\000\000\000\ + \000\000\000\000\036\000\255\255\036\000\047\000\000\000\000\000\ + \131\000\255\255\128\000\036\000\000\000\000\000\036\000\129\000\ + \000\000\036\000\000\000\000\000\000\000\000\000\036\000\000\000\ + \000\000\000\000\000\000\053\000\053\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\130\000\000\000\049\000\000\000\ + \000\000\081\000\000\000\255\255\000\000\000\000\000\000\049\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \255\255\000\000\106\000\255\255\255\255\255\255\255\255\000\000\ + \255\255\000\000\000\000\036\000\000\000\000\000\000\000\255\255\ + \000\000\000\000\000\000\000\000\255\255\000\000\000\000\000\000\ + \000\000\106\000\255\255\000\000\000\000\000\000\000\000\000\000\ + \255\255\255\255\000\000\000\000\000\000\000\000\000\000\000\000\ + \255\255\000\000\000\000\000\000\000\000\255\255\000\000\255\255\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\255\255\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\255\255\000\000\000\000\000\000\255\255\112\000\ + \255\255\255\255\255\255\255\255\000\000\255\255\000\000\000\000\ + \255\255\000\000\000\000\255\255\255\255\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\255\255\112\000\000\000\ + \255\255\000\000\255\255\000\000\000\000\000\000\000\000\255\255\ + \000\000\000\000\000\000\000\000\000\000\255\255\000\000\000\000\ + \000\000\000\000\000\000\000\000\255\255\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\175\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\255\255\000\000\255\255\000\000\000\000\000\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\170\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\156\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\155\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\255\255\000\000\ + \255\255\000\000\000\000\063\000\063\000\063\000\063\000\147\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\063\000\063\000\063\000\ + \063\000\140\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\000\000\000\000\000\000\000\000\000\000\000\000\063\000\ + \063\000\063\000\063\000\126\000\063\000\063\000\063\000\125\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\115\000\063\000\063\000\063\000\114\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\108\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\063\000\063\000\063\000\ + \063\000\096\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\095\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\000\000\000\000\000\000\000\000\000\000\000\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\088\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \075\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\076\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\077\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\000\000\000\000\000\000\000\000\000\000\000\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\078\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\079\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\063\000\063\000\063\000\063\000\080\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\084\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\084\000\000\000\000\000\000\000\ + \119\000\255\255\255\255\255\255\255\255\000\000\255\255\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\119\000\ + \000\000\255\255\000\000\000\000\000\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\081\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\081\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\089\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\255\255\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\255\255\ + \000\000\255\255\000\000\000\000\000\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\090\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\255\255\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\091\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\092\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\093\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\138\000\255\255\145\000\ + \255\255\255\255\000\000\255\255\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\093\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\138\000\000\000\145\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \105\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\097\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\255\255\000\000\255\255\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\098\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \099\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \100\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \101\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\102\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \103\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\104\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\106\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\255\255\255\255\153\000\255\255\255\255\ + \000\000\255\255\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\106\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\255\255\000\000\153\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\109\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\110\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\255\255\000\000\255\255\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\111\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\112\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\164\000\255\255\000\000\000\000\255\255\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\112\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\164\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\121\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\116\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\255\255\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\117\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\118\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\119\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\119\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\122\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\123\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\124\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\135\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\127\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\131\000\182\000\000\000\000\000\181\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\131\000\000\000\128\000\000\000\000\000\000\000\000\000\ + \129\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\130\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\184\000\183\000\185\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\136\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\137\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \138\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\180\000\000\000\000\000\000\000\000\000\000\000\138\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\141\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\142\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\143\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\144\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\145\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\145\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\148\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \149\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \150\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \151\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\152\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\153\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\153\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\166\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\157\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\158\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\159\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \160\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\161\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\162\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\163\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \164\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\164\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\167\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \168\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\169\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\171\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\172\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\173\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\174\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\063\000\063\000\063\000\063\000\ + \176\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\177\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + "; + Lexing.lex_check = + "\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\000\000\000\000\008\000\008\000\000\000\010\000\008\000\ + \014\000\015\000\181\000\011\000\011\000\016\000\016\000\011\000\ + \187\000\016\000\094\000\107\000\182\000\094\000\107\000\182\000\ + \000\000\255\255\008\000\029\000\013\000\013\000\189\000\255\255\ + \013\000\189\000\011\000\000\000\016\000\000\000\255\255\255\255\ + \021\000\021\000\021\000\021\000\021\000\021\000\021\000\021\000\ + \021\000\021\000\021\000\013\000\029\000\255\255\255\255\255\255\ + \000\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\000\000\000\000\000\000\008\000\081\000\ + \027\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\002\000\002\000\002\000\002\000\002\000\ + \002\000\002\000\002\000\000\000\003\000\000\000\023\000\024\000\ + \005\000\005\000\025\000\028\000\005\000\006\000\006\000\011\000\ + \003\000\006\000\026\000\003\000\003\000\003\000\003\000\003\000\ + \003\000\003\000\003\000\003\000\003\000\003\000\030\000\005\000\ + \013\000\007\000\007\000\038\000\006\000\007\000\017\000\017\000\ + \019\000\019\000\017\000\039\000\019\000\040\000\030\000\041\000\ + \018\000\018\000\042\000\043\000\018\000\045\000\044\000\041\000\ + \007\000\039\000\046\000\051\000\082\000\017\000\003\000\019\000\ + \044\000\003\000\003\000\042\000\050\000\003\000\040\000\018\000\ + \003\000\054\000\055\000\043\000\003\000\085\000\130\000\003\000\ + \054\000\086\000\003\000\183\000\255\255\183\000\255\255\255\255\ + \003\000\003\000\005\000\005\000\005\000\255\255\003\000\006\000\ + \006\000\006\000\255\255\255\255\050\000\060\000\060\000\060\000\ + \060\000\060\000\060\000\060\000\060\000\060\000\060\000\084\000\ + \255\255\086\000\255\255\007\000\007\000\007\000\255\255\255\255\ + \045\000\017\000\005\000\019\000\005\000\139\000\255\255\006\000\ + \139\000\006\000\018\000\018\000\018\000\255\255\084\000\255\255\ + \000\000\129\000\008\000\027\000\154\000\255\255\028\000\154\000\ + \093\000\093\000\128\000\007\000\093\000\007\000\061\000\061\000\ + \094\000\107\000\061\000\133\000\026\000\062\000\062\000\134\000\ + \129\000\062\000\018\000\013\000\018\000\255\255\038\000\093\000\ + \255\255\131\000\255\255\255\255\255\255\061\000\128\000\255\255\ + \255\255\030\000\133\000\255\255\062\000\255\255\255\255\255\255\ + \255\255\255\255\041\000\134\000\039\000\046\000\255\255\255\255\ + \131\000\082\000\131\000\044\000\255\255\255\255\042\000\131\000\ + \255\255\040\000\255\255\255\255\255\255\255\255\043\000\255\255\ + \255\255\255\255\255\255\054\000\055\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\131\000\255\255\051\000\255\255\ + \255\255\084\000\255\255\003\000\255\255\255\255\255\255\050\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \085\000\255\255\106\000\106\000\086\000\165\000\106\000\255\255\ + \165\000\255\255\255\255\045\000\255\255\255\255\255\255\005\000\ + \255\255\255\255\255\255\255\255\006\000\255\255\255\255\255\255\ + \255\255\106\000\061\000\255\255\255\255\255\255\255\255\255\255\ + \130\000\062\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \007\000\255\255\255\255\255\255\255\255\017\000\255\255\019\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\018\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\063\000\063\000\063\000\063\000\063\000\063\000\ + \063\000\063\000\129\000\255\255\255\255\255\255\084\000\112\000\ + \112\000\113\000\113\000\112\000\255\255\113\000\255\255\255\255\ + \128\000\255\255\255\255\139\000\133\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\134\000\112\000\255\255\ + \113\000\255\255\154\000\255\255\255\255\255\255\255\255\093\000\ + \255\255\255\255\255\255\255\255\255\255\061\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\062\000\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\064\000\ + \064\000\064\000\064\000\064\000\064\000\064\000\064\000\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\106\000\255\255\165\000\255\255\255\255\255\255\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\065\000\065\000\065\000\065\000\065\000\065\000\065\000\ + \065\000\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\066\000\066\000\066\000\066\000\066\000\ + \066\000\066\000\066\000\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\255\255\112\000\255\255\ + \113\000\255\255\255\255\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\067\000\067\000\067\000\ + \067\000\067\000\067\000\067\000\067\000\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\068\000\ + \068\000\068\000\068\000\068\000\068\000\068\000\068\000\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\255\255\255\255\255\255\255\255\255\255\255\255\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\069\000\069\000\069\000\069\000\069\000\069\000\069\000\ + \069\000\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\070\000\070\000\070\000\070\000\070\000\ + \070\000\070\000\070\000\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\071\000\071\000\071\000\ + \071\000\071\000\071\000\071\000\071\000\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\072\000\ + \072\000\072\000\072\000\072\000\072\000\072\000\072\000\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\255\255\255\255\255\255\255\255\255\255\255\255\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\073\000\073\000\073\000\073\000\073\000\073\000\073\000\ + \073\000\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\074\000\074\000\074\000\074\000\074\000\ + \074\000\074\000\074\000\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\075\000\075\000\075\000\ + \075\000\075\000\075\000\075\000\075\000\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\076\000\ + \076\000\076\000\076\000\076\000\076\000\076\000\076\000\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\255\255\255\255\255\255\255\255\255\255\255\255\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\077\000\077\000\077\000\077\000\077\000\077\000\077\000\ + \077\000\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\078\000\078\000\078\000\078\000\078\000\ + \078\000\078\000\078\000\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\079\000\079\000\079\000\ + \079\000\079\000\079\000\079\000\079\000\080\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\080\000\255\255\255\255\255\255\ + \119\000\119\000\120\000\120\000\119\000\255\255\120\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\119\000\ + \255\255\120\000\255\255\255\255\255\255\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\080\000\ + \080\000\080\000\080\000\080\000\080\000\080\000\080\000\080\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\083\000\083\000\083\000\083\000\083\000\ + \083\000\083\000\083\000\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\088\000\088\000\088\000\ + \088\000\088\000\088\000\088\000\088\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\080\000\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\119\000\ + \255\255\120\000\255\255\255\255\255\255\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\089\000\ + \089\000\089\000\089\000\089\000\089\000\089\000\089\000\083\000\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\090\000\090\000\090\000\090\000\090\000\090\000\ + \090\000\090\000\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\091\000\091\000\091\000\091\000\ + \091\000\091\000\091\000\091\000\092\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\138\000\138\000\145\000\ + \145\000\138\000\255\255\145\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\092\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\138\000\255\255\145\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\092\000\092\000\ + \092\000\092\000\092\000\092\000\092\000\092\000\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \095\000\095\000\095\000\095\000\095\000\095\000\095\000\095\000\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\096\000\096\000\096\000\096\000\096\000\096\000\ + \096\000\096\000\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\138\000\255\255\145\000\255\255\ + \255\255\255\255\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\097\000\097\000\097\000\097\000\ + \097\000\097\000\097\000\097\000\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\098\000\098\000\ + \098\000\098\000\098\000\098\000\098\000\098\000\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \099\000\099\000\099\000\099\000\099\000\099\000\099\000\099\000\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\100\000\100\000\100\000\100\000\100\000\100\000\ + \100\000\100\000\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\101\000\101\000\101\000\101\000\ + \101\000\101\000\101\000\101\000\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\102\000\102\000\ + \102\000\102\000\102\000\102\000\102\000\102\000\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \103\000\103\000\103\000\103\000\103\000\103\000\103\000\103\000\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\104\000\104\000\104\000\104\000\104\000\104\000\ + \104\000\104\000\105\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\146\000\146\000\153\000\153\000\146\000\ + \255\255\153\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\105\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\146\000\255\255\153\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\105\000\105\000\105\000\105\000\ + \105\000\105\000\105\000\105\000\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\108\000\108\000\ + \108\000\108\000\108\000\108\000\108\000\108\000\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \109\000\109\000\109\000\109\000\109\000\109\000\109\000\109\000\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\146\000\255\255\153\000\255\255\255\255\255\255\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\110\000\110\000\110\000\110\000\110\000\110\000\ + \110\000\110\000\111\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\164\000\164\000\255\255\255\255\164\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\111\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\164\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\111\000\111\000\111\000\111\000\ + \111\000\111\000\111\000\111\000\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\114\000\114\000\ + \114\000\114\000\114\000\114\000\114\000\114\000\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \115\000\115\000\115\000\115\000\115\000\115\000\115\000\115\000\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\164\000\255\255\255\255\255\255\255\255\255\255\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\116\000\116\000\116\000\116\000\116\000\116\000\ + \116\000\116\000\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\117\000\117\000\117\000\117\000\ + \117\000\117\000\117\000\117\000\118\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\118\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\118\000\118\000\ + \118\000\118\000\118\000\118\000\118\000\118\000\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \121\000\121\000\121\000\121\000\121\000\121\000\121\000\121\000\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\122\000\122\000\122\000\122\000\122\000\122\000\ + \122\000\122\000\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\123\000\123\000\123\000\123\000\ + \123\000\123\000\123\000\123\000\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\124\000\124\000\ + \124\000\124\000\124\000\124\000\124\000\124\000\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \125\000\125\000\125\000\125\000\125\000\125\000\125\000\125\000\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\126\000\126\000\126\000\126\000\126\000\126\000\ + \126\000\126\000\127\000\178\000\255\255\255\255\178\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\127\000\255\255\127\000\255\255\255\255\255\255\255\255\ + \127\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\127\000\255\255\255\255\ + \255\255\255\255\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\178\000\178\000\178\000\255\255\ + \255\255\255\255\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\127\000\127\000\127\000\127\000\ + \127\000\127\000\127\000\127\000\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\135\000\135\000\ + \135\000\135\000\135\000\135\000\135\000\135\000\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \136\000\136\000\136\000\136\000\136\000\136\000\136\000\136\000\ + \137\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\178\000\255\255\255\255\255\255\255\255\255\255\137\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\137\000\137\000\137\000\137\000\137\000\137\000\ + \137\000\137\000\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\140\000\140\000\140\000\140\000\ + \140\000\140\000\140\000\140\000\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\141\000\141\000\ + \141\000\141\000\141\000\141\000\141\000\141\000\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \142\000\142\000\142\000\142\000\142\000\142\000\142\000\142\000\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\143\000\143\000\143\000\143\000\143\000\143\000\ + \143\000\143\000\144\000\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\144\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\144\000\144\000\144\000\144\000\ + \144\000\144\000\144\000\144\000\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\147\000\147\000\ + \147\000\147\000\147\000\147\000\147\000\147\000\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \148\000\148\000\148\000\148\000\148\000\148\000\148\000\148\000\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\149\000\149\000\149\000\149\000\149\000\149\000\ + \149\000\149\000\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\150\000\150\000\150\000\150\000\ + \150\000\150\000\150\000\150\000\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\151\000\151\000\ + \151\000\151\000\151\000\151\000\151\000\151\000\152\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\152\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \152\000\152\000\152\000\152\000\152\000\152\000\152\000\152\000\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\155\000\155\000\155\000\155\000\155\000\155\000\ + \155\000\155\000\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\156\000\156\000\156\000\156\000\ + \156\000\156\000\156\000\156\000\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\157\000\157\000\ + \157\000\157\000\157\000\157\000\157\000\157\000\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \158\000\158\000\158\000\158\000\158\000\158\000\158\000\158\000\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\159\000\159\000\159\000\159\000\159\000\159\000\ + \159\000\159\000\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\160\000\160\000\160\000\160\000\ + \160\000\160\000\160\000\160\000\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\161\000\161\000\ + \161\000\161\000\161\000\161\000\161\000\161\000\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \162\000\162\000\162\000\162\000\162\000\162\000\162\000\162\000\ + \163\000\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\163\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\163\000\163\000\163\000\163\000\163\000\163\000\ + \163\000\163\000\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\166\000\166\000\166\000\166\000\ + \166\000\166\000\166\000\166\000\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\167\000\167\000\ + \167\000\167\000\167\000\167\000\167\000\167\000\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \168\000\168\000\168\000\168\000\168\000\168\000\168\000\168\000\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\169\000\169\000\169\000\169\000\169\000\169\000\ + \169\000\169\000\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\170\000\170\000\170\000\170\000\ + \170\000\170\000\170\000\170\000\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\171\000\171\000\ + \171\000\171\000\171\000\171\000\171\000\171\000\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \172\000\172\000\172\000\172\000\172\000\172\000\172\000\172\000\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\173\000\173\000\173\000\173\000\173\000\173\000\ + \173\000\173\000\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\174\000\174\000\174\000\174\000\ + \174\000\174\000\174\000\174\000\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\175\000\175\000\ + \175\000\175\000\175\000\175\000\175\000\175\000\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \255\255\255\255\255\255\255\255\255\255\255\255\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \176\000\176\000\176\000\176\000\176\000\176\000\176\000\176\000\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\177\000\177\000\177\000\177\000\177\000\177\000\ + \177\000\177\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + "; + Lexing.lex_base_code = + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\001\000\000\000\000\000\002\000\003\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\001\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\019\000\022\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \005\000\000\000\000\000\001\000\006\000\000\000\000\000\038\000\ + \000\000\000\000\000\000\000\000\007\000\008\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\009\000\010\000\000\000\000\000\000\000\000\000\011\000\ + \012\000\000\000\000\000\000\000\000\000\000\000\013\000\014\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\013\000\ + \000\000\000\000\000\000\014\000\076\000\000\000\000\000\000\000\ + \000\000\017\000\018\000\000\000\000\000\000\000\000\000\000\000\ + \019\000\022\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \046\000\047\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\048\000\051\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000"; + Lexing.lex_backtrk_code = + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \014\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\046\000\046\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\052\000\052\000\000\000\000\000\000\000\000\000\ + \000\000\058\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \064\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\079\000\082\000\000\000\ + \000\000\000\000\088\000\088\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\094\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\100\000\100\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\106\000\106\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000"; + Lexing.lex_default_code = + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \030\000\000\000\030\000\030\000\030\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000"; + Lexing.lex_trans_code = + "\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\011\000\001\000\001\000\001\000\001\000\025\000\033\000\ + \043\000\043\000\049\000\049\000\055\000\055\000\061\000\061\000\ + \000\000\000\000\085\000\085\000\091\000\000\000\000\000\091\000\ + \011\000\006\000\006\000\000\000\000\000\025\000\033\000\043\000\ + \043\000\049\000\049\000\055\000\055\000\061\000\061\000\067\000\ + \067\000\085\000\085\000\091\000\070\000\070\000\091\000\097\000\ + \097\000\103\000\006\000\006\000\103\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\073\000\073\000\000\000\000\000\000\000\097\000\097\000\ + \103\000\000\000\000\000\103\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\025\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\000\ + \000\000\000\000\000\000\000\000"; + Lexing.lex_check_code = + "\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\011\000\000\000\010\000\013\000\014\000\080\000\084\000\ + \092\000\093\000\105\000\106\000\111\000\112\000\118\000\119\000\ + \255\255\255\255\137\000\138\000\144\000\255\255\255\255\145\000\ + \011\000\003\000\029\000\255\255\255\255\080\000\084\000\092\000\ + \093\000\105\000\106\000\111\000\112\000\118\000\119\000\127\000\ + \131\000\137\000\138\000\144\000\127\000\131\000\145\000\152\000\ + \153\000\163\000\003\000\029\000\164\000\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\127\000\131\000\255\255\255\255\255\255\152\000\153\000\ + \163\000\255\255\255\255\164\000\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\079\000\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\082\000\083\000\255\255\255\255\255\255\ + \080\000\084\000\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \082\000\083\000\255\255\255\255\255\255\080\000\084\000\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\255\ + \255\255\255\255\255\255\255\255"; + Lexing.lex_code = + "\255\003\255\002\255\255\005\255\004\255\255\003\255\255\001\003\ + \000\002\255\000\005\255\000\004\255\007\255\006\255\255\007\255\ + \255\006\255\007\255\255\000\006\001\007\255\008\255\255\000\008\ + \255\009\255\255\000\009\255\010\255\255\000\010\255\011\255\255\ + \000\011\255\012\255\255\013\255\255\014\255\255\000\014\255\000\ + \013\255\000\012\255\015\255\255\000\015\255\016\255\255\000\016\ + \255\017\255\255\000\017\255\018\255\255\000\018\255"; +} + +let rec token input lexbuf = + lexbuf.Lexing.lex_mem <- Array.make 19 (-1) ; __ocaml_lex_token_rec input lexbuf 0 +and __ocaml_lex_token_rec input lexbuf __ocaml_lex_state = + match Lexing.new_engine __ocaml_lex_tables __ocaml_lex_state lexbuf with + | 0 -> +# 206 "odoc_parser/odoc_lexer.mll" + ( emit input `End ) +# 1973 "odoc_parser/odoc_lexer.ml" + + | 1 -> +let +# 208 "odoc_parser/odoc_lexer.mll" + prefix +# 1979 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_mem.(0) +and +# 209 "odoc_parser/odoc_lexer.mll" + suffix +# 1984 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(1) lexbuf.Lexing.lex_curr_pos in +# 210 "odoc_parser/odoc_lexer.mll" + ( emit input `Blank_line ~adjust_start_by:prefix ~adjust_end_by:suffix ) +# 1988 "odoc_parser/odoc_lexer.ml" + + | 2 -> +# 213 "odoc_parser/odoc_lexer.mll" + ( emit input `Single_newline ) +# 1993 "odoc_parser/odoc_lexer.ml" + + | 3 -> +# 216 "odoc_parser/odoc_lexer.mll" + ( emit input `Space ) +# 1998 "odoc_parser/odoc_lexer.ml" + + | 4 -> +let +# 218 "odoc_parser/odoc_lexer.mll" + p +# 2004 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos (lexbuf.Lexing.lex_curr_pos + -1) in +# 219 "odoc_parser/odoc_lexer.mll" + ( emit input `Right_brace ~adjust_start_by:p ) +# 2008 "odoc_parser/odoc_lexer.ml" + + | 5 -> +let +# 222 "odoc_parser/odoc_lexer.mll" + w +# 2014 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_curr_pos in +# 223 "odoc_parser/odoc_lexer.mll" + ( emit input (`Word (unescape_word w)) ) +# 2018 "odoc_parser/odoc_lexer.ml" + + | 6 -> +# 226 "odoc_parser/odoc_lexer.mll" + ( code_span + (Buffer.create 1024) 0 (Lexing.lexeme_start lexbuf) input lexbuf ) +# 2024 "odoc_parser/odoc_lexer.ml" + + | 7 -> +# 230 "odoc_parser/odoc_lexer.mll" + ( emit input `Minus ) +# 2029 "odoc_parser/odoc_lexer.ml" + + | 8 -> +# 233 "odoc_parser/odoc_lexer.mll" + ( emit input `Plus ) +# 2034 "odoc_parser/odoc_lexer.ml" + + | 9 -> +# 236 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_style `Bold) ) +# 2039 "odoc_parser/odoc_lexer.ml" + + | 10 -> +# 239 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_style `Italic) ) +# 2044 "odoc_parser/odoc_lexer.ml" + + | 11 -> +# 242 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_style `Emphasis) ) +# 2049 "odoc_parser/odoc_lexer.ml" + + | 12 -> +# 245 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_style `Superscript) ) +# 2054 "odoc_parser/odoc_lexer.ml" + + | 13 -> +# 248 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_style `Subscript) ) +# 2059 "odoc_parser/odoc_lexer.ml" + + | 14 -> +let +# 250 "odoc_parser/odoc_lexer.mll" + modules +# 2065 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_start_pos + 10) (lexbuf.Lexing.lex_curr_pos + -1) in +# 251 "odoc_parser/odoc_lexer.mll" + ( emit input (`Modules modules) ) +# 2069 "odoc_parser/odoc_lexer.ml" + + | 15 -> +let +# 253 "odoc_parser/odoc_lexer.mll" + start +# 2075 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_mem.(0) +and +# 253 "odoc_parser/odoc_lexer.mll" + target +# 2080 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) (lexbuf.Lexing.lex_curr_pos + -1) in +# 254 "odoc_parser/odoc_lexer.mll" + ( emit_reference input start target ) +# 2084 "odoc_parser/odoc_lexer.ml" + + | 16 -> +let +# 256 "odoc_parser/odoc_lexer.mll" + c +# 2090 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_start_pos + 2) (lexbuf.Lexing.lex_curr_pos + -2) in +# 257 "odoc_parser/odoc_lexer.mll" + ( let c = trim_leading_blank_lines c in + let c = trim_trailing_blank_lines c in + emit input (`Code_block c) ) +# 2096 "odoc_parser/odoc_lexer.ml" + + | 17 -> +let +# 261 "odoc_parser/odoc_lexer.mll" + t +# 2102 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_start_pos + 2) (lexbuf.Lexing.lex_curr_pos + -2) in +# 262 "odoc_parser/odoc_lexer.mll" + ( let t = trim_leading_space_or_accept_whitespace input t in + let t = trim_trailing_space_or_accept_whitespace input t in + let t = trim_leading_blank_lines t in + let t = trim_trailing_blank_lines t in + emit input (`Verbatim t) ) +# 2110 "odoc_parser/odoc_lexer.ml" + + | 18 -> +# 269 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_list `Unordered) ) +# 2115 "odoc_parser/odoc_lexer.ml" + + | 19 -> +# 272 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_list `Ordered) ) +# 2120 "odoc_parser/odoc_lexer.ml" + + | 20 -> +# 275 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_list_item `Li) ) +# 2125 "odoc_parser/odoc_lexer.ml" + + | 21 -> +# 278 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_list_item `Dash) ) +# 2130 "odoc_parser/odoc_lexer.ml" + + | 22 -> +let +# 280 "odoc_parser/odoc_lexer.mll" + level +# 2136 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) +and +# 280 "odoc_parser/odoc_lexer.mll" + label +# 2141 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_start_pos + 3) lexbuf.Lexing.lex_curr_pos in +# 281 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_section_heading (heading_level level, Some label)) ) +# 2145 "odoc_parser/odoc_lexer.ml" + + | 23 -> +let +# 283 "odoc_parser/odoc_lexer.mll" + level +# 2151 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in +# 284 "odoc_parser/odoc_lexer.mll" + ( emit input (`Begin_section_heading (heading_level level, None)) ) +# 2155 "odoc_parser/odoc_lexer.ml" + + | 24 -> +let +# 286 "odoc_parser/odoc_lexer.mll" + lang +# 2161 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_mem.(1) +and +# 286 "odoc_parser/odoc_lexer.mll" + content +# 2166 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_mem.(1) + 2) (lexbuf.Lexing.lex_curr_pos + -2) in +# 287 "odoc_parser/odoc_lexer.mll" + ( emit input (`Example (lang, content)) ) +# 2170 "odoc_parser/odoc_lexer.ml" + + | 25 -> +let +# 289 "odoc_parser/odoc_lexer.mll" + author +# 2176 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 290 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Author author)) ) +# 2180 "odoc_parser/odoc_lexer.ml" + + | 26 -> +# 293 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag `Deprecated) ) +# 2185 "odoc_parser/odoc_lexer.ml" + + | 27 -> +let +# 295 "odoc_parser/odoc_lexer.mll" + doc +# 2191 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 296 "odoc_parser/odoc_lexer.mll" + ( emit input (`Doc doc) ) +# 2195 "odoc_parser/odoc_lexer.ml" + + | 28 -> +let +# 298 "odoc_parser/odoc_lexer.mll" + name +# 2201 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 299 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Param name)) ) +# 2205 "odoc_parser/odoc_lexer.ml" + + | 29 -> +let +# 301 "odoc_parser/odoc_lexer.mll" + name +# 2211 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 302 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Raise name)) ) +# 2215 "odoc_parser/odoc_lexer.ml" + + | 30 -> +# 305 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag `Return) ) +# 2220 "odoc_parser/odoc_lexer.ml" + + | 31 -> +let +# 307 "odoc_parser/odoc_lexer.mll" + url +# 2226 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) (lexbuf.Lexing.lex_curr_pos + -1) in +# 308 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`See (`Url, url))) ) +# 2230 "odoc_parser/odoc_lexer.ml" + + | 32 -> +let +# 310 "odoc_parser/odoc_lexer.mll" + filename +# 2236 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) (lexbuf.Lexing.lex_curr_pos + -1) in +# 311 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`See (`File, filename))) ) +# 2240 "odoc_parser/odoc_lexer.ml" + + | 33 -> +let +# 313 "odoc_parser/odoc_lexer.mll" + name +# 2246 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) (lexbuf.Lexing.lex_curr_pos + -1) in +# 314 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`See (`Document, name))) ) +# 2250 "odoc_parser/odoc_lexer.ml" + + | 34 -> +let +# 316 "odoc_parser/odoc_lexer.mll" + version +# 2256 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 317 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Since version)) ) +# 2260 "odoc_parser/odoc_lexer.ml" + + | 35 -> +let +# 319 "odoc_parser/odoc_lexer.mll" + version +# 2266 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 320 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Before version)) ) +# 2270 "odoc_parser/odoc_lexer.ml" + + | 36 -> +let +# 322 "odoc_parser/odoc_lexer.mll" + version +# 2276 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 323 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Version version)) ) +# 2280 "odoc_parser/odoc_lexer.ml" + + | 37 -> +let +# 325 "odoc_parser/odoc_lexer.mll" + identifier +# 2286 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_mem.(0) lexbuf.Lexing.lex_curr_pos in +# 326 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag (`Canonical identifier)) ) +# 2290 "odoc_parser/odoc_lexer.ml" + + | 38 -> +# 329 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag `Inline) ) +# 2295 "odoc_parser/odoc_lexer.ml" + + | 39 -> +# 332 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag `Open) ) +# 2300 "odoc_parser/odoc_lexer.ml" + + | 40 -> +# 335 "odoc_parser/odoc_lexer.mll" + ( emit input (`Tag `Closed) ) +# 2305 "odoc_parser/odoc_lexer.ml" + + | 41 -> +let +# 340 "odoc_parser/odoc_lexer.mll" + level +# 2311 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf (lexbuf.Lexing.lex_start_pos + 1) lexbuf.Lexing.lex_curr_pos in +# 341 "odoc_parser/odoc_lexer.mll" + ( raise_error input (Parse_error.bad_section_level level) ) +# 2315 "odoc_parser/odoc_lexer.ml" + + | 42 -> +let +# 343 "odoc_parser/odoc_lexer.mll" + prefix +# 2321 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos (lexbuf.Lexing.lex_start_pos + 2) in +# 344 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + ~adjust_start_by:prefix + (Parse_error.cannot_be_empty ~what:"heading label") ) +# 2328 "odoc_parser/odoc_lexer.ml" + + | 43 -> +let +# 349 "odoc_parser/odoc_lexer.mll" + markup +# 2334 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_curr_pos in +# 350 "odoc_parser/odoc_lexer.mll" + ( raise_error input (Parse_error.bad_markup markup) ) +# 2338 "odoc_parser/odoc_lexer.ml" + + | 44 -> +# 353 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.unpaired_right_bracket ) +# 2343 "odoc_parser/odoc_lexer.ml" + + | 45 -> +# 356 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + (Parse_error.cannot_be_empty + ~what:(Printf.sprintf "'%s'" (Lexing.lexeme lexbuf))) ) +# 2351 "odoc_parser/odoc_lexer.ml" + + | 46 -> +# 362 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.truncated_param ) +# 2356 "odoc_parser/odoc_lexer.ml" + + | 47 -> +# 365 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.truncated_raise ) +# 2361 "odoc_parser/odoc_lexer.ml" + + | 48 -> +# 368 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.truncated_before ) +# 2366 "odoc_parser/odoc_lexer.ml" + + | 49 -> +# 371 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.truncated_see ) +# 2371 "odoc_parser/odoc_lexer.ml" + + | 50 -> +let +# 373 "odoc_parser/odoc_lexer.mll" + tag +# 2377 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_curr_pos in +# 374 "odoc_parser/odoc_lexer.mll" + ( raise_error input (Parse_error.unknown_tag tag) ) +# 2381 "odoc_parser/odoc_lexer.ml" + + | 51 -> +# 377 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.stray_at ) +# 2386 "odoc_parser/odoc_lexer.ml" + + | 52 -> +# 380 "odoc_parser/odoc_lexer.mll" + ( raise_error input Parse_error.stray_cr ) +# 2391 "odoc_parser/odoc_lexer.ml" + + | 53 -> +# 383 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Modules ""))) ) +# 2401 "odoc_parser/odoc_lexer.ml" + + | 54 -> +let +# 390 "odoc_parser/odoc_lexer.mll" + start +# 2407 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme lexbuf lexbuf.Lexing.lex_start_pos lexbuf.Lexing.lex_mem.(0) in +# 391 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (reference_token start ""))) ) +# 2416 "odoc_parser/odoc_lexer.ml" + + | 55 -> +# 399 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Code_block ""))) ) +# 2426 "odoc_parser/odoc_lexer.ml" + + | 56 -> +# 407 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Verbatim ""))) ) +# 2436 "odoc_parser/odoc_lexer.ml" + + | __ocaml_lex_state -> lexbuf.Lexing.refill_buff lexbuf; + __ocaml_lex_token_rec input lexbuf __ocaml_lex_state + +and code_span buffer nesting_level start_offset input lexbuf = + __ocaml_lex_code_span_rec buffer nesting_level start_offset input lexbuf 178 +and __ocaml_lex_code_span_rec buffer nesting_level start_offset input lexbuf __ocaml_lex_state = + match Lexing.engine __ocaml_lex_tables __ocaml_lex_state lexbuf with + | 0 -> +# 418 "odoc_parser/odoc_lexer.mll" + ( if nesting_level = 0 then + emit input (`Code_span (Buffer.contents buffer)) ~start_offset + else begin + Buffer.add_char buffer ']'; + code_span buffer (nesting_level - 1) start_offset input lexbuf + end ) +# 2453 "odoc_parser/odoc_lexer.ml" + + | 1 -> +# 426 "odoc_parser/odoc_lexer.mll" + ( Buffer.add_char buffer '['; + code_span buffer (nesting_level + 1) start_offset input lexbuf ) +# 2459 "odoc_parser/odoc_lexer.ml" + + | 2 -> +let +# 429 "odoc_parser/odoc_lexer.mll" + c +# 2465 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme_char lexbuf (lexbuf.Lexing.lex_start_pos + 1) in +# 430 "odoc_parser/odoc_lexer.mll" + ( Buffer.add_char buffer c; + code_span buffer nesting_level start_offset input lexbuf ) +# 2470 "odoc_parser/odoc_lexer.ml" + + | 3 -> +# 434 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + (Parse_error.not_allowed + ~what:(Token.describe `Blank_line) + ~in_what:(Token.describe (`Code_span ""))) ) +# 2479 "odoc_parser/odoc_lexer.ml" + + | 4 -> +# 441 "odoc_parser/odoc_lexer.mll" + ( raise_error + input + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Code_span ""))) ) +# 2488 "odoc_parser/odoc_lexer.ml" + + | 5 -> +let +# 447 "odoc_parser/odoc_lexer.mll" + c +# 2494 "odoc_parser/odoc_lexer.ml" += Lexing.sub_lexeme_char lexbuf lexbuf.Lexing.lex_start_pos in +# 448 "odoc_parser/odoc_lexer.mll" + ( Buffer.add_char buffer c; + code_span buffer nesting_level start_offset input lexbuf ) +# 2499 "odoc_parser/odoc_lexer.ml" + + | __ocaml_lex_state -> lexbuf.Lexing.refill_buff lexbuf; + __ocaml_lex_code_span_rec buffer nesting_level start_offset input lexbuf __ocaml_lex_state + +;; + diff --git a/analysis/src/vendor/odoc_parser/odoc_lexer.mli b/analysis/src/vendor/odoc_parser/odoc_lexer.mli new file mode 100644 index 000000000..c71776ef3 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/odoc_lexer.mli @@ -0,0 +1,7 @@ +type input = { + file : string; + offset_to_location : int -> Location_.point; + lexbuf : Lexing.lexbuf; +} + +val token : input -> Lexing.lexbuf -> Token.t Location_.with_location diff --git a/analysis/src/vendor/odoc_parser/odoc_lexer.mll b/analysis/src/vendor/odoc_parser/odoc_lexer.mll new file mode 100644 index 000000000..229f9b1bf --- /dev/null +++ b/analysis/src/vendor/odoc_parser/odoc_lexer.mll @@ -0,0 +1,449 @@ +{ + +let unescape_word : string -> string = fun s -> + (* The common case is that there are no escape sequences. *) + match String.index s '\\' with + | exception Not_found -> s + | _ -> + let buffer = Buffer.create (String.length s) in + let rec scan_word index = + if index >= String.length s then + () + else + let c = s.[index] in + let c, increment = + match c with + | '\\' -> + if index + 1 < String.length s then + match s.[index + 1] with + | '{' | '}' | '[' | ']' | '@' as c -> c, 2 + | _ -> c, 1 + else c, 1 + | _ -> c, 1 + in + Buffer.add_char buffer c; + scan_word (index + increment) + in + scan_word 0; + Buffer.contents buffer + + + +(* This is used for code and verbatim blocks. It can be done with a regular + expression, but the regexp gets quite ugly, so a function is easier to + understand. *) +let trim_leading_blank_lines : string -> string = fun s -> + let rec scan_for_last_newline : int -> int -> int = + fun index trim_until -> + if index >= String.length s then + String.length s + else + match s.[index] with + | ' ' | '\t' | '\r' -> scan_for_last_newline (index + 1) trim_until + | '\n' -> scan_for_last_newline (index + 1) (index + 1) + | _ -> trim_until + in + let trim_until = scan_for_last_newline 0 0 in + String.sub s trim_until (String.length s - trim_until) + +let trim_trailing_blank_lines : string -> string = fun s -> + let rec scan_for_last_newline : int -> int option -> int option = + fun index trim_from -> + if index < 0 then + Some 0 + else + match s.[index] with + | ' ' | '\t' | '\r' -> scan_for_last_newline (index - 1) trim_from + | '\n' -> scan_for_last_newline (index - 1) (Some index) + | _ -> trim_from + in + let last = String.length s - 1 in + match scan_for_last_newline last None with + | None -> + s + | Some trim_from -> + let trim_from = + if trim_from > 0 && s.[trim_from - 1] = '\r' then + trim_from - 1 + else + trim_from + in + String.sub s 0 trim_from + + + +module Location = Location_ +module Error = Error + + + +(* Assuming an ASCII-compatible input encoding here. *) +let heading_level level = + Char.code level - Char.code '0' + + + +type input = { + file : string; + offset_to_location : int -> Location.point; + lexbuf : Lexing.lexbuf; +} + +let offset_span_to_location + ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by input = + let start = + match start_offset with + | None -> Lexing.lexeme_start input.lexbuf + | Some s -> s + in + let start = + match adjust_start_by with + | None -> start + | Some s -> start + String.length s + in + let end_ = + match end_offset with + | None -> Lexing.lexeme_end input.lexbuf + | Some e -> e + in + let end_ = + match adjust_end_by with + | None -> end_ + | Some s -> end_ - String.length s + in + { + Location_.file = input.file; + start = input.offset_to_location start; + end_ = input.offset_to_location end_; + } + +let emit input ?start_offset ?adjust_start_by ?adjust_end_by token = + let location = + offset_span_to_location + ?start_offset ?adjust_start_by ?adjust_end_by input + in + Location.at location token + +let raise_error + input ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by error = + let location = + offset_span_to_location + ?start_offset ?adjust_start_by ?end_offset ?adjust_end_by input + in + Error.raise_exception (error location) + +let reference_token start target = + match start with + | "{!" -> `Simple_reference target + | "{{!" -> `Begin_reference_with_replacement_text target + | "{:" -> `Simple_link target + | "{{:" -> `Begin_link_with_replacement_text target + | _ -> assert false + +let emit_reference input start target = + let target = String.trim target in + let token = reference_token start target in + if target = "" then + raise_error input (Parse_error.cannot_be_empty ~what:(Token.describe token)) + else + emit input token + + + +let trim_leading_space_or_accept_whitespace input text = + match text.[0] with + | ' ' -> String.sub text 1 (String.length text - 1) + | '\t' | '\r' | '\n' -> text + | exception Invalid_argument _ -> "" + | _ -> + raise_error + input + ~end_offset:(Lexing.lexeme_start input.lexbuf + 2) + Parse_error.no_leading_whitespace_in_verbatim + +let trim_trailing_space_or_accept_whitespace input text = + match text.[String.length text - 1] with + | ' ' -> String.sub text 0 (String.length text - 1) + | '\t' | '\r' | '\n' -> text + | exception Invalid_argument _ -> "" + | _ -> + raise_error + input + ~start_offset:(Lexing.lexeme_end input.lexbuf - 2) + Parse_error.no_trailing_whitespace_in_verbatim + +} + + + +let markup_char = + ['{' '}' '[' ']' '@'] +let space_char = + [' ' '\t' '\n' '\r'] +let bullet_char = + ['-' '+'] + +let word_char = + (_ # markup_char # space_char # bullet_char) | ('\\' markup_char) + +let horizontal_space = + [' ' '\t'] +let newline = + '\n' | "\r\n" + +let reference_start = + "{!" | "{{!" | "{:" | "{{:" + +let code_block_text = + ([^ ']'] | ']'+ [^ ']' '}'])* ']'* +let verbatim_text = + ([^ 'v'] | 'v'+ [^ 'v' '}'])* 'v'* + + + +rule token input = parse + | horizontal_space* eof + { emit input `End } + + | (horizontal_space* newline as prefix) + horizontal_space* ((newline horizontal_space*)+ as suffix) + { emit input `Blank_line ~adjust_start_by:prefix ~adjust_end_by:suffix } + + | horizontal_space* newline horizontal_space* + { emit input `Single_newline } + + | horizontal_space+ + { emit input `Space } + + | (horizontal_space* (newline horizontal_space*)? as p) '}' + { emit input `Right_brace ~adjust_start_by:p } + + | word_char (word_char | bullet_char | '@')* + | bullet_char (word_char | bullet_char | '@')+ as w + { emit input (`Word (unescape_word w)) } + + | '[' + { code_span + (Buffer.create 1024) 0 (Lexing.lexeme_start lexbuf) input lexbuf } + + | '-' + { emit input `Minus } + + | '+' + { emit input `Plus } + + | "{b" + { emit input (`Begin_style `Bold) } + + | "{i" + { emit input (`Begin_style `Italic) } + + | "{e" + { emit input (`Begin_style `Emphasis) } + + | "{^" + { emit input (`Begin_style `Superscript) } + + | "{_" + { emit input (`Begin_style `Subscript) } + + | "{!modules:" ([^ '}']* as modules) '}' + { emit input (`Modules modules) } + + | (reference_start as start) ([^ '}']* as target) '}' + { emit_reference input start target } + + | "{[" (code_block_text as c) "]}" + { let c = trim_leading_blank_lines c in + let c = trim_trailing_blank_lines c in + emit input (`Code_block c) } + + | "{v" (verbatim_text as t) "v}" + { let t = trim_leading_space_or_accept_whitespace input t in + let t = trim_trailing_space_or_accept_whitespace input t in + let t = trim_leading_blank_lines t in + let t = trim_trailing_blank_lines t in + emit input (`Verbatim t) } + + | "{ul" + { emit input (`Begin_list `Unordered) } + + | "{ol" + { emit input (`Begin_list `Ordered) } + + | "{li" + { emit input (`Begin_list_item `Li) } + + | "{-" + { emit input (`Begin_list_item `Dash) } + + | '{' (['0'-'9'] as level) ':' (([^ '}'] # space_char)+ as label) + { emit input (`Begin_section_heading (heading_level level, Some label)) } + + | '{' (['0'-'9'] as level) + { emit input (`Begin_section_heading (heading_level level, None)) } + + | "@example" horizontal_space* ([^ '{']* as lang) "{[" (code_block_text as content) "]}" + { emit input (`Example (lang, content)) } + + | "@author" horizontal_space+ ([^ '\r' '\n']* as author) + { emit input (`Tag (`Author author)) } + + | "@deprecated" + { emit input (`Tag `Deprecated) } + + | "@doc" horizontal_space+ ([^ '\r' '\n']* as doc) + { emit input (`Doc doc) } + + | "@param" horizontal_space+ ((_ # space_char)+ as name) + { emit input (`Tag (`Param name)) } + + | "@raise" horizontal_space+ ((_ # space_char)+ as name) + { emit input (`Tag (`Raise name)) } + + | "@return" + { emit input (`Tag `Return) } + + | "@see" horizontal_space* '<' ([^ '>']* as url) '>' + { emit input (`Tag (`See (`Url, url))) } + + | "@see" horizontal_space* '\'' ([^ '>']* as filename) '\'' + { emit input (`Tag (`See (`File, filename))) } + + | "@see" horizontal_space* '"' ([^ '>']* as name) '"' + { emit input (`Tag (`See (`Document, name))) } + + | "@since" horizontal_space+ ([^ '\r' '\n']* as version) + { emit input (`Tag (`Since version)) } + + | "@before" horizontal_space+ ((_ # space_char)+ as version) + { emit input (`Tag (`Before version)) } + + | "@version" horizontal_space+ ([^ '\r' '\n']* as version) + { emit input (`Tag (`Version version)) } + + | "@canonical" horizontal_space+ ([^ '\r' '\n']* as identifier) + { emit input (`Tag (`Canonical identifier)) } + + | "@inline" + { emit input (`Tag `Inline) } + + | "@open" + { emit input (`Tag `Open) } + + | "@closed" + { emit input (`Tag `Closed) } + + + + + | '{' (['0'-'9'] ['0'-'9']+ as level) + { raise_error input (Parse_error.bad_section_level level) } + + | ('{' ['0'-'9'] as prefix) ':' + { raise_error + input + ~adjust_start_by:prefix + (Parse_error.cannot_be_empty ~what:"heading label") } + + | '{' _? as markup + { raise_error input (Parse_error.bad_markup markup) } + + | ']' + { raise_error input Parse_error.unpaired_right_bracket } + + | '@' ("author" | "since" | "version" | "canonical") + { raise_error + input + (Parse_error.cannot_be_empty + ~what:(Printf.sprintf "'%s'" (Lexing.lexeme lexbuf))) } + + | "@param" + { raise_error input Parse_error.truncated_param } + + | "@raise" + { raise_error input Parse_error.truncated_raise } + + | "@before" + { raise_error input Parse_error.truncated_before } + + | "@see" + { raise_error input Parse_error.truncated_see } + + | '@' ['a'-'z' 'A'-'Z']+ as tag + { raise_error input (Parse_error.unknown_tag tag) } + + | '@' + { raise_error input Parse_error.stray_at } + + | '\r' + { raise_error input Parse_error.stray_cr } + + | "{!modules:" [^ '}']* eof + { raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Modules ""))) } + + | (reference_start as start) [^ '}']* eof + { raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (reference_token start ""))) } + + | "{[" code_block_text eof + { raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Code_block ""))) } + + | "{v" verbatim_text eof + { raise_error + input + ~start_offset:(Lexing.lexeme_end lexbuf) + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Verbatim ""))) } + + + +and code_span buffer nesting_level start_offset input = parse + | ']' + { if nesting_level = 0 then + emit input (`Code_span (Buffer.contents buffer)) ~start_offset + else begin + Buffer.add_char buffer ']'; + code_span buffer (nesting_level - 1) start_offset input lexbuf + end } + + | '[' + { Buffer.add_char buffer '['; + code_span buffer (nesting_level + 1) start_offset input lexbuf } + + | '\\' ('[' | ']' as c) + { Buffer.add_char buffer c; + code_span buffer nesting_level start_offset input lexbuf } + + | newline newline + { raise_error + input + (Parse_error.not_allowed + ~what:(Token.describe `Blank_line) + ~in_what:(Token.describe (`Code_span ""))) } + + | eof + { raise_error + input + (Parse_error.not_allowed + ~what:(Token.describe `End) + ~in_what:(Token.describe (`Code_span ""))) } + + | _ as c + { Buffer.add_char buffer c; + code_span buffer nesting_level start_offset input lexbuf } diff --git a/analysis/src/vendor/odoc_parser/parse_error.ml b/analysis/src/vendor/odoc_parser/parse_error.ml new file mode 100644 index 000000000..1df239dd9 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/parse_error.ml @@ -0,0 +1,72 @@ +module Location = Location_ +module Error = Error + + + +let bad_markup : string -> Location.span -> Error.t = + Error.format "'%s': bad markup" + +let bad_section_level : string -> Location.span -> Error.t = + Error.format "'%s': bad section level (2-4 allowed)" + +let cannot_be_empty : what:string -> Location.span -> Error.t = fun ~what -> + Error.format "%s cannot be empty" what + +let must_begin_on_its_own_line : what:string -> Location.span -> Error.t = + fun ~what -> + Error.format "%s must begin on its own line" what + +let must_be_followed_by_whitespace : what:string -> Location.span -> Error.t = + fun ~what -> + Error.format "%s must be followed by space, a tab, or a new line" what + +let not_allowed + : ?suggestion:string -> what:string -> in_what:string -> Location.span -> + Error.t = + fun ?suggestion ~what ~in_what location -> + let message = Printf.sprintf "%s is not allowed in %s" what in_what in + let message = + match suggestion with + | None -> message + | Some suggestion -> Printf.sprintf "%s\nSuggestion: %s" message suggestion + in + Error.make message location + +let no_leading_whitespace_in_verbatim : Location.span -> Error.t = + Error.make "'{v' must be followed by whitespace" + +let no_trailing_whitespace_in_verbatim : Location.span -> Error.t = + Error.make "'v}' must be preceded by whitespace" + +let only_one_title_allowed : Location.span -> Error.t = + Error.make "only one title-level heading is allowed" + +let sections_not_allowed : Location.span -> Error.t = + Error.make "sections not allowed in this comment" + +let stray_at : Location.span -> Error.t = + Error.make "stray '@'" + +let stray_cr : Location.span -> Error.t = + Error.make "stray '\\r' (carriage return character)" + +let truncated_before : Location.span -> Error.t = + Error.make "'@before' expects version number on the same line" + +let truncated_param : Location.span -> Error.t = + Error.make "'@param' expects parameter name on the same line" + +let truncated_raise : Location.span -> Error.t = + Error.make "'@raise' expects exception constructor on the same line" + +let truncated_see : Location.span -> Error.t = + Error.make "'@see' must be followed by , 'file', or \"document title\"" + +let unknown_tag : string -> Location.span -> Error.t = + Error.format "unknown tag '%s'" + +let unpaired_right_brace : Location.span -> Error.t = + Error.make "unpaired '}' (end of markup)" + +let unpaired_right_bracket : Location.span -> Error.t = + Error.make "unpaired ']' (end of code)" diff --git a/analysis/src/vendor/odoc_parser/parser_.ml b/analysis/src/vendor/odoc_parser/parser_.ml new file mode 100644 index 000000000..fc303e985 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/parser_.ml @@ -0,0 +1,111 @@ +(* odoc uses an ocamllex lexer. The "engine" for such lexers is the standard + [Lexing] module. + + As the [Lexing] module reads the input, it keeps track of only the byte + offset into the input. It is normally the job of each particular lexer + implementation to decide which character sequences count as newlines, and + keep track of line/column locations. This is usually done by writing several + extra regular expressions, and calling [Lexing.new_line] at the right time. + + Keeping track of newlines like this makes the odoc lexer somewhat too + diffiult to read, however. To factor the aspect of keeping track of newlines + fully out of the odoc lexer, instead of having it keep track of newlines as + it's scanning the input, the input is pre-scanned before feeding it into the + lexer. A table of all the newlines is assembled, and used to convert offsets + into line/column pairs after the lexer emits tokens. + + [offset_to_location ~input ~comment_location offset] converts the byte + [offset], relative to the beginning of a comment, into a location, relative + to the beginning of the file containing the comment. [input] is the comment + text, and [comment_location] is the location of the comment within its file. + The function is meant to be partially applied to its first two arguments, at + which point it creates the table described above. The remaining function is + then passed to the lexer, so it can apply the table to its emitted tokens. *) +let offset_to_location + : input:string -> comment_location:Lexing.position -> + (int -> Location_.point) = + fun ~input ~comment_location -> + + let rec find_newlines line_number input_index newlines_accumulator = + if input_index >= String.length input then + newlines_accumulator + else + (* This is good enough to detect CR-LF also. *) + if input.[input_index] = '\n' then + find_newlines + (line_number + 1) (input_index + 1) + ((line_number + 1, input_index + 1)::newlines_accumulator) + else + find_newlines line_number (input_index + 1) newlines_accumulator + in + + let reversed_newlines : (int * int) list = + find_newlines 1 0 [(1, 0)] in + + fun byte_offset -> + let rec scan_to_last_newline reversed_newlines_prefix = + match reversed_newlines_prefix with + | [] -> + assert false + | (line_in_comment, line_start_offset)::prefix -> + if line_start_offset > byte_offset then + scan_to_last_newline prefix + else + let column_in_comment = byte_offset - line_start_offset in + let line_in_file = + line_in_comment + comment_location.Lexing.pos_lnum - 1 in + let column_in_file = + if line_in_comment = 1 then + column_in_comment + + comment_location.Lexing.pos_cnum - + comment_location.Lexing.pos_bol + else + column_in_comment + in + {Location_.line = line_in_file; column = column_in_file} + in + scan_to_last_newline reversed_newlines + + + +let parse_comment + ~permissive ~sections_allowed ~containing_definition ~location ~text = + + let token_stream = + let lexbuf = Lexing.from_string text in + let offset_to_location = + offset_to_location ~input:text ~comment_location:location in + let input : Odoc_lexer.input = + { + file = location.Lexing.pos_fname; + offset_to_location; + lexbuf; + } + in + Stream.from (fun _token_index -> Some (Odoc_lexer.token input lexbuf)) + in + + match Syntax.parse token_stream with + | Error.Error error -> + {Error.result = Error.Error error; warnings = []} + | Ok ast -> + Semantics.ast_to_comment + ~permissive + ~sections_allowed + ~parent_of_sections:containing_definition + ast + +let errors_to_warnings parsed = + match Error.(parsed.result) with + | Error.Ok _ -> + parsed + + | Error fatal_error -> + { + result = Ok []; + warnings = fatal_error::parsed.warnings; + } + + + +type sections_allowed = Ast.sections_allowed diff --git a/analysis/src/vendor/odoc_parser/parser_.mli b/analysis/src/vendor/odoc_parser/parser_.mli new file mode 100644 index 000000000..4b8e92852 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/parser_.mli @@ -0,0 +1,16 @@ +type sections_allowed = Ast.sections_allowed + +val parse_comment : + permissive:bool -> + sections_allowed:sections_allowed -> + containing_definition:Paths.Identifier.label_parent -> + location:Lexing.position -> + text:string -> + ((Comment.docs, Error.t) Error.result) Error.with_warnings + +(** Converts fatal errors to warnings for now, by emitting a blank comment. This + is a temporary measure, because the code that drives the parser does not yet + have proper error handling written. *) +val errors_to_warnings : + ((Comment.docs, Error.t) Error.result) Error.with_warnings -> + ((Comment.docs, Error.t) Error.result) Error.with_warnings diff --git a/analysis/src/vendor/odoc_parser/paths.ml b/analysis/src/vendor/odoc_parser/paths.ml new file mode 100644 index 000000000..87683a628 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/paths.ml @@ -0,0 +1,1671 @@ +(* + * Copyright (c) 2014 Leo White + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +module Kind = Paths_types.Kind + +open Kind + +module Reversed = struct + type elt = + | Root of string + | Module of string + | ModuleType of string + | Argument of int * string + + type t = elt list + + let rec remove_prefix prefix ~of_ = + match prefix, of_ with + | x1 :: xs1, x2 :: xs2 when x1 = x2 -> + remove_prefix xs1 ~of_:xs2 + | _, _ -> of_ +end + +module Identifier = struct + + include Paths_types.Identifier + + let signature_of_module : module_ -> _ = function + | Root _ | Module _ | Argument _ as x -> x + + let signature_of_module_type : module_type -> _ = function + | ModuleType _ as x -> x + + let class_signature_of_class : class_ -> _ = function + | Class _ as x -> x + + let class_signature_of_class_type : class_type -> _ = function + | ClassType _ as x -> x + + let datatype_of_type : type_ -> datatype = function + | x -> x + + let parent_of_signature : signature -> parent = function + | Root _ | Module _ | Argument _ | ModuleType _ as x -> x + + let parent_of_class_signature : class_signature -> parent = + function Class _ | ClassType _ as x -> x + + let parent_of_datatype : datatype -> parent = + function Type _ | CoreType _ as x -> x + + let label_parent_of_parent : parent -> label_parent = + function Root _ | Module _ | Argument _ | ModuleType _ | Type _ + | CoreType _ | Class _ | ClassType _ as x -> x + + let label_parent_of_page : page -> label_parent = + function Page _ as x -> x + + let any : type k. k t -> any = function + | Root _ as x -> x + | Page _ as x -> x + | Module _ as x -> x + | Argument _ as x -> x + | ModuleType _ as x -> x + | Type _ as x -> x + | CoreType _ as x -> x + | Constructor _ as x -> x + | Field _ as x -> x + | Extension _ as x -> x + | Exception _ as x -> x + | CoreException _ as x -> x + | Value _ as x -> x + | Class _ as x -> x + | ClassType _ as x -> x + | Method _ as x -> x + | InstanceVariable _ as x -> x + | Label _ as x -> x + + let name : type k. k t -> string = function + | Root(_, name) -> name + | Page(_, name) -> name + | Module(_, name) -> name + | Argument(_, _, name) -> name + | ModuleType(_, name) -> name + | Type(_, name) -> name + | CoreType name -> name + | Constructor(_, name) -> name + | Field(_, name) -> name + | Extension(_, name) -> name + | Exception(_, name) -> name + | CoreException name -> name + | Value(_, name) -> name + | Class(_, name) -> name + | ClassType(_, name) -> name + | Method(_, name) -> name + | InstanceVariable(_, name) -> name + | Label(_, name) -> name + + let equal id1 id2 = + let rec loop : type k. k t -> k t -> bool = + fun id1 id2 -> + match id1, id2 with + | Root(r1, s1), Root(r2, s2) -> + s1 = s2 && Root.equal r1 r2 + | Module(id1, s1), Module(id2, s2) -> + s1 = s2 && loop id1 id2 + | Argument(id1, n1, s1), Argument(id2, n2, s2) -> + n1 = n2 && s1 = s2 && loop id1 id2 + | ModuleType(id1, s1), ModuleType(id2, s2) -> + s1 = s2 && loop id1 id2 + | Type(id1, s1), Type(id2, s2) -> + s1 = s2 && loop id1 id2 + | CoreType s1, CoreType s2 -> + s1 = s2 + | Constructor(id1, s1), Constructor(id2, s2) -> + s1 = s2 && loop id1 id2 + | Field(id1, s1), Field(id2, s2) -> + s1 = s2 && loop id1 id2 + | Extension(id1, s1), Extension(id2, s2) -> + s1 = s2 && loop id1 id2 + | Exception(id1, s1), Exception(id2, s2) -> + s1 = s2 && loop id1 id2 + | CoreException s1, CoreException s2 -> + s1 = s2 + | Value(id1, s1), Value(id2, s2) -> + s1 = s2 && loop id1 id2 + | Class(id1, s1), Class(id2, s2) -> + s1 = s2 && loop id1 id2 + | ClassType(id1, s1), ClassType(id2, s2) -> + s1 = s2 && loop id1 id2 + | Method(id1, s1), Method(id2, s2) -> + s1 = s2 && loop id1 id2 + | InstanceVariable(id1, s1), InstanceVariable(id2, s2) -> + s1 = s2 && loop id1 id2 + | Label(id1, s1), Label(id2, s2) -> + s1 = s2 && loop id1 id2 + | _, _ -> false + in + loop id1 id2 + + let hash id = + let rec loop : type k. k t -> int = + fun id -> + match id with + | Root(r, s) -> + Hashtbl.hash (1, Root.hash r, s) + | Page(r, s) -> + Hashtbl.hash (2, Root.hash r, s) + | Module(id, s) -> + Hashtbl.hash (3, loop id, s) + | Argument(id, n, s) -> + Hashtbl.hash (4, loop id, n, s) + | ModuleType(id, s) -> + Hashtbl.hash (5, loop id, s) + | Type(id, s) -> + Hashtbl.hash (6, loop id, s) + | CoreType s -> + Hashtbl.hash (7, s) + | Constructor(id, s) -> + Hashtbl.hash (8, loop id, s) + | Field(id, s) -> + Hashtbl.hash (9, loop id, s) + | Extension(id, s) -> + Hashtbl.hash (10, loop id, s) + | Exception(id, s) -> + Hashtbl.hash (11, loop id, s) + | CoreException s -> + Hashtbl.hash (12, s) + | Value(id, s) -> + Hashtbl.hash (13, loop id, s) + | Class(id, s) -> + Hashtbl.hash (14, loop id, s) + | ClassType(id, s) -> + Hashtbl.hash (15, loop id, s) + | Method(id, s) -> + Hashtbl.hash (16, loop id, s) + | InstanceVariable(id, s) -> + Hashtbl.hash (17, loop id, s) + | Label(id, s) -> + Hashtbl.hash (18, loop id, s) + in + loop id + + let rec signature_root : signature -> Root.t = function + | Root(r, _) -> r + | Module(id, _) -> signature_root id + | Argument(id, _, _) -> signature_root id + | ModuleType(id, _) -> signature_root id + + let module_root : module_ -> Root.t = function + | Root(r, _) -> r + | Module(id, _) -> signature_root id + | Argument(id, _, _) -> signature_root id + + let module_type_root : module_type -> Root.t = function + | ModuleType(id, _) -> signature_root id + + let class_signature_root : class_signature -> Root.t = function + | Class(id, _) + | ClassType(id, _) -> signature_root id + + let label_parent_root : label_parent -> Root.t = function + | Root (r, _) -> r + | Page (r, _) -> r + | Module (s, _) -> signature_root s + | Argument (s, _, _) -> signature_root s + | ModuleType (s, _) -> signature_root s + | Type (s, _) -> signature_root s + | CoreType _ -> assert false + | Class (s, _) -> signature_root s + | ClassType (s, _) -> signature_root s + + let to_reversed i = + let rec loop acc : signature -> Reversed.t = function + | Root (_, s) -> Reversed.Root s :: acc + | Module (i, s) -> loop (Reversed.Module s :: acc) i + | ModuleType (i, s) -> loop (Reversed.ModuleType s :: acc) i + | Argument (i, d, s) -> loop (Reversed.Argument (d, s) :: acc) i + in + loop [] i +end + + + +module Path = struct + + (* Separate types module to avoid repeating type definitions *) + module rec Types : sig + + module Resolved = Paths_types.Resolved_path + + module Path = Paths_types.Path + + end = Types + + let rec equal_resolved_path : type k. k Types.Resolved.t -> k Types.Resolved.t -> bool = + fun p1 p2 -> + let open Types.Resolved in + match p1, p2 with + | Identifier id1, Identifier id2 -> + Identifier.equal id1 id2 + | Subst(sub1, p1), Subst(sub2, p2) -> + equal_resolved_path p1 p2 + && equal_resolved_path sub1 sub2 + | SubstAlias(sub1, p1), SubstAlias(sub2, p2) -> + equal_resolved_path p1 p2 + && equal_resolved_path sub1 sub2 + | Module(p1, s1), Module(p2, s2) -> + s1 = s2 && equal_resolved_path p1 p2 + | Apply(p1, arg1), Apply(p2, arg2) -> + equal_path arg1 arg2 + && equal_resolved_path p1 p2 + | ModuleType(p1, s1), ModuleType(p2, s2) -> + s1 = s2 && equal_resolved_path p1 p2 + | Type(p1, s1), Type(p2, s2) -> + s1 = s2 && equal_resolved_path p1 p2 + | Class(p1, s1), Class(p2, s2) -> + s1 = s2 && equal_resolved_path p1 p2 + | ClassType(p1, s1), ClassType(p2, s2) -> + s1 = s2 && equal_resolved_path p1 p2 + | _, _ -> false + + and equal_path : type k. k Types.Path.t -> k Types.Path.t -> bool = + fun p1 p2 -> + let open Types.Path in + match p1, p2 with + | Resolved p1, Resolved p2 -> + equal_resolved_path p1 p2 + | Root s1, Root s2 -> + s1 = s2 + | Dot(p1, s1), Dot(p2, s2) -> + s1 = s2 && equal_path p1 p2 + | Apply(p1, arg1), Apply(p2, arg2) -> + equal_path arg1 arg2 && equal_path p1 p2 + | _, _ -> false + + let rec hash_resolved_path : type k. k Types.Resolved.t -> int = + fun p -> + let open Types.Resolved in + match p with + | Identifier id -> + Identifier.hash id + | Subst(sub, p) -> + Hashtbl.hash (19, hash_resolved_path sub, + hash_resolved_path p) + | SubstAlias(sub, p) -> + Hashtbl.hash (20, hash_resolved_path sub, + hash_resolved_path p) + | Hidden p -> Hashtbl.hash (21, hash_resolved_path p) + | Module(p, s) -> + Hashtbl.hash (22, hash_resolved_path p, s) + | Canonical(p, canonical) -> + Hashtbl.hash (23, hash_resolved_path p, hash_path canonical) + | Apply(p, arg) -> + Hashtbl.hash (24, hash_resolved_path p, hash_path arg) + | ModuleType(p, s) -> + Hashtbl.hash (25, hash_resolved_path p, s) + | Type(p, s) -> + Hashtbl.hash (26, hash_resolved_path p, s) + | Class(p, s) -> + Hashtbl.hash (27, hash_resolved_path p, s) + | ClassType(p, s) -> + Hashtbl.hash (28, hash_resolved_path p, s) + + and hash_path : type k. k Types.Path.t -> int = + fun p -> + let open Types.Path in + match p with + | Resolved p -> hash_resolved_path p + | Root s -> + Hashtbl.hash (29, s) + | Forward s -> + Hashtbl.hash (30, s) + | Dot(p, s) -> + Hashtbl.hash (31, hash_path p, s) + | Apply(p, arg) -> + Hashtbl.hash (32, hash_path p, hash_path arg) + + let equal p1 p2 = equal_path p1 p2 + + let hash p = hash_path p + + let rec is_resolved_hidden : type k. k Types.Resolved.t -> bool = + let open Types.Resolved in + function + | Identifier _ -> false + | Canonical (_, _) -> false + | Hidden _ -> true + | Subst(p1, p2) -> is_resolved_hidden p1 || is_resolved_hidden p2 + | SubstAlias(p1, p2) -> is_resolved_hidden p1 || is_resolved_hidden p2 + | Module (p, _) -> is_resolved_hidden p + | Apply (p, _) -> is_resolved_hidden p + | ModuleType (p, _) -> is_resolved_hidden p + | Type (p, _) -> is_resolved_hidden p + | Class (p, _) -> is_resolved_hidden p + | ClassType (p, _) -> is_resolved_hidden p + + and is_path_hidden : type k. k Types.Path.t -> bool = + let open Types.Path in + function + | Resolved r -> is_resolved_hidden r + | Root _ -> false + | Forward _ -> false + | Dot(p, _) -> is_path_hidden p + | Apply(p1, p2) -> is_path_hidden p1 || is_path_hidden p2 + + module Resolved = struct + + open Identifier + + include Types.Resolved + + let ident_module : Identifier.module_ -> _ = function + | Root _ | Module _ | Argument _ as x -> Identifier x + + let ident_module_type : Identifier.module_type -> _ = function + | ModuleType _ as x -> Identifier x + + let ident_type : Identifier.type_ -> _ = function + | Type _ | CoreType _ as x -> Identifier x + + let ident_class : Identifier.class_ -> _ = function + | Class _ as x -> Identifier x + + let ident_class_type : Identifier.class_type -> _ = function + | ClassType _ as x -> Identifier x + + let any : type k. k t -> any = function + | Identifier (Root _) as x -> x + | Identifier (Module _) as x -> x + | Identifier (Argument _) as x -> x + | Identifier (ModuleType _) as x -> x + | Identifier (Type _) as x -> x + | Identifier (CoreType _) as x -> x + | Identifier (Class _) as x -> x + | Identifier (ClassType _) as x -> x + | Subst _ as x -> x + | SubstAlias _ as x -> x + | Hidden _ as x -> x + | Module _ as x -> x + | Canonical _ as x -> x + | Apply _ as x -> x + | ModuleType _ as x -> x + | Type _ as x -> x + | Class _ as x -> x + | ClassType _ as x -> x + + let open_module : 'k. module_ -> ([< kind > `Module ] as 'k) t = function + | Identifier (Root _ | Module _ | Argument _) | Subst _ | SubstAlias _ + | Hidden _ | Module _ | Canonical _ | Apply _ as x -> x + + let rec parent_module_type_identifier : module_type -> Identifier.signature = function + | Identifier id -> Identifier.signature_of_module_type id + | ModuleType(m, n) -> ModuleType(parent_module_identifier m, n) + + and parent_module_identifier : module_ -> Identifier.signature = function + | Identifier id -> Identifier.signature_of_module id + | Subst(sub, _) -> parent_module_type_identifier sub + | SubstAlias(sub, _) -> parent_module_identifier sub + | Hidden p -> parent_module_identifier p + | Module(m, n) -> Module(parent_module_identifier m, n) + | Canonical(_, Types.Path.Resolved p) -> parent_module_identifier p + | Canonical(p, _) -> parent_module_identifier p + | Apply(m, _) -> parent_module_identifier m + + let rec identifier : type k. k t -> k Identifier.t = function + | Identifier id -> id + | Subst(_, p) -> identifier (open_module p) + | SubstAlias(_, p) -> identifier (open_module p) + | Hidden p -> identifier (open_module p) + | Module(m, n) -> Module(parent_module_identifier m, n) + | Canonical(_, Types.Path.Resolved p) -> begin + match identifier p with + | Root _ | Module _ | Argument _ as x -> x + end + | Canonical(p, _) -> begin + match identifier p with + | Root _ | Module _ | Argument _ as x -> x + end + | Apply(m, _) -> begin + match identifier m with + | Root _ | Module _ | Argument _ as x -> x + end + | ModuleType(m, n) -> ModuleType(parent_module_identifier m, n) + | Type(m, n) -> Type(parent_module_identifier m, n) + | Class(m, n) -> Class(parent_module_identifier m, n) + | ClassType(m, n) -> ClassType(parent_module_identifier m, n) + + let equal p1 p2 = equal_resolved_path p1 p2 + + let hash p = hash_resolved_path p + + type 'kind rebase_result = + | Stop of 'kind t + | Continue of 'kind Identifier.t * Reversed.t + + let rec rebase_module_path : Reversed.t -> module_ -> Kind.path_module rebase_result = + fun new_base t -> + match t with + | Identifier id -> + let rev = Identifier.(to_reversed @@ signature_of_module id) in + let new_base' = Reversed.remove_prefix rev ~of_:new_base in + if new_base == new_base' then + Stop t + else + Continue (id, new_base') + | Subst (_, p) + | SubstAlias (_, p) + | Hidden p -> begin + match rebase_module_path new_base p with + | Stop p' when p == p' -> Stop t + | otherwise -> otherwise + end + | Module (m, s) -> + begin match rebase_module_path new_base m with + | Stop m' -> if m == m' then Stop t else Stop (Module (m', s)) + | Continue (id, new_base) -> + let id = Identifier.Module(Identifier.signature_of_module id, s) in + match new_base with + | Reversed.Module s' :: rest when s = s' -> + Continue (id, rest) + | _ -> + Stop (Identifier id) + end + | Canonical (_, Types.Path.Resolved p) -> + (* We only care about printing at this point, so let's drop the lhs. *) + rebase_module_path new_base p + | Canonical (rp, p) -> + begin match rebase_module_path new_base rp with + | Stop rp' -> Stop (Canonical (rp', p)) + | _ -> + (* We might come back at some point with a resolved rhs? So we don't want to + drop it. *) + Stop t + end + | Apply _ -> Stop t + (* TODO: rewrite which side? *) + + let rebase : type k. Reversed.t -> k t -> k t = + fun new_base t -> + match t with + | Identifier _ -> t + | Subst _ -> t (* TODO: rewrite which side? *) + | SubstAlias _ -> t (* TODO: rewrite which side? *) + | Hidden p -> begin + match rebase_module_path new_base p with + | Stop p' -> + if p == p' then t else open_module p' + | Continue (id, _) -> open_module (Identifier id) + end + | Module (mp, s) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> + Identifier Identifier.(Module (signature_of_module id, s)) + | Stop mp' -> Module (mp', s) + end + | Canonical (p, Types.Path.Resolved rp) -> + begin match rebase_module_path new_base rp with + | Continue (id, _) -> ident_module id + | Stop rp -> + (* Easier to reexport a canonical than get the type for rp right... *) + Canonical (p, Types.Path.Resolved rp) + end + | Canonical (rp, p) -> + begin match rebase_module_path new_base rp with + | Stop rp' -> Canonical (rp', p) + | _ -> + (* We might come back at some point with a resolved rhs? So we don't want to + drop it. *) + t + end + | Apply (mp, arg) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> Apply (Identifier id, arg) + | Stop mp' -> Apply (mp', arg) + end + | ModuleType (mp, s) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> + Identifier Identifier.(ModuleType (signature_of_module id, s)) + | Stop mp' -> ModuleType (mp', s) + end + | Type (mp, s) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> + Identifier Identifier.(Type (signature_of_module id, s)) + | Stop mp' -> Type (mp', s) + end + | Class (mp, s) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> + Identifier Identifier.(Class (signature_of_module id, s)) + | Stop mp' -> Class (mp', s) + end + | ClassType (mp, s) -> + begin match rebase_module_path new_base mp with + | Continue (id, _) -> + Identifier Identifier.(ClassType (signature_of_module id, s)) + | Stop mp' -> ClassType (mp', s) + end + + let rebase id t = + let rev = Identifier.to_reversed id in + rebase rev t + + let signature_of_module : module_ -> Kind.signature t = function + | Identifier (Root _) as x -> x + | Identifier (Module _) as x -> x + | Identifier (Argument _) as x -> x + | Module _ as x -> x + | Canonical _ as x -> x + | Apply _ as x -> x + | Hidden _ as x -> x + | Subst _ as x -> x + | SubstAlias _ as x -> x + + let rec equal_identifier : + type k. k Identifier.t -> k t -> bool = + fun id p -> + match id, p with + | _, Identifier id' -> Identifier.equal id id' + | Module (id, s1), Module (p, s2) when s1 = s2 -> + equal_identifier id (signature_of_module p) + | ModuleType (id, s1), ModuleType (p, s2) when s1 = s2 -> + equal_identifier id (signature_of_module p) + | _, _ -> + false + + + let is_hidden = is_resolved_hidden + end + + open Identifier + open Resolved + + include Types.Path + + let ident_module : Identifier.module_ -> _ = function + | Root _ | Module _ | Argument _ as x -> Resolved (Identifier x) + + let ident_module_type : Identifier.module_type -> _ = function + | ModuleType _ as x -> Resolved (Identifier x) + + let ident_type : Identifier.type_ -> _ = function + | Type _ | CoreType _ as x -> Resolved (Identifier x) + + let ident_class : Identifier.class_ -> _ = function + | Class _ as x -> Resolved (Identifier x) + + let ident_class_type : Identifier.class_type -> _ = function + | ClassType _ as x -> Resolved (Identifier x) + + let any : type k. k t -> any = function + | Resolved (Identifier (Root _)) as x -> x + | Resolved (Identifier (Module _)) as x -> x + | Resolved (Identifier (Argument _)) as x -> x + | Resolved (Identifier (ModuleType _)) as x -> x + | Resolved (Identifier (Type _)) as x -> x + | Resolved (Identifier (CoreType _)) as x -> x + | Resolved (Identifier (Class _)) as x -> x + | Resolved (Identifier (ClassType _)) as x -> x + | Resolved (Hidden _) as x -> x + | Resolved (Module _) as x -> x + | Resolved (Canonical _) as x -> x + | Resolved (Apply _) as x -> x + | Resolved (ModuleType _) as x -> x + | Resolved (Type _) as x -> x + | Resolved (Class _) as x -> x + | Resolved (ClassType _) as x -> x + | Resolved (Subst _) as x -> x + | Resolved (SubstAlias _) as x -> x + | Root _ as x -> x + | Forward _ as x -> x + | Dot _ as x -> x + | Apply _ as x -> x + + let module_ p name = + match p with + | Resolved p -> Resolved (Module(p, name)) + | p -> Dot(p, name) + + let apply p arg = + match p with + | Resolved p -> Resolved (Apply(p, arg)) + | p -> Apply(p, arg) + + let module_type p name = + match p with + | Resolved p -> Resolved (ModuleType(p, name)) + | p -> Dot(p, name) + + let type_ p name = + match p with + | Resolved p -> Resolved (Type(p, name)) + | p -> Dot(p, name) + + let class_ p name = + match p with + | Resolved p -> Resolved (Class(p, name)) + | p -> Dot(p, name) + + let class_type_ p name = + match p with + | Resolved p -> Resolved (ClassType(p, name)) + | p -> Dot(p, name) + + let type_of_class_type : class_type -> type_ = function + | Resolved (Identifier (Class _)) as x -> x + | Resolved (Identifier (ClassType _)) as x -> x + | Resolved (Class _) as x -> x + | Resolved (ClassType _) as x -> x + | Dot _ as x -> x + + let is_hidden = is_path_hidden +end + + + +module Fragment = struct + + module Resolved = struct + + include Paths_types.Resolved_fragment + + let signature_of_module : module_ -> signature = function + | Subst _ | SubstAlias _ | Module _ as x -> x + + let any_sort : type b c. (b, c) raw -> (b, sort) raw = + function + | Root as x -> x + | Subst _ as x -> x + | SubstAlias _ as x -> x + | Module (_,_) as x -> x + | Type (_,_) as x -> x + | Class (_,_) as x -> x + | ClassType (_,_) as x -> x + + let open_sort : module_ -> (Kind.fragment_module, [< sort > `Branch ]) raw = + function + | Module _ | Subst _ | SubstAlias _ as x -> x + + let open_module : module_ -> ([< kind > `Module ]) t = + function + | Module _ | Subst _ | SubstAlias _ as x -> x + + let any : type k. k t -> any = function + | Subst _ as x -> x + | SubstAlias _ as x -> x + | Module _ as x -> x + | Type _ as x -> x + | Class _ as x -> x + | ClassType _ as x -> x + + let rec parent_resolved_path root = function + | Root -> root + | Subst(sub, p) -> + Path.Resolved.Subst(sub, parent_resolved_path root (open_sort p)) + | SubstAlias(sub, p) -> + Path.Resolved.SubstAlias(sub, parent_resolved_path root (open_sort p)) + | Module(m, n) -> + Path.Resolved.Module(parent_resolved_path root m, n) + + let rec resolved_path + : type k. Path.Resolved.module_ -> + k t -> k Path.Resolved.t = + fun root frag -> + match frag with + | Subst(sub, p) -> + Path.Resolved.Subst(sub, resolved_path root p) + | SubstAlias(sub, p) -> + Path.Resolved.SubstAlias(sub, resolved_path root p) + | Module(m, n) -> + Path.Resolved.Module(parent_resolved_path root m, n) + | Type( m, n) -> + Path.Resolved.Type(parent_resolved_path root m, n) + | Class( m, n) -> + Path.Resolved.Class(parent_resolved_path root m, n) + | ClassType( m, n) -> + Path.Resolved.ClassType(parent_resolved_path root m, n) + + let rec parent_unresolved_path root = function + | Root -> root + | Subst(_, p) -> parent_unresolved_path root (open_sort p) + | SubstAlias(_, p) -> parent_unresolved_path root (open_sort p) + | Module(m, n) -> Path.Dot(parent_unresolved_path root m, n) + + let rec unresolved_path + : type k. Path.module_ -> k t -> k Path.t = + fun root -> function + | Subst(_, p) -> unresolved_path root (open_module p) + | SubstAlias(_, p) -> unresolved_path root (open_module p) + | Module(m, n) -> Path.Dot(parent_unresolved_path root m, n) + | Type( m, n) -> Path.Dot(parent_unresolved_path root m, n) + | Class( m, n) -> Path.Dot(parent_unresolved_path root m, n) + | ClassType( m, n) -> Path.Dot(parent_unresolved_path root m, n) + + let parent_path root frag = + match root with + | Path.Resolved root -> Path.Resolved (parent_resolved_path root frag) + | _ -> parent_unresolved_path root frag + + let path (root : Path.module_) frag = + match root with + | Path.Resolved root -> Path.Resolved (resolved_path root frag) + | _ -> unresolved_path root frag + + let rec parent_identifier root = function + | Root -> root + | Subst(sub, _) -> Path.Resolved.parent_module_type_identifier sub + | SubstAlias(sub, _) -> Path.Resolved.parent_module_identifier sub + | Module(m, n) -> Identifier.Module(parent_identifier root m, n) + + let rec identifier : + type k. Identifier.signature -> k t -> k Identifier.t = + fun root -> function + | Subst(_, p) -> identifier root (open_module p) + | SubstAlias(_, p) -> identifier root (open_module p) + | Module(m, n) -> Identifier.Module(parent_identifier root m, n) + | Type(m, n) -> Identifier.Type(parent_identifier root m, n) + | Class(m, n) -> Identifier.Class(parent_identifier root m, n) + | ClassType(m, n) -> + Identifier.ClassType(parent_identifier root m, n) + + type ('a, 'b) base_name = + | Base : ('a, [< sort > `Root]) base_name + | Branch : string * signature -> ('a, [< sort > `Branch]) base_name + + let rec split_parent + : type s . (fragment_module, s) raw -> ('a, s) base_name = + function + | Root -> Base + | Subst(_, p) -> split_parent (open_sort p) + | SubstAlias(_, p) -> split_parent (open_sort p) + | Module(m, name) -> + match split_parent m with + | Base -> Branch(name, Root) + | Branch(base, m) -> Branch(base, Module(m, name)) + + let rec split : type k . k t -> string * k t option = function + | Subst(_, p) -> split (open_module p) + | SubstAlias(_, p) -> split (open_module p) + | Module(m, name) -> begin + match split_parent m with + | Base -> name, None + | Branch(base, m)-> base, Some (Module(m, name)) + end + | Type(m, name) -> begin + match split_parent m with + | Base -> name, None + | Branch(base, m)-> base, Some (Type(m, name)) + end + | Class(m, name) -> begin + match split_parent m with + | Base -> name, None + | Branch(base, m)-> base, Some (Class(m, name)) + end + | ClassType(m, name) -> begin + match split_parent m with + | Base -> name, None + | Branch(base, m)-> base, Some (ClassType(m, name)) + end + + let equal p1 p2 = + let rec loop : type k s. (k, s) raw -> (k, s) raw -> bool = + fun p1 p2 -> + match p1, p2 with + | Root, Root -> true + | Subst(sub1, p1), Subst(sub2, p2) -> + Path.Resolved.equal sub1 sub2 + && loop p1 p2 + | SubstAlias(sub1, p1), SubstAlias(sub2, p2) -> + Path.Resolved.equal sub1 sub2 + && loop p1 p2 + | Module(p1, s1), Module(p2, s2) -> + s1 = s2 && loop p1 p2 + | Type(p1, s1), Type(p2, s2) -> + s1 = s2 && loop p1 p2 + | Class(p1, s1), Class(p2, s2) -> + s1 = s2 && loop p1 p2 + | ClassType(p1, s1), ClassType(p2, s2) -> + s1 = s2 && loop p1 p2 + | _, _ -> false + in + loop p1 p2 + + let hash p = + let rec loop : type k s. (k, s) raw -> int = + fun p -> + match p with + | Root -> Hashtbl.hash 32 + | Subst(sub, p) -> + Hashtbl.hash (34, Path.Resolved.hash sub, loop p) + | SubstAlias(sub, p) -> + Hashtbl.hash (35, Path.Resolved.hash sub, loop p) + | Module(p, s) -> + Hashtbl.hash (36, loop p, s) + | Type(p, s) -> + Hashtbl.hash (37, loop p, s) + | Class(p, s) -> + Hashtbl.hash (38, loop p, s) + | ClassType(p, s) -> + Hashtbl.hash (39, loop p, s) + in + loop p + + end + + open Resolved + + include Paths_types.Fragment + + let signature_of_module : module_ -> signature = function + | Resolved(Subst _ | SubstAlias _ | Module _) | Dot _ as x -> x + + let any_sort : type b c. (b, c) raw -> (b, sort) raw = function + | Resolved r -> Resolved (any_sort r) + | Dot _ as x -> x + + let any : type k. k t -> any = function + | Resolved (Subst _) as x -> x + | Resolved (SubstAlias _) as x -> x + | Resolved (Module _) as x -> x + | Resolved (Type _) as x -> x + | Resolved (Class _) as x -> x + | Resolved (ClassType _) as x -> x + | Dot _ as x -> x + + let rec parent_path root = function + | Resolved r -> Resolved.parent_path root r + | Dot(m, n) -> Path.Dot(parent_path root m, n) + + let path : type k. Path.module_ -> k t -> k Path.t = + fun root -> function + | Resolved r -> Resolved.path root r + | Dot(m, s) -> Path.Dot(parent_path root m, s) + + type ('a, 'b) base_name = + | Base : ('a, [< sort > `Root]) base_name + | Branch : string * signature -> ('a, [< sort > `Branch]) base_name + + let rec split_parent + : type s . (fragment_module, s) raw -> ('a, s) base_name = + function + | Resolved r -> begin + match Resolved.split_parent r with + | Base -> Base + | Branch(base, m) -> Branch(base, Resolved m) + end + | Dot(m, name) -> begin + match split_parent m with + | Base -> Branch(name, Resolved Root) + | Branch(base, m) -> Branch(base, Dot(m, name)) + end + + let split : type k . k t -> string * k t option = function + | Resolved r -> + let base, m = Resolved.split r in + let m = + match m with + | None -> None + | Some m -> Some (Resolved m) + in + base, m + | Dot(m, name) -> + match split_parent m with + | Base -> name, None + | Branch(base, m) -> base, Some(Dot(m, name)) + + let equal p1 p2 = + let rec loop : type k s. (k, s) raw -> (k, s) raw -> bool = + fun p1 p2 -> + match p1, p2 with + | Resolved p1, Resolved p2 -> + Resolved.equal p1 p2 + | Dot(p1, s1), Dot(p2, s2) -> + s1 = s2 && loop p1 p2 + | _, _ -> false + in + loop p1 p2 + + let hash p = + let rec loop : type k s. (k, s) raw -> int = + fun p -> + match p with + | Resolved p -> Resolved.hash p + | Dot(p, s) -> + Hashtbl.hash (40, loop p, s) + in + loop p + +end + +module Reference = struct + module rec Types : sig + module Resolved = Paths_types.Resolved_reference + + module Reference = Paths_types.Reference + end = Types + + let rec hash_resolved : type k. k Types.Resolved.t -> int = + fun p -> + let open Types.Resolved in + match p with + | Identifier id -> + Identifier.hash id + | SubstAlias (r1, r2) -> + Hashtbl.hash (41, Path.Resolved.hash r1, hash_resolved r2) + | Module(p, s) -> + Hashtbl.hash (42, hash_resolved p, s) + | Canonical (rp, p) -> + Hashtbl.hash (43, hash_resolved rp, hash_reference p) + | ModuleType(p, s) -> + Hashtbl.hash (44, hash_resolved p, s) + | Type(p, s) -> + Hashtbl.hash (45, hash_resolved p, s) + | Constructor(p, s) -> + Hashtbl.hash (46, hash_resolved p, s) + | Field(p, s) -> + Hashtbl.hash (47, hash_resolved p, s) + | Extension(p, s) -> + Hashtbl.hash (48, hash_resolved p, s) + | Exception(p, s) -> + Hashtbl.hash (49, hash_resolved p, s) + | Value(p, s) -> + Hashtbl.hash (50, hash_resolved p, s) + | Class(p, s) -> + Hashtbl.hash (51, hash_resolved p, s) + | ClassType(p, s) -> + Hashtbl.hash (52, hash_resolved p, s) + | Method(p, s) -> + Hashtbl.hash (53, hash_resolved p, s) + | InstanceVariable(p, s) -> + Hashtbl.hash (54, hash_resolved p, s) + | Label(p, s) -> + Hashtbl.hash (55, hash_resolved p, s) + + and hash_reference : type k. k Types.Reference.t -> int = + fun p -> + let open Types.Reference in + match p with + | Resolved p -> hash_resolved p + | Root (s, k) -> Hashtbl.hash (56, s, k) + | Dot (p,s) -> Hashtbl.hash (57, hash_reference p, s) + | Module (p,s) -> Hashtbl.hash (58, hash_reference p, s) + | ModuleType (p,s) -> Hashtbl.hash (59, hash_reference p, s) + | Type (p,s) -> Hashtbl.hash (60, hash_reference p, s) + | Constructor (p,s) -> Hashtbl.hash (61, hash_reference p, s) + | Field (p,s) -> Hashtbl.hash (62, hash_reference p, s) + | Extension (p,s) -> Hashtbl.hash (63, hash_reference p, s) + | Exception (p,s) -> Hashtbl.hash (64, hash_reference p, s) + | Value (p,s) -> Hashtbl.hash (65, hash_reference p, s) + | Class (p,s) -> Hashtbl.hash (66, hash_reference p, s) + | ClassType (p,s) -> Hashtbl.hash (67, hash_reference p, s) + | Method (p,s) -> Hashtbl.hash (68, hash_reference p, s) + | InstanceVariable (p,s) -> Hashtbl.hash (69, hash_reference p, s) + | Label (p,s) -> Hashtbl.hash (70, hash_reference p, s) + + module Resolved = struct + open Identifier + + include Types.Resolved + + let ident_module : Identifier.module_ -> _ = function + | Root _ | Module _ | Argument _ as x -> Identifier x + + let ident_module_type : Identifier.module_type -> _ = function + | ModuleType _ as x -> Identifier x + + let ident_type : Identifier.type_ -> _ = function + | Type _ | CoreType _ as x -> Identifier x + + let ident_constructor : Identifier.constructor -> _ = function + | Constructor _ as x -> Identifier x + + let ident_field : Identifier.field -> _ = function + | Field _ as x -> Identifier x + + let ident_extension : Identifier.extension -> _ = function + | Extension _ as x -> Identifier x + + let ident_exception : Identifier.exception_ -> _ = function + | Exception _ | CoreException _ as x -> Identifier x + + let ident_value : Identifier.value -> _ = function + | Value _ as x -> Identifier x + + let ident_class : Identifier.class_ -> _ = function + | Class _ as x -> Identifier x + + let ident_class_type : Identifier.class_type -> _ = function + | ClassType _ as x -> Identifier x + + let ident_method : Identifier.method_ -> _ = function + | Method _ as x -> Identifier x + + let ident_instance_variable : Identifier.instance_variable -> _ = + function InstanceVariable _ as x -> Identifier x + + let ident_label : Identifier.label -> _ = function + | Label _ as x -> Identifier x + + let ident_page : Identifier.page -> _ = function + | Page _ as x -> Identifier x + + let signature_of_module : module_ -> _ = function + | Identifier (Root _ | Module _ | Argument _) + | SubstAlias _ + | Module _ + | Canonical _ as x -> x + + let signature_of_module_type : module_type -> _ = function + | Identifier (ModuleType _) | ModuleType _ as x -> x + + let class_signature_of_class : class_ -> _ = function + | Identifier (Class _) | Class _ as x -> x + + let class_signature_of_class_type : class_type -> _ = function + | Identifier (Class _ | ClassType _) | Class _ | ClassType _ as x -> x + + let parent_of_signature : signature -> _ = function + | Identifier (Root _ | Module _ | Argument _ | ModuleType _) + | SubstAlias _ | Module _ | ModuleType _ | Canonical _ as x -> x + + let parent_of_class_signature : class_signature -> _ = + function + | Identifier (Class _ | ClassType _) | Class _ | ClassType _ as x -> x + + let parent_of_datatype : datatype -> _ = function + | Identifier (Type _ |CoreType _) | Type _ as x -> x + + let label_parent_of_parent : parent -> label_parent = function + | Identifier (Root _ | Module _ | Argument _ | ModuleType _ + |Type _ | CoreType _ | Class _ | ClassType _) + | SubstAlias _ | Module _ | ModuleType _ | Canonical _ + | Type _ | Class _ | ClassType _ as x -> x + + let label_parent_of_page : page -> label_parent = function + | Identifier Page _ as x -> x + + let any : type k. k t -> any = function + | Identifier (Root _ ) as x -> x + | Identifier (Page _ ) as x -> x + | Identifier (Module _) as x -> x + | Identifier (Argument _ ) as x -> x + | Identifier (ModuleType _) as x -> x + | Identifier (Type _) as x -> x + | Identifier (CoreType _) as x -> x + | Identifier (Constructor _) as x -> x + | Identifier (Field _) as x -> x + | Identifier (Extension _) as x -> x + | Identifier (Exception _) as x -> x + | Identifier (CoreException _) as x -> x + | Identifier (Value _) as x -> x + | Identifier (Class _) as x -> x + | Identifier (ClassType _) as x -> x + | Identifier (Method _) as x -> x + | Identifier (InstanceVariable _) as x -> x + | Identifier (Label _) as x -> x + | SubstAlias _ as x -> x + | Module _ as x -> x + | Canonical _ as x -> x + | ModuleType _ as x -> x + | Type _ as x -> x + | Constructor _ as x -> x + | Field _ as x -> x + | Extension _ as x -> x + | Exception _ as x -> x + | Value _ as x -> x + | Class _ as x -> x + | ClassType _ as x -> x + | Method _ as x -> x + | InstanceVariable _ as x -> x + | Label _ as x -> x + + let open_module : 'b. module_ -> ([< kind > `Module ] as 'b) t = + function + | Identifier (Root _ | Module _ | Argument _) | SubstAlias _ + | Module _ | Canonical _ as x -> x + + let rec parent_signature_identifier : signature -> Identifier.signature = + function + | Identifier id -> id + | SubstAlias(sub, _) -> Path.Resolved.parent_module_identifier sub + | Module(m, n) -> Module(parent_signature_identifier m, n) + | Canonical(_, Types.Reference.Resolved r) -> + parent_signature_identifier (open_module r) + | Canonical (r, _) -> parent_signature_identifier (open_module r) + | ModuleType(m, s) -> ModuleType(parent_signature_identifier m, s) + + let parent_type_identifier : datatype -> Identifier.datatype = + function + | Identifier id -> id + | Type(sg, s) -> Type(parent_signature_identifier sg, s) + + let parent_class_signature_identifier : + class_signature -> Identifier.class_signature = + function + | Identifier id -> id + | Class(sg, s) -> Class(parent_signature_identifier sg, s) + | ClassType(sg, s) -> ClassType(parent_signature_identifier sg, s) + + let rec parent_identifier : parent -> Identifier.parent = + function + | Identifier id -> id + | SubstAlias(sub, _) -> + Identifier.parent_of_signature + (Path.Resolved.parent_module_identifier sub) + | Module(m, n) -> Module(parent_signature_identifier m, n) + | Canonical(_, Types.Reference.Resolved r) -> + parent_identifier (open_module r) + | Canonical (r, _) -> parent_identifier (open_module r) + | ModuleType(m, s) -> ModuleType(parent_signature_identifier m, s) + | Type(sg, s) -> Type(parent_signature_identifier sg, s) + | Class(sg, s) -> Class(parent_signature_identifier sg, s) + | ClassType(sg, s) -> ClassType(parent_signature_identifier sg, s) + + let rec label_parent_identifier : label_parent -> Identifier.label_parent = + function + | Identifier id -> id + | SubstAlias(sub, _) -> + Identifier.label_parent_of_parent ( + Identifier.parent_of_signature + (Path.Resolved.parent_module_identifier sub)) + | Module(m, n) -> Module(parent_signature_identifier m, n) + | Canonical(_, Types.Reference.Resolved r) -> + label_parent_identifier (open_module r) + | Canonical (r, _) -> label_parent_identifier (open_module r) + | ModuleType(m, s) -> ModuleType(parent_signature_identifier m, s) + | Type(sg, s) -> Type(parent_signature_identifier sg, s) + | Class(sg, s) -> Class(parent_signature_identifier sg, s) + | ClassType(sg, s) -> ClassType(parent_signature_identifier sg, s) + + let rec identifier: type k. k t -> k Identifier.t = function + | Identifier id -> id + | SubstAlias(_, p) -> identifier (open_module p) + | Module(s, n) -> Module(parent_signature_identifier s, n) + | Canonical(_, Types.Reference.Resolved p) -> begin + match identifier p with + | Root _ | Module _ | Argument _ as x -> x + end + | Canonical(p, _) -> begin + match identifier p with + | Root _ | Module _ | Argument _ as x -> x + end + | ModuleType(s, n) -> ModuleType(parent_signature_identifier s, n) + | Type(s, n) -> Type(parent_signature_identifier s, n) + | Constructor(s, n) -> Constructor(parent_type_identifier s, n) + | Field(s, n) -> Field(parent_identifier s, n) + | Extension(s, n) -> Extension(parent_signature_identifier s, n) + | Exception(s, n) -> Exception(parent_signature_identifier s, n) + | Value(s, n) -> Value(parent_signature_identifier s, n) + | Class(s, n) -> Class(parent_signature_identifier s, n) + | ClassType(s, n) -> ClassType(parent_signature_identifier s, n) + | Method(s, n) -> Method(parent_class_signature_identifier s, n) + | InstanceVariable(s, n) -> + InstanceVariable(parent_class_signature_identifier s, n) + | Label(s, n) -> Label (label_parent_identifier s, n) + + let equal r1 r2 = + let rec loop : type k. k t -> k t -> bool = + fun id1 id2 -> + match id1, id2 with + | Identifier id1, Identifier id2 -> + Identifier.equal id1 id2 + | Module(r1, s1), Module(r2, s2) -> + s1 = s2 && loop r1 r2 + | ModuleType(r1, s1), ModuleType(r2, s2) -> + s1 = s2 && loop r1 r2 + | Type(r1, s1), Type(r2, s2) -> + s1 = s2 && loop r1 r2 + | Constructor(r1, s1), Constructor(r2, s2) -> + s1 = s2 && loop r1 r2 + | Field(r1, s1), Field(r2, s2) -> + s1 = s2 && loop r1 r2 + | Extension(r1, s1), Extension(r2, s2) -> + s1 = s2 && loop r1 r2 + | Exception(r1, s1), Exception(r2, s2) -> + s1 = s2 && loop r1 r2 + | Value(r1, s1), Value(r2, s2) -> + s1 = s2 && loop r1 r2 + | Class(r1, s1), Class(r2, s2) -> + s1 = s2 && loop r1 r2 + | ClassType(r1, s1), ClassType(r2, s2) -> + s1 = s2 && loop r1 r2 + | Method(r1, s1), Method(r2, s2) -> + s1 = s2 && loop r1 r2 + | InstanceVariable(r1, s1), InstanceVariable(r2, s2) -> + s1 = s2 && loop r1 r2 + | Label(r1, s1), Label(r2, s2) -> + s1 = s2 && loop r1 r2 + | _, _ -> false + in + loop r1 r2 + + let hash p = hash_resolved p + + type 'kind rebase_result = + | Stop of 'kind t + | Continue of 'kind Identifier.t * Reversed.t + + let rec rebase_module_reference : + Reversed.t -> module_ -> Kind.reference_module rebase_result = + fun new_base t -> + match t with + | Identifier id -> + let rev = Identifier.(to_reversed @@ signature_of_module id) in + let new_base = Reversed.remove_prefix rev ~of_:new_base in + Continue (id, new_base) + | SubstAlias _ -> Stop t (* FIXME? *) + | Module (m, s) -> + begin match rebase_signature_reference new_base m with + | Stop m' -> if m == m' then Stop t else Stop (Module (m', s)) + | Continue (id, new_base) -> + let id = Identifier.Module(id, s) in + match new_base with + | Reversed.Module s' :: rest when s = s' -> + Continue (id, rest) + | _ -> + Stop (Identifier id) + end + | Canonical (_, Types.Reference.Resolved p) -> + (* We only care about printing at this point, so let's drop the lhs. *) + rebase_module_reference new_base (signature_of_module p) + | Canonical (rp, p) -> + begin match rebase_module_reference new_base (signature_of_module rp) with + | Stop rp' -> Stop (Canonical (rp', p)) + | _ -> + (* We might come back at some point with a resolved rhs? So we don't want to + drop it. *) + Stop t + end + + and rebase_signature_reference : + Reversed.t -> signature -> Kind.signature rebase_result = + fun new_base t -> + match t with + | Identifier id -> + let rev = Identifier.(to_reversed id) in + let new_base = Reversed.remove_prefix rev ~of_:new_base in + Continue (id, new_base) + | ModuleType (m, s) -> + begin match rebase_signature_reference new_base m with + | Stop m' -> if m == m' then Stop t else Stop (Module (m', s)) + | Continue (id, new_base) -> + let id = Identifier.ModuleType(id, s) in + match new_base with + | Reversed.ModuleType s' :: rest when s = s' -> + Continue (id, rest) + | _ -> + Stop (Identifier id) + end + | Module _ | Canonical _ as x -> + begin match rebase_module_reference new_base x with + | Stop rp -> Stop (signature_of_module rp) + | Continue (id, rev) -> + Continue (Identifier.signature_of_module id, rev) + end + | SubstAlias _ -> Stop t (* FIXME? *) + + let rec rebase : type k. Reversed.t -> k t -> k t = + fun new_base t -> + match t with + | Identifier _ -> t + | SubstAlias _ -> t (* TODO: rewrite necessary? *) + | Module (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Module(id, s)) + | Stop mp' -> Module (mp', s) + end + | Canonical (p, Types.Reference.Resolved rp) -> + begin match rebase_module_reference new_base (signature_of_module rp) with + | Continue (id, _) -> ident_module id + | Stop rp -> + (* Easier to reexport a canonical than get the type for rp right... *) + Canonical (p, Types.Reference.Resolved rp) + end + | Canonical (rp, p) -> + begin match rebase_module_reference new_base rp with + | Stop rp' -> Canonical (rp', p) + | _ -> + (* We might come back at some point with a resolved rhs? So we don't want to + drop it. *) + t + end + | ModuleType (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.ModuleType (id, s)) + | Stop mp' -> ModuleType (mp', s) + end + | Type (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Type (id, s)) + | Stop mp' -> Type (mp', s) + end + | Constructor (parent, s) -> + Constructor(rebase new_base parent, s) + | Field (parent, s) -> + Field(rebase new_base parent, s) + | Extension (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Extension (id, s)) + | Stop mp' -> Extension (mp', s) + end + | Exception (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Exception (id, s)) + | Stop mp' -> Exception (mp', s) + end + | Value (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Value (id, s)) + | Stop mp' -> Value (mp', s) + end + | Class (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.Class (id, s)) + | Stop mp' -> Class (mp', s) + end + | ClassType (mp, s) -> + begin match rebase_signature_reference new_base mp with + | Continue (id, _) -> + Identifier (Identifier.ClassType (id, s)) + | Stop mp' -> ClassType (mp', s) + end + | Method (mp, s) -> + Method (rebase new_base mp, s) + | InstanceVariable (mp, s) -> + InstanceVariable (rebase new_base mp, s) + | Label (mp, s) -> + Label (rebase new_base mp, s) + + let rebase id t = + let rev = Identifier.to_reversed id in + rebase rev t + end + + open Identifier + open Resolved + + include Types.Reference + + let ident_module : Identifier.module_ -> _ = function + | Root _ | Module _ | Argument _ as x -> Resolved (Identifier x) + + let ident_module_type : Identifier.module_type -> _ = function + | ModuleType _ as x -> Resolved (Identifier x) + + let ident_type : Identifier.type_ -> _ = function + | Type _ | CoreType _ as x -> Resolved (Identifier x) + + let ident_constructor : Identifier.constructor -> _ = function + | Constructor _ as x -> Resolved (Identifier x) + + let ident_field : Identifier.field -> _ = function + | Field _ as x -> Resolved (Identifier x) + + let ident_extension : Identifier.extension -> _ = function + | Extension _ as x -> Resolved (Identifier x) + + let ident_exception : Identifier.exception_ -> _ = function + | Exception _ | CoreException _ as x -> Resolved (Identifier x) + + let ident_value : Identifier.value -> _ = function + | Value _ as x -> Resolved (Identifier x) + + let ident_class : Identifier.class_ -> _ = function + | Class _ as x -> Resolved (Identifier x) + + let ident_class_type : Identifier.class_type -> _ = function + | ClassType _ as x -> Resolved (Identifier x) + + let ident_method : Identifier.method_ -> _ = function + | Method _ as x -> Resolved (Identifier x) + + let ident_instance_variable : Identifier.instance_variable -> _ = + function InstanceVariable _ as x -> Resolved (Identifier x) + + let ident_label : Identifier.label -> _ = function + | Label _ as x -> Resolved (Identifier x) + + let signature_of_module : module_ -> signature = function + | Resolved (Identifier (Root _ | Module _ | Argument _) + | SubstAlias _ | Module _ | Canonical _) + | Root (_, (TUnknown | TModule)) + | Dot (_, _) + | Module (_,_) as x -> x + + let signature_of_module_type : module_type -> signature = function + | Resolved (Identifier (ModuleType _) | ModuleType _) + | Root (_, (TUnknown | TModuleType)) + | Dot (_, _) + | ModuleType (_,_) as x -> x + + let class_signature_of_class : class_ -> class_signature = function + | Resolved (Identifier (Class _) | Class _) + | Root (_, (TUnknown | TClass)) + | Dot (_, _) + | Class (_,_) as x -> x + + let class_signature_of_class_type : class_type -> class_signature = function + | Resolved (Identifier (Class _ | ClassType _) | Class _ | ClassType _) + | Root (_, (TUnknown | TClass | TClassType)) + | Dot (_, _) + | Class (_,_) + | ClassType (_,_) as x -> x + + let parent_of_signature : signature -> parent = function + | Resolved (Identifier (Root _ | Module _ | Argument _ | ModuleType _) + | SubstAlias _ | Module _ | ModuleType _ | Canonical _) + | Root (_, (TUnknown | TModule | TModuleType)) + | Dot (_, _) + | Module (_,_) + | ModuleType (_,_) as x -> x + + let parent_of_class_signature : class_signature -> parent = function + | Resolved (Identifier (Class _ | ClassType _) | Class _ | ClassType _) + | Root (_, (TUnknown | TClass | TClassType)) + | Dot (_, _) + | Class (_,_) + | ClassType (_,_) as x -> x + + let parent_of_datatype : datatype -> parent = function + | Resolved (Identifier (Type _ | CoreType _) | Type _) + | Root (_, (TUnknown | TType)) + | Dot (_, _) + | Type (_,_) as x -> x + + let label_parent_of_parent : parent -> label_parent = function + | Resolved (Identifier (Root _ | Module _ | Argument _ | ModuleType _ + |Type _ | CoreType _ | Class _ | ClassType _) + | SubstAlias _ | Module _ | ModuleType _ | Canonical _ + | Type _ | Class _ | ClassType _) + | Root (_, (TUnknown | TModule | TModuleType | TType | TClass | TClassType)) + | Dot (_, _) + | Module (_,_) + | ModuleType (_,_) + | Type (_,_) + | Class (_,_) + | ClassType (_,_) + as x -> x + + let any : type k. k t -> any = function + | Resolved (Identifier (Root _)) as x -> x + | Resolved (Identifier (Page _)) as x -> x + | Resolved (Identifier (Module _)) as x -> x + | Resolved (Identifier (Argument _)) as x -> x + | Resolved (Identifier (ModuleType _)) as x -> x + | Resolved (Identifier (Type _)) as x -> x + | Resolved (Identifier (CoreType _)) as x -> x + | Resolved (Identifier (Constructor _)) as x -> x + | Resolved (Identifier (Field _)) as x -> x + | Resolved (Identifier (Extension _)) as x -> x + | Resolved (Identifier (Exception _)) as x -> x + | Resolved (Identifier (CoreException _)) as x -> x + | Resolved (Identifier (Value _)) as x -> x + | Resolved (Identifier (Class _)) as x -> x + | Resolved (Identifier (ClassType _)) as x -> x + | Resolved (Identifier (Method _)) as x -> x + | Resolved (Identifier (InstanceVariable _)) as x -> x + | Resolved (Identifier (Label _)) as x -> x + | Resolved (SubstAlias _) as x -> x + | Resolved (Module _) as x -> x + | Resolved (Canonical _) as x -> x + | Resolved (ModuleType _) as x -> x + | Resolved (Type _) as x -> x + | Resolved (Constructor _) as x -> x + | Resolved (Field _) as x -> x + | Resolved (Extension _) as x -> x + | Resolved (Exception _) as x -> x + | Resolved (Value _) as x -> x + | Resolved (Class _) as x -> x + | Resolved (ClassType _) as x -> x + | Resolved (Method _) as x -> x + | Resolved (InstanceVariable _) as x -> x + | Resolved (Label _) as x -> x + | Root (_, TUnknown) as x -> x + | Root (_, TModule) as x -> x + | Root (_, TModuleType) as x -> x + | Root (_, TType) as x -> x + | Root (_, TConstructor) as x -> x + | Root (_, TField) as x -> x + | Root (_, TExtension) as x -> x + | Root (_, TException) as x -> x + | Root (_, TClass) as x -> x + | Root (_, TClassType) as x -> x + | Root (_, TValue) as x -> x + | Root (_, TMethod) as x -> x + | Root (_, TInstanceVariable) as x -> x + | Root (_, TLabel) as x -> x + | Root (_, TPage) as x -> x + | Dot (_, _) as x -> x + | Module (_,_) as x -> x + | ModuleType (_,_) as x -> x + | Type (_,_) as x -> x + | Constructor (_,_) as x -> x + | Field (_,_) as x -> x + | Extension (_,_) as x -> x + | Exception (_,_) as x -> x + | Class (_,_) as x -> x + | ClassType (_,_) as x -> x + | Value (_,_) as x -> x + | Method (_,_) as x -> x + | InstanceVariable (_,_) as x -> x + | Label (_,_) as x -> x + + let module_ p name = + match p with + | Resolved p -> Resolved (Module(p, name)) + | p -> Module(p, name) + + let module_type p name = + match p with + | Resolved p -> Resolved (ModuleType(p, name)) + | p -> ModuleType(p, name) + + let type_ p name = + match p with + | Resolved p -> Resolved (Type(p, name)) + | p -> Type(p, name) + + let constructor p arg = + match p with + | Resolved p -> Resolved (Constructor(p, arg)) + | p -> Constructor(p, arg) + + let field p arg = + match p with + | Resolved p -> Resolved (Field(p, arg)) + | p -> Field(p, arg) + + let extension p arg = + match p with + | Resolved p -> Resolved (Extension(p, arg)) + | p -> Extension(p, arg) + + let exception_ p arg = + match p with + | Resolved p -> Resolved (Exception(p, arg)) + | p -> Exception(p, arg) + + let value p arg = + match p with + | Resolved p -> Resolved (Value(p, arg)) + | p -> Value(p, arg) + + let class_ p name = + match p with + | Resolved p -> Resolved (Class(p, name)) + | p -> Class(p, name) + + let class_type p name = + match p with + | Resolved p -> Resolved (ClassType(p, name)) + | p -> ClassType(p, name) + + let method_ p arg = + match p with + | Resolved p -> Resolved (Method(p, arg)) + | p -> Method(p, arg) + + let instance_variable p arg = + match p with + | Resolved p -> Resolved (InstanceVariable(p, arg)) + | p -> InstanceVariable(p, arg) + + let label p arg = + match p with + | Resolved p -> Resolved (Label(p, arg)) + | p -> Label(p, arg) + + let equal r1 r2 = + let rec loop : type k. k t -> k t -> bool = + fun r1 r2 -> + match r1, r2 with + | Resolved r1, Resolved r2 -> + Resolved.equal r1 r2 + | Root (s1, k1), Root (s2, k2) -> + s1 = s2 && k1 = k2 + | Dot(r1, s1), Dot(r2, s2) -> + s1 = s2 && loop r1 r2 + | Module(r1, s1), Module(r2, s2) -> + s1 = s2 && loop r1 r2 + | ModuleType(r1, s1), ModuleType(r2, s2) -> + s1 = s2 && loop r1 r2 + | Type(r1, s1), Type(r2, s2) -> + s1 = s2 && loop r1 r2 + | Constructor(r1, s1), Constructor(r2, s2) -> + s1 = s2 && loop r1 r2 + | Field(r1, s1), Field(r2, s2) -> + s1 = s2 && loop r1 r2 + | Extension(r1, s1), Extension(r2, s2) -> + s1 = s2 && loop r1 r2 + | Exception(r1, s1), Exception(r2, s2) -> + s1 = s2 && loop r1 r2 + | Class(r1, s1), Class(r2, s2) -> + s1 = s2 && loop r1 r2 + | ClassType(r1, s1), ClassType(r2, s2) -> + s1 = s2 && loop r1 r2 + | Method(r1, s1), Method(r2, s2) -> + s1 = s2 && loop r1 r2 + | InstanceVariable(r1, s1), InstanceVariable(r2, s2) -> + s1 = s2 && loop r1 r2 + | Label(r1, s1), Label(r2, s2) -> + s1 = s2 && loop r1 r2 + | _, _ -> false + in + loop r1 r2 + + let hash p = hash_reference p + +end diff --git a/analysis/src/vendor/odoc_parser/paths.mli b/analysis/src/vendor/odoc_parser/paths.mli new file mode 100644 index 000000000..89aa5067a --- /dev/null +++ b/analysis/src/vendor/odoc_parser/paths.mli @@ -0,0 +1,371 @@ +(* + * Copyright (c) 2014 Leo White + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +(** Paths of documentation *) + +module Kind = Paths_types.Kind + +(** Identifiers for definitions *) +module Identifier : sig + + include module type of Paths_types.Identifier + + (** {2 Explicit coercions} *) + + val signature_of_module : module_ -> signature + + val signature_of_module_type : module_type -> signature + + val class_signature_of_class : class_ -> class_signature + + val class_signature_of_class_type : class_type -> class_signature + + val datatype_of_type : type_ -> datatype + + val parent_of_signature : signature -> parent + + val parent_of_class_signature : class_signature -> parent + + val parent_of_datatype : datatype -> parent + + val label_parent_of_parent : parent -> label_parent + + val label_parent_of_page : page -> label_parent + + val any : 'kind t -> any + + (** {2 Generic operations} *) + + val equal : 'kind t -> 'kind t -> bool + + val hash : 'kind t -> int + + (** {3 Printing} *) + + val name : 'kind t -> string + + (** {2 Root retrieval} *) + + val signature_root : signature -> Root.t + + val module_root : module_ -> Root.t + + val module_type_root : module_type -> Root.t + + val class_signature_root : class_signature -> Root.t + + val label_parent_root : label_parent -> Root.t +end + +(** Normal OCaml paths (i.e. the ones present in types) *) +module rec Path : sig + + module Resolved : sig + + include module type of Paths_types.Resolved_path + + (** {2 Creators} *) + + val ident_module : Identifier.module_ -> [< kind > `Module] t + + val ident_module_type : Identifier.module_type -> [< kind > `ModuleType] t + + val ident_type : Identifier.type_ -> [< kind > `Type] t + + val ident_class : Identifier.class_ -> [< kind > `Class] t + + val ident_class_type : Identifier.class_type -> [< kind > `ClassType] t + + (** {2 Explicit coercion} *) + + val any : 'kind t -> any + + (** {2 Generic operations} *) + + val equal : 'kind t -> 'kind t -> bool + + val hash : 'kind t -> int + + val identifier: 'kind t -> 'kind Identifier.t + (** [identifier rp] extracts the identifier present at the "root" of [rp]. *) + + val is_hidden : 'kind t -> bool + (** [is_hidden rp] is [true] when some prefix of [rp] (which is not under a + [Canonical]) is the [Hidden] constructor. + + [Canonical] are treated specialy because we expect them to rewrite a + hidden path to a non-hidden one. *) + + val rebase : Identifier.signature -> 'kind t -> 'kind t + + val equal_identifier : 'kind Identifier.t -> 'kind t -> bool + end + + include module type of Paths_types.Path + + (** {2 Creators} *) + + val ident_module : Identifier.module_ -> [< kind > `Module] t + + val ident_module_type : Identifier.module_type -> [< kind > `ModuleType] t + + val ident_type : Identifier.type_ -> [< kind > `Type] t + + val ident_class : Identifier.class_ -> [< kind > `Class] t + + val ident_class_type : Identifier.class_type -> [< kind > `ClassType] t + + val module_ : module_ -> string -> [< kind > `Module] t + + val apply : module_ -> module_ -> [< kind > `Module] t + + val module_type : module_ -> string -> [< kind > `ModuleType] t + + val type_ : module_ -> string -> [< kind > `Type] t + + val class_ : module_ -> string -> [< kind > `Class] t + + val class_type_ : module_ -> string -> [< kind > `ClassType] t + + (** {2 Explicit coercions} *) + + val any : 'kind t -> any + + val type_of_class_type : class_type -> type_ + + (** {2 Generic operations} *) + + val equal : 'kind t -> 'kind t -> bool + + val hash : 'kind t -> int + + val is_hidden : 'kind t -> bool + (** cf. {!Resolved.is_hidden} *) +end + +(** OCaml path fragments for specifying module substitutions *) +module Fragment : sig + + module Resolved : sig + + include module type of Paths_types.Resolved_fragment + + (** {2 Explicit coercions} *) + + val signature_of_module : module_ -> signature + + val any : 'b t -> any + + val any_sort : ('b, 'c) raw -> ('b, sort) raw + + (** {2 Attaching fragments to valid paths} *) + + val path: Path.module_ -> 'b t -> 'b Path.t + + val identifier: Identifier.signature -> 'b t -> 'b Identifier.t + + (** {2 Generic operations} *) + + val equal : 'b t -> 'b t -> bool + + val hash : 'b t -> int + + val split : 'b t -> string * 'b t option + + end + + include module type of Paths_types.Fragment + + (** {2 Explicit coercions} *) + + val signature_of_module : module_ -> signature + + val any_sort : ('b, 'c) raw -> ('b, sort) raw + + val any : 'b t -> any + + (** {2 Attaching fragments to valid paths} *) + + val path: Path.module_ -> 'b t -> 'b Path.t + + (** {2 Generic operations} *) + + val equal : 'b t -> 'b t -> bool + + val hash : 'b t -> int + + val split: 'b t -> string * 'b t option + +end + +(** References present in documentation comments ([{!Foo.Bar}]) *) +module rec Reference : sig + + module Resolved : sig + + include module type of Paths_types.Resolved_reference + + (** {2 Creators} *) + + val ident_module : Identifier.module_ -> [< kind > `Module] t + + val ident_module_type : Identifier.module_type -> [< kind > `ModuleType] t + + val ident_type : Identifier.type_ -> [< kind > `Type] t + + val ident_constructor : Identifier.constructor -> [< kind > `Constructor] t + + val ident_field : Identifier.field -> [< kind > `Field] t + + val ident_extension : Identifier.extension -> [< kind > `Extension] t + + val ident_exception : Identifier.exception_ -> [< kind > `Exception] t + + val ident_value : Identifier.value -> [< kind > `Value] t + + val ident_class : Identifier.class_ -> [< kind > `Class] t + + val ident_class_type : Identifier.class_type -> [< kind > `ClassType] t + + val ident_method : Identifier.method_ -> [< kind > `Method] t + + val ident_instance_variable : Identifier.instance_variable -> + [< kind > `InstanceVariable] t + + val ident_label : Identifier.label -> [< kind > `Label] t + + val ident_page : Identifier.page -> [< kind > `Page] t + + (** {2 Explicit coercions} *) + + val signature_of_module : module_ -> signature + + val signature_of_module_type : module_type -> signature + + val class_signature_of_class : class_ -> class_signature + + val class_signature_of_class_type : class_type -> class_signature + + val parent_of_signature : signature -> parent + + val parent_of_class_signature : class_signature -> parent + + val parent_of_datatype : datatype -> parent + + val label_parent_of_parent : parent -> label_parent + + val label_parent_of_page : page -> label_parent + + val any : 'kind t -> any + + (** {2 Generic operations} *) + + val equal : 'kind t -> 'kind t -> bool + + val hash : 'kind t -> int + + val identifier: 'kind t -> 'kind Identifier.t + (** [identifier rr] extracts the identifier present at the "root" of [rr]. *) + + val rebase : Identifier.signature -> 'kind t -> 'kind t + + end + + include module type of Paths_types.Reference + + (** {2 Creators} *) + + val ident_module : Identifier.module_ -> [< kind > `Module] t + + val ident_module_type : Identifier.module_type -> [< kind > `ModuleType] t + + val ident_type : Identifier.type_ -> [< kind > `Type] t + + val ident_constructor : Identifier.constructor -> [< kind > `Constructor] t + + val ident_field : Identifier.field -> [< kind > `Field] t + + val ident_extension : Identifier.extension -> [< kind > `Extension] t + + val ident_exception : Identifier.exception_ -> [< kind > `Exception] t + + val ident_value : Identifier.value -> [< kind > `Value] t + + val ident_class : Identifier.class_ -> [< kind > `Class] t + + val ident_class_type : Identifier.class_type -> [< kind > `ClassType] t + + val ident_method : Identifier.method_ -> [< kind > `Method] t + + val ident_instance_variable : Identifier.instance_variable -> + [< kind > `InstanceVariable] t + + val ident_label : Identifier.label -> [< kind > `Label] t + + val module_ : signature -> string -> [< kind > `Module] t + + val module_type : signature -> string -> + [< kind > `ModuleType] t + + val type_ : signature -> string -> [< kind > `Type] t + + val constructor : datatype -> string -> [< kind > `Constructor] t + + val field : parent -> string -> [< kind > `Field] t + + val extension : signature -> string -> [< kind > `Extension] t + + val exception_ : signature -> string -> [< kind > `Exception] t + + val value : signature -> string -> [< kind > `Value] t + + val class_ : signature -> string -> [< kind > `Class] t + + val class_type : signature -> string -> [< kind > `ClassType] t + + val method_ : class_signature -> string -> [< kind > `Method] t + + val instance_variable : class_signature -> string -> + [< kind > `InstanceVariable] t + + val label : label_parent -> string -> [< kind > `Label] t + + (** {2 Explicit coercions} *) + + val signature_of_module : module_ -> signature + + val signature_of_module_type : module_type -> signature + + val class_signature_of_class : class_ -> class_signature + + val class_signature_of_class_type : class_type -> class_signature + + val parent_of_signature : signature -> parent + + val parent_of_class_signature : class_signature -> parent + + val parent_of_datatype : datatype -> parent + + val label_parent_of_parent : parent -> label_parent + + val any : 'kind t -> any + + (** {2 Generic operations} *) + + val equal : 'kind t -> 'kind t -> bool + + val hash : 'kind t -> int +end diff --git a/analysis/src/vendor/odoc_parser/paths_types.ml b/analysis/src/vendor/odoc_parser/paths_types.ml new file mode 100644 index 000000000..c3a4f4bc5 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/paths_types.ml @@ -0,0 +1,383 @@ +(** {1 Paths} *) + +(** Every path is annotated with its kind. *) +module Kind = +struct + (** {4 General purpose kinds} *) + + (** Any possible referent *) + type any = + [ `Module | `ModuleType | `Type + | `Constructor | `Field | `Extension + | `Exception | `Value | `Class | `ClassType + | `Method | `InstanceVariable | `Label | `Page ] + + (** A referent that can contain signature items *) + type signature = [ `Module | `ModuleType ] + + (** A referent that can contain class signature items *) + type class_signature = [ `Class | `ClassType ] + + (** A referent that can contain datatype items *) + type datatype = [ `Type ] + + (** A referent that can contain page items *) + type page = [ `Page ] + + (** A referent that can contain other items *) + type parent = [ signature | class_signature | datatype ] + + type label_parent = [ parent | page ] + + (** {4 Identifier kinds} + + The kind of an identifier directly corresponds to the kind of its + referent. *) + + type identifier = any + + type identifier_module = [ `Module ] + type identifier_module_type = [ `ModuleType ] + type identifier_type = [ `Type ] + type identifier_constructor = [ `Constructor ] + type identifier_field = [ `Field ] + type identifier_extension = [ `Extension ] + type identifier_exception = [ `Exception ] + type identifier_value = [ `Value ] + type identifier_class = [ `Class ] + type identifier_class_type = [ `ClassType ] + type identifier_method = [ `Method ] + type identifier_instance_variable = [ `InstanceVariable ] + type identifier_label = [ `Label ] + type identifier_page = [ `Page ] + + (** {4 Path kinds} + + There are four kinds of OCaml path: + + - module + - module type + - type + - class type + + These kinds do not directly correspond to the kind of their + referent (e.g. a type path may refer to a class definition). *) + + type path = [ `Module | `ModuleType | `Type | `Class | `ClassType ] + + type path_module = [ `Module ] + type path_module_type = [ `ModuleType ] + type path_type = [ `Type | `Class | `ClassType ] + type path_class_type = [ `Class | `ClassType ] + + (** {4 Fragment kinds} + + There are two kinds of OCaml path fragment: + + - module + - type + + These kinds do not directly correspond to the kind of their + referent (e.g. a type path fragment may refer to a class + definition). *) + + type fragment = [ `Module | `Type | `Class | `ClassType ] + + type fragment_module = [ `Module ] + type fragment_type = [ `Type | `Class | `ClassType ] + + (** {4 Reference kinds} + + There is one reference kind for each kind of referent. However, + the kind of a reference does not refer to the kind of its + referent, but to the kind with which the reference was annotated. + + This means that reference kinds do not correspond directly to the + kind of their referent because we used more relaxed rules when + resolving a reference. For example, a reference annotated as being + to a constructor can be resolved to the definition of an exception + (which is a sort of constructor). *) + + type reference = any + + type reference_module = [ `Module ] + type reference_module_type = [ `ModuleType ] + type reference_type = [ `Type | `Class | `ClassType ] + type reference_constructor = [ `Constructor | `Extension | `Exception ] + type reference_field = [ `Field ] + type reference_extension = [ `Extension | `Exception ] + type reference_exception = [ `Exception ] + type reference_value = [ `Value ] + type reference_class = [ `Class ] + type reference_class_type = [ `Class | `ClassType ] + type reference_method = [ `Method ] + type reference_instance_variable = [ `InstanceVariable ] + type reference_label = [ `Label ] + type reference_page = [ `Page ] +end + +module Identifier = +struct + type kind = Kind.identifier + + type 'kind t = + | Root : Root.t * string -> [< kind > `Module] t + | Page : Root.t * string -> [< kind > `Page] t + | Module : signature * string -> [< kind > `Module] t + | Argument : signature * int * string -> [< kind > `Module] t + | ModuleType : signature * string -> [< kind > `ModuleType] t + | Type : signature * string -> [< kind > `Type] t + | CoreType : string -> [< kind > `Type] t + | Constructor : datatype * string -> [< kind > `Constructor] t + | Field : parent * string -> [< kind > `Field] t + | Extension : signature * string -> [< kind > `Extension] t + | Exception : signature * string -> [< kind > `Exception] t + | CoreException : string -> [< kind > `Exception] t + | Value : signature * string -> [< kind > `Value] t + | Class : signature * string -> [< kind > `Class] t + | ClassType : signature * string -> [< kind > `ClassType] t + | Method : class_signature * string -> [< kind > `Method] t + | InstanceVariable : class_signature * string -> + [< kind > `InstanceVariable] t + | Label : label_parent * string -> [< kind > `Label] t + + and any = kind t + and signature = Kind.signature t + and class_signature = Kind.class_signature t + and datatype = Kind.datatype t + and parent = Kind.parent t + and label_parent = Kind.label_parent t + + type module_ = Kind.identifier_module t + type module_type = Kind.identifier_module_type t + type type_ = Kind.identifier_type t + type constructor = Kind.identifier_constructor t + type field = Kind.identifier_field t + type extension = Kind.identifier_extension t + type exception_ = Kind.identifier_exception t + type value = Kind.identifier_value t + type class_ = Kind.identifier_class t + type class_type = Kind.identifier_class_type t + type method_ = Kind.identifier_method t + type instance_variable = Kind.identifier_instance_variable t + type label = Kind.identifier_label t + type page = Kind.identifier_page t + + type path_module = Kind.path_module t + type path_module_type = Kind.path_module_type t + type path_type = Kind.path_type t + type path_class_type = Kind.path_class_type t + + type fragment_module = Kind.fragment_module t + type fragment_type = Kind.fragment_type t + + type reference_module = Kind.reference_module t + type reference_module_type = Kind.reference_module_type t + type reference_type = Kind.reference_type t + type reference_constructor = Kind.reference_constructor t + type reference_field = Kind.reference_field t + type reference_extension = Kind.reference_extension t + type reference_exception = Kind.reference_exception t + type reference_value = Kind.reference_value t + type reference_class = Kind.reference_class t + type reference_class_type = Kind.reference_class_type t + type reference_method = Kind.reference_method t + type reference_instance_variable = Kind.reference_instance_variable t + type reference_label = Kind.reference_label t + type reference_page = Kind.reference_page t +end + +module rec Path : +sig + type kind = Kind.path + + type 'kind t = + | Resolved : 'kind Resolved_path.t -> 'kind t + | Root : string -> [< kind > `Module] t + | Forward : string -> [< kind > `Module] t + | Dot : module_ * string -> [< kind] t + | Apply : module_ * module_ -> [< kind > `Module] t + + and any = kind t + and module_ = Kind.path_module t + and module_type = Kind.path_module_type t + and type_ = Kind.path_type t + and class_type = Kind.path_class_type t +end = Path + +and Resolved_path : +sig + type kind = Kind.path + + type 'kind t = + | Identifier : 'kind Identifier.t -> ([< kind] as 'kind) t + | Subst : module_type * module_ -> [< kind > `Module] t + | SubstAlias : module_ * module_ -> [< kind > `Module] t + | Hidden : module_ -> [< kind > `Module ] t + | Module : module_ * string -> [< kind > `Module] t + (* TODO: The canonical path should be a reference not a path *) + | Canonical : module_ * Path.module_ -> [< kind > `Module] t + | Apply : module_ * Path.module_ -> [< kind > `Module] t + | ModuleType : module_ * string -> [< kind > `ModuleType] t + | Type : module_ * string -> [< kind > `Type] t + | Class : module_ * string -> [< kind > `Class] t + | ClassType : module_ * string -> [< kind > `ClassType] t + + and any = kind t + and module_ = Kind.path_module t + and module_type = Kind.path_module_type t + and type_ = Kind.path_type t + and class_type = Kind.path_class_type t +end = Resolved_path + +module rec Fragment : +sig + type kind = Kind.fragment + + type sort = [ `Root | `Branch ] + + type ('b, 'c) raw = + | Resolved : ('b, 'c) Resolved_fragment.raw -> ('b, 'c) raw + | Dot : signature * string -> ([< kind], [< sort > `Branch]) raw + + and 'b t = ('b, [`Branch]) raw + and any = kind t + and signature = (Kind.fragment_module, [`Root | `Branch]) raw + + type module_ = Kind.fragment_module t + type type_ = Kind.fragment_type t +end = Fragment + +and Resolved_fragment : +sig + type kind = Kind.fragment + + type sort = [ `Root | `Branch ] + + type ('b, 'c) raw = + | Root : ('b, [< sort > `Root]) raw + | Subst : Resolved_path.module_type * module_ -> + ([< kind > `Module] as 'b, [< sort > `Branch] as 'c) raw + | SubstAlias : Resolved_path.module_ * module_ -> + ([< kind > `Module] as 'b, [< sort > `Branch] as 'c) raw + | Module : signature * string -> ([< kind > `Module], [< sort > `Branch]) raw + | Type : signature * string -> ([< kind > `Type], [< sort > `Branch]) raw + | Class : signature * string -> ([< kind > `Class], [< sort > `Branch]) raw + | ClassType : signature * string -> ([< kind > `ClassType], [< sort > `Branch]) raw + + and 'b t = ('b, [`Branch]) raw + and any = kind t + and signature = (Kind.fragment_module, [`Root | `Branch]) raw + and module_ = Kind.fragment_module t + + type type_ = Kind.fragment_type t +end = Resolved_fragment + +module rec Reference : +sig + type kind = Kind.reference + + type _ tag = + | TUnknown : [< kind ] tag + | TModule : [< kind > `Module ] tag + | TModuleType : [< kind > `ModuleType ] tag + | TType : [< kind > `Type ] tag + | TConstructor : [< kind > `Constructor ] tag + | TField : [< kind > `Field ] tag + | TExtension : [< kind > `Extension ] tag + | TException : [< kind > `Exception ] tag + | TValue : [< kind > `Value ] tag + | TClass : [< kind > `Class ] tag + | TClassType : [< kind > `ClassType ] tag + | TMethod : [< kind > `Method ] tag + | TInstanceVariable : [< kind > `InstanceVariable ] tag + | TLabel : [< kind > `Label ] tag + | TPage : [< kind > `Page ] tag + + type 'kind t = + | Resolved : 'kind Resolved_reference.t -> 'kind t + | Root : string * 'kind tag -> 'kind t + | Dot : label_parent * string -> [< kind ] t + | Module : signature * string -> [< kind > `Module] t + | ModuleType : signature * string -> [< kind > `ModuleType] t + | Type : signature * string -> [< kind > `Type] t + | Constructor : datatype * string -> [< kind > `Constructor] t + | Field : parent * string -> [< kind > `Field] t + | Extension : signature * string -> [< kind > `Extension] t + | Exception : signature * string -> [< kind > `Exception] t + | Value : signature * string -> [< kind > `Value] t + | Class : signature * string -> [< kind > `Class] t + | ClassType : signature * string -> [< kind > `ClassType] t + | Method : class_signature * string -> [< kind > `Method] t + | InstanceVariable : class_signature * string -> + [< kind > `InstanceVariable] t + | Label : label_parent * string -> [< kind > `Label] t + + and any = kind t + and signature = Kind.signature t + and class_signature = Kind.class_signature t + and datatype = Kind.datatype t + and parent = Kind.parent t + and label_parent = [ Kind.parent | Kind.page ] t + + type module_ = Kind.reference_module t + type module_type = Kind.reference_module_type t + type type_ = Kind.reference_type t + type constructor = Kind.reference_constructor t + type field = Kind.reference_field t + type extension = Kind.reference_extension t + type exception_ = Kind.reference_exception t + type value = Kind.reference_value t + type class_ = Kind.reference_class t + type class_type = Kind.reference_class_type t + type method_ = Kind.reference_method t + type instance_variable = Kind.reference_instance_variable t + type label = Kind.reference_label t + type page = Kind.reference_page t +end = Reference + +and Resolved_reference : +sig + type kind = Kind.reference + + type 'kind t = + | Identifier : 'kind Identifier.t -> 'kind t + | SubstAlias : Resolved_path.module_ * module_ -> [< kind > `Module ] t + | Module : signature * string -> [< kind > `Module] t + | Canonical : module_ * Reference.module_ -> [< kind > `Module] t + | ModuleType : signature * string -> [< kind > `ModuleType] t + | Type : signature * string -> [< kind > `Type] t + | Constructor : datatype * string -> [< kind > `Constructor] t + | Field : parent * string -> [< kind > `Field] t + | Extension : signature * string -> [< kind > `Extension] t + | Exception : signature * string -> [< kind > `Exception] t + | Value : signature * string -> [< kind > `Value] t + | Class : signature * string -> [< kind > `Class] t + | ClassType : signature * string -> [< kind > `ClassType] t + | Method : class_signature * string -> [< kind > `Method] t + | InstanceVariable : class_signature * string -> + [< kind > `InstanceVariable] t + | Label : label_parent * string -> [< kind > `Label] t + + and any = kind t + and signature = Kind.signature t + and class_signature = Kind.class_signature t + and datatype = Kind.datatype t + and parent = Kind.parent t + and module_ = Kind.reference_module t + and label_parent = Kind.label_parent t + + type module_type = Kind.reference_module_type t + type type_ = Kind.reference_type t + type constructor = Kind.reference_constructor t + type field = Kind.reference_field t + type extension = Kind.reference_extension t + type exception_ = Kind.reference_exception t + type value = Kind.reference_value t + type class_ = Kind.reference_class t + type class_type = Kind.reference_class_type t + type method_ = Kind.reference_method t + type instance_variable = Kind.reference_instance_variable t + type label = Kind.reference_label t + type page = Kind.reference_page t +end = Resolved_reference diff --git a/analysis/src/vendor/odoc_parser/root.ml b/analysis/src/vendor/odoc_parser/root.ml new file mode 100644 index 000000000..c9281c357 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/root.ml @@ -0,0 +1,74 @@ +(* + * Copyright (c) 2014 Leo White + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + + + +let contains_double_underscore s = + let len = String.length s in + let rec aux i = + if i > len - 2 then false else + if s.[i] = '_' && s.[i + 1] = '_' then true + else aux (i + 1) + in + aux 0 + +module Package = +struct + type t = string + + module Table = Hashtbl.Make(struct + type nonrec t = t + let equal : t -> t -> bool = (=) + let hash : t -> int = Hashtbl.hash + end) +end + +module Odoc_file = +struct + type m = {name : string; hidden : bool} + type t = + | Page of string + | Compilation_unit of m + + let create_unit ~force_hidden name = + let hidden = force_hidden || contains_double_underscore name in + Compilation_unit {name; hidden} + + let create_page name = Page name + + let name = function + | Page name + | Compilation_unit {name; _} -> name +end + +type t = { + package : Package.t; + file : Odoc_file.t; + digest : Digest.t; +} + +let equal : t -> t -> bool = (=) +let hash : t -> int = Hashtbl.hash + +let to_string t = Printf.sprintf "%s::%s" t.package (Odoc_file.name t.file) + +module Hash_table = + Hashtbl.Make + (struct + type nonrec t = t + let equal = equal + let hash = hash + end) diff --git a/analysis/src/vendor/odoc_parser/root.mli b/analysis/src/vendor/odoc_parser/root.mli new file mode 100644 index 000000000..832df2e3c --- /dev/null +++ b/analysis/src/vendor/odoc_parser/root.mli @@ -0,0 +1,56 @@ +(* + * Copyright (c) 2014 Leo White + * + * Permission to use, copy, modify, and distribute this software for any + * purpose with or without fee is hereby granted, provided that the above + * copyright notice and this permission notice appear in all copies. + * + * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES + * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF + * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR + * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES + * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN + * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF + * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + *) + +(** A root can be seen as a unique representative of a odoc file. + + {{!t}Roots} are used by doc-ock (at the root of every resolved + path/identifier/reference) and present at the beginning of every [.odoc] + file. +*) + +module Package : +sig + type t = string +end + +module Odoc_file : +sig + type m = {name : string; hidden : bool} + type t = + | Page of string + | Compilation_unit of m + + val create_unit : force_hidden:bool -> string -> t + val create_page : string -> t + + val name : t -> string +end + +type t = { + package : Package.t; + file : Odoc_file.t; + digest : Digest.t; +} + +val equal : t -> t -> bool +val hash : t -> int + +val to_string : t -> string + +module Hash_table : Hashtbl.S with type key = t + +val contains_double_underscore : string -> bool +(* not the best place for this but. *) diff --git a/analysis/src/vendor/odoc_parser/semantics.ml b/analysis/src/vendor/odoc_parser/semantics.ml new file mode 100644 index 000000000..b233c1bb4 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/semantics.ml @@ -0,0 +1,333 @@ +module Location = Location_ +module Error = Error +module Comment = Comment + +type 'a with_location = 'a Location.with_location + + + +type status = { + permissive : bool; + mutable warnings : Error.t list; + sections_allowed : Ast.sections_allowed; + parent_of_sections : Paths.Identifier.label_parent; +} + +let warning status message = + if status.permissive then + status.warnings <- message::status.warnings + else + Error.raise_exception message + + + +(* TODO This and Token.describe probably belong in Parse_error. *) +let describe_element = function + | `Reference (`Simple, _, _) -> + Token.describe (`Simple_reference "") + | `Reference (`With_text, _, _) -> + Token.describe (`Begin_reference_with_replacement_text "") + | `Link _ -> + Token.describe (`Begin_link_with_replacement_text "") + | `Heading (level, _, _) -> + Token.describe (`Begin_section_heading (level, None)) + + + +let leaf_inline_element + : status -> Comment.leaf_inline_element with_location -> + Comment.leaf_inline_element with_location = + fun status element -> + + begin match element.value with + | `Code_span c as token -> + begin match String.index c '\n' with + | exception Not_found -> () + | _ -> + Parse_error.not_allowed + ~what:(Token.describe `Single_newline) + ~in_what:(Token.describe token) + element.location + |> warning status + end + | _ -> () + end; + + element + + + +let rec non_link_inline_element + : status -> surrounding:_ -> Ast.inline_element with_location -> + Comment.non_link_inline_element with_location = + fun status ~surrounding element -> + + match element with + | {value = #Comment.leaf_inline_element; _} as element -> + let element = leaf_inline_element status element in + (element :> Comment.non_link_inline_element with_location) + + | {value = `Styled (style, content); _} -> + `Styled (style, non_link_inline_elements status ~surrounding content) + |> Location.same element + + | {value = `Reference _; _} + | {value = `Link _; _} as element -> + Parse_error.not_allowed + ~what:(describe_element element.value) + ~in_what:(describe_element surrounding) + element.location + |> Error.raise_exception + +and non_link_inline_elements status ~surrounding elements = + List.map (non_link_inline_element status ~surrounding) elements + + + +let rec inline_element + : status -> Ast.inline_element with_location -> + Comment.inline_element with_location = + fun status element -> + + match element with + | {value = #Comment.leaf_inline_element; _} as element -> + (leaf_inline_element status element :> Comment.inline_element with_location) + + | {value = `Styled (style, content); location} -> + `Styled (style, inline_elements status content) + |> Location.at location + + | {value = `Reference (_, target, content) as value; location} -> + `Reference + (target, non_link_inline_elements status ~surrounding:value content) + |> Location.at location + + | {value = `Link (target, content) as value; location} -> + `Link (target, non_link_inline_elements status ~surrounding:value content) + |> Location.at location + +and inline_elements status elements = + List.map (inline_element status) elements + + + +let rec nestable_block_element + : status -> Ast.nestable_block_element with_location -> + Comment.nestable_block_element with_location = + fun status element -> + + match element with + | {value = `Paragraph content; location} -> + Location.at location (`Paragraph (inline_elements status content)) + + | {value = `Code_block _; _} + | {value = `Doc _; _} + | {value = `Example _; _} + | {value = `Verbatim _; _} + | {value = `Modules _; _} as element -> + element + + | {value = `List (kind, items); location} -> + `List (kind, List.map (nestable_block_elements status) items) + |> Location.at location + +and nestable_block_elements status elements = + List.map (nestable_block_element status) elements + + + +let tag : status -> Ast.tag -> Comment.tag = fun status tag -> + match tag with + | `Author _ + | `Since _ + | `Version _ + | `Canonical _ + | `Inline + | `Open + | `Closed as tag -> + tag + + | `Deprecated content -> + `Deprecated (nestable_block_elements status content) + + | `Param (name, content) -> + `Param (name, nestable_block_elements status content) + + | `Raise (name, content) -> + `Raise (name, nestable_block_elements status content) + + | `Return content -> + `Return (nestable_block_elements status content) + + | `See (kind, target, content) -> + `See (kind, target, nestable_block_elements status content) + + | `Before (version, content) -> + `Before (version, nestable_block_elements status content) + + + +(* When the user does not give a section heading a label (anchor), we generate + one from the text in the heading. This is the common case. This involves + simply scanning the AST for words, lowercasing them, and joining them with + hyphens. + + This must be done in the parser (i.e. early, not at HTML/other output + generation time), so that the cross-referencer can see these anchors. *) +let generate_heading_label : Comment.link_content -> string = fun content -> + + (* Code spans can contain spaces, so we need to replace them with hyphens. We + also lowercase all the letters, for consistency with the rest of this + procedure. *) + let replace_spaces_with_hyphens_and_lowercase s = + let result = Bytes.create (String.length s) in + s |> String.iteri begin fun index c -> + let c = + match c with + | ' ' | '\t' | '\r' | '\n' -> '-' + | _ -> Char.lowercase_ascii c + in + Bytes.set result index c + end; + Bytes.unsafe_to_string result + in + + (* Perhaps this should be done using a [Buffer.t]; we can switch to that as + needed. *) + let rec scan_inline_elements anchor = function + | [] -> + anchor + | element::more -> + let anchor = + match element.Location.value with + | `Space -> + anchor ^ "-" + | `Word w -> + anchor ^ (String.lowercase_ascii w) + | `Code_span c -> + anchor ^ (replace_spaces_with_hyphens_and_lowercase c) + | `Styled (_, content) -> + scan_inline_elements anchor content + in + scan_inline_elements anchor more + in + scan_inline_elements "" content + +let section_heading + : status -> + parsed_a_title:bool -> + Location.span -> + int -> + string option -> + (Ast.inline_element with_location) list -> + bool * (Comment.block_element with_location) = + fun status ~parsed_a_title location level label content -> + + let content = + non_link_inline_elements + status ~surrounding:(`Heading (level, label, content)) content + in + + let label = + match label with + | Some label -> label + | None -> generate_heading_label content + in + let label = Paths.Identifier.Label (status.parent_of_sections, label) in + + match status.sections_allowed, level with + | `None, _ -> + warning status (Parse_error.sections_not_allowed location); + let content = (content :> (Comment.inline_element with_location) list) in + let element = + Location.at location + (`Paragraph [Location.at location + (`Styled (`Bold, content))]) + in + parsed_a_title, element + + | `All, 1 -> + if parsed_a_title then + Error.raise_exception (Parse_error.only_one_title_allowed location); + let element = `Heading (`Title, label, content) in + let element = Location.at location element in + true, element + + | _ -> + let level = + match level with + | 2 -> `Section + | 3 -> `Subsection + | 4 -> `Subsubsection + | _ -> + Parse_error.bad_section_level (string_of_int level) location + |> warning status; + if level < 2 then + `Section + else + `Subsubsection + in + let element = `Heading (level, label, content) in + let element = Location.at location element in + parsed_a_title, element + + + +let top_level_block_elements + : status -> (Ast.block_element with_location) list -> + (Comment.block_element with_location) list = + fun status ast_elements -> + + let rec traverse + : parsed_a_title:bool -> + (Comment.block_element with_location) list -> + (Ast.block_element with_location) list -> + (Comment.block_element with_location) list = + fun ~parsed_a_title comment_elements_acc ast_elements -> + + match ast_elements with + | [] -> + List.rev comment_elements_acc + + | ast_element::ast_elements -> + match ast_element with + | {value = #Ast.nestable_block_element; _} as element -> + let element = nestable_block_element status element in + let element = (element :> Comment.block_element with_location) in + traverse ~parsed_a_title (element::comment_elements_acc) ast_elements + + | {value = `Tag the_tag; _} -> + let element = Location.same ast_element (`Tag (tag status the_tag)) in + traverse ~parsed_a_title (element::comment_elements_acc) ast_elements + + | {value = `Heading (level, label, content); _} -> + let parsed_a_title, element = + section_heading + status + ~parsed_a_title + ast_element.Location.location + level + label + content + in + traverse ~parsed_a_title (element::comment_elements_acc) ast_elements + in + + traverse ~parsed_a_title:false [] ast_elements + + + +let ast_to_comment ~permissive ~sections_allowed ~parent_of_sections ast = + let status = + { + permissive; + warnings = []; + sections_allowed; + parent_of_sections; + } + in + + let result = Error.catch (fun () -> top_level_block_elements status ast) in + let warnings = List.rev status.warnings in + + {Error.result; warnings} diff --git a/analysis/src/vendor/odoc_parser/semantics.mli b/analysis/src/vendor/odoc_parser/semantics.mli new file mode 100644 index 000000000..491ce4e27 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/semantics.mli @@ -0,0 +1,6 @@ +val ast_to_comment : + permissive:bool -> + sections_allowed:Ast.sections_allowed -> + parent_of_sections:Paths.Identifier.label_parent -> + Ast.docs -> + ((Comment.docs, Error.t) Error.result) Error.with_warnings diff --git a/analysis/src/vendor/odoc_parser/syntax.ml b/analysis/src/vendor/odoc_parser/syntax.ml new file mode 100644 index 000000000..0941b1aef --- /dev/null +++ b/analysis/src/vendor/odoc_parser/syntax.ml @@ -0,0 +1,1111 @@ +(* This module is a recursive descent parser for the ocamldoc syntax. The parser + consumes a token stream of type [Token.t Stream.t], provided by the lexer, + and produces a comment AST of the type defined in [Parser_.Ast]. + + The AST has two main levels: inline elements, which can appear inside + paragraphs, and are spaced horizontally when presented, and block elements, + such as paragraphs and lists, which are spaced vertically when presented. + Block elements contain inline elements, but not vice versa. + + Corresponding to this, the parser has three "main" functions: + + - [delimited_inline_element_list] parses a run of inline elements that is + delimited by curly brace markup ([{...}]). + - [paragraph] parses a run of inline elements that make up a paragraph, and + is not explicitly delimited with curly braces. + - [block_element_list] parses a sequence of block elements. A comment is a + sequence of block elements, so [block_element_list] is the top-level + parser. It is also used for list item and tag content. *) + + + +module Location = Location_ +module Error = Error +module Comment = Comment + +type 'a with_location = 'a Location.with_location + + + +(* {2 Input} *) + +type input = (Token.t Location.with_location) Stream.t + +let junk = Stream.junk + +let peek input = + match Stream.peek input with + | Some token -> token + | None -> assert false + (* The last token in the stream is always [`End], and it is never consumed by + the parser, so the [None] case is impossible. *) + +let npeek = Stream.npeek + + + +(* {2 Non-link inline elements} *) + +(* Convenient abbreviation for use in patterns. *) +type token_that_always_begins_an_inline_element = [ + | `Word of string + | `Code_span of string + | `Begin_style of Comment.style + | `Simple_reference of string + | `Begin_reference_with_replacement_text of string + | `Simple_link of string + | `Begin_link_with_replacement_text of string +] + +(* Check that the token constructors above actually are all in [Token.t]. *) +let _check_subset : token_that_always_begins_an_inline_element -> Token.t = + fun t -> (t :> Token.t) + +(* Consumes tokens that make up a single non-link inline element: + + - a horizontal space ([`Space], significant in inline elements), + - a word (see [word]), + - a code span ([...], [`Code_span _]), or + - styled text ({e ...}). + + The latter requires a recursive call to [delimited_inline_element_list], + defined below. + + This should be part of [delimited_inline_element_list]; however, it is also + called by function [paragraph]. As a result, it is factored out, and made + mutually-recursive with [delimited_inline_element_list]. + + This is called only when it is known that the first token in the list is the + beginning of an inline element. In the case of [`Minus] and [`Plus], that + means the caller has determined that they are not a list bullet (i.e., not + the first non-whitespace tokens on their line). + + This function consumes exactly the tokens that make up the element. *) +let rec inline_element + : input -> Location.span -> _ -> Ast.inline_element with_location = + fun input location next_token -> + + match next_token with + | `Space -> + junk input; + Location.at location `Space + + | `Word w -> + junk input; + Location.at location (`Word w) + (* This is actually the same memory representation as the token, complete + with location, and is probably the most common case. Perhaps the token + can be reused somehow. The same is true of [`Space], [`Code_span]. *) + + | `Minus -> + junk input; + Location.at location (`Word "-") + + | `Plus -> + junk input; + Location.at location (`Word "+") + + | `Code_span c -> + junk input; + Location.at location (`Code_span c) + + | `Begin_style s as parent_markup -> + junk input; + + let requires_leading_whitespace = + match s with + | `Bold | `Italic | `Emphasis -> true + | `Superscript | `Subscript -> false + in + let content, brace_location = + delimited_inline_element_list + ~parent_markup + ~parent_markup_location:location + ~requires_leading_whitespace + input + in + + let location = Location.span [location; brace_location] in + + if content = [] then + Parse_error.cannot_be_empty ~what:(Token.describe parent_markup) location + |> Error.raise_exception; + + Location.at location (`Styled (s, content)) + + | `Simple_reference r -> + junk input; + Location.at location (`Reference (`Simple, Helpers.read_reference r, [])) + + | `Begin_reference_with_replacement_text r as parent_markup -> + junk input; + + let content, brace_location = + delimited_inline_element_list + ~parent_markup + ~parent_markup_location:location + ~requires_leading_whitespace:false + input + in + + let location = Location.span [location; brace_location] in + + if content = [] then + Parse_error.cannot_be_empty ~what:(Token.describe parent_markup) location + |> Error.raise_exception; + + `Reference (`With_text, Helpers.read_reference r, content) + |> Location.at location + + | `Simple_link u -> + junk input; + Location.at location (`Link (u, [])) + + | `Begin_link_with_replacement_text u as parent_markup -> + junk input; + + let content, brace_location = + delimited_inline_element_list + ~parent_markup + ~parent_markup_location:location + ~requires_leading_whitespace:false + input + in + + `Link (u, content) + |> Location.at (Location.span [location; brace_location]) + +(* Consumes tokens that make up a sequence of inline elements that is ended by + a '}', a [`Right_brace] token. The brace token is also consumed. + + The sequences are also preceded by some markup like '{b'. Some of these + markup tokens require whitespace immediately after the token, and others not. + The caller indicates which way that is through the + [~requires_leading_whitespace] argument. + + Whitespace is significant in inline element lists. In particular, "foo [bar]" + is represented as [`Word "foo"; `Space; `Code_span "bar"], while "foo[bar]" + is [`Word "foo"; `Code_span "bar"]. It doesn't matter how much whitespace is + there, just whether it is present or not. Single newlines and horizontal + space in any amount are allowed. Blank lines are not, as these are separators + for {e block} elements. + + The first and last elements emitted will not be [`Space], i.e. [`Space] + appears only between other non-link inline elements. + + The [~parent_markup] and [~parent_markup_location] arguments are used for + generating error messages. *) +and delimited_inline_element_list + : parent_markup:[< Token.t ] -> + parent_markup_location:Location.span -> + requires_leading_whitespace:bool -> + input -> + (Ast.inline_element with_location) list * Location.span = + fun + ~parent_markup + ~parent_markup_location + ~requires_leading_whitespace + input -> + + (* [~at_start_of_line] is used to interpret [`Minus] and [`Plus]. These are + word tokens if not the first non-whitespace tokens on their line. Then, + they are allowed in a non-link element list. *) + let rec consume_elements + : at_start_of_line:bool -> (Ast.inline_element with_location) list -> + (Ast.inline_element with_location) list * Location.span = + fun ~at_start_of_line acc -> + + let next_token = peek input in + match next_token.value with + | `Right_brace -> + junk input; + List.rev acc, next_token.location + + (* The [`Space] token is not space at the beginning or end of line, because + that is combined into [`Single_newline] or [`Blank_line] tokens. It is + also not at the beginning of markup (after e.g. '{b'), because that is + handled separately before calling + [consume_non_link_inline_elements], and not immediately before '}', + because that is combined into the [`Right_brace] token by the lexer. So, + it is an internal space, and we want to add it to the non-link inline + element list. *) + | `Space + | #token_that_always_begins_an_inline_element as token -> + let acc = (inline_element input next_token.location token)::acc in + consume_elements ~at_start_of_line:false acc + + | `Single_newline -> + junk input; + let element = Location.same next_token `Space in + consume_elements ~at_start_of_line:true (element::acc) + + | `Minus + | `Plus as bullet -> + if not at_start_of_line then + let acc = (inline_element input next_token.location bullet)::acc in + consume_elements ~at_start_of_line:false acc + else + let suggestion = + Printf.sprintf + "move %s so it isn't the first thing on the line" + (Token.print bullet) + in + Parse_error.not_allowed + ~what:(Token.describe bullet) + ~in_what:(Token.describe parent_markup) + ~suggestion + next_token.location + |> Error.raise_exception + + | other_token -> + Parse_error.not_allowed + ~what:(Token.describe other_token) + ~in_what:(Token.describe parent_markup) + next_token.location + |> Error.raise_exception + in + + let first_token = peek input in + match first_token.value with + | `Space -> + junk input; + consume_elements ~at_start_of_line:false [] + (* [~at_start_of_line] is [false] here because the preceding token was some + some markup like '{b', and we didn't move to the next line, so the next + token will not be the first non-whitespace token on its line. *) + + | `Single_newline -> + junk input; + consume_elements ~at_start_of_line:true [] + + | `Blank_line -> + (* In case the markup is immediately followed by a blank line, the error + message printed by the catch-all case below can be confusing, as it will + suggest that the markup must be followed by a newline (which it is). It + just must not be followed by two newlines. To explain that clearly, + handle that case specifically. *) + Parse_error.not_allowed + ~what:(Token.describe `Blank_line) + ~in_what:(Token.describe parent_markup) + first_token.location + |> Error.raise_exception + + | `Right_brace -> + junk input; + [], first_token.location + + | _ -> + if requires_leading_whitespace then + Parse_error.must_be_followed_by_whitespace + ~what:(Token.describe parent_markup) parent_markup_location + |> Error.raise_exception + else + consume_elements ~at_start_of_line:false [] + + + +(* {2 Paragraphs} *) + +(* Consumes tokens that make up a paragraph. + + A paragraph is a sequence of inline elements that ends on a blank line, or + explicit block markup such as a verbatim block on a new line. + + Because of the significance of newlines, paragraphs are parsed line-by-line. + The function [paragraph] is called only when the current token is the first + non-whitespace token on its line, and begins an inline element. [paragraph] + then parses a line of inline elements. Afterwards, it looks ahead to the next + line. If that line also begins with an inline element, it parses that line, + and so on. *) +let paragraph : input -> Ast.nestable_block_element with_location = + fun input -> + + (* Parses a single line of a paragraph, consisting of inline elements. The + only valid ways to end a paragraph line are with [`End], [`Single_newline], + [`Blank_line], and [`Right_brace]. Everything else either belongs in the + paragraph, or signifies an attempt to begin a block element inside a + paragraph line, which is an error. These errors are caught elsewhere; the + paragraph parser just stops. *) + let rec paragraph_line + : (Ast.inline_element with_location) list -> + (Ast.inline_element with_location) list = + fun acc -> + + let next_token = peek input in + match next_token.value with + | `Space + | `Minus + | `Plus + | #token_that_always_begins_an_inline_element as token -> + let element = inline_element input next_token.location token in + paragraph_line (element::acc) + + | _ -> + acc + in + + (* After each line is parsed, decides whether to parse more lines. *) + let rec additional_lines + : (Ast.inline_element with_location) list -> + (Ast.inline_element with_location) list = + fun acc -> + + match npeek 2 input with + | {value = `Single_newline; location}:: + {value = #token_that_always_begins_an_inline_element; _}::_ -> + junk input; + let acc = (Location.at location `Space)::acc in + let acc = paragraph_line acc in + additional_lines acc + + | _ -> + List.rev acc + in + + let elements = paragraph_line [] |> additional_lines in + `Paragraph elements + |> Location.at (Location.span (List.map Location.location elements)) + + + +(* {2 Block elements} *) + +(* {3 Helper types} *) + +(* The interpretation of tokens in the block parser depends on where on a line + each token appears. The five possible "locations" are: + + - [`At_start_of_line], when only whitespace has been read on the current + line. + - [`After_tag], when a valid tag token, such as [@deprecated], has been read, + and only whitespace has been read since. + - [`After_shorthand_bullet], when a valid shorthand list item bullet, such as + [-], has been read, and only whitespace has been read since. + - [`After_explicit_list_bullet], when a valid explicit bullet, such as [{li], + has been read, and only whitespace has been read since. + - [`After_text], when any other valid non-whitespace token has already been + read on the current line. + + Here are some examples of how this affects the interpretation of tokens: + + - A paragraph can start anywhere except [`After_text] (two paragraphs cannot + be on the same line, but paragraphs can be nested in just about anything). + - [`Minus] is interpreted as a list item bullet [`At_start_of_line], + [`After_tag], and [`After_explicit_list_bullet]. + - Tags are only allowed [`At_start_of_line]. + + To track the location accurately, the functions that make up the block parser + pass explicit [where_in_line] values around and return them. + + In a few cases, [where_in_line] can be inferred from what helper was called. + For example, the [paragraph] parser always stops on the same line as the last + significant token that is in the paragraph it consumed, so the location must + be [`After_text]. *) +type where_in_line = [ + | `At_start_of_line + | `After_tag + | `After_shorthand_bullet + | `After_explicit_list_bullet + | `After_text +] + +(* The block parsing loop, function [block_element_list], stops when it + encounters certain tokens. + + When it is called for the whole comment, or for in explicit list item + ([{li foo}]), it can only stop on end of input or a right brace. + + When it is called inside a shorthand list item ([- foo]), it stops on end of + input, right brace, a blank line (indicating end of shorthand list), plus or + minus (indicating the start of the next liste item), or a section heading or + tag, which cannot be nested in list markup. + + The block parser [block_element_list] explicitly returns the token that + stopped it, with a type more precise than [Token.t stream_head]: if it was + called for the whole comment or an explicit list item, the stop token will + have type [stops_at_delimiters stream_head], and if it was called for a + shorthand list item, the stop token will have type + [implicit_stop stream_head]. This allows the calling parsers to write precise + cases for exactly the tokens that might be at the front of the stream after + the block parser returns. *) +type stops_at_delimiters = [ + | `End + | `Right_brace +] + +type stopped_implicitly = [ + | `End + | `Blank_line + | `Right_brace + | `Minus + | `Plus + | Token.section_heading + | Token.tag +] + +(* Ensure that the above two types are really subsets of [Token.t]. *) +let _check_subset : stops_at_delimiters -> Token.t = fun t -> (t :> Token.t) +let _check_subset : stopped_implicitly -> Token.t = fun t -> (t :> Token.t) + +(* The different contexts in which the block parser [block_element_list] can be + called. The block parser's behavior depends somewhat on the context. For + example, while paragraphs are allowed anywhere, shorthand lists are not + allowed immediately inside other shorthand lists, while tags are not allowed + anywhere except at the comment top level. + + Besides telling the block parser how to behave, each context also carries two + types, which determine the return type of the block parser: + + - The type of blocks the parser returns. Note that [nestable_block_element] + is included in [block_element]. However, the extra block kinds in + [block_element] are only allowed at the comment top level. + - The type of token that the block parser stops at. See discussion above. *) +type ('block, 'stops_at_which_tokens) context = + | Top_level : + (Ast.block_element, stops_at_delimiters) context + | In_shorthand_list : + (Ast.nestable_block_element, stopped_implicitly) context + | In_explicit_list : + (Ast.nestable_block_element, stops_at_delimiters) context + | In_tag : + (Ast.nestable_block_element, Token.t) context + +(* This is a no-op. It is needed to prove to the type system that nestable block + elements are acceptable block elements in all contexts. *) +let accepted_in_all_contexts + : type block stops_at_which_tokens. + (block, stops_at_which_tokens) context -> + Ast.nestable_block_element -> + block = + fun context block -> + match context with + | Top_level -> (block :> Ast.block_element) + | In_shorthand_list -> block + | In_explicit_list -> block + | In_tag -> block + +(* {3 The block element loop} *) + +(* {2 Block element lists} *) + +(* Consumes tokens making up a sequence of block elements. These are: + + - paragraphs, + - code blocks, + - verbatim text blocks, + - lists, and + - section headings. *) +let rec block_element_list + : type block stops_at_which_tokens. + (block, stops_at_which_tokens) context -> + parent_markup:[< Token.t | `Comment ] -> + input -> + (block with_location) list * + stops_at_which_tokens with_location * + where_in_line = + fun context ~parent_markup input -> + + let rec consume_block_elements + : parsed_a_tag:bool -> + where_in_line -> + (block with_location) list -> + (block with_location) list * + stops_at_which_tokens with_location * + where_in_line = + fun ~parsed_a_tag where_in_line acc -> + + let describe token = + match token with + | #token_that_always_begins_an_inline_element -> "paragraph" + | _ -> Token.describe token + in + + let raise_if_after_text {Location.location; value = token} = + if where_in_line = `After_text then + Parse_error.must_begin_on_its_own_line ~what:(describe token) location + |> Error.raise_exception + in + + let raise_if_after_tags {Location.location; value = token} = + if parsed_a_tag then + let suggestion = + Printf.sprintf + "move %s before any tags" (Token.describe token) + in + Parse_error.not_allowed + ~what:(describe token) + ~in_what:"the tags section" + ~suggestion + location + |> Error.raise_exception + in + + let raise_because_not_at_top_level {Location.location; value = token} = + let suggestion = + Printf.sprintf + "move %s outside of any other markup" (Token.print token) + in + Parse_error.not_allowed + ~what:(Token.describe token) + ~in_what:(Token.describe parent_markup) + ~suggestion + location + |> Error.raise_exception + in + + + + match peek input with + (* Terminators: the two tokens that terminate anything. *) + | {value = `End; _} + | {value = `Right_brace; _} as next_token -> + (* This little absurdity is needed to satisfy the type system. Without it, + OCaml is unable to prove that [stream_head] has the right type for all + possible values of [context]. *) + begin match context with + | Top_level -> + List.rev acc, next_token, where_in_line + | In_shorthand_list -> + List.rev acc, next_token, where_in_line + | In_explicit_list -> + List.rev acc, next_token, where_in_line + | In_tag -> + List.rev acc, next_token, where_in_line + end + + + + (* Whitespace. This can terminate some kinds of block elements. It is also + necessary to track it to interpret [`Minus] and [`Plus] correctly, as + well as to ensure that all block elements begin on their own line. *) + | {value = `Space; _} -> + junk input; + consume_block_elements ~parsed_a_tag where_in_line acc + + | {value = `Single_newline; _} -> + junk input; + consume_block_elements ~parsed_a_tag `At_start_of_line acc + + | {value = `Blank_line; _} as next_token -> + begin match context with + (* Blank lines terminate shorthand lists ([- foo]). They also terminate + paragraphs, but the paragraph parser is aware of that internally. *) + | In_shorthand_list -> + List.rev acc, next_token, where_in_line + (* Otherwise, blank lines are pretty much like single newlines. *) + | _ -> + junk input; + consume_block_elements ~parsed_a_tag `At_start_of_line acc + end + + + + (* Explicit list items ([{li ...}] and [{- ...}]) can never appear directly + in block content. They can only appear inside [{ul ...}] and [{ol ...}]. + So, catch those. *) + | {value = `Begin_list_item _ as token; location} -> + let suggestion = + Printf.sprintf + "move %s into %s, or use %s" + (Token.print token) + (Token.describe (`Begin_list `Unordered)) + (Token.describe (`Minus)) + in + Parse_error.not_allowed + ~what:(Token.describe token) + ~in_what:(Token.describe parent_markup) + ~suggestion + location + |> Error.raise_exception + + + + (* Tags. These can appear at the top level only. Also, once one tag is seen, + the only top-level elements allowed are more tags. *) + | {value = `Tag tag as token; location} as next_token -> + begin match context with + (* Tags cannot make sense in an explicit list ([{ul {li ...}}]). *) + | In_explicit_list -> + raise_because_not_at_top_level next_token + (* If a tag starts at the beginning of a line, it terminates the preceding + tag and/or the current shorthand list. In this case, return to the + caller, and let the caller decide how to interpret the tag token. *) + | In_shorthand_list -> + if where_in_line = `At_start_of_line then + List.rev acc, next_token, where_in_line + else + raise_because_not_at_top_level next_token + | In_tag -> + if where_in_line = `At_start_of_line then + List.rev acc, next_token, where_in_line + else + raise_because_not_at_top_level next_token + + (* If this is the top-level call to [block_element_list], parse the + tag. *) + | Top_level -> + if where_in_line <> `At_start_of_line then + Parse_error.must_begin_on_its_own_line + ~what:(Token.describe token) location + |> Error.raise_exception; + + junk input; + + begin match tag with + | `Author s | `Since s | `Version s | `Canonical s as tag -> + let s = String.trim s in + if s = "" then + Parse_error.cannot_be_empty ~what:(Token.describe token) location + |> Error.raise_exception; + let tag = + match tag with + | `Author _ -> `Author s + | `Since _ -> `Since s + | `Version _ -> `Version s + | `Canonical _ -> + let path = Helpers.read_path_longident s in + let module_ = Helpers.read_mod_longident s in + `Canonical (path, module_) + in + let tag = Location.at location (`Tag tag) in + consume_block_elements ~parsed_a_tag:true `After_text (tag::acc) + + | `Deprecated | `Return as tag -> + let content, _stream_head, where_in_line = + block_element_list In_tag ~parent_markup:token input in + let tag = + match tag with + | `Deprecated -> `Deprecated content + | `Return -> `Return content + in + let location = + location::(List.map Location.location content) + |> Location.span + in + let tag = Location.at location (`Tag tag) in + consume_block_elements ~parsed_a_tag:true where_in_line (tag::acc) + + | `Param _ | `Raise _ | `Before _ as tag -> + let content, _stream_head, where_in_line = + block_element_list In_tag ~parent_markup:token input in + let tag = + match tag with + | `Param s -> `Param (s, content) + | `Raise s -> `Raise (s, content) + | `Before s -> `Before (s, content) + in + let location = + location::(List.map Location.location content) + |> Location.span + in + let tag = Location.at location (`Tag tag) in + consume_block_elements ~parsed_a_tag:true where_in_line (tag::acc) + + | `See (kind, target) -> + let content, _next_token, where_in_line = + block_element_list In_tag ~parent_markup:token input in + let location = + location::(List.map Location.location content) + |> Location.span + in + let tag = `Tag (`See (kind, target, content)) in + let tag = Location.at location tag in + consume_block_elements ~parsed_a_tag:true where_in_line (tag::acc) + + | `Inline | `Open | `Closed as tag -> + let tag = Location.at location (`Tag tag) in + consume_block_elements ~parsed_a_tag:true `After_text (tag::acc) + end + end + + + + | {value = #token_that_always_begins_an_inline_element; _} as next_token -> + raise_if_after_text next_token; + raise_if_after_tags next_token; + + let block = paragraph input in + let block = + Location_.map (accepted_in_all_contexts context) block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag `After_text acc + + | {value = `Example (lang, content); location} as next_token -> + raise_if_after_text next_token; + raise_if_after_tags next_token; + + junk input; + let block = `Example(String.trim(lang), content) in + let block = accepted_in_all_contexts context block in + let block = Location.at location block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag `After_text acc + + | {value = `Code_block s | `Verbatim s | `Doc s as token; location} as next_token -> + raise_if_after_text next_token; + raise_if_after_tags next_token; + if s = "" then + Parse_error.cannot_be_empty ~what:(Token.describe token) location + |> Error.raise_exception; + + junk input; + let block = + match token with + | `Code_block _ -> `Code_block s + | `Doc _ -> `Doc s + | `Verbatim _ -> `Verbatim s + in + let block = accepted_in_all_contexts context block in + let block = Location.at location block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag `After_text acc + + | {value = `Modules s as token; location} as next_token -> + raise_if_after_text next_token; + raise_if_after_tags next_token; + + junk input; + + (* TODO Use some library for a splitting function, or move this out into a + Util module. *) + let split_string delimiters s = + let rec scan_delimiters acc index = + if index >= String.length s then + List.rev acc + else + if String.contains delimiters s.[index] then + scan_delimiters acc (index + 1) + else + scan_word acc index (index + 1) + + and scan_word acc start_index index = + if index >= String.length s then + let word = String.sub s start_index (index - start_index) in + List.rev (word::acc) + else + if String.contains delimiters s.[index] then + let word = String.sub s start_index (index - start_index) in + scan_delimiters (word::acc) (index + 1) + else + scan_word acc start_index (index + 1) + + in + + scan_delimiters [] 0 + in + + let modules = + split_string " \t\r\n" s + |> List.map Helpers.read_mod_longident + in + + if modules = [] then + Parse_error.cannot_be_empty ~what:(Token.describe token) location + |> Error.raise_exception; + + let block = accepted_in_all_contexts context (`Modules modules) in + let block = Location.at location block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag `After_text acc + + + + | {value = `Begin_list kind as token; location} as next_token -> + raise_if_after_text next_token; + raise_if_after_tags next_token; + + junk input; + + let items, brace_location = + explicit_list_items ~parent_markup:token input in + if items = [] then + Parse_error.cannot_be_empty ~what:(Token.describe token) location + |> Error.raise_exception; + + let location = Location.span [location; brace_location] in + let block = `List (kind, items) in + let block = accepted_in_all_contexts context block in + let block = Location.at location block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag `After_text acc + + + + | {value = `Minus | `Plus as token; location} as next_token -> + begin match where_in_line with + | `After_text | `After_shorthand_bullet -> + Parse_error.must_begin_on_its_own_line + ~what:(Token.describe token) location + |> Error.raise_exception + | _ -> + () + end; + + raise_if_after_tags next_token; + + begin match context with + | In_shorthand_list -> + List.rev acc, next_token, where_in_line + | _ -> + let items, where_in_line = + shorthand_list_items next_token where_in_line input in + let kind = + match token with + | `Minus -> `Unordered + | `Plus -> `Ordered + in + let location = + location::(List.map Location.location (List.flatten items)) + |> Location.span + in + let block = `List (kind, items) in + let block = accepted_in_all_contexts context block in + let block = Location.at location block in + let acc = block::acc in + consume_block_elements ~parsed_a_tag where_in_line acc + end + + + + | {value = `Begin_section_heading (level, label) as token; location} + as next_token -> + + raise_if_after_tags next_token; + + begin match context with + | In_shorthand_list -> + if where_in_line = `At_start_of_line then + List.rev acc, next_token, where_in_line + else + raise_because_not_at_top_level next_token + | In_explicit_list -> + raise_because_not_at_top_level next_token + | In_tag -> + raise_because_not_at_top_level next_token + + | Top_level -> + if where_in_line <> `At_start_of_line then + Parse_error.must_begin_on_its_own_line + ~what:(Token.describe token) location + |> Error.raise_exception; + + junk input; + + let content, brace_location = + delimited_inline_element_list + ~parent_markup:token + ~parent_markup_location:location + ~requires_leading_whitespace:true + input + in + if content = [] then + Parse_error.cannot_be_empty ~what:(Token.describe token) location + |> Error.raise_exception; + + let location = Location.span [location; brace_location] in + let heading = `Heading (level, label, content) in + let heading = Location.at location heading in + let acc = heading::acc in + consume_block_elements ~parsed_a_tag `After_text acc + end + in + + let where_in_line = + match context with + | Top_level -> `At_start_of_line + | In_shorthand_list -> `After_shorthand_bullet + | In_explicit_list -> `After_explicit_list_bullet + | In_tag -> `After_tag + in + + consume_block_elements ~parsed_a_tag:false where_in_line [] + +(* {3 Lists} *) + +(* Consumes a sequence of implicit list items. Each one consists of a [`Minus] + or [`Plus] token, followed by block elements until: + + - a blank line, or + - a list bullet of the opposite kind (e.g. [`Plus] for a [`Minus] list). + + This function is called when the next token is known to be [`Minus] or + [`Plus]. It consumes that token, and calls the block element parser (see + above). That parser returns to [implicit_list_items] only on [`Blank_line], + [`End], [`Minus] or [`Plus] at the start of a line, or [`Right_brace]. *) +and shorthand_list_items + : [ `Minus | `Plus ] with_location -> + where_in_line -> + input -> + ((Ast.nestable_block_element with_location) list) list * + where_in_line = + fun first_token where_in_line input -> + + let bullet_token = first_token.value in + + let rec consume_list_items + : [> ] with_location -> + where_in_line -> + ((Ast.nestable_block_element with_location) list) list -> + ((Ast.nestable_block_element with_location) list) list * + where_in_line = + fun next_token where_in_line acc -> + + match next_token.value with + | `End + | `Right_brace + | `Blank_line + | `Tag _ + | `Begin_section_heading _ -> + List.rev acc, where_in_line + + | `Minus + | `Plus as bullet -> + if bullet = bullet_token then begin + junk input; + + let content, stream_head, where_in_line = + block_element_list In_shorthand_list ~parent_markup:bullet input in + if content = [] then + Parse_error.cannot_be_empty + ~what:(Token.describe bullet) next_token.location + |> Error.raise_exception; + + let acc = content::acc in + consume_list_items stream_head where_in_line acc + end + else + List.rev acc, where_in_line + in + + consume_list_items + (first_token :> stopped_implicitly with_location) where_in_line [] + +(* Consumes a sequence of explicit list items (starting with '{li ...}' and + '{-...}', which are represented by [`Begin_list_item _] tokens). + + This function is called immediately after '{ul' or '{ol' ([`Begin_list _]) is + read. The only "valid" way to exit is by reading a [`Right_brace] token, + which is consumed. + + Whitespace inside the list, but outside list items, is not significant – this + parsing function consumes all of it. Otherwise, only list item start tokens + are accepted. Everything else is an error. *) +and explicit_list_items + : parent_markup:[< Token.t ] -> + input -> + ((Ast.nestable_block_element with_location) list) list * + Location.span = + fun ~parent_markup input -> + + let rec consume_list_items + : ((Ast.nestable_block_element with_location) list) list -> + ((Ast.nestable_block_element with_location) list) list * + Location.span = + fun acc -> + + let next_token = peek input in + match next_token.value with + | `End -> + Parse_error.not_allowed + next_token.location + ~what:(Token.describe `End) + ~in_what:(Token.describe parent_markup) + |> Error.raise_exception + + | `Right_brace -> + junk input; + List.rev acc, next_token.location + + | `Space + | `Single_newline + | `Blank_line -> + junk input; + consume_list_items acc + + | `Begin_list_item kind as token -> + junk input; + + (* '{li', represented by [`Begin_list_item `Li], must be followed by + whitespace. *) + if kind = `Li then begin + match (peek input).value with + | `Space | `Single_newline | `Blank_line | `Right_brace -> + () + (* The presence of [`Right_brace] above requires some explanation: + + - It is better to be silent about missing whitespace if the next + token is [`Right_brace], because the error about an empty list + item will be generated below, and that error is more important to + the user. + - The [`Right_brace] token also happens to include all whitespace + before it, as a convenience for the rest of the parser. As a + result, not ignoring it could be wrong: there could in fact be + whitespace in the concrete syntax immediately after '{li', just + it is not represented as [`Space], [`Single_newline], or + [`Blank_line]. *) + | _ -> + Parse_error.must_be_followed_by_whitespace + next_token.location ~what:(Token.print token) + |> Error.raise_exception + end; + + let content, token_after_list_item, _where_in_line = + block_element_list In_explicit_list ~parent_markup:token input in + + if content = [] then + Parse_error.cannot_be_empty + next_token.location ~what:(Token.describe token) + |> Error.raise_exception; + + begin match token_after_list_item.value with + | `Right_brace -> + junk input + | `End -> + Parse_error.not_allowed + token_after_list_item.location + ~what:(Token.describe `End) + ~in_what:(Token.describe token) + |> Error.raise_exception + end; + + let acc = content::acc in + consume_list_items acc + + | token -> + let suggestion = + match token with + | `Begin_section_heading _ | `Tag _ -> + Printf.sprintf "move %s outside the list" (Token.describe token) + | _ -> + Printf.sprintf + "move %s into a list item, %s or %s" + (Token.describe token) + (Token.print (`Begin_list_item `Li)) + (Token.print (`Begin_list_item `Dash)) + in + Parse_error.not_allowed + next_token.location + ~what:(Token.describe token) + ~in_what:(Token.describe parent_markup) + ~suggestion + |> Error.raise_exception + in + + consume_list_items [] + + + +(* {2 Entry point} *) + +let parse token_stream = + Error.catch begin fun () -> + let elements, last_token, _where_in_line = + block_element_list Top_level ~parent_markup:`Comment token_stream in + + match last_token.value with + | `End -> + elements + | `Right_brace -> + Parse_error.unpaired_right_brace last_token.location + |> Error.raise_exception + end diff --git a/analysis/src/vendor/odoc_parser/syntax.mli b/analysis/src/vendor/odoc_parser/syntax.mli new file mode 100644 index 000000000..6b3602a86 --- /dev/null +++ b/analysis/src/vendor/odoc_parser/syntax.mli @@ -0,0 +1,3 @@ +val parse : + (Token.t Location_.with_location) Stream.t -> + (Ast.docs, Error.t) Error.result diff --git a/analysis/src/vendor/odoc_parser/token.ml b/analysis/src/vendor/odoc_parser/token.ml new file mode 100644 index 000000000..2b4fb4a5d --- /dev/null +++ b/analysis/src/vendor/odoc_parser/token.ml @@ -0,0 +1,233 @@ +(* This module contains the token type, emitted by the lexer, and consumed by + the comment syntax parser. It also contains two functions that format tokens + for error messages. *) + + + +type section_heading = [ + `Begin_section_heading of int * string option +] + +type tag = [ + | `Tag of [ + | `Author of string + | `Deprecated + | `Param of string + | `Raise of string + | `Return + | `See of [ `Url | `File | `Document ] * string + | `Since of string + | `Before of string + | `Version of string + | `Canonical of string + | `Inline + | `Open + | `Closed + ] +] + +type t = [ + (* End of input. *) + | `End + + (* Runs of whitespace. [Blank_line] is any run of whitespace that contains two + or more newline characters. [Single_newline] is any run of whitespace that + contains exactly one newline character. [Space] is any run of whitespace + that contains no newline characters. + + It is an important invariant in the parser that no adjacent whitespace + tokens are emitted by the lexer. Otherwise, there would be the need for + unbounded lookahead, a (co-?)ambiguity between + [Single_newline Single_newline] and [Blank_line], and other problems. *) + | `Space + | `Single_newline + | `Blank_line + + (* A right curly brace ([}]), i.e. end of markup. *) + | `Right_brace + + (* Words are anything that is not whitespace or markup. Markup symbols can be + be part of words if escaped. + + Words can contain plus and minus symbols, but those are emitted as [Plus] + and [Minus] tokens. The parser combines plus and minus into words, except + when they appear first on a line, in which case the tokens are list item + bullets. *) + | `Word of string + | `Code_span of string + | `Begin_style of Comment.style + + (* Other inline element markup. *) + | `Simple_reference of string + | `Begin_reference_with_replacement_text of string + | `Simple_link of string + | `Begin_link_with_replacement_text of string + + (* Leaf block element markup. *) + | `Code_block of string + | `Verbatim of string + | `Modules of string + | `Example of (string * string) + | `Doc of string + + (* List markup. *) + | `Begin_list of [ `Unordered | `Ordered ] + | `Begin_list_item of [ `Li | `Dash ] + | `Minus + | `Plus + + | section_heading + | tag +] + + + +let print : [< t ] -> string = function + | `Begin_style `Bold -> + "'{b'" + | `Begin_style `Italic -> + "'{i'" + | `Begin_style `Emphasis -> + "'{e'" + | `Begin_style `Superscript -> + "'{^'" + | `Begin_style `Subscript -> + "'{_'" + | `Begin_reference_with_replacement_text _ -> + "'{{!'" + | `Begin_link_with_replacement_text _ -> + "'{{:'" + | `Begin_list_item `Li -> + "'{li ...}'" + | `Begin_list_item `Dash -> + "'{- ...}'" + | `Minus -> + "'-'" + | `Plus -> + "'+'" + | `Begin_section_heading (level, label) -> + let label = + match label with + | None -> "" + | Some label -> ":" ^ label + in + Printf.sprintf "'{%i%s'" level label + | `Tag (`Author _) -> + "'@author'" + | `Tag `Deprecated -> + "'@deprecated'" + | (`Example _) -> + "'@example'" + | (`Doc _) -> + "'@doc'" + | `Tag (`Param _) -> + "'@param'" + | `Tag (`Raise _) -> + "'@raise'" + | `Tag `Return -> + "'@return'" + | `Tag (`See _) -> + "'@see'" + | `Tag (`Since _) -> + "'@since'" + | `Tag (`Before _) -> + "'@before'" + | `Tag (`Version _) -> + "'@version'" + | `Tag (`Canonical _) -> + "'@canonical'" + | `Tag `Inline -> + "'@inline'" + | `Tag `Open -> + "'@open'" + | `Tag `Closed -> + "'@closed'" + +(* [`Minus] and [`Plus] are interpreted as if they start list items. Therefore, + for error messages based on [Token.describe] to be accurate, formatted + [`Minus] and [`Plus] should always be plausibly list item bullets. *) +let describe : [< t | `Comment ] -> string = function + | `Word w -> + Printf.sprintf "'%s'" w + | `Code_span _ -> + "'[...]' (code)" + | `Begin_style `Bold -> + "'{b ...}' (boldface text)" + | `Begin_style `Italic -> + "'{i ...}' (italic text)" + | `Begin_style `Emphasis -> + "'{e ...}' (emphasized text)" + | `Begin_style `Superscript -> + "'{^...}' (superscript)" + | `Begin_style `Subscript -> + "'{_...}' (subscript)" + | `Simple_reference _ -> + "'{!...}' (cross-reference)" + | `Begin_reference_with_replacement_text _ -> + "'{{!...} ...}' (cross-reference)" + | `Simple_link _ -> + "'{:...} (external link)'" + | `Begin_link_with_replacement_text _ -> + "'{{:...} ...}' (external link)" + | `End -> + "end of text" + | `Space -> + "whitespace" + | `Single_newline -> + "line break" + | `Blank_line -> + "blank line" + | `Right_brace -> + "'}'" + | `Code_block _ -> + "'{[...]}' (code block)" + | `Verbatim _ -> + "'{v ... v}' (verbatim text)" + | `Modules _ -> + "'{!modules ...}'" + | `Begin_list `Unordered -> + "'{ul ...}' (bulleted list)" + | `Begin_list `Ordered -> + "'{ol ...}' (numbered list)" + | `Begin_list_item `Li -> + "'{li ...}' (list item)" + | `Begin_list_item `Dash -> + "'{- ...}' (list item)" + | `Minus -> + "'-' (bulleted list item)" + | `Plus -> + "'+' (numbered list item)" + | `Begin_section_heading (level, _) -> + Printf.sprintf "'{%i ...}' (section heading)" level + | `Tag (`Author _) -> + "'@author'" + | `Tag `Deprecated -> + "'@deprecated'" + | (`Example _) -> + "'@example'" + | (`Doc _) -> + "'@doc'" + | `Tag (`Param _) -> + "'@param'" + | `Tag (`Raise _) -> + "'@raise'" + | `Tag `Return -> + "'@return'" + | `Tag (`See _) -> + "'@see'" + | `Tag (`Since _) -> + "'@since'" + | `Tag (`Before _) -> + "'@before'" + | `Tag (`Version _) -> + "'@version'" + | `Tag (`Canonical _) -> + "'@canonical'" + | `Tag `Inline -> + "'@inline'" + | `Tag `Open -> + "'@open'" + | `Tag `Closed -> + "'@closed'" + | `Comment -> + "top-level text" diff --git a/analysis/src/vendor/omd/Readme.md b/analysis/src/vendor/omd/Readme.md new file mode 100644 index 000000000..ae899021c --- /dev/null +++ b/analysis/src/vendor/omd/Readme.md @@ -0,0 +1,3 @@ +The source code in this directory was taken, with some modifications, from the `omd` project (https://github.com/ocaml/omd/tree/master/src). + +It is under the ISC license. \ No newline at end of file diff --git a/analysis/src/vendor/omd/html_characters.ml b/analysis/src/vendor/omd/html_characters.ml new file mode 100644 index 000000000..937ba5541 --- /dev/null +++ b/analysis/src/vendor/omd/html_characters.ml @@ -0,0 +1,1827 @@ +(* UTF-8 and HTML entities *) +let characters_htmlentities_descriptions = +(* data extracted from http://www.w3schools.com/ + on December, 18th, 2013 *) +[ +" ", +" ", +"space"; +"!", +"!", +"exclamation mark"; +"\"", +""", +"quotation mark"; +"#", +"#", +"number sign"; +"$", +"$", +"dollar sign"; +"%", +"%", +"percent sign"; +"&", +"&", +"ampersand"; +"'", +"'", +"apostrophe"; +"(", +"(", +"left parenthesis"; +")", +")", +"right parenthesis"; +"*", +"*", +"asterisk"; +"+", +"+", +"plus sign"; +",", +",", +"comma"; +"-", +"-", +"hyphen"; +".", +".", +"period"; +"/", +"/", +"slash"; +"0", +"0", +"digit 0"; +"1", +"1", +"digit 1"; +"2", +"2", +"digit 2"; +"3", +"3", +"digit 3"; +"4", +"4", +"digit 4"; +"5", +"5", +"digit 5"; +"6", +"6", +"digit 6"; +"7", +"7", +"digit 7"; +"8", +"8", +"digit 8"; +"9", +"9", +"digit 9"; +":", +":", +"colon"; +";", +";", +"semicolon"; +"<", +"<", +"less-than"; +"=", +"=", +"equals-to"; +">", +">", +"greater-than"; +"?", +"?", +"question mark"; +"@", +"@", +"at sign"; +"A", +"A", +"uppercase A"; +"B", +"B", +"uppercase B"; +"C", +"C", +"uppercase C"; +"D", +"D", +"uppercase D"; +"E", +"E", +"uppercase E"; +"F", +"F", +"uppercase F"; +"G", +"G", +"uppercase G"; +"H", +"H", +"uppercase H"; +"I", +"I", +"uppercase I"; +"J", +"J", +"uppercase J"; +"K", +"K", +"uppercase K"; +"L", +"L", +"uppercase L"; +"M", +"M", +"uppercase M"; +"N", +"N", +"uppercase N"; +"O", +"O", +"uppercase O"; +"P", +"P", +"uppercase P"; +"Q", +"Q", +"uppercase Q"; +"R", +"R", +"uppercase R"; +"S", +"S", +"uppercase S"; +"T", +"T", +"uppercase T"; +"U", +"U", +"uppercase U"; +"V", +"V", +"uppercase V"; +"W", +"W", +"uppercase W"; +"X", +"X", +"uppercase X"; +"Y", +"Y", +"uppercase Y"; +"Z", +"Z", +"uppercase Z"; +"[", +"[", +"left square bracket"; +"\\", +"\", +"backslash"; +"]", +"]", +"right square bracket"; +"^", +"^", +"caret"; +"_", +"_", +"underscore"; +"`", +"`", +"grave accent"; +"a", +"a", +"lowercase a"; +"b", +"b", +"lowercase b"; +"c", +"c", +"lowercase c"; +"d", +"d", +"lowercase d"; +"e", +"e", +"lowercase e"; +"f", +"f", +"lowercase f"; +"g", +"g", +"lowercase g"; +"h", +"h", +"lowercase h"; +"i", +"i", +"lowercase i"; +"j", +"j", +"lowercase j"; +"k", +"k", +"lowercase k"; +"l", +"l", +"lowercase l"; +"m", +"m", +"lowercase m"; +"n", +"n", +"lowercase n"; +"o", +"o", +"lowercase o"; +"p", +"p", +"lowercase p"; +"q", +"q", +"lowercase q"; +"r", +"r", +"lowercase r"; +"s", +"s", +"lowercase s"; +"t", +"t", +"lowercase t"; +"u", +"u", +"lowercase u"; +"v", +"v", +"lowercase v"; +"w", +"w", +"lowercase w"; +"x", +"x", +"lowercase x"; +"y", +"y", +"lowercase y"; +"z", +"z", +"lowercase z"; +"{", +"{", +"left curly brace"; +"|", +"|", +"vertical bar"; +"}", +"}", +"right curly brace"; +"~", +"~", +"tilde"; +"\000", +"�", +"null character"; +"\001", +"", +"start of header"; +"\002", +"", +"start of text"; +"\003", +"", +"end of text"; +"\004", +"", +"end of transmission"; +"\005", +"", +"enquiry"; +"\006", +"", +"acknowledge"; +"\007", +"", +"bell (ring)"; +"\008", +"", +"backspace"; +"\009", +" ", +"horizontal tab"; +"\010", +" ", +"line feed"; +"\011", +" ", +"vertical tab"; +"\012", +" ", +"form feed"; +"\013", +" ", +"carriage return"; +"\014", +"", +"shift out"; +"\015", +"", +"shift in"; +"\016", +"", +"data link escape"; +"\017", +"", +"device control 1"; +"\018", +"", +"device control 2"; +"\019", +"", +"device control 3"; +"\020", +"", +"device control 4"; +"\021", +"", +"negative acknowledge"; +"\022", +"", +"synchronize"; +"\023", +"", +"end transmission block"; +"\024", +"", +"cancel"; +"\025", +"", +"end of medium"; +"\026", +"", +"substitute"; +"\027", +"", +"escape"; +"\028", +"", +"file separator"; +"\029", +"", +"group separator"; +"\030", +"", +"record separator"; +"\031", +"", +"unit separator"; +"\127", +"", +"delete (rubout)"; +"\"", +""", +"quotation mark"; +"'", +"'", +"apostrophe"; +"&", +"&", +"ampersand"; +"<", +"<", +"less-than"; +">", +">", +"greater-than"; +"\xc2\xa0", +" ", +"non-breaking space"; +"\xc2\xa0", +" ", +"non-breaking space"; +"¡", +"¡", +"inverted exclamation mark"; +"¡", +"¡", +"inverted exclamation mark"; +"¢", +"¢", +"cent"; +"¢", +"¢", +"cent"; +"£", +"£", +"pound"; +"£", +"£", +"pound"; +"¤", +"¤", +"currency"; +"¤", +"¤", +"currency"; +"¥", +"¥", +"yen"; +"¥", +"¥", +"yen"; +"¦", +"¦", +"broken vertical bar"; +"¦", +"¦", +"broken vertical bar"; +"§", +"§", +"section"; +"§", +"§", +"section"; +"¨", +"¨", +"spacing diaeresis"; +"¨", +"¨", +"spacing diaeresis"; +"©", +"©", +"copyright"; +"©", +"©", +"copyright"; +"ª", +"ª", +"feminine ordinal indicator"; +"ª", +"ª", +"feminine ordinal indicator"; +"«", +"«", +"angle quotation mark (left)"; +"«", +"«", +"angle quotation mark (left)"; +"¬", +"¬", +"negation"; +"¬", +"¬", +"negation"; +"�­", +"­", +"soft hyphen"; +"�­", +"­", +"soft hyphen"; +"®", +"®", +"registered trademark"; +"®", +"®", +"registered trademark"; +"¯", +"¯", +"spacing macron"; +"¯", +"¯", +"spacing macron"; +"°", +"°", +"degree"; +"°", +"°", +"degree"; +"±", +"±", +"plus-or-minus"; +"±", +"±", +"plus-or-minus"; +"²", +"²", +"superscript 2"; +"²", +"²", +"superscript 2"; +"³", +"³", +"superscript 3"; +"³", +"³", +"superscript 3"; +"´", +"´", +"spacing acute"; +"´", +"´", +"spacing acute"; +"µ", +"µ", +"micro"; +"µ", +"µ", +"micro"; +"¶", +"¶", +"paragraph"; +"¶", +"¶", +"paragraph"; +"·", +"·", +"middle dot"; +"·", +"·", +"middle dot"; +"¸", +"¸", +"spacing cedilla"; +"¸", +"¸", +"spacing cedilla"; +"¹", +"¹", +"superscript 1"; +"¹", +"¹", +"superscript 1"; +"º", +"º", +"masculine ordinal indicator"; +"º", +"º", +"masculine ordinal indicator"; +"»", +"»", +"angle quotation mark (right)"; +"»", +"»", +"angle quotation mark (right)"; +"¼", +"¼", +"fraction 1/4"; +"¼", +"¼", +"fraction 1/4"; +"½", +"½", +"fraction 1/2"; +"½", +"½", +"fraction 1/2"; +"¾", +"¾", +"fraction 3/4"; +"¾", +"¾", +"fraction 3/4"; +"¿", +"¿", +"inverted question mark"; +"¿", +"¿", +"inverted question mark"; +"×", +"×", +"multiplication"; +"×", +"×", +"multiplication"; +"÷", +"÷", +"division"; +"÷", +"÷", +"division"; +"À", +"À", +"capital a, grave accent"; +"À", +"À", +"capital a, grave accent"; +"Á", +"Á", +"capital a, acute accent"; +"Á", +"Á", +"capital a, acute accent"; +"Â", +"Â", +"capital a, circumflex accent"; +"Â", +"Â", +"capital a, circumflex accent"; +"Ã", +"Ã", +"capital a, tilde"; +"Ã", +"Ã", +"capital a, tilde"; +"Ä", +"Ä", +"capital a, umlaut mark"; +"Ä", +"Ä", +"capital a, umlaut mark"; +"Å", +"Å", +"capital a, ring"; +"Å", +"Å", +"capital a, ring"; +"Æ", +"Æ", +"capital ae"; +"Æ", +"Æ", +"capital ae"; +"Ç", +"Ç", +"capital c, cedilla"; +"Ç", +"Ç", +"capital c, cedilla"; +"È", +"È", +"capital e, grave accent"; +"È", +"È", +"capital e, grave accent"; +"É", +"É", +"capital e, acute accent"; +"É", +"É", +"capital e, acute accent"; +"Ê", +"Ê", +"capital e, circumflex accent"; +"Ê", +"Ê", +"capital e, circumflex accent"; +"Ë", +"Ë", +"capital e, umlaut mark"; +"Ë", +"Ë", +"capital e, umlaut mark"; +"Ì", +"Ì", +"capital i, grave accent"; +"Ì", +"Ì", +"capital i, grave accent"; +"Í", +"Í", +"capital i, acute accent"; +"Í", +"Í", +"capital i, acute accent"; +"Î", +"Î", +"capital i, circumflex accent"; +"Î", +"Î", +"capital i, circumflex accent"; +"Ï", +"Ï", +"capital i, umlaut mark"; +"Ï", +"Ï", +"capital i, umlaut mark"; +"Ð", +"Ð", +"capital eth, Icelandic"; +"Ð", +"Ð", +"capital eth, Icelandic"; +"Ñ", +"Ñ", +"capital n, tilde"; +"Ñ", +"Ñ", +"capital n, tilde"; +"Ò", +"Ò", +"capital o, grave accent"; +"Ò", +"Ò", +"capital o, grave accent"; +"Ó", +"Ó", +"capital o, acute accent"; +"Ó", +"Ó", +"capital o, acute accent"; +"Ô", +"Ô", +"capital o, circumflex accent"; +"Ô", +"Ô", +"capital o, circumflex accent"; +"Õ", +"Õ", +"capital o, tilde"; +"Õ", +"Õ", +"capital o, tilde"; +"Ö", +"Ö", +"capital o, umlaut mark"; +"Ö", +"Ö", +"capital o, umlaut mark"; +"Ø", +"Ø", +"capital o, slash"; +"Ø", +"Ø", +"capital o, slash"; +"Ù", +"Ù", +"capital u, grave accent"; +"Ù", +"Ù", +"capital u, grave accent"; +"Ú", +"Ú", +"capital u, acute accent"; +"Ú", +"Ú", +"capital u, acute accent"; +"Û", +"Û", +"capital u, circumflex accent"; +"Û", +"Û", +"capital u, circumflex accent"; +"Ü", +"Ü", +"capital u, umlaut mark"; +"Ü", +"Ü", +"capital u, umlaut mark"; +"Ý", +"Ý", +"capital y, acute accent"; +"Ý", +"Ý", +"capital y, acute accent"; +"Þ", +"Þ", +"capital THORN, Icelandic"; +"Þ", +"Þ", +"capital THORN, Icelandic"; +"ß", +"ß", +"small sharp s, German"; +"ß", +"ß", +"small sharp s, German"; +"à", +"à", +"small a, grave accent"; +"à", +"à", +"small a, grave accent"; +"á", +"á", +"small a, acute accent"; +"á", +"á", +"small a, acute accent"; +"â", +"â", +"small a, circumflex accent"; +"â", +"â", +"small a, circumflex accent"; +"ã", +"ã", +"small a, tilde"; +"ã", +"ã", +"small a, tilde"; +"ä", +"ä", +"small a, umlaut mark"; +"ä", +"ä", +"small a, umlaut mark"; +"å", +"å", +"small a, ring"; +"å", +"å", +"small a, ring"; +"æ", +"æ", +"small ae"; +"æ", +"æ", +"small ae"; +"ç", +"ç", +"small c, cedilla"; +"ç", +"ç", +"small c, cedilla"; +"è", +"è", +"small e, grave accent"; +"è", +"è", +"small e, grave accent"; +"é", +"é", +"small e, acute accent"; +"é", +"é", +"small e, acute accent"; +"ê", +"ê", +"small e, circumflex accent"; +"ê", +"ê", +"small e, circumflex accent"; +"ë", +"ë", +"small e, umlaut mark"; +"ë", +"ë", +"small e, umlaut mark"; +"ì", +"ì", +"small i, grave accent"; +"ì", +"ì", +"small i, grave accent"; +"í", +"í", +"small i, acute accent"; +"í", +"í", +"small i, acute accent"; +"î", +"î", +"small i, circumflex accent"; +"î", +"î", +"small i, circumflex accent"; +"ï", +"ï", +"small i, umlaut mark"; +"ï", +"ï", +"small i, umlaut mark"; +"ð", +"ð", +"small eth, Icelandic"; +"ð", +"ð", +"small eth, Icelandic"; +"ñ", +"ñ", +"small n, tilde"; +"ñ", +"ñ", +"small n, tilde"; +"ò", +"ò", +"small o, grave accent"; +"ò", +"ò", +"small o, grave accent"; +"ó", +"ó", +"small o, acute accent"; +"ó", +"ó", +"small o, acute accent"; +"ô", +"ô", +"small o, circumflex accent"; +"ô", +"ô", +"small o, circumflex accent"; +"õ", +"õ", +"small o, tilde"; +"õ", +"õ", +"small o, tilde"; +"ö", +"ö", +"small o, umlaut mark"; +"ö", +"ö", +"small o, umlaut mark"; +"ø", +"ø", +"small o, slash"; +"ø", +"ø", +"small o, slash"; +"ù", +"ù", +"small u, grave accent"; +"ù", +"ù", +"small u, grave accent"; +"ú", +"ú", +"small u, acute accent"; +"ú", +"ú", +"small u, acute accent"; +"û", +"û", +"small u, circumflex accent"; +"û", +"û", +"small u, circumflex accent"; +"ü", +"ü", +"small u, umlaut mark"; +"ü", +"ü", +"small u, umlaut mark"; +"ý", +"ý", +"small y, acute accent"; +"ý", +"ý", +"small y, acute accent"; +"þ", +"þ", +"small thorn, Icelandic"; +"þ", +"þ", +"small thorn, Icelandic"; +"ÿ", +"ÿ", +"small y, umlaut mark"; +"ÿ", +"ÿ", +"small y, umlaut mark"; +"∀", +"∀", +"for all"; +"∀", +"∀", +"for all"; +"∂", +"∂", +"part"; +"∂", +"∂", +"part"; +"∃", +"∃", +"exists"; +"∃", +"∃", +"exists"; +"∅", +"∅", +"empty"; +"∅", +"∅", +"empty"; +"∇", +"∇", +"nabla"; +"∇", +"∇", +"nabla"; +"∈", +"∈", +"isin"; +"∈", +"∈", +"isin"; +"∉", +"∉", +"notin"; +"∉", +"∉", +"notin"; +"∋", +"∋", +"ni"; +"∋", +"∋", +"ni"; +"∏", +"∏", +"prod"; +"∏", +"∏", +"prod"; +"∑", +"∑", +"sum"; +"∑", +"∑", +"sum"; +"−", +"−", +"minus"; +"−", +"−", +"minus"; +"∗", +"∗", +"lowast"; +"∗", +"∗", +"lowast"; +"√", +"√", +"square root"; +"√", +"√", +"square root"; +"∝", +"∝", +"proportional to"; +"∝", +"∝", +"proportional to"; +"∞", +"∞", +"infinity"; +"∞", +"∞", +"infinity"; +"∠", +"∠", +"angle"; +"∠", +"∠", +"angle"; +"∧", +"∧", +"and"; +"∧", +"∧", +"and"; +"∨", +"∨", +"or"; +"∨", +"∨", +"or"; +"∩", +"∩", +"cap"; +"∩", +"∩", +"cap"; +"∪", +"∪", +"cup"; +"∪", +"∪", +"cup"; +"∫", +"∫", +"integral"; +"∫", +"∫", +"integral"; +"∴", +"∴", +"therefore"; +"∴", +"∴", +"therefore"; +"∼", +"∼", +"similar to"; +"∼", +"∼", +"similar to"; +"≅", +"≅", +"congruent to"; +"≅", +"≅", +"congruent to"; +"≈", +"≈", +"almost equal"; +"≈", +"≈", +"almost equal"; +"≠", +"≠", +"not equal"; +"≠", +"≠", +"not equal"; +"≡", +"≡", +"equivalent"; +"≡", +"≡", +"equivalent"; +"≤", +"≤", +"less or equal"; +"≤", +"≤", +"less or equal"; +"≥", +"≥", +"greater or equal"; +"≥", +"≥", +"greater or equal"; +"⊂", +"⊂", +"subset of"; +"⊂", +"⊂", +"subset of"; +"⊃", +"⊃", +"superset of"; +"⊃", +"⊃", +"superset of"; +"⊄", +"⊄", +"not subset of"; +"⊄", +"⊄", +"not subset of"; +"⊆", +"⊆", +"subset or equal"; +"⊆", +"⊆", +"subset or equal"; +"⊇", +"⊇", +"superset or equal"; +"⊇", +"⊇", +"superset or equal"; +"⊕", +"⊕", +"circled plus"; +"⊕", +"⊕", +"circled plus"; +"⊗", +"⊗", +"circled times"; +"⊗", +"⊗", +"circled times"; +"⊥", +"⊥", +"perpendicular"; +"⊥", +"⊥", +"perpendicular"; +"⋅", +"⋅", +"dot operator"; +"⋅", +"⋅", +"dot operator"; +"Α", +"Α", +"Alpha"; +"Α", +"Α", +"Alpha"; +"Β", +"Β", +"Beta"; +"Β", +"Β", +"Beta"; +"Γ", +"Γ", +"Gamma"; +"Γ", +"Γ", +"Gamma"; +"Δ", +"Δ", +"Delta"; +"Δ", +"Δ", +"Delta"; +"Ε", +"Ε", +"Epsilon"; +"Ε", +"Ε", +"Epsilon"; +"Ζ", +"Ζ", +"Zeta"; +"Ζ", +"Ζ", +"Zeta"; +"Η", +"Η", +"Eta"; +"Η", +"Η", +"Eta"; +"Θ", +"Θ", +"Theta"; +"Θ", +"Θ", +"Theta"; +"Ι", +"Ι", +"Iota"; +"Ι", +"Ι", +"Iota"; +"Κ", +"Κ", +"Kappa"; +"Κ", +"Κ", +"Kappa"; +"Λ", +"Λ", +"Lambda"; +"Λ", +"Λ", +"Lambda"; +"Μ", +"Μ", +"Mu"; +"Μ", +"Μ", +"Mu"; +"Ν", +"Ν", +"Nu"; +"Ν", +"Ν", +"Nu"; +"Ξ", +"Ξ", +"Xi"; +"Ξ", +"Ξ", +"Xi"; +"Ο", +"Ο", +"Omicron"; +"Ο", +"Ο", +"Omicron"; +"Π", +"Π", +"Pi"; +"Π", +"Π", +"Pi"; +"Ρ", +"Ρ", +"Rho"; +"Ρ", +"Ρ", +"Rho"; +"Σ", +"Σ", +"Sigma"; +"Σ", +"Σ", +"Sigma"; +"Τ", +"Τ", +"Tau"; +"Τ", +"Τ", +"Tau"; +"Υ", +"Υ", +"Upsilon"; +"Υ", +"Υ", +"Upsilon"; +"Φ", +"Φ", +"Phi"; +"Φ", +"Φ", +"Phi"; +"Χ", +"Χ", +"Chi"; +"Χ", +"Χ", +"Chi"; +"Ψ", +"Ψ", +"Psi"; +"Ψ", +"Ψ", +"Psi"; +"Ω", +"Ω", +"Omega"; +"Ω", +"Ω", +"Omega"; +"α", +"α", +"alpha"; +"α", +"α", +"alpha"; +"β", +"β", +"beta"; +"β", +"β", +"beta"; +"γ", +"γ", +"gamma"; +"γ", +"γ", +"gamma"; +"δ", +"δ", +"delta"; +"δ", +"δ", +"delta"; +"ε", +"ε", +"epsilon"; +"ε", +"ε", +"epsilon"; +"ζ", +"ζ", +"zeta"; +"ζ", +"ζ", +"zeta"; +"η", +"η", +"eta"; +"η", +"η", +"eta"; +"θ", +"θ", +"theta"; +"θ", +"θ", +"theta"; +"ι", +"ι", +"iota"; +"ι", +"ι", +"iota"; +"κ", +"κ", +"kappa"; +"κ", +"κ", +"kappa"; +"λ", +"λ", +"lambda"; +"λ", +"λ", +"lambda"; +"μ", +"μ", +"mu"; +"μ", +"μ", +"mu"; +"ν", +"ν", +"nu"; +"ν", +"ν", +"nu"; +"ξ", +"ξ", +"xi"; +"ξ", +"ξ", +"xi"; +"ο", +"ο", +"omicron"; +"ο", +"ο", +"omicron"; +"π", +"π", +"pi"; +"π", +"π", +"pi"; +"ρ", +"ρ", +"rho"; +"ρ", +"ρ", +"rho"; +"ς", +"ς", +"sigmaf"; +"ς", +"ς", +"sigmaf"; +"σ", +"σ", +"sigma"; +"σ", +"σ", +"sigma"; +"τ", +"τ", +"tau"; +"τ", +"τ", +"tau"; +"υ", +"υ", +"upsilon"; +"υ", +"υ", +"upsilon"; +"φ", +"φ", +"phi"; +"φ", +"φ", +"phi"; +"χ", +"χ", +"chi"; +"χ", +"χ", +"chi"; +"ψ", +"ψ", +"psi"; +"ψ", +"ψ", +"psi"; +"ω", +"ω", +"omega"; +"ω", +"ω", +"omega"; +"ϑ", +"ϑ", +"theta symbol"; +"ϑ", +"ϑ", +"theta symbol"; +"ϒ", +"ϒ", +"upsilon symbol"; +"ϒ", +"ϒ", +"upsilon symbol"; +"ϖ", +"ϖ", +"pi symbol"; +"ϖ", +"ϖ", +"pi symbol"; +"Œ", +"Œ", +"capital ligature OE"; +"Œ", +"Œ", +"capital ligature OE"; +"œ", +"œ", +"small ligature oe"; +"œ", +"œ", +"small ligature oe"; +"Š", +"Š", +"capital S with caron"; +"Š", +"Š", +"capital S with caron"; +"š", +"š", +"small S with caron"; +"š", +"š", +"small S with caron"; +"Ÿ", +"Ÿ", +"capital Y with diaeres"; +"Ÿ", +"Ÿ", +"capital Y with diaeres"; +"ƒ", +"ƒ", +"f with hook"; +"ƒ", +"ƒ", +"f with hook"; +"ˆ", +"ˆ", +"modifier letter circumflex accent"; +"ˆ", +"ˆ", +"modifier letter circumflex accent"; +"˜", +"˜", +"small tilde"; +"˜", +"˜", +"small tilde"; +" ", +" ", +"en space"; +" ", +" ", +"en space"; +" ", +" ", +"em space"; +" ", +" ", +"em space"; +" ", +" ", +"thin space"; +" ", +" ", +"thin space"; +"‌", +"‌", +"zero width non-joiner"; +"‌", +"‌", +"zero width non-joiner"; +"‍", +"‍", +"zero width joiner"; +"‍", +"‍", +"zero width joiner"; +"‎", +"‎", +"left-to-right mark"; +"‎", +"‎", +"left-to-right mark"; +"‏", +"‏", +"right-to-left mark"; +"‏", +"‏", +"right-to-left mark"; +"–", +"–", +"en dash"; +"–", +"–", +"en dash"; +"—", +"—", +"em dash"; +"—", +"—", +"em dash"; +"‘", +"‘", +"left single quotation mark"; +"‘", +"‘", +"left single quotation mark"; +"’", +"’", +"right single quotation mark"; +"’", +"’", +"right single quotation mark"; +"‚", +"‚", +"single low-9 quotation mark"; +"‚", +"‚", +"single low-9 quotation mark"; +"“", +"“", +"left double quotation mark"; +"“", +"“", +"left double quotation mark"; +"”", +"”", +"right double quotation mark"; +"”", +"”", +"right double quotation mark"; +"„", +"„", +"double low-9 quotation mark"; +"„", +"„", +"double low-9 quotation mark"; +"†", +"†", +"dagger"; +"†", +"†", +"dagger"; +"‡", +"‡", +"double dagger"; +"‡", +"‡", +"double dagger"; +"•", +"•", +"bullet"; +"•", +"•", +"bullet"; +"…", +"…", +"horizontal ellipsis"; +"…", +"…", +"horizontal ellipsis"; +"‰", +"‰", +"per mille "; +"‰", +"‰", +"per mille "; +"′", +"′", +"minutes"; +"′", +"′", +"minutes"; +"″", +"″", +"seconds"; +"″", +"″", +"seconds"; +"‹", +"‹", +"single left angle quotation"; +"‹", +"‹", +"single left angle quotation"; +"›", +"›", +"single right angle quotation"; +"›", +"›", +"single right angle quotation"; +"‾", +"‾", +"overline"; +"‾", +"‾", +"overline"; +"€", +"€", +"euro"; +"€", +"€", +"euro"; +"™", +"™", +"trademark"; +"™", +"™", +"trademark"; +"™", +"™", +"trademark"; +"™", +"™", +"trademark"; +"←", +"←", +"left arrow"; +"←", +"←", +"left arrow"; +"↑", +"↑", +"up arrow"; +"↑", +"↑", +"up arrow"; +"→", +"→", +"right arrow"; +"→", +"→", +"right arrow"; +"↓", +"↓", +"down arrow"; +"↓", +"↓", +"down arrow"; +"↔", +"↔", +"left right arrow"; +"↔", +"↔", +"left right arrow"; +"↵", +"↵", +"carriage return arrow"; +"↵", +"↵", +"carriage return arrow"; +"⌈", +"⌈", +"left ceiling"; +"⌈", +"⌈", +"left ceiling"; +"⌉", +"⌉", +"right ceiling"; +"⌉", +"⌉", +"right ceiling"; +"⌊", +"⌊", +"left floor"; +"⌊", +"⌊", +"left floor"; +"⌋", +"⌋", +"right floor"; +"⌋", +"⌋", +"right floor"; +"◊", +"◊", +"lozenge"; +"◊", +"◊", +"lozenge"; +"♠", +"♠", +"spade"; +"♠", +"♠", +"spade"; +"♣", +"♣", +"club"; +"♣", +"♣", +"club"; +"♥", +"♥", +"heart"; +"♥", +"♥", +"heart"; +"♦", +"♦", +"diamond"; +"♦", +"♦", +"diamond"; +] diff --git a/analysis/src/vendor/omd/omd.ml b/analysis/src/vendor/omd/omd.ml new file mode 100644 index 000000000..b5c5dfb7e --- /dev/null +++ b/analysis/src/vendor/omd/omd.ml @@ -0,0 +1,163 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +module Representation = Omd_representation + +include Omd_representation +include Omd_backend + +let of_input lex ?extensions:e ?default_lang:d s = + let module E = Omd_parser.Default_env(struct end) in + let module Parser = Omd_parser.Make( + struct + include E + let extensions = match e with Some x -> x | None -> E.extensions + let default_lang = match d with Some x -> x | None -> E.default_lang + end + ) in + let md = + Parser.parse (lex s) + in + Parser.make_paragraphs md + +let of_string = of_input Omd_lexer.lex + +let to_html : + ?override:(Omd_representation.element -> string option) -> + ?pindent:bool -> + ?nl2br:bool -> + ?cs:code_stylist -> + t -> + string + = + html_of_md + +let to_text : t -> string = text_of_md + +let to_markdown : t -> string = markdown_of_md + + +let rec set_default_lang lang = function + | Code("", code) :: tl -> Code(lang, code) :: set_default_lang lang tl + | Code_block("", code) :: tl -> Code_block(lang, code) + :: set_default_lang lang tl + (* Recurse on all elements even though code (blocks) are not allowed + everywhere. *) + | H1 t :: tl -> H1(set_default_lang lang t) :: set_default_lang lang tl + | H2 t :: tl -> H2(set_default_lang lang t) :: set_default_lang lang tl + | H3 t :: tl -> H3(set_default_lang lang t) :: set_default_lang lang tl + | H4 t :: tl -> H4(set_default_lang lang t) :: set_default_lang lang tl + | H5 t :: tl -> H5(set_default_lang lang t) :: set_default_lang lang tl + | H6 t :: tl -> H6(set_default_lang lang t) :: set_default_lang lang tl + | Paragraph t :: tl -> Paragraph(set_default_lang lang t) + :: set_default_lang lang tl + | Emph t :: tl -> Emph(set_default_lang lang t) :: set_default_lang lang tl + | Bold t :: tl -> Bold(set_default_lang lang t) :: set_default_lang lang tl + | Ul t :: tl -> Ul(List.map (set_default_lang lang) t) + :: set_default_lang lang tl + | Ol t :: tl -> Ol(List.map (set_default_lang lang) t) + :: set_default_lang lang tl + | Ulp t :: tl -> Ulp(List.map (set_default_lang lang) t) + :: set_default_lang lang tl + | Olp t :: tl -> Olp(List.map (set_default_lang lang) t) + :: set_default_lang lang tl + | Url(href, t, title) :: tl -> Url(href, set_default_lang lang t, title) + :: set_default_lang lang tl + | Blockquote t :: tl -> Blockquote(set_default_lang lang t) + :: set_default_lang lang tl + (* Elements that do not contain Markdown. *) + | (Text _|Code _|Code_block _|Br|Hr|NL|Ref _|Img_ref _|Raw _|Raw_block _ + |Html _|Html_block _|Html_comment _|Img _|X _) as e :: tl -> + e :: set_default_lang lang tl + | [] -> [] + + +(* Table of contents + ***********************************************************************) + +(* Given a list of headers — in the order of the document — go to the + requested subsection. We first seek for the [number]th header at + [level]. *) +let rec find_start headers level number subsections = + match headers with + | [] -> [] + | (H1 _, _, _) :: tl -> deal_with_header 1 headers tl level number subsections + | (H2 _, _, _) :: tl -> deal_with_header 2 headers tl level number subsections + | (H3 _, _, _) :: tl -> deal_with_header 3 headers tl level number subsections + | (H4 _, _, _) :: tl -> deal_with_header 4 headers tl level number subsections + | (H5 _, _, _) :: tl -> deal_with_header 5 headers tl level number subsections + | (H6 _, _, _) :: tl -> deal_with_header 6 headers tl level number subsections + | _ :: _ -> assert false + +and deal_with_header h_level headers tl level number subsections = + if h_level > level then (* Skip, right [level]-header not yet reached. *) + if number = 0 then + (* Assume empty section at [level], do not consume token. *) + (match subsections with + | [] -> headers (* no subsection to find *) + | n :: subsections -> find_start headers (level + 1) n subsections) + else find_start tl level number subsections + else if h_level = level then ( + (* At proper [level]. Have we reached the [number] one? *) + if number <= 1 then ( + match subsections with + | [] -> tl (* no subsection to find *) + | n :: subsections -> find_start tl (level + 1) n subsections + ) + else find_start tl level (number - 1) subsections + ) + else (* h_level < level *) + [] (* Sought [level] has not been found in the current section *) + +(* Assume we are at the start of the headers we are interested in. + Return the list of TOC entries for [min_level] and the [headers] + not used for the TOC entries. *) +let rec make_toc (headers:(element*string*string)list) ~min_level ~max_level = + if min_level > max_level then [], headers + else ( + match headers with + | [] -> [], [] + | (H1 t, id, _) :: tl -> toc_entry headers 1 t id tl ~min_level ~max_level + | (H2 t, id, _) :: tl -> toc_entry headers 2 t id tl ~min_level ~max_level + | (H3 t, id, _) :: tl -> toc_entry headers 3 t id tl ~min_level ~max_level + | (H4 t, id, _) :: tl -> toc_entry headers 4 t id tl ~min_level ~max_level + | (H5 t, id, _) :: tl -> toc_entry headers 5 t id tl ~min_level ~max_level + | (H6 t, id, _) :: tl -> toc_entry headers 6 t id tl ~min_level ~max_level + | _ :: _ -> assert false + ) +and toc_entry headers h_level t id tl ~min_level ~max_level = + if h_level > max_level then (* too deep, skip *) + make_toc tl ~min_level ~max_level + else if h_level < min_level then + (* section we wanted the TOC for is finished, do not comsume the token *) + [], headers + else if h_level = min_level then ( + let sub_toc, tl = make_toc tl ~min_level:(min_level + 1) ~max_level in + let toc_entry = match sub_toc with + | [] -> [Url("#" ^ id, t, ""); NL] + | _ -> [Url("#" ^ id, t, ""); NL; Ul sub_toc; NL] in + let toc, tl = make_toc tl ~min_level ~max_level in + toc_entry :: toc, tl + ) else (* h_level > min_level *) + let sub_toc, tl = make_toc headers ~min_level:(min_level + 1) ~max_level in + let toc, tl = make_toc tl ~min_level ~max_level in + [Ul sub_toc] :: toc, tl + +let toc ?(start=[]) ?(depth=2) md = + if depth < 1 then invalid_arg "Omd.toc: ~depth must be >= 1"; + let headers = Omd_backend.headers_of_md ~remove_header_links:true md in + let headers = match start with + | [] -> headers + | number :: subsections -> + if number < 0 then invalid_arg("Omd.toc: level 1 start must be >= 0"); + find_start headers 1 number subsections in + let len = List.length start in + let toc, _ = make_toc headers + ~min_level:(len + 1) ~max_level:(len + depth) in + match toc with + | [] -> [] + | _ -> [Ul(toc)] diff --git a/analysis/src/vendor/omd/omd.mli b/analysis/src/vendor/omd/omd.mli new file mode 100644 index 000000000..0648236ba --- /dev/null +++ b/analysis/src/vendor/omd/omd.mli @@ -0,0 +1,161 @@ +(** A markdown parser in OCaml, with no extra dependencies. + + This module represents this entire Markdown library written in + OCaml only. + + Its main purpose is to allow you to use the Markdown library while + keeping you away from the other modules. + + If you want to extend the Markdown parser, you can do it without + accessing any module of this library but this one, and by doing + so, you are free from having to maintain a fork of this library. + + N.B. This module is supposed to be reentrant, + if it's not then please report the bug. *) + + +(************************************************************************) +(** {2 Representation of Markdown documents} *) + +module Representation = Omd_representation + +type t = element list +(** Representation of a Markdown document. *) + +and ref_container = + (< add_ref: string -> string -> string -> unit ; + get_ref : string -> (string*string) option; + get_all : (string * (string * string)) list; + >) + +(** A element of a Markdown document. *) +and element = Omd_representation.element = + | H1 of t (** Header of level 1 *) + | H2 of t (** Header of level 2 *) + | H3 of t (** Header of level 3 *) + | H4 of t (** Header of level 4 *) + | H5 of t (** Header of level 5 *) + | H6 of t (** Header of level 6 *) + | Paragraph of t + (** A Markdown paragraph (must be enabled in {!of_string}) *) + | Text of string (** Text. *) + | Emph of t (** Emphasis (italic) *) + | Bold of t (** Bold *) + | Ul of t list (** Unumbered list *) + | Ol of t list (** Ordered (i.e. numbered) list *) + | Ulp of t list + | Olp of t list + | Code of name * string + (** [Code(lang, code)] represent [code] within the text (Markdown: + `code`). The language [lang] cannot be specified from Markdown, + it can be from {!of_string} though or when programatically + generating Markdown documents. Beware that the [code] is taken + verbatim from Markdown and may contain characters that must be + escaped for HTML. *) + | Code_block of name * string + (** [Code_block(lang, code)]: a code clock (e.g. indented by 4 + spaces in the text). The first parameter [lang] is the language + if specified. Beware that the [code] is taken verbatim from + Markdown and may contain characters that must be escaped for + HTML. *) + | Br (** (Forced) line break *) + | Hr (** Horizontal rule *) + | NL (** Newline character. Newline characters that act + like delimiters (e.g. for paragraphs) are removed from the AST. *) + | Url of href * t * title + | Ref of ref_container * name * string * fallback + | Img_ref of ref_container * name * alt * fallback + | Html of name * (string * string option) list * t + | Html_block of name * (string * string option) list * t + | Html_comment of string + (** An HTML comment, including "". *) + | Raw of string + (** Raw: something that shall never be converted *) + | Raw_block of string + (** Raw_block: a block with contents that shall never be converted *) + | Blockquote of t (** Quoted block *) + | Img of alt * src * title + | X of (< (* extension of [element]. *) + name: string; + (* N.B. [to_html] means that htmlentities will not + be applied to its output. *) + to_html: ?indent:int -> (t -> string) -> t -> string option; + to_sexpr: (t -> string) -> t -> string option; + to_t: t -> t option >) + +and fallback = < to_string : string ; to_t : t > +(** Fallback for references in case they refer to non-existant references *) + +and name = string +(** Markdown reference name. *) + +and alt = string +(** HTML img tag attribute. *) + +and src = string +(** HTML attribute. *) + +and href = string +(** HTML attribute. *) + +and title = string +(** HTML attribute. *) + +type code_stylist = lang:string -> string -> string +(** Function that takes a language name and some code and returns + that code with style. *) + + +(************************************************************************) +(** {2 Input and Output} *) + +val of_string : ?extensions:Omd_representation.extensions -> + ?default_lang: name -> + string -> t +(** [of_string s] returns the Markdown representation of the string + [s]. + + @param lang language for blocks of code where it was not + specified. Default: [""]. + + If you want to use a custom lexer or parser, use {!Omd_lexer.lex} + and {!Omd_parser.parse}. *) + +val set_default_lang : name -> t -> t +(** [set_default_lang lang md] return a copy of [md] where the + language of all [Code] or [Code_block] with an empty language is + set to [lang]. *) + +val to_html : + ?override:(Omd_representation.element -> string option) -> + ?pindent:bool -> ?nl2br:bool -> ?cs:code_stylist -> t -> string +(** Translate markdown representation into raw HTML. If you need a + full HTML representation, you mainly have to figure out how to + convert [Html of string] and [Html_block of string] + into your HTML representation. *) + +val to_markdown : t -> string +(** Translate markdown representation into textual markdown. *) + +val to_text : t -> string +(** Translate markdown representation into raw text. *) + + +(************************************************************************) +(** {2 Tansforming Markdown documents} *) + +val toc : ?start:int list -> ?depth:int -> t -> t +(** [toc md] returns [toc] a table of contents for [md]. + + @param start gives the section for which the TOC must be built. + For example [~start:[2;3]] will build the TOC for subsections of + the second [H1] header, and within that section, the third [h2] + header. If a number is [0], it means to look for the first + section at that level but stop if one encounters any other + subsection. If no subsection exists, an empty TOC [[]] will be + returned. Default: [[]] i.e. list all sections, starting with the + first [H1]. + + @param depth the table of contents. Default: [2]. *) + +;; diff --git a/analysis/src/vendor/omd/omd_backend.ml b/analysis/src/vendor/omd/omd_backend.ml new file mode 100644 index 000000000..94aee9363 --- /dev/null +++ b/analysis/src/vendor/omd/omd_backend.ml @@ -0,0 +1,1225 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +type code_stylist = lang:string -> string -> string + +open Printf +open Omd_representation +open Omd_utils + +let default_language = ref "" + + + +let text_of_md md = + let b = Buffer.create 128 in + let rec loop = function + | X _ :: tl -> + loop tl + | Blockquote q :: tl -> + loop q; + loop tl + | Ref(_src, name, _text, _fallback) :: tl -> + Buffer.add_string b (htmlentities ~md:true name); + loop tl + | Img_ref(_rc, name, _alt, _fallback) :: tl -> + Buffer.add_string b (htmlentities ~md:true name); + loop tl + | Paragraph md :: tl -> + loop md; + Buffer.add_char b '\n'; + Buffer.add_char b '\n'; + loop tl + | Img(alt, _src, _title) :: tl -> + Buffer.add_string b (htmlentities ~md:true alt); + loop tl + | Text t :: tl -> + Buffer.add_string b (htmlentities ~md:true t); + loop tl + | Raw t :: tl -> + Buffer.add_string b t; + loop tl + | Raw_block t :: tl -> + Buffer.add_char b '\n'; + Buffer.add_string b t; + Buffer.add_char b '\n'; + loop tl + | Emph md :: tl -> + loop md; + loop tl + | Bold md :: tl -> + loop md; + loop tl + | (Ul l | Ol l) :: tl -> + List.iter (fun item -> loop item; Buffer.add_char b '\n') l; + loop tl + | (Ulp l | Olp l) :: tl -> + List.iter loop l; + loop tl + | Code_block(_lang, c) :: tl -> + Buffer.add_string b (htmlentities ~md:false c); + loop tl + | Code(_lang, c) :: tl -> + Buffer.add_string b (htmlentities ~md:false c); + loop tl + | Br :: tl -> + loop tl + | Hr :: tl -> + loop tl + | Html(_tagname, _attrs, body) :: tl -> + loop body; + loop tl + | Html_block(_stagname, _attrs, body) :: tl -> + loop body; + loop tl + | Html_comment _s :: tl -> + loop tl + | Url (_href,s,_title) :: tl -> + loop s; + loop tl + | H1 md :: tl + | H2 md :: tl + | H3 md :: tl + | H4 md :: tl + | H5 md :: tl + | H6 md :: tl -> + loop md; + loop tl + | NL :: tl -> + Buffer.add_string b "\n"; + loop tl + | [] -> () + in + loop md; + Buffer.contents b + +let default_code_stylist ~lang:_ code = code + +let filter_text_omd_rev l = + let rec loop b r = function + | [] -> if b then r else l + | ("media:type", Some "text/omd")::tl -> + loop true r tl + | e::tl -> + loop b (e::r) tl + in + loop false [] l + +let remove_links : t -> t = + Omd_representation.visit + (fun e -> + match e with + | Url(_, t, _) -> Some t + | _ -> None + ) + +let rec html_and_headers_of_md + ?(remove_header_links=false) + ?(override=(fun (_e:element) -> (None:string option))) + ?(pindent=false) + ?(nl2br=false) + ?cs:(code_style=default_code_stylist) + md + = + let ids = object(_this) + val mutable ids = StringSet.add "" StringSet.empty + method mangle id = + let rec m i = + if StringSet.mem id ids then + let idx = if i > 0 then id^"_"^string_of_int i else id in + if StringSet.mem idx ids then + m (i+1) + else + (ids <- StringSet.add idx ids; + idx) + else + (ids <- StringSet.add id ids; + id) + in m 0 + end in + let empty s = + let rec loop i = + if i < String.length s then + match s.[i] with + | ' ' | '\n' -> loop (i+1) + | _ -> false + else + true + in + loop 0 + in + let remove_trailing_blanks s = + let rec loop i = + if i < 0 then "" + else + match s.[i] with + | ' '|'\t'|'\n' -> + loop (pred i) + | _ -> + if i = String.length s - 1 then + s + else + String.sub s 0 (i+1) + in loop (String.length s - 1) + in + let b = Buffer.create 64 in + let headers = ref [] in + let rec loop indent = function + | X x as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + (match x#to_t md with + | Some t -> loop indent t + | None -> + match x#to_html ~indent:indent + (html_of_md ~override ~pindent ~nl2br ~cs:code_style) md + with + | Some s -> Buffer.add_string b s + | None -> ()); + loop indent tl + end + | Blockquote q as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b "
"; + loop indent q; + Buffer.add_string b "
"; + loop indent tl + end + | Ref(rc, name, text, fallback) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + begin match rc#get_ref name with + | Some(href, title) -> + loop indent + (Url(htmlentities ~md:true href, + [Text(text)], + htmlentities ~md:true title) + ::tl) + | None -> + loop indent (fallback#to_t); + loop indent tl + end + end + | Img_ref(rc, name, alt, fallback) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + begin match rc#get_ref name with + | Some(src, title) -> + loop indent + (Img(htmlentities ~md:true alt, + htmlentities ~md:true src, + htmlentities ~md:true title)::tl) + | None -> + loop indent (fallback#to_t); + loop indent tl + end + end + | Paragraph [] :: tl -> loop indent tl + | Paragraph md as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + (let s = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + if empty s then + () + else + begin + Buffer.add_string b "

"; + Buffer.add_string b (remove_trailing_blanks s); + Buffer.add_string b "

\n"; + end); + loop indent tl + end + | Img(alt, src, title) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b "";
+          Buffer.add_string b (htmlentities ~md:true alt);
+          Buffer.add_string b " "" then + (Buffer.add_string b " title='"; + Buffer.add_string b (htmlentities ~md:true title); + Buffer.add_string b "' "); + Buffer.add_string b "/>"; + loop indent tl + end + | Text t as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + (* Buffer.add_string b t; *) + Buffer.add_string b (htmlentities ~md:true t); + loop indent tl + end + | Emph md as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b ""; + loop indent md; + Buffer.add_string b ""; + loop indent tl + end + | Bold md as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b ""; + loop indent md; + Buffer.add_string b ""; + loop indent tl + end + | (Ul l|Ol l|Ulp l|Olp l as e) :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b (match e with + | Ol _|Olp _ -> "
    " + | _ -> "
      "); + List.iter + ( + fun li -> + Buffer.add_string b "
    • "; + loop (indent+2) li; + Buffer.add_string b "
    • " + ) + l; + Buffer.add_string b (match e with + | Ol _|Olp _ -> "
" + | _ -> ""); + loop indent tl + end + | Code_block(lang, c) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + if lang = "" && !default_language = "" then + Buffer.add_string b "
"
+          else if lang = "" then
+            bprintf b "
"
+              !default_language !default_language
+          else
+            bprintf b "
" lang lang;
+          let new_c = code_style ~lang:lang c in
+          if c = new_c then
+            Buffer.add_string b (htmlentities ~md:false c)
+          else
+            Buffer.add_string b new_c;
+          Buffer.add_string b "
"; + loop indent tl + end + | Code(lang, c) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + if lang = "" && !default_language = "" then + Buffer.add_string b "" + else if lang = "" then + bprintf b "" !default_language + else + bprintf b "" lang; + let new_c = code_style ~lang:lang c in + if c = new_c then + Buffer.add_string b (htmlentities ~md:false c) + else + Buffer.add_string b new_c; + Buffer.add_string b ""; + loop indent tl + end + | Br as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b "
"; + loop indent tl + end + | Hr as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b "
"; + loop indent tl + end + | Raw s as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b s; + loop indent tl + end + | Raw_block s as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b s; + loop indent tl + end + | Html(tagname, attrs, []) as e :: tl + when StringSet.mem tagname html_void_elements -> + let attrs = filter_text_omd_rev attrs in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Printf.bprintf b " />"; + loop indent tl + end + | Html(tagname, attrs, body) as e :: tl -> + let attrs = filter_text_omd_rev attrs in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Buffer.add_string b ">"; + loop indent body; + Printf.bprintf b "" tagname; + loop indent tl + end + | Html_block(tagname, attrs, body) as e :: tl -> + let attrs = filter_text_omd_rev attrs in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + if body = [] && StringSet.mem tagname html_void_elements then + ( + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Buffer.add_string b " />"; + loop indent tl + ) + else + ( + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Buffer.add_string b ">"; + loop indent body; + Printf.bprintf b "" tagname; + loop indent tl + ) + end + | Html_comment s as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + Buffer.add_string b s; + loop indent tl + end + | Url (href,s,title) as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let s = html_of_md ~override ~pindent ~nl2br ~cs:code_style s in + Buffer.add_string b " "" then + begin + Buffer.add_string b " title='"; + Buffer.add_string b (htmlentities ~md:true title); + Buffer.add_string b "'"; + end; + Buffer.add_string b ">"; + Buffer.add_string b s; + Buffer.add_string b ""; + loop indent tl + end + | (H1 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H1 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "

"; + Buffer.add_string b ih; + Buffer.add_string b "

"; + loop indent tl + end + | (H2 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H2 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "

"; + Buffer.add_string b ih; + Buffer.add_string b "

"; + loop indent tl + end + | (H3 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H3 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "

"; + Buffer.add_string b ih; + Buffer.add_string b "

"; + loop indent tl + end + | (H4 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H4 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "

"; + Buffer.add_string b ih; + Buffer.add_string b "

"; + loop indent tl + end + | (H5 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H5 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "
"; + Buffer.add_string b ih; + Buffer.add_string b "
"; + loop indent tl + end + | (H6 md as e) :: tl -> + let e, md = + if not remove_header_links then + e, md + else + let md = remove_links md in + H6 md, md in + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + let ih = html_of_md ~override ~pindent ~nl2br ~cs:code_style md in + let id = id_of_string ids (text_of_md md) in + headers := (e, id, ih) :: !headers; + Buffer.add_string b "
"; + Buffer.add_string b ih; + Buffer.add_string b "
"; + loop indent tl + end + | NL as e :: tl -> + begin match override e with + | Some s -> + Buffer.add_string b s; + loop indent tl + | None -> + if nl2br then + Buffer.add_string b "
" + else + Buffer.add_string b "\n"; + loop indent tl + end + | [] -> + () + in + loop 0 md; + Buffer.contents b, List.rev !headers + +and string_of_attrs attrs = + let b = Buffer.create 1024 in + List.iter + (function + | (a, Some v) -> + if not(String.contains v '\'') then + Printf.bprintf b " %s='%s'" a v + else if not(String.contains v '"') then + Printf.bprintf b " %s=\"%s\"" a v + else + Printf.bprintf b " %s=\"%s\"" a v + | a, None -> + (* if html4 then *) + (* Printf.bprintf b " %s='%s'" a a *) + (* else *) + Printf.bprintf b " %s=''" a (* HTML5 *) + ) + attrs; + Buffer.contents b + +and html_of_md + ?(override=(fun (_e:element) -> (None:string option))) + ?(pindent=false) + ?(nl2br=false) + ?cs + md + = + fst (html_and_headers_of_md ~override ~pindent ~nl2br ?cs md) +and headers_of_md ?remove_header_links md = + snd (html_and_headers_of_md ?remove_header_links md) + + +let rec sexpr_of_md md = + let b = Buffer.create 64 in + let rec loop = function + | X x :: tl -> + (match x#to_t md with + | Some t -> + Buffer.add_string b "(X"; + loop t; + Buffer.add_string b ")" + | None -> + match x#to_sexpr sexpr_of_md md with + | Some s -> + Buffer.add_string b "(X"; + Buffer.add_string b s; + Buffer.add_string b ")" + | None -> + match x#to_html ~indent:0 html_of_md md with + | Some s -> + Buffer.add_string b "(X"; + Buffer.add_string b s; + Buffer.add_string b ")" + | None -> ()); + loop tl + | Blockquote q :: tl -> + Buffer.add_string b "(Blockquote"; + loop q; + Buffer.add_string b ")"; + loop tl + | Ref(_rc, name, text, _) :: tl -> + bprintf b "(Ref %S %S)" name text; + loop tl + | Img_ref(_rc, name, alt, _) :: tl -> + bprintf b "(Img_ref %S %S)" name alt; + loop tl + | Paragraph md :: tl -> + Buffer.add_string b "(Paragraph"; + loop md; + Buffer.add_string b ")"; + loop tl + | Img(alt, src, title) :: tl -> + bprintf b "(Img %S %S %S)" alt src title; + loop tl + | Text t :: tl -> + bprintf b "(Text %S" t; + let rec f = function + | Text t :: tl -> + bprintf b " %S" t; + f tl + | x -> x + in + let tl = f tl in + bprintf b ")"; + loop tl + | Emph md :: tl -> + Buffer.add_string b "(Emph"; + loop md; + Buffer.add_string b ")"; + loop tl + | Bold md :: tl -> + Buffer.add_string b "(Bold"; + loop md; + Buffer.add_string b ")"; + loop tl + | Ol l :: tl -> + bprintf b "(Ol"; + List.iter(fun li -> bprintf b "(Li "; loop li; bprintf b ")") l; + bprintf b ")"; + loop tl + | Ul l :: tl -> + bprintf b "(Ul"; + List.iter(fun li -> bprintf b "(Li "; loop li;bprintf b ")") l; + bprintf b ")"; + loop tl + | Olp l :: tl -> + bprintf b "(Olp"; + List.iter(fun li -> bprintf b "(Li "; loop li; bprintf b ")") l; + bprintf b ")"; + loop tl + | Ulp l :: tl -> + bprintf b "(Ulp"; + List.iter(fun li -> bprintf b "(Li "; loop li;bprintf b ")") l; + bprintf b ")"; + loop tl + | Code(_lang, c) :: tl -> + bprintf b "(Code %S)" c; + loop tl + | Code_block(_lang, c) :: tl -> + bprintf b "(Code_block %s)" c; + loop tl + | Br :: tl -> + Buffer.add_string b "(Br)"; + loop tl + | Hr :: tl -> + Buffer.add_string b "(Hr)"; + loop tl + | Raw s :: tl -> + bprintf b "(Raw %S)" s; + loop tl + | Raw_block s :: tl -> + bprintf b "(Raw_block %S)" s; + loop tl + | Html(tagname, attrs, body) :: tl -> + bprintf b "(Html %s %s " tagname (string_of_attrs attrs); + loop body; + bprintf b ")"; + loop tl + | Html_block(tagname, attrs, body) :: tl -> + bprintf b "(Html_block %s %s " tagname (string_of_attrs attrs); + loop body; + bprintf b ")"; + loop tl + | Html_comment s :: tl -> + bprintf b "(Html_comment %S)" s; + loop tl + | Url (href,s,title) :: tl -> + bprintf b "(Url %S %S %S)" href (html_of_md s) title; + loop tl + | H1 md :: tl -> + Buffer.add_string b "(H1"; + loop md; + Buffer.add_string b ")"; + loop tl + | H2 md :: tl -> + Buffer.add_string b "(H2"; + loop md; + Buffer.add_string b ")"; + loop tl + | H3 md :: tl -> + Buffer.add_string b "(H3"; + loop md; + Buffer.add_string b ")"; + loop tl + | H4 md :: tl -> + Buffer.add_string b "(H4"; + loop md; + Buffer.add_string b ")"; + loop tl + | H5 md :: tl -> + Buffer.add_string b "(H5"; + loop md; + Buffer.add_string b ")"; + loop tl + | H6 md :: tl -> + Buffer.add_string b "(H6"; + loop md; + Buffer.add_string b ")"; + loop tl + | NL :: tl -> + Buffer.add_string b "(NL)"; + loop tl + | [] -> () + in + loop md; + Buffer.contents b + + +let escape_markdown_characters s = + let b = Buffer.create (String.length s * 2) in + for i = 0 to String.length s - 1 do + match s.[i] with + | '.' as c -> + if i > 0 && + match s.[i-1] with + | '0' .. '9' -> i+1 < String.length s && s.[i+1] = ' ' + | _ -> false + then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '-' as c -> + if (i = 0 || match s.[i-1] with ' '| '\n' -> true | _ -> false) + && (i+1 < String.length s && (s.[i+1] = ' '||s.[i+1] = '-')) + then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '+' as c -> + if (i = 0 || match s.[i-1] with ' '| '\n' -> true | _ -> false) + && (i+1 < String.length s && s.[i+1] = ' ') + then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '!' as c -> + if i+1 < String.length s && s.[i+1] = '[' then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '<' as c -> + if i <> String.length s - 1 && + (match s.[i+1] with 'a' .. 'z' | 'A' .. 'Z' -> false | _ -> true) + then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '>' as c -> + if i = 0 || + (match s.[i-1] with ' ' | '\n' -> false | _ -> true) + then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '#' as c -> + if i = 0 || s.[i-1] = '\n' then + Buffer.add_char b '\\'; + Buffer.add_char b c + | '\\' | '[' | ']' | '(' | ')' | '`' | '*' as c -> + Buffer.add_char b '\\'; + Buffer.add_char b c + | c -> + Buffer.add_char b c + done; + Buffer.contents b + +let rec markdown_of_md md = + if debug then eprintf "(OMD) markdown_of_md(%S)\n%!" (sexpr_of_md md); + let quote ?(indent=0) s = + let b = Buffer.create (String.length s) in + let l = String.length s in + let rec loop nl i = + if i < l then + begin + if nl && i < l - 1 then + (for i = 1 to indent do + Buffer.add_char b ' ' + done; + Buffer.add_string b "> "); + match s.[i] with + | '\n' -> + Buffer.add_char b '\n'; + loop true (succ i) + | c -> + Buffer.add_char b c; + loop false (succ i) + end + else + Buffer.contents b + in loop true 0 + in + let b = Buffer.create 64 in + let add_spaces n = for i = 1 to n do Buffer.add_char b ' ' done in + let references = ref None in + let rec loop ?(fst_p_in_li=true) ?(is_in_list=false) list_indent l = + (* [list_indent: int] is the indentation level in number of spaces. *) + (* [is_in_list: bool] is necessary to know if we are inside a paragraph + which is inside a list item because those need to be indented! *) + let loop ?(fst_p_in_li=fst_p_in_li) ?(is_in_list=is_in_list) list_indent l = + loop ~fst_p_in_li:fst_p_in_li ~is_in_list:is_in_list list_indent l + in + match l with + | X x :: tl -> + (match x#to_t md with + | Some t -> loop list_indent t + | None -> + match x#to_html ~indent:0 html_of_md md with + | Some s -> Buffer.add_string b s + | None -> ()); + loop list_indent tl + | Blockquote q :: tl -> + Buffer.add_string b (quote ~indent:list_indent (markdown_of_md q)); + if tl <> [] then Buffer.add_string b "\n"; + loop list_indent tl + | Ref(rc, _name, _text, fallback) :: tl -> + if !references = None then references := Some rc; + loop list_indent (Raw(fallback#to_string)::tl) + | Img_ref(rc, _name, _alt, fallback) :: tl -> + if !references = None then references := Some rc; + loop list_indent (Raw(fallback#to_string)::tl) + | Paragraph [] :: tl -> loop list_indent tl + | Paragraph md :: tl -> + if is_in_list then + if fst_p_in_li then + add_spaces (list_indent-2) + else + add_spaces list_indent; + loop ~fst_p_in_li:false list_indent md; + Printf.bprintf b "\n\n"; + loop ~fst_p_in_li:false list_indent tl + | Img(alt, src, title) :: tl -> + Printf.bprintf b "![%s](%s \"%s\")" alt src title; + loop list_indent tl + | Text t :: tl -> + Printf.bprintf b "%s" (escape_markdown_characters t); + loop list_indent tl + | Emph md :: tl -> + Buffer.add_string b "*"; + loop list_indent md; + Buffer.add_string b "*"; + loop list_indent tl + | Bold md :: tl -> + Buffer.add_string b "**"; + loop list_indent md; + Buffer.add_string b "**"; + loop list_indent tl + | Ol l :: tl -> + if Buffer.length b > 0 && Buffer.nth b (Buffer.length b - 1) <> '\n' then + Buffer.add_char b '\n'; + let c = ref 0 in (* don't use List.iteri because it's not in 3.12 *) + List.iter(fun li -> + incr c; + add_spaces list_indent; + Printf.bprintf b "%d. " !c; + loop ~is_in_list:true (list_indent+4) li; + Buffer.add_char b '\n'; + ) l; + if list_indent = 0 then Buffer.add_char b '\n'; + loop list_indent tl + | Ul l :: tl -> + if Buffer.length b > 0 && Buffer.nth b (Buffer.length b - 1) <> '\n' then + Buffer.add_char b '\n'; + List.iter(fun li -> + add_spaces list_indent; + Printf.bprintf b "- "; + loop ~is_in_list:true (list_indent+4) li; + Buffer.add_char b '\n'; + ) l; + if list_indent = 0 then Buffer.add_char b '\n'; + loop list_indent tl + | Olp l :: tl -> + let c = ref 0 in (* don't use List.iteri because it's not in 3.12 *) + List.iter(fun li -> + if Buffer.length b > 0 && Buffer.nth b (Buffer.length b - 1) <> '\n' + then Buffer.add_char b '\n'; + add_spaces list_indent; + incr c; + bprintf b "%d. " !c; + loop ~is_in_list:true (list_indent+4) li; + (* Paragraphs => No need of '\n' *) + ) l; + loop list_indent tl + | Ulp l :: tl -> + List.iter(fun li -> + if Buffer.length b > 0 && Buffer.nth b (Buffer.length b - 1) <> '\n' + then Buffer.add_char b '\n'; + add_spaces list_indent; + bprintf b "+ "; + loop ~is_in_list:true (list_indent+4) li; + (* Paragraphs => No need of '\n' *) + ) l; + begin match tl with + | (H1 _ | H2 _ | H3 _ | H4 _ | H5 _ | H6 _)::_ + | NL::(H1 _ | H2 _ | H3 _ | H4 _ | H5 _ | H6 _)::_ -> + Buffer.add_char b '\n' + | _ -> () + end; + loop list_indent tl + | Code(_lang, c) :: tl -> (* FIXME *) + let n = (* compute how many backquotes we need to use *) + let filter (n:int) (s:int list) = + if n > 0 && n < 10 then + List.filter (fun e -> e <> n) s + else + s + in + let l = String.length c in + let rec loop s x b i = + if i = l then + match filter b s with + | hd::_ -> hd + | [] -> x+1 + else + match c.[i] with + | '`' -> loop s x (succ b) (succ i) + | _ -> loop (filter b s) (max b x) 0 (succ i) + in + loop [1;2;3;4;5;6;7;8;9;10] 0 0 0 + in + begin + Printf.bprintf b "%s" (String.make n '`'); + if (String.length c > 0) && c.[0] = '`' then Buffer.add_char b ' '; + Printf.bprintf b "%s" c; + if (String.length c > 0) && c.[String.length c - 1] = '`' then Buffer.add_char b ' '; + Printf.bprintf b "%s" (String.make n '`'); + end; + loop list_indent tl + | Code_block(lang, c) :: tl -> + let n = (* compute how many backquotes we need to use *) + let filter n s = + if n > 0 && n < 10 then + List.filter (fun e -> e <> n) s + else + s + in + let l = String.length c in + let rec loop s b i = + if i = l then + match filter b s with + | hd::_ -> hd + | [] -> 0 + else + match c.[i] with + | '`' -> loop s (succ b) (succ i) + | _ -> loop (filter b s) 0 (succ i) + in + loop [3;4;5;6;7;8;9;10] 0 0 + in + let output_indented_block n s = + let rec loop p i = + if i = String.length s then + () + else + match p with + | '\n' -> + Printf.bprintf b "%s" (String.make n ' '); + Buffer.add_char b s.[i]; + loop s.[i] (succ i) + | _ -> + Buffer.add_char b s.[i]; + loop s.[i] (succ i) + in loop '\n' 0 + in + if n = 0 then (* FIXME *) + begin + (* case where we can't use backquotes *) + Buffer.add_char b '\n'; + output_indented_block (4+list_indent) c; + if tl <> [] then Buffer.add_string b "\n\n" + end + else + begin + Buffer.add_string b (String.make (list_indent) ' '); + Printf.bprintf b "%s%s\n" (String.make n '`') + (if lang = "" then !default_language else lang); + output_indented_block (list_indent) c; + if Buffer.nth b (Buffer.length b - 1) <> '\n' then + Buffer.add_char b '\n'; + Buffer.add_string b (String.make (list_indent) ' '); + Printf.bprintf b "%s\n" (String.make n '`'); + end; + loop list_indent tl + | Br :: tl -> + Buffer.add_string b "
"; + loop list_indent tl + | Hr :: tl -> + Buffer.add_string b "* * *\n"; + loop list_indent tl + | Raw s :: tl -> + Buffer.add_string b s; + loop list_indent tl + | Raw_block s :: tl -> + Buffer.add_char b '\n'; + Buffer.add_string b s; + Buffer.add_char b '\n'; + loop list_indent tl + | Html(tagname, attrs, []) :: tl + when StringSet.mem tagname html_void_elements -> + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Buffer.add_string b " />"; + loop list_indent tl + | Html(tagname, attrs, body) :: tl -> + let a = filter_text_omd_rev attrs in + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs a); + Buffer.add_string b ">"; + if a == attrs then + loop list_indent body + else + Buffer.add_string b (html_of_md body); + Printf.bprintf b "" tagname; + loop list_indent tl + | (Html_block(tagname, attrs, body))::tl -> + let needs_newlines = + match tl with + | NL :: Paragraph p :: _ + | Paragraph p :: _ -> p <> [] + | (H1 _ | H2 _ | H3 _ | H4 _ | H5 _ | H6 _ + | Ul _ | Ol _ | Ulp _ | Olp _ | Code (_, _) | Code_block (_, _) + | Text _ | Emph _ | Bold _ | Br |Hr | Url (_, _, _) + | Ref (_, _, _, _) | Img_ref (_, _, _, _) + | Html (_, _, _) + | Blockquote _ | Img (_, _, _)) :: _ -> true + | ( Html_block (_, _, _) | Html_comment _ + | Raw _|Raw_block _) :: _-> false + | X _ :: _ -> false + | NL :: _ -> false + | [] -> false + in + if body = [] && StringSet.mem tagname html_void_elements then + ( + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs attrs); + Buffer.add_string b " />"; + if needs_newlines then Buffer.add_string b "\n\n"; + loop list_indent tl + ) + else + ( + let a = filter_text_omd_rev attrs in + Printf.bprintf b "<%s" tagname; + Buffer.add_string b (string_of_attrs a); + Buffer.add_string b ">"; + if a == attrs then + loop list_indent body + else + Buffer.add_string b (html_of_md body); + Printf.bprintf b "" tagname; + if needs_newlines then Buffer.add_string b "\n\n"; + loop list_indent tl + ) + | Html_comment s :: tl -> + Buffer.add_string b s; + loop list_indent tl + | Url (href,s,title) :: tl -> + if title = "" then + bprintf b "[%s](%s)" (markdown_of_md s) href + else + bprintf b "[%s](%s \"%s\")" (markdown_of_md s) href title; + loop list_indent tl + | H1 md :: tl -> + Buffer.add_string b "# "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | H2 md :: tl -> + Buffer.add_string b "## "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | H3 md :: tl -> + Buffer.add_string b "### "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | H4 md :: tl -> + Buffer.add_string b "#### "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | H5 md :: tl -> + Buffer.add_string b "##### "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | H6 md :: tl -> + Buffer.add_string b "###### "; + loop list_indent md; + Buffer.add_string b "\n"; + loop list_indent tl + | NL :: tl -> + if Buffer.length b = 1 + || (Buffer.length b > 1 && + not(Buffer.nth b (Buffer.length b - 1) = '\n' + && Buffer.nth b (Buffer.length b - 2) = '\n')) + then + Buffer.add_string b "\n"; + loop list_indent tl + | [] -> () + in + loop 0 md; + begin match !references with + | None -> () + | Some r -> + Buffer.add_char b '\n'; + List.iter + (fun (name, (url, title)) -> + if title = "" then + bprintf b "[%s]: %s \n" name url + else + bprintf b "[%s]: %s \"%s\"\n" name url title + ) + r#get_all + end; + let res = Buffer.contents b in + if debug then + eprintf "(OMD) markdown_of_md(%S) => %S\n%!" + (sexpr_of_md md) res; + res diff --git a/analysis/src/vendor/omd/omd_backend.mli b/analysis/src/vendor/omd/omd_backend.mli new file mode 100644 index 000000000..cbdad2e56 --- /dev/null +++ b/analysis/src/vendor/omd/omd_backend.mli @@ -0,0 +1,97 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +type code_stylist = lang:string -> string -> string +(** Function that takes a language name and some code and returns + that code with style. *) + +val default_language : string ref +(** default language for code blocks can be set to any name, + by default it is the empty string *) + +val html_of_md : + ?override:(Omd_representation.element -> string option) -> + ?pindent:bool -> + ?nl2br:bool -> + ?cs:code_stylist -> + Omd_representation.t -> string +(** [html_of_md md] returns a string containing the HTML version of + [md]. Note that [md] uses the internal representation of + Markdown. + + The optional parameter [override] allows to override an precise + behaviour for a constructor of Omd_representation.element, + as in the following example: + +let customized_to_html = + Omd.html_of_md + ~override:(function + | Url (href,s,title) -> + Some(" "" then + " title='" ^ (Omd_utils.htmlentities ~md:true title) ^ "'" + else "") + ^ ">" + ^ Omd_backend.html_of_md s ^ " target='_blank'") + | _ -> None) + *) + +val headers_of_md : + ?remove_header_links:bool -> + Omd_representation.t -> + (Omd_representation.element * string * string) list +(** [headers_of_md md] returns a list of 3-tuples; in each of them the + first element is the header (e.g., [H1(foo)]), the second is the + HTML id (as produced by [html_of_md]), and the third element is + the HTML version of [foo]. The third elements of those 3-tuples + exist because if you use [html_and_headers_of_md], then you have + the guarantee that the HTML version of [foo] is the same for + both the headers and the HTML version of [md]. + If [remove_header_links], then remove links inside headers (h1, h2, ...). + Default value of [remove_header_links]: cf. [html_and_headers_of_md]. + *) + +val html_and_headers_of_md : + ?remove_header_links:bool -> + ?override:(Omd_representation.element -> string option) -> + ?pindent:bool -> + ?nl2br:bool -> + ?cs:code_stylist -> + Omd_representation.t -> + string * + (Omd_representation.element * Omd_utils.StringSet.elt * string) list +(** [html_and_headers_of_md md] is the same as [(html_of_md md, + headers_of_md md)] except that it's two times faster. + If you need both headers and html, don't use [html_of_md] + and [headers_of_md] but this function instead. + If [remove_header_links], then remove links inside headers (h1, h2, ...). + Default value of [remove_header_links]: false. +*) + +val escape_markdown_characters : string -> string +(** [escape_markdown_characters s] returns a string where + markdown-significant characters in [s] have been + backslash-escaped. Note that [escape_markdown_characters] takes a + "raw" string, therefore it doesn't have the whole context in which + the string appears, thus the escaping cannot really be + minimal. However the implementation tries to minimalise the extra + escaping. *) + +val text_of_md : Omd_representation.t -> string +(** [text_of_md md] is basically the same as [html_of_md md] but without + the HTML tags in the output. *) + +val markdown_of_md : Omd_representation.t -> string +(** [markdown_of_md md] is basically the same as [html_of_md md] but + with the output in Markdown syntax rather than HTML. *) + +val sexpr_of_md : Omd_representation.t -> string +(** [sexpr_of_md md] is basically the same as [html_of_md md] but with + the output in s-expressions rather than HTML. This is mainly used + for debugging. *) + diff --git a/analysis/src/vendor/omd/omd_html.ml b/analysis/src/vendor/omd/omd_html.ml new file mode 100644 index 000000000..6dffb5b22 --- /dev/null +++ b/analysis/src/vendor/omd/omd_html.ml @@ -0,0 +1,61 @@ +(***********************************************************************) +(* OMD: Markdown tool in OCaml *) +(* (c) 2014 by Philippe Wang *) +(* Licence: ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +type html = html_node list + +and html_node = + | Node of nodename * attributes * html + | Data of string + | Rawdata of string + | Comment of string + +and nodename = string + +and attributes = attribute list + +and attribute = string * string option + +let to_string html = + let b = Buffer.create 1024 in + let pp f = Printf.bprintf b f in + let rec loop = function + | Node(nodename, attributes, html) -> + pp "<%s" nodename; + ppa attributes; + pp ">"; + List.iter loop html; + pp "" nodename + | Data s -> pp "%s" s + | Rawdata s -> pp "%s" s + | Comment c -> pp "" c + and ppa attrs = + List.iter + (function + | (a, Some v) -> + if not (String.contains v '\'') then + pp " %s='%s'" a v + else if (not (String.contains v '"')) then + pp " %s=\"%s\"" a v + else + ( + pp " %s=\"" a; + for i = 0 to String.length v - 1 do + match v.[i] with + | '"' -> pp """ + | c -> pp "%c" c + done; + pp "\"" + ) + | a, None -> + Printf.bprintf b " %s=''" a (* HTML5 *) + ) + attrs + in + List.iter loop html; + Buffer.contents b + + diff --git a/analysis/src/vendor/omd/omd_lexer.ml b/analysis/src/vendor/omd/omd_lexer.ml new file mode 100644 index 000000000..3c4e8d533 --- /dev/null +++ b/analysis/src/vendor/omd/omd_lexer.ml @@ -0,0 +1,375 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +(* Implementation notes ********************************************* + + * - This module should depend on OCaml's standard library only and + * should be as 'pure OCaml' (i.e. depend as least as possible on + * external tools) as possible. + + * - `while' loops are sometimes preferred to recursion because this + * may be used on systems where tail recursion is not well + * supported. (I tried to write "while" as often as possible, but it + * turned out that it was pretty inconvenient, so I do use + * recursion. When I have time, I'll do some tests and see if I + * need to convert recursive loops into iterative loops. Sorry if it + * makes it harder to read.) + +*) + +(* class type tag = object method is_me : 'a. 'a -> bool end *) + +open Omd_representation + +type token = Omd_representation.tok +type t = Omd_representation.tok list + +let string_of_token = function + | Tag (name, o) -> + if Omd_utils.debug then "TAG("^name^")" ^ o#to_string else o#to_string + | Ampersand -> "&" + | Ampersands n -> assert (n >= 0); String.make (2+n) '&' + | At -> "@" + | Ats n -> assert (n >= 0); String.make (2+n) '@' + | Backquote -> "`" + | Backquotes n -> assert (n >= 0); String.make (2+n) '`' + | Backslash -> "\\" + | Backslashs n -> assert (n >= 0); String.make (2+n) '\\' + | Bar -> "|" + | Bars n -> assert (n >= 0); String.make (2+n) '|' + | Caret -> "^" + | Carets n -> assert (n >= 0); String.make (2+n) '^' + | Cbrace -> "}" + | Cbraces n -> assert (n >= 0); String.make (2+n) '}' + | Colon -> ":" + | Colons n -> assert (n >= 0); String.make (2+n) ':' + | Comma -> "," + | Commas n -> assert (n >= 0); String.make (2+n) ',' + | Cparenthesis -> ")" + | Cparenthesiss n -> assert (n >= 0); String.make (2+n) ')' + | Cbracket -> "]" + | Cbrackets n -> assert (n >= 0); String.make (2+n) ']' + | Dollar -> "$" + | Dollars n -> assert (n >= 0); String.make (2+n) '$' + | Dot -> "." + | Dots n -> assert (n >= 0); String.make (2+n) '.' + | Doublequote -> "\"" + | Doublequotes n -> assert (n >= 0); String.make (2+n) '"' + | Exclamation -> "!" + | Exclamations n -> assert (n >= 0); String.make (2+n) '!' + | Equal -> "=" + | Equals n -> assert (n >= 0); String.make (2+n) '=' + | Greaterthan -> ">" + | Greaterthans n -> assert (n >= 0); String.make (2+n) '>' + | Hash -> "#" + | Hashs n -> assert (n >= 0); String.make (2+n) '#' + | Lessthan -> "<" + | Lessthans n -> assert (n >= 0); String.make (2+n) '<' + | Minus -> "-" + | Minuss n -> assert (n >= 0); String.make (2+n) '-' + | Newline -> "\n" + | Newlines n -> assert (n >= 0); String.make (2+n) '\n' + | Number s -> s + | Obrace -> "{" + | Obraces n -> assert (n >= 0); String.make (2+n) '{' + | Oparenthesis -> "(" + | Oparenthesiss n -> assert (n >= 0); String.make (2+n) '(' + | Obracket -> "[" + | Obrackets n -> assert (n >= 0); String.make (2+n) '[' + | Percent -> "%" + | Percents n -> assert (n >= 0); String.make (2+n) '%' + | Plus -> "+" + | Pluss n -> assert (n >= 0); String.make (2+n) '+' + | Question -> "?" + | Questions n -> assert (n >= 0); String.make (2+n) '?' + | Quote -> "'" + | Quotes n -> assert (n >= 0); String.make (2+n) '\'' + | Semicolon -> ";" + | Semicolons n -> assert (n >= 0); String.make (2+n) ';' + | Slash -> "/" + | Slashs n -> assert (n >= 0); String.make (2+n) '/' + | Space -> " " + | Spaces n -> assert (n >= 0); String.make (2+n) ' ' + | Star -> "*" + | Stars n -> assert (n >= 0); String.make (2+n) '*' + | Tab -> " " + | Tabs n -> assert (n >= 0); String.make ((2+n)*4) ' ' + | Tilde -> "~" + | Tildes n -> assert (n >= 0); String.make (2+n) '~' + | Underscore -> "_" + | Underscores n -> assert (n >= 0); String.make (2+n) '_' + | Word s -> s + + +let size_and_newlines = function + | Tag _ -> (0, 0) + | Ampersand | At | Backquote | Backslash | Bar | Caret | Cbrace + | Colon | Comma | Cparenthesis | Cbracket | Dollar | Dot + | Doublequote | Exclamation | Equal | Greaterthan | Hash | Lessthan + | Minus | Obrace | Oparenthesis | Obracket | Percent | Plus + | Question | Quote | Semicolon | Slash | Space | Star | Tab + | Tilde | Underscore -> (1, 0) + | Ampersands x | Ats x | Backquotes x | Backslashs x | Bars x | Carets x + | Cbraces x | Colons x | Commas x | Cparenthesiss x | Cbrackets x + | Dollars x | Dots x + | Doublequotes x | Exclamations x | Equals x | Greaterthans x | Hashs x + | Lessthans x + | Minuss x | Obraces x | Oparenthesiss x | Obrackets x | Percents x | Pluss x + | Questions x | Quotes x | Semicolons x | Slashs x | Spaces x | Stars x + | Tabs x + | Tildes x | Underscores x -> (2+x, 0) + | Newline -> (0, 1) + | Newlines x -> (0, 2+x) + | Number s | Word s -> (String.length s, 0) + +let length t = + let c, nl = size_and_newlines t in + c + nl + +let split_first = function + | Ampersands n -> Ampersand, (if n > 0 then Ampersands(n-1) else Ampersand) + | Ats n -> At, (if n > 0 then Ats(n-1) else At) + | Backquotes n -> Backquote, (if n > 0 then Backquotes(n-1) else Backquote) + | Backslashs n -> Backslash, (if n > 0 then Backslashs(n-1) else Backslash) + | Bars n -> Bar, (if n > 0 then Bars(n-1) else Bar) + | Carets n -> Caret, (if n > 0 then Carets(n-1) else Caret) + | Cbraces n -> Cbrace, (if n > 0 then Cbraces(n-1) else Cbrace) + | Colons n -> Colon, (if n > 0 then Colons(n-1) else Colon) + | Commas n -> Comma, (if n > 0 then Commas(n-1) else Comma) + | Cparenthesiss n -> Cparenthesis, (if n > 0 then Cparenthesiss(n-1) + else Cparenthesis) + | Cbrackets n -> Cbracket, (if n > 0 then Cbrackets(n-1) else Cbracket) + | Dollars n -> Dollar, (if n > 0 then Dollars(n-1) else Dollar) + | Dots n -> Dot, (if n > 0 then Dots(n-1) else Dot) + | Doublequotes n -> Doublequote, (if n > 0 then Doublequotes(n-1) + else Doublequote) + | Exclamations n -> Exclamation, (if n > 0 then Exclamations(n-1) + else Exclamation) + | Equals n -> Equal, (if n > 0 then Equals(n-1) else Equal) + | Greaterthans n -> Greaterthan, (if n > 0 then Greaterthans(n-1) + else Greaterthan) + | Hashs n -> Hash, (if n > 0 then Hashs(n-1) else Hash) + | Lessthans n -> Lessthan, (if n > 0 then Lessthans(n-1) else Lessthan) + | Minuss n -> Minus, (if n > 0 then Minuss(n-1) else Minus) + | Newlines n -> Newline, (if n > 0 then Newlines(n-1) else Newline) + | Obraces n -> Obrace, (if n > 0 then Obraces(n-1) else Obrace) + | Oparenthesiss n -> Oparenthesis, (if n > 0 then Oparenthesiss(n-1) + else Oparenthesis) + | Obrackets n -> Obracket, (if n > 0 then Obrackets(n-1) else Obracket) + | Percents n -> Percent, (if n > 0 then Percents(n-1) else Percent) + | Pluss n -> Plus, (if n > 0 then Pluss(n-1) else Plus) + | Questions n -> Question, (if n > 0 then Questions(n-1) else Question) + | Quotes n -> Quote, (if n > 0 then Quotes(n-1) else Quote) + | Semicolons n -> Semicolon, (if n > 0 then Semicolons(n-1) else Semicolon) + | Slashs n -> Slash, (if n > 0 then Slashs(n-1) else Slash) + | Spaces n -> Space, (if n > 0 then Spaces(n-1) else Space) + | Stars n -> Star, (if n > 0 then Stars(n-1) else Star) + | Tabs n -> Tab, (if n > 0 then Tabs(n-1) else Tab) + | Tildes n -> Tilde, (if n > 0 then Tildes(n-1) else Tilde) + | Underscores n -> Underscore, (if n > 0 then Underscores(n-1) + else Underscore) + | Ampersand | At | Backquote | Backslash | Bar | Caret | Cbrace | Colon + | Comma | Cparenthesis | Cbracket | Dollar | Dot | Doublequote + | Exclamation | Equal | Greaterthan | Hash | Lessthan | Minus + | Newline | Number _ | Obrace | Oparenthesis | Obracket | Percent + | Plus | Question | Quote | Semicolon | Slash | Space | Star | Tab + | Tilde | Underscore | Tag _ | Word _ -> + invalid_arg "Omd_lexer.split_first" + +module type Input = +sig + type t + val length : t -> int + val get : t -> int -> char + val sub : t -> pos:int -> len:int -> string +end + +module Lex(I : Input) : +sig + val lex : I.t -> t +end = +struct + let lex (s : I.t) = + let result = ref [] in + let i = ref 0 in + let l = I.length s in + let rcount c = + (* [rcount c] returns the number of immediate consecutive + occurrences of [c]. By side-effect, it increases the reference + counter [i]. *) + let rec loop r = + if !i = l then r + else if I.get s !i = c then (incr i; loop (r+1)) + else r + in + loop 1 + in + let word () = + let start = !i in + let rec loop () = + begin + if !i = l then + Word(I.sub s ~pos:start ~len:(!i-start)) + else + match I.get s !i with + | ' ' | '\t' | '\n' | '\r' | '#' | '*' | '-' | '+' | '`' | '\'' + | '"' | '\\' | '_' | '[' | ']' | '{' | '}' | '(' | ')' | ':' + | ';' | '>' | '~' | '<' | '@' | '&' | '|' | '^' | '.' | '/' + | '$' | '%' | '!' | '?' | '=' -> + Word(I.sub s ~pos:start ~len:(!i-start)) + | _s -> incr i; loop() + end + in + loop() + in + let maybe_number () = + let start = !i in + while + !i < l && + match I.get s !i with + | '0' .. '9' -> true + | _ -> false + do + incr i + done; + if !i = l then + Number(I.sub s ~pos:start ~len:(!i-start)) + else + begin match I.get s !i with + | ' ' | '\t' | '\n' | '\r' | '#' | '*' | '-' | '+' | '`' | '\'' | '"' + | '\\' | '_' | '[' | ']' | '{' | '}' | '(' | ')' | ':' | ';' | '>' + | '~' | '<' | '@' | '&' | '|' | '^' | '.' | '/' | '$' | '%' | '!' + | '?' | '=' -> + Number(I.sub s ~pos:start ~len:(!i-start)) + | _ -> + i := start; + word() + end + in + + let n_occ c = incr i; rcount c in + + while !i < l do + let c = I.get s !i in + let w = match c with + | ' ' -> let n = n_occ c in if n = 1 then Space else Spaces (n-2) + | '\t' -> let n = n_occ c in if n = 1 then Spaces(2) else Spaces(4*n-2) + | '\n' -> let n = n_occ c in if n = 1 then Newline else Newlines (n-2) + | '\r' -> (* eliminating \r by converting all styles to unix style *) + incr i; + let rec count_rn x = + if !i < l && I.get s (!i) = '\n' then + if !i + 1 < l && I.get s (!i+1) = '\r' then + (i := !i + 2; count_rn (x+1)) + else + x + else + x + in + let rn = 1 + count_rn 0 in + if rn = 1 then + match n_occ c with + | 1 -> Newline + | x -> assert(x>=2); Newlines(x-2) + else + (assert(rn>=2);Newlines(rn-2)) + | '#' -> let n = n_occ c in if n = 1 then Hash else Hashs (n-2) + | '*' -> let n = n_occ c in if n = 1 then Star else Stars (n-2) + | '-' -> let n = n_occ c in if n = 1 then Minus else Minuss (n-2) + | '+' -> let n = n_occ c in if n = 1 then Plus else Pluss (n-2) + | '`' -> let n = n_occ c in if n = 1 then Backquote else Backquotes (n-2) + | '\'' -> let n = n_occ c in if n = 1 then Quote else Quotes (n-2) + | '"' -> let n = n_occ c in if n = 1 then Doublequote + else Doublequotes (n-2) + | '\\' -> let n = n_occ c in if n = 1 then Backslash + else Backslashs (n-2) + | '_' -> let n = n_occ c in if n = 1 then Underscore + else Underscores (n-2) + | '[' -> let n = n_occ c in if n = 1 then Obracket + else Obrackets (n-2) + | ']' -> let n = n_occ c in if n = 1 then Cbracket else Cbrackets (n-2) + | '{' -> let n = n_occ c in if n = 1 then Obrace else Obraces (n-2) + | '}' -> let n = n_occ c in if n = 1 then Cbrace else Cbraces (n-2) + | '(' -> let n = n_occ c in if n = 1 then Oparenthesis + else Oparenthesiss (n-2) + | ')' -> let n = n_occ c in if n = 1 then Cparenthesis + else Cparenthesiss (n-2) + | ':' -> let n = n_occ c in if n = 1 then Colon else Colons (n-2) + | ';' -> let n = n_occ c in if n = 1 then Semicolon else Semicolons (n-2) + | '>' -> let n = n_occ c in if n = 1 then Greaterthan + else Greaterthans (n-2) + | '~' -> let n = n_occ c in if n = 1 then Tilde else Tildes (n-2) + | '<' -> let n = n_occ c in if n = 1 then Lessthan else Lessthans (n-2) + | '@' -> let n = n_occ c in if n = 1 then At else Ats (n-2) + | '&' -> let n = n_occ c in if n = 1 then Ampersand else Ampersands (n-2) + | '|' -> let n = n_occ c in if n = 1 then Bar else Bars (n-2) + | '^' -> let n = n_occ c in if n = 1 then Caret else Carets (n-2) + | ',' -> let n = n_occ c in if n = 1 then Comma else Commas (n-2) + | '.' -> let n = n_occ c in if n = 1 then Dot else Dots (n-2) + | '/' -> let n = n_occ c in if n = 1 then Slash else Slashs (n-2) + | '$' -> let n = n_occ c in if n = 1 then Dollar else Dollars (n-2) + | '%' -> let n = n_occ c in if n = 1 then Percent else Percents (n-2) + | '=' -> let n = n_occ c in if n = 1 then Equal else Equals (n-2) + | '!' -> let n = n_occ c in if n = 1 then Exclamation + else Exclamations (n-2) + | '?' -> let n = n_occ c in if n = 1 then Question else Questions (n-2) + | '0' .. '9' -> maybe_number() + | _ -> word() in + result := w :: !result + done; + List.rev !result +end + +module Lex_string = Lex(StringLabels) +let lex = Lex_string.lex +let make_space = function + | 0 -> invalid_arg "Omd_lexer.make_space" + | 1 -> Space + | n -> if n < 0 then invalid_arg "Omd_lexer.make_space" else Spaces (n-2) + + +(* +(** [string_of_tl l] returns the string representation of l. + [estring_of_tl l] returns the escaped string representation of l + (same semantics as [String.escaped (string_of_tl l)]). *) +let string_of_tl, estring_of_tl = + let g escaped tl = + let b = Buffer.create 42 in + let rec loop : 'a t list -> unit = function + | e::tl -> + Buffer.add_string b (if escaped then String.escaped (string_of_t e) + else string_of_t e); + loop tl + | [] -> + () + in + Buffer.contents (loop tl; b) + in g false, g true +*) + +let string_of_tokens tl = + let b = Buffer.create 128 in + List.iter (fun e -> Buffer.add_string b (string_of_token e)) tl; + Buffer.contents b + + +let destring_of_tokens ?(limit=max_int) tl = + let b = Buffer.create 1024 in + let rec loop (i:int) (tlist:tok list) : unit = match tlist with + | e::tl -> + if limit = i then + loop i [] + else + begin + Buffer.add_string b (String.escaped (string_of_token e)); + Buffer.add_string b "::"; + loop (succ i) tl + end + | [] -> + Buffer.add_string b "[]" + in + Buffer.contents (loop 0 tl; b) diff --git a/analysis/src/vendor/omd/omd_lexer.mli b/analysis/src/vendor/omd/omd_lexer.mli new file mode 100644 index 000000000..bcebdd368 --- /dev/null +++ b/analysis/src/vendor/omd/omd_lexer.mli @@ -0,0 +1,38 @@ +type token = Omd_representation.tok +type t = token list + +val lex : string -> t +(** Translate a raw string into tokens for the parser. To implement + an extension to the lexer, one may process its result before + giving it to the parser. To implement an extension to the + parser, one may extend it using the constructor [Tag] + from type [tok] and/or using the extensions mechanism + of the parser (cf. the optional argument [extensions]). + The main difference is that [Tag] is processed by the parser + in highest priority whereas functions in [extensions] are applied + with lowest priority. *) + +val string_of_tokens : t -> string +(** [string_of_tokens t] return the string corresponding to the token + list [t]. *) + +val length : token -> int +(** [length t] number of characters of the string represented as [t] + (i.e. [String.length(string_of_token t)]). *) + +val string_of_token : token -> string +(** [string_of_token tk] return the string corresponding to the token + [tk]. *) + +val make_space : int -> token + +val split_first : token -> token * token +(** [split_first(Xs n)] returns [(X, X(n-1))] where [X] is a token + carrying an int count. + + @raise Invalid_argument is passed a single token. *) + + +val destring_of_tokens : ?limit:int -> t -> string +(** Converts the tokens to a simple string representation useful for + debugging. *) diff --git a/analysis/src/vendor/omd/omd_parser.ml b/analysis/src/vendor/omd/omd_parser.ml new file mode 100644 index 000000000..174ade358 --- /dev/null +++ b/analysis/src/vendor/omd/omd_parser.ml @@ -0,0 +1,4453 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013-2014 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +open Printf +open Omd_representation +open Omd_utils +module L = Omd_lexer + +type r = Omd_representation.t +(** accumulator (beware, reversed tokens) *) + +and p = Omd_representation.tok list +(** context information: previous elements *) + +and l = Omd_representation.tok list +(** tokens to parse *) + +and main_loop = + ?html:bool -> + r -> (* accumulator (beware, reversed tokens) *) + p -> (* info: previous elements *) + l -> (* tokens to parse *) + Omd_representation.t (* final result *) +(** most important loop *) + + +(** N.B. Please do not use tabulations in your Markdown file! *) + +module type Env = sig + val rc: Omd_representation.ref_container + val extensions : Omd_representation.extensions + val default_lang : string + val gh_uemph_or_bold_style : bool + val blind_html : bool + val strict_html : bool + val warning : bool + val warn_error : bool +end + +module Unit = struct end + +module Default_env (Unit:sig end) : Env = struct + let rc = new Omd_representation.ref_container + let extensions = [] + let default_lang = "" + let gh_uemph_or_bold_style = true + let blind_html = false + let strict_html = false + let warning = false + let warn_error = false +end + +module Make (Env:Env) = +struct + include Env + + let warn = Omd_utils.warn ~we:warn_error + + (** set of known HTML codes *) + let htmlcodes_set = StringSet.of_list (* This list should be checked... *) + (* list extracted from: http://www.w3.org/TR/html4/charset.html *) + [ "AElig"; "Aacute"; "Acirc"; "Agrave"; "Alpha"; "Aring"; "Atilde"; + "Auml"; "Beta"; "Ccedil"; "Chi"; "Dagger"; "Delta"; "ETH"; "Eacute"; + "Ecirc"; "Egrave"; "Epsilon"; "Eta"; "Euml"; "Gamma"; "Iacute"; + "Icirc"; "Igrave"; "Iota"; "Iuml"; "Kappa"; "Lambda"; "Mu"; "Ntilde"; + "Nu"; "OElig"; "Oacute"; "Ocirc"; "Ograve"; "Omega"; "Omicron"; + "Oslash"; "Otilde"; "Ouml"; "Phi"; "Pi"; "Prime"; "Psi"; "Rho"; + "Scaron"; "Sigma"; "THORN"; "Tau"; "Theta"; "Uacute"; "Ucirc"; + "Ugrave"; "Upsilon"; "Uuml"; "Xi"; "Yacute"; "Yuml"; "Zeta"; "aacute"; + "acirc"; "acute"; "aelig"; "agrave"; "alefsym"; "alpha"; "amp"; "and"; + "ang"; "aring"; "asymp"; "atilde"; "auml"; "bdquo"; "beta"; "brvbar"; + "bull"; "cap"; "ccedil"; "cedil"; "cent"; "chi"; "circ"; "clubs"; + "cong"; "copy"; "crarr"; "cup"; "curren"; "dArr"; "dagger"; "darr"; + "deg"; "delta"; "diams"; "divide"; "eacute"; "ecirc"; "egrave"; + "empty"; "emsp"; "ensp"; "epsilon"; "equiv"; "eta"; "eth"; "euml"; + "euro"; "exist"; "fnof"; "forall"; "frac12"; "frac14"; "frac34"; + "frasl"; "gamma"; "ge"; "gt"; "hArr"; "harr"; "hearts"; "hellip"; + "iacute"; "icirc"; "iexcl"; "igrave"; "image"; "infin"; "int"; "iota"; + "iquest"; "isin"; "iuml"; "kappa"; "lArr"; "lambda"; "lang"; "laquo"; + "larr"; "lceil"; "ldquo"; "le"; "lfloor"; "lowast"; "loz"; "lrm"; + "lsaquo"; "lsquo"; "lt"; "macr"; "mdash"; "micro"; "middot"; "minus"; + "mu"; "nabla"; "nbsp"; "ndash"; "ne"; "ni"; "not"; "notin"; "nsub"; + "ntilde"; "nu"; "oacute"; "ocirc"; "oelig"; "ograve"; "oline"; + "omega"; "omicron"; "oplus"; "or"; "ordf"; "ordm"; "oslash"; "otilde"; + "otimes"; "ouml"; "para"; "part"; "permil"; "perp"; "phi"; "pi"; + "piv"; "plusmn"; "pound"; "prime"; "prod"; "prop"; "psi"; "quot"; + "rArr"; "radic"; "rang"; "raquo"; "rarr"; "rceil"; "rdquo"; "real"; + "reg"; "rfloor"; "rho"; "rlm"; "rsaquo"; "rsquo"; "sbquo"; "scaron"; + "sdot"; "sect"; "shy"; "sigma"; "sigmaf"; "sim"; "spades"; "sub"; + "sube"; "sum"; "sup"; "sup1"; "sup2"; "sup3"; "supe"; "szlig"; "tau"; + "there4"; "theta"; "thetasym"; "thinsp"; "thorn"; "tilde"; "times"; + "trade"; "uArr"; "uacute"; "uarr"; "ucirc"; "ugrave"; "uml"; "upsih"; + "upsilon"; "uuml"; "weierp"; "xi"; "yacute"; "yen"; "yuml"; "zeta"; + "zwj"; "zwnj"; ] + + + (** set of known inline HTML tags *) + let inline_htmltags_set = + (StringSet.of_list + (* from https://developer.mozilla.org/en-US/docs/HTML/Inline_elements *) + [ "b";"big";"i";"small";"tt"; + "abbr";"acronym";"cite";"code";"dfn";"em";"kbd";"strong";"samp";"var"; + "a";"bdo";"br";"img";"map";"object";"q";"span";"sub";"sup"; + "button";"input";"label";"select";"textarea";]) + + (** N.B. it seems that there is no clear distinction between inline + tags and block-level tags: in HTML4 it was not clear, in HTML5 + it's even more complicated. So, the choice *here* is to specify + a set of tags considered as "inline", cf. [inline_htmltags_set]. + So there will be inline tags, non-inline tags, and unknown + tags.*) + + (** set of HTML tags that may appear out of a body *) + let notinbodytags = StringSet.of_list + [ + "title"; + "link"; + "meta"; + "style"; + "html"; + "head"; + "body"; + ] + + (** All known HTML tags *) + let htmltags_set = + StringSet.union notinbodytags + (StringSet.union inline_htmltags_set + (StringSet.of_list + [ + "a";"abbr";"acronym";"address";"applet";"area";"article";"aside" + ;"audio";"b";"base";"basefont";"bdi";"bdo";"big";"blockquote" + ;"br";"button";"canvas";"caption";"center";"cite";"code";"col" + ;"colgroup";"command";"datalist";"dd";"del";"details";"dfn" + ;"dialog";"dir";"div";"dl";"dt";"em";"embed";"fieldset" + ;"figcaption";"figure";"font";"footer";"form";"frame";"frameset" + ;"h2";"h3";"h4";"h5";"h6" + ;"h1";"header";"hr";"i";"iframe";"img";"input";"ins";"kbd" + ;"keygen";"label";"legend";"li";"map";"mark";"menu";"meter";"nav" + ;"noframes";"noscript";"object";"ol";"optgroup";"option";"output" + ;"p";"param";"pre";"progress";"q";"rp";"rt";"ruby";"s";"samp" + ;"script";"section";"select";"small";"source";"span";"strike" + ;"strong";"style";"sub";"summary";"sup";"table";"tbody";"td" + ;"textarea";"tfoot";"th";"thead";"time";"tr";"track";"tt";"u" + ;"ul";"var";"video";"wbr" + ])) + + + (** This functions fixes bad lexing trees, which may be built when + extraction a portion of another lexing tree. *) + let fix l = + let rec loop accu = function + | Ampersand::Ampersand::tl -> + if trackfix then eprintf "(OMD) Ampersand 1\n"; + loop accu (Ampersands 0::tl) + | Ampersands n::Ampersand::tl -> + if trackfix then eprintf "(OMD) Ampersand 2\n"; + loop accu (Ampersands(n+1)::tl) + | Ampersand::Ampersands n::tl -> + if trackfix then eprintf "(OMD) Ampersand 3\n"; + loop accu (Ampersands(n+1)::tl) + | Ampersands a::Ampersands b::tl -> + if trackfix then eprintf "(OMD) Ampersand 4\n"; + loop accu (Ampersands(a+b+2)::tl) + | At::At::tl -> + if trackfix then eprintf "(OMD) At 1\n"; + loop accu (Ats 0::tl) + | Ats n::At::tl -> + if trackfix then eprintf "(OMD) At 2\n"; + loop accu (Ats(n+1)::tl) + | At::Ats n::tl -> + if trackfix then eprintf "(OMD) At 3\n"; + loop accu (Ats(n+1)::tl) + | Ats a::Ats b::tl -> + if trackfix then eprintf "(OMD) At 4\n"; + loop accu (Ats(a+b+2)::tl) + | Backquote::Backquote::tl -> + if trackfix then eprintf "(OMD) Backquote 1\n"; + loop accu (Backquotes 0::tl) + | Backquotes n::Backquote::tl -> + if trackfix then eprintf "(OMD) Backquote 2\n"; + loop accu (Backquotes(n+1)::tl) + | Backquote::Backquotes n::tl -> + if trackfix then eprintf "(OMD) Backquote 3\n"; + loop accu (Backquotes(n+1)::tl) + | Backquotes a::Backquotes b::tl -> + if trackfix then eprintf "(OMD) Backquote 4\n"; + loop accu (Backquotes(a+b+2)::tl) + | Backslash::Backslash::tl -> + if trackfix then eprintf "(OMD) Backslash 1\n"; + loop accu (Backslashs 0::tl) + | Backslashs n::Backslash::tl -> + if trackfix then eprintf "(OMD) Backslash 2\n"; + loop accu (Backslashs(n+1)::tl) + | Backslash::Backslashs n::tl -> + if trackfix then eprintf "(OMD) Backslash 3\n"; + loop accu (Backslashs(n+1)::tl) + | Backslashs a::Backslashs b::tl -> + if trackfix then eprintf "(OMD) Backslash 4\n"; + loop accu (Backslashs(a+b+2)::tl) + | Bar::Bar::tl -> + if trackfix then eprintf "(OMD) Bar 1\n"; + loop accu (Bars 0::tl) + | Bars n::Bar::tl -> + if trackfix then eprintf "(OMD) Bar 2\n"; + loop accu (Bars(n+1)::tl) + | Bar::Bars n::tl -> + if trackfix then eprintf "(OMD) Bar 3\n"; + loop accu (Bars(n+1)::tl) + | Bars a::Bars b::tl -> + if trackfix then eprintf "(OMD) Bar 4\n"; + loop accu (Bars(a+b+2)::tl) + | Caret::Caret::tl -> + if trackfix then eprintf "(OMD) Caret 1\n"; + loop accu (Carets 0::tl) + | Carets n::Caret::tl -> + if trackfix then eprintf "(OMD) Caret 2\n"; + loop accu (Carets(n+1)::tl) + | Caret::Carets n::tl -> + if trackfix then eprintf "(OMD) Caret 3\n"; + loop accu (Carets(n+1)::tl) + | Carets a::Carets b::tl -> + if trackfix then eprintf "(OMD) Caret 4\n"; + loop accu (Carets(a+b+2)::tl) + | Cbrace::Cbrace::tl -> + if trackfix then eprintf "(OMD) Cbrace 1\n"; + loop accu (Cbraces 0::tl) + | Cbraces n::Cbrace::tl -> + if trackfix then eprintf "(OMD) Cbrace 2\n"; + loop accu (Cbraces(n+1)::tl) + | Cbrace::Cbraces n::tl -> + if trackfix then eprintf "(OMD) Cbrace 3\n"; + loop accu (Cbraces(n+1)::tl) + | Cbraces a::Cbraces b::tl -> + if trackfix then eprintf "(OMD) Cbrace 4\n"; + loop accu (Cbraces(a+b+2)::tl) + | Colon::Colon::tl -> + if trackfix then eprintf "(OMD) Colon 1\n"; + loop accu (Colons 0::tl) + | Colons n::Colon::tl -> + if trackfix then eprintf "(OMD) Colon 2\n"; + loop accu (Colons(n+1)::tl) + | Colon::Colons n::tl -> + if trackfix then eprintf "(OMD) Colon 3\n"; + loop accu (Colons(n+1)::tl) + | Colons a::Colons b::tl -> + if trackfix then eprintf "(OMD) Colon 4\n"; + loop accu (Colons(a+b+2)::tl) + | Comma::Comma::tl -> + if trackfix then eprintf "(OMD) Comma 1\n"; + loop accu (Commas 0::tl) + | Commas n::Comma::tl -> + if trackfix then eprintf "(OMD) Comma 2\n"; + loop accu (Commas(n+1)::tl) + | Comma::Commas n::tl -> + if trackfix then eprintf "(OMD) Comma 3\n"; + loop accu (Commas(n+1)::tl) + | Commas a::Commas b::tl -> + if trackfix then eprintf "(OMD) Comma 4\n"; + loop accu (Commas(a+b+2)::tl) + | Cparenthesis::Cparenthesis::tl -> + if trackfix then eprintf "(OMD) Cparenthesis 1\n"; + loop accu (Cparenthesiss 0::tl) + | Cparenthesiss n::Cparenthesis::tl -> + if trackfix then eprintf "(OMD) Cparenthesis 2\n"; + loop accu (Cparenthesiss(n+1)::tl) + | Cparenthesis::Cparenthesiss n::tl -> + if trackfix then eprintf "(OMD) Cparenthesis 3\n"; + loop accu (Cparenthesiss(n+1)::tl) + | Cparenthesiss a::Cparenthesiss b::tl -> + if trackfix then eprintf "(OMD) Cparenthesis 4\n"; + loop accu (Cparenthesiss(a+b+2)::tl) + | Cbracket::Cbracket::tl -> + if trackfix then eprintf "(OMD) Cbracket 1\n"; + loop accu (Cbrackets 0::tl) + | Cbrackets n::Cbracket::tl -> + if trackfix then eprintf "(OMD) Cbracket 2\n"; + loop accu (Cbrackets(n+1)::tl) + | Cbracket::Cbrackets n::tl -> + if trackfix then eprintf "(OMD) Cbracket 3\n"; + loop accu (Cbrackets(n+1)::tl) + | Cbrackets a::Cbrackets b::tl -> + if trackfix then eprintf "(OMD) Cbracket 4\n"; + loop accu (Cbrackets(a+b+2)::tl) + | Dollar::Dollar::tl -> + if trackfix then eprintf "(OMD) Dollar 1\n"; + loop accu (Dollars 0::tl) + | Dollars n::Dollar::tl -> + if trackfix then eprintf "(OMD) Dollar 2\n"; + loop accu (Dollars(n+1)::tl) + | Dollar::Dollars n::tl -> + if trackfix then eprintf "(OMD) Dollar 3\n"; + loop accu (Dollars(n+1)::tl) + | Dollars a::Dollars b::tl -> + if trackfix then eprintf "(OMD) Dollar 4\n"; + loop accu (Dollars(a+b+2)::tl) + | Dot::Dot::tl -> + if trackfix then eprintf "(OMD) Dot 1\n"; + loop accu (Dots 0::tl) + | Dots n::Dot::tl -> + if trackfix then eprintf "(OMD) Dot 2\n"; + loop accu (Dots(n+1)::tl) + | Dot::Dots n::tl -> + if trackfix then eprintf "(OMD) Dot 3\n"; + loop accu (Dots(n+1)::tl) + | Dots a::Dots b::tl -> + if trackfix then eprintf "(OMD) Dot 4\n"; + loop accu (Dots(a+b+2)::tl) + | Doublequote::Doublequote::tl -> + if trackfix then eprintf "(OMD) Doublequote 1\n"; + loop accu (Doublequotes 0::tl) + | Doublequotes n::Doublequote::tl -> + if trackfix then eprintf "(OMD) Doublequote 2\n"; + loop accu (Doublequotes(n+1)::tl) + | Doublequote::Doublequotes n::tl -> + if trackfix then eprintf "(OMD) Doublequote 3\n"; + loop accu (Doublequotes(n+1)::tl) + | Doublequotes a::Doublequotes b::tl -> + if trackfix then eprintf "(OMD) Doublequote 4\n"; + loop accu (Doublequotes(a+b+2)::tl) + | Exclamation::Exclamation::tl -> + if trackfix then eprintf "(OMD) Exclamation 1\n"; + loop accu (Exclamations 0::tl) + | Exclamations n::Exclamation::tl -> + if trackfix then eprintf "(OMD) Exclamation 2\n"; + loop accu (Exclamations(n+1)::tl) + | Exclamation::Exclamations n::tl -> + if trackfix then eprintf "(OMD) Exclamation 3\n"; + loop accu (Exclamations(n+1)::tl) + | Exclamations a::Exclamations b::tl -> + if trackfix then eprintf "(OMD) Exclamation 4\n"; + loop accu (Exclamations(a+b+2)::tl) + | Equal::Equal::tl -> + if trackfix then eprintf "(OMD) Equal 1\n"; + loop accu (Equals 0::tl) + | Equals n::Equal::tl -> + if trackfix then eprintf "(OMD) Equal 2\n"; + loop accu (Equals(n+1)::tl) + | Equal::Equals n::tl -> + if trackfix then eprintf "(OMD) Equal 3\n"; + loop accu (Equals(n+1)::tl) + | Equals a::Equals b::tl -> + if trackfix then eprintf "(OMD) Equal 4\n"; + loop accu (Equals(a+b+2)::tl) + | Greaterthan::Greaterthan::tl -> + if trackfix then eprintf "(OMD) Greaterthan 1\n"; + loop accu (Greaterthans 0::tl) + | Greaterthans n::Greaterthan::tl -> + if trackfix then eprintf "(OMD) Greaterthan 2\n"; + loop accu (Greaterthans(n+1)::tl) + | Greaterthan::Greaterthans n::tl -> + if trackfix then eprintf "(OMD) Greaterthan 3\n"; + loop accu (Greaterthans(n+1)::tl) + | Greaterthans a::Greaterthans b::tl -> + if trackfix then eprintf "(OMD) Greaterthan 4\n"; + loop accu (Greaterthans(a+b+2)::tl) + | Hash::Hash::tl -> + if trackfix then eprintf "(OMD) Hash 1\n"; + loop accu (Hashs 0::tl) + | Hashs n::Hash::tl -> + if trackfix then eprintf "(OMD) Hash 2\n"; + loop accu (Hashs(n+1)::tl) + | Hash::Hashs n::tl -> + if trackfix then eprintf "(OMD) Hash 3\n"; + loop accu (Hashs(n+1)::tl) + | Hashs a::Hashs b::tl -> + if trackfix then eprintf "(OMD) Hash 4\n"; + loop accu (Hashs(a+b+2)::tl) + | Lessthan::Lessthan::tl -> + if trackfix then eprintf "(OMD) Lessthan 1\n"; + loop accu (Lessthans 0::tl) + | Lessthans n::Lessthan::tl -> + if trackfix then eprintf "(OMD) Lessthan 2\n"; + loop accu (Lessthans(n+1)::tl) + | Lessthan::Lessthans n::tl -> + if trackfix then eprintf "(OMD) Lessthan 3\n"; + loop accu (Lessthans(n+1)::tl) + | Lessthans a::Lessthans b::tl -> + if trackfix then eprintf "(OMD) Lessthan 4\n"; + loop accu (Lessthans(a+b+2)::tl) + | Minus::Minus::tl -> + if trackfix then eprintf "(OMD) Minus 1\n"; + loop accu (Minuss 0::tl) + | Minuss n::Minus::tl -> + if trackfix then eprintf "(OMD) Minus 2\n"; + loop accu (Minuss(n+1)::tl) + | Minus::Minuss n::tl -> + if trackfix then eprintf "(OMD) Minus 3\n"; + loop accu (Minuss(n+1)::tl) + | Minuss a::Minuss b::tl -> + if trackfix then eprintf "(OMD) Minus 4\n"; + loop accu (Minuss(a+b+2)::tl) + | Newline::Newline::tl -> + if trackfix then eprintf "(OMD) Newline 1\n"; + loop accu (Newlines 0::tl) + | Newlines n::Newline::tl -> + if trackfix then eprintf "(OMD) Newline 2\n"; + loop accu (Newlines(n+1)::tl) + | Newline::Newlines n::tl -> + if trackfix then eprintf "(OMD) Newline 3\n"; + loop accu (Newlines(n+1)::tl) + | Newlines a::Newlines b::tl -> + if trackfix then eprintf "(OMD) Newline 4\n"; + loop accu (Newlines(a+b+2)::tl) + | Obrace::Obrace::tl -> + if trackfix then eprintf "(OMD) Obrace 1\n"; + loop accu (Obraces 0::tl) + | Obraces n::Obrace::tl -> + if trackfix then eprintf "(OMD) Obrace 2\n"; + loop accu (Obraces(n+1)::tl) + | Obrace::Obraces n::tl -> + if trackfix then eprintf "(OMD) Obrace 3\n"; + loop accu (Obraces(n+1)::tl) + | Obraces a::Obraces b::tl -> + if trackfix then eprintf "(OMD) Obrace 4\n"; + loop accu (Obraces(a+b+2)::tl) + | Oparenthesis::Oparenthesis::tl -> + if trackfix then eprintf "(OMD) Oparenthesis 1\n"; + loop accu (Oparenthesiss 0::tl) + | Oparenthesiss n::Oparenthesis::tl -> + if trackfix then eprintf "(OMD) Oparenthesis 2\n"; + loop accu (Oparenthesiss(n+1)::tl) + | Oparenthesis::Oparenthesiss n::tl -> + if trackfix then eprintf "(OMD) Oparenthesis 3\n"; + loop accu (Oparenthesiss(n+1)::tl) + | Oparenthesiss a::Oparenthesiss b::tl -> + if trackfix then eprintf "(OMD) Oparenthesis 4\n"; + loop accu (Oparenthesiss(a+b+2)::tl) + | Obracket::Obracket::tl -> + if trackfix then eprintf "(OMD) Obracket 1\n"; + loop accu (Obrackets 0::tl) + | Obrackets n::Obracket::tl -> + if trackfix then eprintf "(OMD) Obracket 2\n"; + loop accu (Obrackets(n+1)::tl) + | Obracket::Obrackets n::tl -> + if trackfix then eprintf "(OMD) Obracket 3\n"; + loop accu (Obrackets(n+1)::tl) + | Obrackets a::Obrackets b::tl -> + if trackfix then eprintf "(OMD) Obracket 4\n"; + loop accu (Obrackets(a+b+2)::tl) + | Percent::Percent::tl -> + if trackfix then eprintf "(OMD) Percent 1\n"; + loop accu (Percents 0::tl) + | Percents n::Percent::tl -> + if trackfix then eprintf "(OMD) Percent 2\n"; + loop accu (Percents(n+1)::tl) + | Percent::Percents n::tl -> + if trackfix then eprintf "(OMD) Percent 3\n"; + loop accu (Percents(n+1)::tl) + | Percents a::Percents b::tl -> + if trackfix then eprintf "(OMD) Percent 4\n"; + loop accu (Percents(a+b+2)::tl) + | Plus::Plus::tl -> + if trackfix then eprintf "(OMD) Plus 1\n"; + loop accu (Pluss 0::tl) + | Pluss n::Plus::tl -> + if trackfix then eprintf "(OMD) Plus 2\n"; + loop accu (Pluss(n+1)::tl) + | Plus::Pluss n::tl -> + if trackfix then eprintf "(OMD) Plus 3\n"; + loop accu (Pluss(n+1)::tl) + | Pluss a::Pluss b::tl -> + if trackfix then eprintf "(OMD) Plus 4\n"; + loop accu (Pluss(a+b+2)::tl) + | Question::Question::tl -> + if trackfix then eprintf "(OMD) Question 1\n"; + loop accu (Questions 0::tl) + | Questions n::Question::tl -> + if trackfix then eprintf "(OMD) Question 2\n"; + loop accu (Questions(n+1)::tl) + | Question::Questions n::tl -> + if trackfix then eprintf "(OMD) Question 3\n"; + loop accu (Questions(n+1)::tl) + | Questions a::Questions b::tl -> + if trackfix then eprintf "(OMD) Question 4\n"; + loop accu (Questions(a+b+2)::tl) + | Quote::Quote::tl -> + if trackfix then eprintf "(OMD) Quote 1\n"; + loop accu (Quotes 0::tl) + | Quotes n::Quote::tl -> + if trackfix then eprintf "(OMD) Quote 2\n"; + loop accu (Quotes(n+1)::tl) + | Quote::Quotes n::tl -> + if trackfix then eprintf "(OMD) Quote 3\n"; + loop accu (Quotes(n+1)::tl) + | Quotes a::Quotes b::tl -> + if trackfix then eprintf "(OMD) Quote 4\n"; + loop accu (Quotes(a+b+2)::tl) + | Semicolon::Semicolon::tl -> + if trackfix then eprintf "(OMD) Semicolon 1\n"; + loop accu (Semicolons 0::tl) + | Semicolons n::Semicolon::tl -> + if trackfix then eprintf "(OMD) Semicolon 2\n"; + loop accu (Semicolons(n+1)::tl) + | Semicolon::Semicolons n::tl -> + if trackfix then eprintf "(OMD) Semicolon 3\n"; + loop accu (Semicolons(n+1)::tl) + | Semicolons a::Semicolons b::tl -> + if trackfix then eprintf "(OMD) Semicolon 4\n"; + loop accu (Semicolons(a+b+2)::tl) + | Slash::Slash::tl -> + if trackfix then eprintf "(OMD) Slash 1\n"; + loop accu (Slashs 0::tl) + | Slashs n::Slash::tl -> + if trackfix then eprintf "(OMD) Slash 2\n"; + loop accu (Slashs(n+1)::tl) + | Slash::Slashs n::tl -> + if trackfix then eprintf "(OMD) Slash 3\n"; + loop accu (Slashs(n+1)::tl) + | Slashs a::Slashs b::tl -> + if trackfix then eprintf "(OMD) Slash 4\n"; + loop accu (Slashs(a+b+2)::tl) + | Space::Space::tl -> + if trackfix then eprintf "(OMD) Space 1\n"; + loop accu (Spaces 0::tl) + | Spaces n::Space::tl -> + if trackfix then eprintf "(OMD) Space 2\n"; + loop accu (Spaces(n+1)::tl) + | Space::Spaces n::tl -> + if trackfix then eprintf "(OMD) Space 3\n"; + loop accu (Spaces(n+1)::tl) + | Spaces a::Spaces b::tl -> + if trackfix then eprintf "(OMD) Space 4\n"; + loop accu (Spaces(a+b+2)::tl) + | Star::Star::tl -> + if trackfix then eprintf "(OMD) Star 1\n"; + loop accu (Stars 0::tl) + | Stars n::Star::tl -> + if trackfix then eprintf "(OMD) Star 2\n"; + loop accu (Stars(n+1)::tl) + | Star::Stars n::tl -> + if trackfix then eprintf "(OMD) Star 3\n"; + loop accu (Stars(n+1)::tl) + | Stars a::Stars b::tl -> + if trackfix then eprintf "(OMD) Star 4\n"; + loop accu (Stars(a+b+2)::tl) + | Tab::Tab::tl -> + if trackfix then eprintf "(OMD) Tab 1\n"; + loop accu (Tabs 0::tl) + | Tabs n::Tab::tl -> + if trackfix then eprintf "(OMD) Tab 2\n"; + loop accu (Tabs(n+1)::tl) + | Tab::Tabs n::tl -> + if trackfix then eprintf "(OMD) Tab 3\n"; + loop accu (Tabs(n+1)::tl) + | Tabs a::Tabs b::tl -> + if trackfix then eprintf "(OMD) Tab 4\n"; + loop accu (Tabs(a+b+2)::tl) + | Tilde::Tilde::tl -> + if trackfix then eprintf "(OMD) Tilde 1\n"; + loop accu (Tildes 0::tl) + | Tildes n::Tilde::tl -> + if trackfix then eprintf "(OMD) Tilde 2\n"; + loop accu (Tildes(n+1)::tl) + | Tilde::Tildes n::tl -> + if trackfix then eprintf "(OMD) Tilde 3\n"; + loop accu (Tildes(n+1)::tl) + | Tildes a::Tildes b::tl -> + if trackfix then eprintf "(OMD) Tilde 4\n"; + loop accu (Tildes(a+b+2)::tl) + | Underscore::Underscore::tl -> + if trackfix then eprintf "(OMD) Underscore 1\n"; + loop accu (Underscores 0::tl) + | Underscores n::Underscore::tl -> + if trackfix then eprintf "(OMD) Underscore 2\n"; + loop accu (Underscores(n+1)::tl) + | Underscore::Underscores n::tl -> + if trackfix then eprintf "(OMD) Underscore 3\n"; + loop accu (Underscores(n+1)::tl) + | Underscores a::Underscores b::tl -> + if trackfix then eprintf "(OMD) Underscore 4\n"; + loop accu (Underscores(a+b+2)::tl)| x::tl -> loop (x::accu) tl + | [] -> List.rev accu + in + loop [] l + + + (* Remove all [NL] and [Br] at the beginning. *) + let rec remove_initial_newlines = function + | [] -> [] + | (NL | Br) :: tl -> remove_initial_newlines tl + | l -> l + + + (** - recognizes paragraphs + - glues following blockquotes *) + let make_paragraphs md = + let rec loop cp accu = function (* cp means current paragraph *) + | [] -> + let accu = + match cp with + | [] | [NL] | [Br] -> accu + | (NL|Br)::cp -> Paragraph(List.rev cp)::accu + | cp -> Paragraph(List.rev cp)::accu + in + List.rev accu + | Blockquote b1 :: Blockquote b2 :: tl -> + loop cp accu (Blockquote(b1@b2):: tl) + | Blockquote b :: tl -> + let e = Blockquote(loop [] [] b) in + (match cp with + | [] | [NL] | [Br] -> loop cp (e::accu) tl + | _ -> loop [] (e::Paragraph(List.rev cp)::accu) tl) + | (Ulp b) :: tl -> + let e = Ulp(List.map (fun li -> loop [] [] li) b) in + (match cp with + | [] | [NL] | [Br] -> loop cp (e::accu) tl + | _ -> loop [] (e::Paragraph(List.rev cp)::accu) tl) + | (Olp b) :: tl -> + let e = Olp(List.map (fun li -> loop [] [] li) b) in + (match cp with + | [] | [NL] | [Br] -> loop cp (e::accu) tl + | _ -> loop [] (e::Paragraph(List.rev cp)::accu) tl) + | Html_comment _ as e :: tl -> + (match cp with + | [] -> loop [] (e::accu) tl + | [NL] | [Br] -> loop [] (e::NL::accu) tl + | _ -> loop (e::cp) accu tl) + | (Raw_block _ | Html_block _) as e :: tl -> + (match cp with + | [] | [NL] | [Br] -> loop cp (e::cp@accu) tl + | _ -> loop [] (e::Paragraph(List.rev cp)::accu) tl) + | (Code_block _ | H1 _ | H2 _ | H3 _ | H4 _ | H5 _ | H6 _ + | Ol _ | Ul _) as e :: tl -> + (match cp with + | [] | [NL] | [Br] -> loop cp (e::accu) tl + | _ -> loop [] (e::Paragraph(List.rev cp)::accu) tl) + | Text "\n" :: _ | Paragraph _ :: _ -> + invalid_arg "Omd_parser.make_paragraphs" + | (NL|Br) :: (NL|Br) :: tl -> + let tl = remove_initial_newlines tl in + begin match cp with + | [] | [NL] | [Br] -> loop [] (NL::NL::accu) tl + | _ -> loop [] (Paragraph(List.rev cp)::accu) tl + end + | X(x) as e :: tl -> + (* If the extension returns a block as first element, + then consider the extension as a block. However + don't take its contents as it is yet, the contents + of the extension shall be considered final as late + as possible. *) + begin match x#to_t md with + | None -> loop (e::cp) accu tl + | Some(t) -> + match t with + | ( H1 _ + | H2 _ + | H3 _ + | H4 _ + | H5 _ + | H6 _ + | Paragraph _ + | Ul _ + | Ol _ + | Ulp _ + | Olp _ + | Code_block _ + | Hr + | Html_block _ + | Raw_block _ + | Blockquote _ + ) :: _ + -> + (match cp with + | [] | [NL] | [Br] -> + loop cp (e::accu) tl + | _ -> + loop [] (e::Paragraph(List.rev cp)::accu) tl) + | _ -> + loop (e::cp) accu tl + end + | e::tl -> + loop (e::cp) accu tl + in + let remove_white_crumbs l = + let rec loop = function + | [] -> [] + | Text " " :: tl + | NL::tl + | Br::tl + -> + loop tl + | l -> l + in + List.rev (loop (List.rev l)) + in + let rec clean_paragraphs = + if debug then eprintf "(OMD) clean_paragraphs\n"; + function + | [] -> [] + | Paragraph[]::tl -> tl + | Paragraph(p) :: tl -> + Paragraph(clean_paragraphs + (remove_initial_newlines + (remove_white_crumbs(normalise_md p)))) + :: clean_paragraphs tl + | H1 v :: tl -> H1(clean_paragraphs v) + :: clean_paragraphs tl + | H2 v :: tl -> H2(clean_paragraphs v) + :: clean_paragraphs tl + | H3 v :: tl -> H3(clean_paragraphs v) + :: clean_paragraphs tl + | H4 v :: tl -> H4(clean_paragraphs v) + :: clean_paragraphs tl + | H5 v :: tl -> H5(clean_paragraphs v) + :: clean_paragraphs tl + | H6 v :: tl -> H6(clean_paragraphs v) + :: clean_paragraphs tl + | Emph v :: tl -> Emph(clean_paragraphs v) + :: clean_paragraphs tl + | Bold v :: tl -> Bold(clean_paragraphs v) + :: clean_paragraphs tl + | Ul v :: tl -> Ul(List.map clean_paragraphs v) + :: clean_paragraphs tl + | Ol v :: tl -> Ol(List.map clean_paragraphs v) + :: clean_paragraphs tl + | Ulp v :: tl -> Ulp(List.map clean_paragraphs v) + :: clean_paragraphs tl + | Olp v :: tl -> Olp(List.map clean_paragraphs v) + :: clean_paragraphs tl + | Blockquote v :: tl -> Blockquote(clean_paragraphs v) + :: clean_paragraphs tl + | Url(href,v,title) :: tl -> Url(href,(clean_paragraphs v),title) + :: clean_paragraphs tl + | Text _ + | Code _ + | Code_block _ + | Br + | Hr + | NL + | Ref _ + | Img_ref _ + | Raw _ + | Raw_block _ + | Html _ + | Html_block _ + | Html_comment _ + | Img _ + | X _ as v :: tl -> v :: clean_paragraphs tl + in + let r = clean_paragraphs(loop [] [] md) + in + if debug then eprintf "(OMD) clean_paragraphs %S --> %S\n%!" + (Omd_backend.sexpr_of_md md) + (Omd_backend.sexpr_of_md r); + r + + + (** [assert_well_formed] is a developer's function that helps to + track badly constructed token lists. This function has an + effect only if [trackfix] is [true]. *) + let assert_well_formed (l:tok list) : unit = + if trackfix then + let rec equiv l1 l2 = match l1, l2 with + | [], [] -> true + | Tag _::tl1, Tag _::tl2-> equiv tl1 tl2 + | e1::tl1, e2::tl2 -> e1 = e2 && equiv tl1 tl2 + | _ -> false + in + assert(equiv (fix l) l); + () + + (** Generate fallback for references. *) + let extract_fallback _main_loop remains l = + if debug then eprintf "(OMD) Omd_parser.extract_fallback\n%!"; + let rec loop accu = function + | [] -> List.rev accu + | e::tl as r -> + if r == remains then + List.rev accu + else + match e, remains with + | Cbrackets 0, Cbracket::r when tl = r -> + let accu = Word "]" :: accu in + List.rev accu + | Cbrackets n, Cbrackets m::r when m + 1 = n && tl = r -> + let accu = Word "]" :: accu in + List.rev accu + | _ -> + loop (e::accu) tl + in + let a = loop [] l in + object + method to_string = L.string_of_tokens a + method to_t = [Text(L.string_of_tokens a)] + end + + + let unindent_rev n lexemes = + if debug then eprintf "(OMD) CALL: Omd_parser.unindent_rev\n%!"; + assert_well_formed lexemes; + let rec loop accu cl = function + | Newlines x::(Space|Spaces _)::Newlines y::tl -> + loop accu cl (Newlines(x+y+2)::tl) + | Newline::(Space|Spaces _)::Newlines x::tl -> + loop accu cl (Newlines(1+x)::tl) + | Newlines x::(Space|Spaces _)::Newline::tl -> + loop accu cl (Newlines(1+x)::tl) + | Newline::(Space|Spaces _)::Newline::tl -> + loop accu cl (Newlines(0)::tl) + + | (Newline|Newlines 0 as nl)::(Space|Spaces _ as s)::( + (Number _::Dot::(Space|Spaces _)::_) + | ((Star|Plus|Minus)::(Space|Spaces _)::_) + as tl) as l -> + if n = L.length s then + loop (nl::cl@accu) [] tl + else + (cl@accu), l + | (Newline|Newlines 0 as nl)::(Space|Spaces _ as s)::tl -> + let x = L.length s - n in + loop (nl::cl@accu) + (if x > 0 then [L.make_space x] else []) + tl + | Newlines(_)::_ as l -> + (cl@accu), l + | Newline::_ as l -> + (cl@accu), l + | e::tl -> + loop accu (e::cl) tl + | [] as l -> + (cl@accu), l + in + match loop [] [] lexemes with + | [], right -> [], right + | l, right -> + assert_well_formed l; + l, right + + let unindent n lexemes = + let fst, snd = unindent_rev n lexemes in + List.rev fst, snd + + let rec is_blank = function + | (Space | Spaces _ | Newline | Newlines _) :: tl -> + is_blank tl + | [] -> true + | _ -> false + + let semph_or_bold (n:int) (l:l) = + (* FIXME: use rpl call/return convention *) + assert_well_formed l; + assert (n>0 && n<4); + match + fsplit + ~excl:(function Newlines _ :: _ -> true | _ -> false) + ~f:(function + | Backslash::Star::tl -> + Continue_with([Star;Backslash],tl) + | Backslash::Stars 0::tl -> + Continue_with([Star;Backslash],Star::tl) + | Backslash::Stars n::tl -> + Continue_with([Star;Backslash],Stars(n-1)::tl) + | (Backslashs b as x)::Star::tl -> + if b mod 2 = 0 then + Continue_with([x],Star::tl) + else + Continue_with([Star;x],tl) + | (Backslashs b as x)::(Stars 0 as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Star;x],Star::tl) + | (Backslashs b as x)::(Stars n as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Star;x],Stars(n-1)::tl) + | (Space|Spaces _ as x)::(Star|Stars _ as s)::tl -> + Continue_with([s;x],tl) + | (Star|Stars _ as s)::tl -> + if L.length s = n then + Split([],tl) + else + Continue + | _ -> Continue) + l + with + | None -> + None + | Some(left,right) -> + if is_blank left then None else Some(left,right) + + let sm_uemph_or_bold (n:int) (l:l) = + assert_well_formed l; + (* FIXME: use rpl call/return convention *) + assert (n>0 && n<4); + match + fsplit + ~excl:(function Newlines _ :: _ -> true | _ -> false) + ~f:(function + | Backslash::Underscore::tl -> + Continue_with([Underscore;Backslash],tl) + | Backslash::Underscores 0::tl -> + Continue_with([Underscore;Backslash],Underscore::tl) + | Backslash::Underscores n::tl -> + Continue_with([Underscore;Backslash],Underscores(n-1)::tl) + | (Backslashs b as x)::Underscore::tl -> + if b mod 2 = 0 then + Continue_with([x],Underscore::tl) + else + Continue_with([Underscore;x],tl) + | (Backslashs b as x)::(Underscores 0 as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Underscore;x],Underscore::tl) + | (Backslashs b as x)::(Underscores n as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Underscore;x],Underscores(n-1)::tl) + | (Space|Spaces _ as x)::(Underscore|Underscores _ as s)::tl -> + Continue_with([s;x],tl) + | (Underscore|Underscores _ as s)::tl -> + if L.length s = n then + Split([],tl) + else + Continue + | _ -> Continue) + l + with + | None -> + None + | Some(left,right) -> + if is_blank left then None else Some(left,right) + + + let gh_uemph_or_bold (n:int) (l:l) = + assert_well_formed l; + (* FIXME: use rpl call/return convention *) + assert (n>0 && n<4); + match + fsplit + ~excl:(function Newlines _ :: _ -> true | _ -> false) + ~f:(function + | Backslash::Underscore::tl -> + Continue_with([Underscore;Backslash],tl) + | Backslash::Underscores 0::tl -> + Continue_with([Underscore;Backslash],Underscore::tl) + | Backslash::Underscores n::tl -> + Continue_with([Underscore;Backslash],Underscores(n-1)::tl) + | (Backslashs b as x)::Underscore::tl -> + if b mod 2 = 0 then + Continue_with([x],Underscore::tl) + else + Continue_with([Underscore;x],tl) + | (Backslashs b as x)::(Underscores 0 as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Underscore;x],Underscore::tl) + | (Backslashs b as x)::(Underscores n as s)::tl -> + if b mod 2 = 0 then + Continue_with([x],s::tl) + else + Continue_with([Underscore;x],Underscores(n-1)::tl) + | (Space|Spaces _ as x)::(Underscore|Underscores _ as s)::tl -> + Continue_with([s;x],tl) + | (Underscore|Underscores _ as s)::(Word _|Number _ as w):: tl -> + Continue_with([w;s],tl) + | (Underscore|Underscores _ as s)::tl -> + if L.length s = n then + Split([],tl) + else + Continue + | _ -> Continue) + l + with + | None -> + None + | Some(left,right) -> + if is_blank left then None else Some(left,right) + + + let uemph_or_bold n l = + assert_well_formed l; + (* FIXME: use rpl call/return convention *) + if gh_uemph_or_bold_style then + gh_uemph_or_bold n l + else + sm_uemph_or_bold n l + + let eat_blank = + eat (function |Space|Spaces _|Newline|Newlines _ -> true| _ -> false) + + + (* used by tag__maybe_h1 and tag__maybe_h2 *) + let setext_title main_loop (l:l) : (Omd_representation.tok list * l) option = + assert_well_formed l; + let rec detect_balanced_bqs n r l = + (* If there's a balanced (complete) backquote-started code block + then it should be "ignored", else it means the line that + follows is part of a code block, so it's not defining a + setext-style title. *) + if debug then + eprintf "(OMD) detect_balanced_bqs n=%d r=%S l=%S\n%!" + n (L.string_of_tokens r) (L.string_of_tokens l); + match l with + | [] -> + None + | (Newline|Newlines _)::_ -> + None + | Backslash::Backquote::tl -> + detect_balanced_bqs n (Backquote::Backslash::r) tl + | Backslash::Backquotes 0::tl -> + detect_balanced_bqs n (Backquote::Backslash::r) (Backquote::tl) + | Backslash::Backquotes x::tl -> + detect_balanced_bqs n (Backquote::Backslash::r) (Backquotes(x-1)::tl) + | Backslashs(m) as b::Backquote::tl when m mod 2 = 1 -> + detect_balanced_bqs n (Backquote::b::r) tl + | Backslashs(m) as b::Backquotes 0::tl when m mod 2 = 1 -> + detect_balanced_bqs n (Backquote::b::r) (Backquote::tl) + | Backslashs(m) as b::Backquotes x::tl when m mod 2 = 1 -> + detect_balanced_bqs n (Backquote::b::r) (Backquotes(x-1)::tl) + | (Backquote as b)::tl when n = 1 -> + Some(List.rev (b::r), tl) + | (Backquotes x as b)::tl when n = x+2 -> + Some(List.rev (b::r), tl) + | e::tl -> + detect_balanced_bqs n (e::r) tl + in + let rec loop r = function + | [] -> + if r = [] then + None + else + Some(List.rev r, []) + | Backslash::Backquote::tl -> + loop (Backquote::Backslash::r) tl + | Backslashs(m) as b::Backquote::tl when m mod 2 = 1 -> + loop (Backquote::b::r) tl + | Backslash::Backquotes 0::tl -> + loop (Backquote::Backslash::r) (Backquote::tl) + | Backslash::Backquotes x::tl -> + loop (Backquote::Backslash::r) (Backquotes(x-1)::tl) + | Backslashs(m) as b::Backquotes 0::tl when m mod 2 = 1 -> + loop (Backquote::b::r) (Backquote::tl) + | Backslashs(m) as b::Backquotes x::tl when m mod 2 = 1 -> + loop (Backquote::b::r) (Backquotes(x-1)::tl) + | Backquote::tl -> + begin match detect_balanced_bqs 1 [] tl with + | Some(bl,tl) -> loop (bl@r) tl + | _ -> None + end + | Backquotes(x)::tl -> + begin match detect_balanced_bqs (x+2) [] tl with + | Some(bl,tl) -> loop (bl@r) tl + | _ -> None + end + | Newline::(Equal|Equals _|Minus|Minuss _)::tl -> + if r = [] then + None + else + Some(List.rev r, tl) + | (Newline|Newlines _)::_ -> + if debug then + eprintf "(OMD) Omd_parser.setext_title is wrongly used!\n%!"; + None + | e::tl -> + loop (e::r) tl + in + if match l with + | Lessthan::Word _::_ -> + begin match main_loop [] [] l with + | (Html_block _ | Code_block _ | Raw_block _)::_ -> + true + | _ -> + false + end + | _ -> false + then + None + else + let result = loop [] l in + if debug then + eprintf "(OMD) setext_title l=%S result=%S,%S\n%!" + (L.string_of_tokens l) + (match result with + | None -> "" + | Some (x,_tl) -> L.string_of_tokens x) + (match result with + | None -> "" + | Some (_x,tl) -> L.string_of_tokens tl); + result + + let tag__maybe_h1 (main_loop:main_loop) = + Tag("tag__maybe_h1", + object + method parser_extension r p l = + match p with + | ([]|[Newline|Newlines _]) -> + begin match setext_title main_loop l with + | None -> + None + | Some(title, tl) -> + let title = H1(main_loop [] [] title) in + Some((title::r), [Newline], tl) + end + | _ -> + if debug then + eprintf "(OMD) Warning: Omd_parser.tag__maybe_h1 is wrongly \ + used (p=%S)!\n" + (L.string_of_tokens p); + None + method to_string = "" + end + ) + + let tag__maybe_h2 (main_loop:main_loop) = + Tag("tag__maybe_h2", + object + method parser_extension r p l = + match p with + | ([]|[Newline|Newlines _]) -> + begin match setext_title main_loop l with + | None -> + None + | Some(title, tl) -> + let title = H2(main_loop [] [] title) in + Some((title::r), [Newline], tl) + end + | _ -> + if debug then + eprintf "(OMD) Warning: Omd_parser.tag__maybe_h2 is wrongly \ + used (p=%S)!\n" + (L.string_of_tokens p); + None + method to_string = "" + end + ) + + let tag__md md = (* [md] should be in reverse *) + Tag("tag__md", + object + method parser_extension r _p l = Some(md@r, [], l) + method to_string = "" + end + ) + + (* Let's tag the lines that *might* be titles using setext-style. + "might" because if they are, for instance, in a code section, + then they are not titles at all. *) + let tag_setext main_loop lexemes = + assert_well_formed lexemes; + let rec loop pl res = function + | [] | [Newline|Newlines _] -> + pl@res + | (Newline as e1)::(Equal|Equals _ as e2)::tl -> (* might be a H1. *) + begin + match + fsplit_rev + ~f:(function + | (Space|Spaces _|Equal|Equals _)::_tl -> Continue + | [] -> Split([],[]) + | _::_ as l -> Split([], l)) + tl + with + | Some(rleft, (([]|(Newline|Newlines _)::_) as right)) -> + loop [] (rleft@(e2::e1::pl@tag__maybe_h1 main_loop::res)) right + | Some(rleft, right) -> + loop [] (rleft@(e2::e1::pl@res)) right + | None -> + loop [] (e2::e1::pl@res) [] + end + | (Newline as e1)::(Minus|Minuss _ as e2)::tl -> (* might be a H2. *) + begin + match + fsplit_rev + ~f:(function + | (Space|Spaces _|Minus|Minuss _)::_tl -> Continue + | [] -> Split([],[]) + | _::_ as l -> Split([], l)) + tl + with + | Some(rleft, (([]|(Newline|Newlines _)::_) as right)) -> + loop [] (rleft@(e2::e1::pl@tag__maybe_h2 main_loop::res)) right + | Some(rleft, right) -> + loop [] (rleft@(e2::e1::pl@res)) right + | None -> + loop [] (e2::e1::pl@res) [] + end + | (Newline | Newlines _ as e1)::tl -> + loop [] (e1::pl@res) tl + | e::tl -> + loop (e::pl) res tl + in + List.rev (loop [] [] lexemes) + + + let hr_m l = + assert_well_formed l; + let rec loop n = function + | ((Newlines _|Newline)::tl) | ([] as tl) -> + if n >= 3 then Some tl else None + | (Space|Spaces _)::tl -> + loop n tl + | Minus::tl -> + loop (n+1) tl + | Minuss x::tl -> + loop (x+2+n) tl + | _::_ -> + None + in loop 0 l + + let hr_s l = + assert_well_formed l; + let rec loop n = function + | ((Newline|Newlines _)::tl) | ([] as tl) -> + if n >= 3 then Some tl else None + | (Space|Spaces _)::tl -> + loop n tl + | Star::tl -> + loop (n+1) tl + | Stars x::tl -> + loop (x+2+n) tl + | _::_ -> + None + in loop 0 l + + let hr l = + match hr_m l with + | None -> hr_s l + | Some _ as tl -> tl + + (** [bcode] parses code that's delimited by backquote(s) *) + let bcode ?(default_lang=default_lang) r p l = + assert_well_formed l; + let e, tl = + match l with + | (Backquote|Backquotes _ as e)::tl -> e, tl + | _ -> failwith "Omd_parser.bcode is wrongly called" + in + let rec code_block accu = function + | [] -> + None + | Backquote::tl -> + if e = Backquote then + match accu with + | Newline::accu -> + Some(List.rev accu, tl) + | _ -> + Some(List.rev accu, tl) + else + code_block (Backquote::accu) tl + | (Backquotes _n as b)::tl -> + if e = b then + match accu with + | Newline::accu -> + Some(List.rev accu, tl) + | _ -> + Some(List.rev accu, tl) + else + code_block (b::accu) tl + | Tag(_, _)::tl -> + code_block accu tl + + | Backslash :: (Backquote as t) :: tl -> + code_block (t :: accu) tl + | Backslash :: Backquotes 0 :: tl -> (* \````... *) + code_block (Backquote :: accu) tl + | Backslash :: Backquotes n :: tl -> assert (n >= 0); (* \````... *) + code_block (Backquotes n :: accu) tl + + | e::tl -> + code_block (e::accu) tl + in + match code_block [] tl with + | None -> None + | Some(cb, l) -> + if List.exists (function (Newline|Newlines _) -> true | _ -> false) cb + && (match p with []|[Newline|Newlines _] -> true | _ -> false) + && (match e with Backquotes n when n > 0 -> true | _ -> false) + then + match cb with + | Word lang :: (Space|Spaces _) :: Newline :: tl + | Word lang :: Newline :: tl -> + let code = L.string_of_tokens tl in + Some(Code_block(lang, code) :: r, [Backquote], l) + | Word lang :: (Space|Spaces _) :: Newlines 0 :: tl + | Word lang :: Newlines 0 :: tl -> + let code = L.string_of_tokens(Newline::tl) in + Some(Code_block(lang, code) :: r, [Backquote], l) + | Word lang :: (Space|Spaces _) :: Newlines n :: tl + | Word lang :: Newlines n :: tl -> + let code = L.string_of_tokens (Newlines(n-1)::tl) in + Some(Code_block(lang, code) :: r, [Backquote], l) + | Newline :: tl -> + let code = L.string_of_tokens tl in + Some(Code_block(default_lang, code) :: r, [Backquote], l) + | _ -> + let rec loop collector cb = match cb with + | [] -> None + | Newline :: tl -> + let code = L.string_of_tokens tl in + Some(Code_block(L.string_of_tokens (List.rev collector), code) :: r, [Backquote], l) + | something :: tl -> loop (something :: collector) tl + in + loop [] cb + else + let clean_bcode s = + let rec loop1 i = + if i = String.length s then 0 + else match s.[i] with + | ' ' -> loop1(i+1) + | _ -> i + in + let rec loop2 i = + if i = -1 then String.length s + else match s.[i] with + | ' ' -> loop2(i-1) + | _ -> i+1 + in + match loop1 0, loop2 (String.length s - 1) with + | 0, n when n = String.length s - 1 -> s + | i, n -> String.sub s i (n-i) + in + let code = L.string_of_tokens cb in + if debug then + eprintf "(OMD) clean_bcode %S => %S\n%!" code (clean_bcode code); + Some(Code(default_lang, clean_bcode code) :: r, [Backquote], l) + + + exception NL_exception + exception Premature_ending + + (* !!DO NOT DELETE THIS!! + The program that generates the generated part that follows right after. + List.iter (fun (a,b,c) -> + print_endline ("let read_until_"^a^" ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: ("^b^" as b) :: tl -> + loop (b::accu) n tl + | Backslash :: ("^b^"s 0) :: tl -> + loop ("^b^"::accu) n ("^b^"::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl" + ^(if c<>"" then " + | Backslash :: ("^c^" as b) :: tl -> + loop (b::accu) n tl + | Backslash :: ("^c^"s 0) :: tl -> + loop ("^c^"::accu) n ("^c^"::tl) + | "^c^" as e :: tl -> + loop (e::accu) (n+1) tl + | "^c^"s x as e :: tl -> + loop (e::accu) (n+x+2) tl + " else "")^ + " | "^b^" as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | "^b^"s 0 :: tl -> + if n = 0 then + List.rev accu, "^b^"::tl + else + loop ("^b^"::accu) (n-1) ("^b^"::tl) + | "^b^"s x :: tl -> + if n = 0 then + List.rev accu, "^b^"s(x-1)::tl + else + loop + (match accu with + | "^b^"::accu -> "^b^"s(0)::accu + | "^b^"s x::accu -> "^b^"s(x+1)::accu + | _ -> "^b^"::accu) + (n-1) + ("^b^"s(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf \"Omd_parser.read_until_"^a^" %S bq=%b no_nl=%b\\n%!\" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf \"Omd_parser.read_until_"^a^" %S bq=%b no_nl=%b => %S\\n%!\" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res +")) + + [ "gt", "Greaterthan", "Lessthan"; + "lt", "Lessthan", ""; + "cparenth", "Cparenthesis", "Oparenthesis"; + "oparenth", "Oparenthesis", ""; + "dq", "Doublequote", ""; + "q", "Quote", ""; + "obracket", "Obracket", ""; + "cbracket", "Cbracket", "Obracket"; + "space", "Space", ""; + ] + *) + + (* begin generated part *) + +let read_until_gt ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Greaterthan as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Greaterthans 0) :: tl -> + loop (Greaterthan::accu) n (Greaterthan::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl + | Backslash :: (Lessthan as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Lessthans 0) :: tl -> + loop (Lessthan::accu) n (Lessthan::tl) + | Lessthan as e :: tl -> + loop (e::accu) (n+1) tl + | Lessthans x as e :: tl -> + loop (e::accu) (n+x+2) tl + | Greaterthan as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Greaterthans 0 :: tl -> + if n = 0 then + List.rev accu, Greaterthan::tl + else + loop (Greaterthan::accu) (n-1) (Greaterthan::tl) + | Greaterthans x :: tl -> + if n = 0 then + List.rev accu, Greaterthans(x-1)::tl + else + loop + (match accu with + | Greaterthan::accu -> Greaterthans(0)::accu + | Greaterthans x::accu -> Greaterthans(x+1)::accu + | _ -> Greaterthan::accu) + (n-1) + (Greaterthans(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_gt %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_gt %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_lt ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Lessthan as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Lessthans 0) :: tl -> + loop (Lessthan::accu) n (Lessthan::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Lessthan as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Lessthans 0 :: tl -> + if n = 0 then + List.rev accu, Lessthan::tl + else + loop (Lessthan::accu) (n-1) (Lessthan::tl) + | Lessthans x :: tl -> + if n = 0 then + List.rev accu, Lessthans(x-1)::tl + else + loop + (match accu with + | Lessthan::accu -> Lessthans(0)::accu + | Lessthans x::accu -> Lessthans(x+1)::accu + | _ -> Lessthan::accu) + (n-1) + (Lessthans(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_lt %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_lt %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_cparenth ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Cparenthesis as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Cparenthesiss 0) :: tl -> + loop (Cparenthesis::accu) n (Cparenthesis::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl + | Backslash :: (Oparenthesis as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Oparenthesiss 0) :: tl -> + loop (Oparenthesis::accu) n (Oparenthesis::tl) + | Oparenthesis as e :: tl -> + loop (e::accu) (n+1) tl + | Oparenthesiss x as e :: tl -> + loop (e::accu) (n+x+2) tl + | Cparenthesis as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Cparenthesiss 0 :: tl -> + if n = 0 then + List.rev accu, Cparenthesis::tl + else + loop (Cparenthesis::accu) (n-1) (Cparenthesis::tl) + | Cparenthesiss x :: tl -> + if n = 0 then + List.rev accu, Cparenthesiss(x-1)::tl + else + loop + (match accu with + | Cparenthesis::accu -> Cparenthesiss(0)::accu + | Cparenthesiss x::accu -> Cparenthesiss(x+1)::accu + | _ -> Cparenthesis::accu) + (n-1) + (Cparenthesiss(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_cparenth %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_cparenth %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_oparenth ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Oparenthesis as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Oparenthesiss 0) :: tl -> + loop (Oparenthesis::accu) n (Oparenthesis::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Oparenthesis as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Oparenthesiss 0 :: tl -> + if n = 0 then + List.rev accu, Oparenthesis::tl + else + loop (Oparenthesis::accu) (n-1) (Oparenthesis::tl) + | Oparenthesiss x :: tl -> + if n = 0 then + List.rev accu, Oparenthesiss(x-1)::tl + else + loop + (match accu with + | Oparenthesis::accu -> Oparenthesiss(0)::accu + | Oparenthesiss x::accu -> Oparenthesiss(x+1)::accu + | _ -> Oparenthesis::accu) + (n-1) + (Oparenthesiss(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_oparenth %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_oparenth %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_dq ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Doublequote as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Doublequotes 0) :: tl -> + loop (Doublequote::accu) n (Doublequote::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Doublequote as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Doublequotes 0 :: tl -> + if n = 0 then + List.rev accu, Doublequote::tl + else + loop (Doublequote::accu) (n-1) (Doublequote::tl) + | Doublequotes x :: tl -> + if n = 0 then + List.rev accu, Doublequotes(x-1)::tl + else + loop + (match accu with + | Doublequote::accu -> Doublequotes(0)::accu + | Doublequotes x::accu -> Doublequotes(x+1)::accu + | _ -> Doublequote::accu) + (n-1) + (Doublequotes(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_dq %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_dq %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_q ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Quote as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Quotes 0) :: tl -> + loop (Quote::accu) n (Quote::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Quote as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Quotes 0 :: tl -> + if n = 0 then + List.rev accu, Quote::tl + else + loop (Quote::accu) (n-1) (Quote::tl) + | Quotes x :: tl -> + if n = 0 then + List.rev accu, Quotes(x-1)::tl + else + loop + (match accu with + | Quote::accu -> Quotes(0)::accu + | Quotes x::accu -> Quotes(x+1)::accu + | _ -> Quote::accu) + (n-1) + (Quotes(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_q %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_q %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_obracket ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Obracket as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Obrackets 0) :: tl -> + loop (Obracket::accu) n (Obracket::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Obracket as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Obrackets 0 :: tl -> + if n = 0 then + List.rev accu, Obracket::tl + else + loop (Obracket::accu) (n-1) (Obracket::tl) + | Obrackets x :: tl -> + if n = 0 then + List.rev accu, Obrackets(x-1)::tl + else + loop + (match accu with + | Obracket::accu -> Obrackets(0)::accu + | Obrackets x::accu -> Obrackets(x+1)::accu + | _ -> Obracket::accu) + (n-1) + (Obrackets(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_obracket %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_obracket %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_cbracket ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Cbracket as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Cbrackets 0) :: tl -> + loop (Cbracket::accu) n (Cbracket::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl + | Backslash :: (Obracket as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Obrackets 0) :: tl -> + loop (Obracket::accu) n (Obracket::tl) + | Obracket as e :: tl -> + loop (e::accu) (n+1) tl + | Obrackets x as e :: tl -> + loop (e::accu) (n+x+2) tl + | Cbracket as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Cbrackets 0 :: tl -> + if n = 0 then + List.rev accu, Cbracket::tl + else + loop (Cbracket::accu) (n-1) (Cbracket::tl) + | Cbrackets x :: tl -> + if n = 0 then + List.rev accu, Cbrackets(x-1)::tl + else + loop + (match accu with + | Cbracket::accu -> Cbrackets(0)::accu + | Cbrackets x::accu -> Cbrackets(x+1)::accu + | _ -> Cbracket::accu) + (n-1) + (Cbrackets(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_cbracket %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_cbracket %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + +let read_until_space ?(bq=false) ?(no_nl=false) l = + assert_well_formed l; + let rec loop accu n = function + | Backslash :: (Space as b) :: tl -> + loop (b::accu) n tl + | Backslash :: (Spaces 0) :: tl -> + loop (Space::accu) n (Space::tl) + | Backslashs 0 :: tl -> + loop (Backslash::accu) n tl + | Backslashs 1 :: tl -> + loop (Backslash::accu) n (Backslash::tl) + | Backslashs 2 :: tl -> + loop (Backslashs 0::accu) n tl + | (Backslashs x) :: tl -> + if x mod 2 = 0 then + loop (Backslashs(x/2-1)::accu) n tl + else + loop (Backslashs(x/2-1)::accu) n (Backslash::tl) + | (Backquote|Backquotes _ as e)::tl as l -> + if bq then + match bcode [] [] l with + | None -> loop (e::accu) n tl + | Some (r, _, tl) -> + loop (* not very pretty kind of hack *) + (List.rev(L.lex(Omd_backend.markdown_of_md r))@accu) + n + tl + else + loop (e::accu) n tl | Space as e :: tl -> + if n = 0 then + List.rev accu, tl + else + loop (e::accu) (n-1) tl + | Spaces 0 :: tl -> + if n = 0 then + List.rev accu, Space::tl + else + loop (Space::accu) (n-1) (Space::tl) + | Spaces x :: tl -> + if n = 0 then + List.rev accu, Spaces(x-1)::tl + else + loop + (match accu with + | Space::accu -> Spaces(0)::accu + | Spaces x::accu -> Spaces(x+1)::accu + | _ -> Space::accu) + (n-1) + (Spaces(x-1)::tl) + | (Newline|Newlines _ as e)::tl -> + if no_nl then + raise NL_exception + else + loop (e::accu) n tl + | e::tl -> + loop (e::accu) n tl + | [] -> + raise Premature_ending + in + if debug then + eprintf "Omd_parser.read_until_space %S bq=%b no_nl=%b\n%!" (L.string_of_tokens l) bq no_nl; + let res = loop [] 0 l in + if debug then + eprintf "Omd_parser.read_until_space %S bq=%b no_nl=%b => %S\n%!" (L.string_of_tokens l) bq no_nl (L.string_of_tokens (fst res)); + res + (* /end generated part *) + + let read_until_newline l = + assert_well_formed l; + let rec loop accu n = + function + | ((Backslash as a)) :: ((Newline as b)) :: tl -> + loop (b :: a :: accu) n tl + | Backslash :: Newlines 0 :: tl -> + loop (Newline :: Backslash :: accu) n (Newline :: tl) + | ((Backslashs 0 as e)) :: tl -> loop (e :: accu) n tl + | ((Backslashs x as e)) :: tl -> + if (x mod 2) = 0 + then loop (e :: accu) n tl + else loop ((Backslashs (x - 1)) :: accu) n (Backslash :: tl) + | ((Newline as e)) :: tl -> + if n = 0 then ((List.rev accu), tl) else loop (e :: accu) (n - 1) tl + | Newlines 0 :: tl -> + if n = 0 + then ((List.rev accu), (Newline :: tl)) + else loop (Newline :: accu) (n - 1) (Newline :: tl) + | Newlines n :: tl -> ((List.rev accu), ((Newlines (n - 1)) :: tl)) + | e :: tl -> loop (e :: accu) n tl + | [] -> raise Premature_ending + in loop [] 0 l + + (* H1, H2, H3, ... *) + let read_title (main_loop:main_loop) n r _previous lexemes = + let title, rest = + let rec loop accu = function + | Backslash::Hash::tl -> + loop (Hash::Backslash::accu) tl + | Backslashs(n)::Hash::tl when n mod 2 = 1 -> + loop (Hash::Backslashs(n-1)::accu) tl + | Backslash::Hashs(h)::tl -> + begin match tl with + | [] + | (Space|Spaces _)::(Newline|Newlines _)::_ + | (Newline|Newlines _)::_ -> + loop (Hash::Backslash::accu) + ((if h = 0 then Hash else Hashs(h-1))::tl) + | _ -> + loop (Hashs(h)::Backslash::accu) tl + end + | Backslashs(n)::Hashs(h)::tl when n mod 2 = 1 -> + begin match tl with + | [] + | (Space|Spaces _)::(Newline|Newlines _)::_ + | (Newline|Newlines _)::_ -> + loop (Hash::Backslashs(n)::accu) + ((if h = 0 then Hash else Hashs(h-1))::tl) + | _ -> + loop (Hashs(h)::Backslashs(n)::accu) tl + end + | (Hash|Hashs _) :: ((Newline|Newlines _) :: _ as l) + | (Hash|Hashs _) :: (Space|Spaces _) :: ((Newline|Newlines _)::_ as l) + | ((Newline|Newlines _) :: _ as l) + | ([] as l) + | (Space|Spaces _) :: (Hash|Hashs _) :: ((Newline|Newlines _) :: _ as l) + | (Space|Spaces _) :: (Hash|Hashs _) :: (Space|Spaces _) + :: ((Newline|Newlines _)::_ as l) + | (Space|Spaces _) :: ((Newline|Newlines _) :: _ as l) + | (Space|Spaces _) :: ([] as l) -> + main_loop [] [] (List.rev accu), l + | [Hash|Hashs _] + | [(Space|Spaces _); Hash|Hashs _] + | [(Space|Spaces _); (Hash|Hashs _); (Space|Spaces _)] -> + main_loop [] [] (List.rev accu), [] + | x::tl -> + loop (x::accu) tl + in + loop [] lexemes + in + match n with + | 1 -> Some(H1 title :: r, [Newline], rest) + | 2 -> Some(H2 title :: r, [Newline], rest) + | 3 -> Some(H3 title :: r, [Newline], rest) + | 4 -> Some(H4 title :: r, [Newline], rest) + | 5 -> Some(H5 title :: r, [Newline], rest) + | 6 -> Some(H6 title :: r, [Newline], rest) + | _ -> None + + let maybe_extension extensions r p l = + match extensions with + | [] -> None + | _ -> + List.fold_left + (function + | None -> + (fun f -> f#parser_extension r p l) + | Some(nr, np, nl) as e -> + (fun f -> match f#parser_extension nr np nl with + | None -> e + | Some _ as k -> k) + ) + None + extensions + + (* blockquotes *) + let emailstyle_quoting (main_loop:main_loop) r _p lexemes = + assert_well_formed lexemes; + let rec loop block cl = + function + | Newline::Greaterthan::(Newline::_ as tl) -> + loop (Newline::cl@block) [] tl + | Newline::Greaterthan::Space::tl -> + loop (Newline::cl@block) [] tl + | Newline::Greaterthan::Spaces 0::tl -> + loop (Newline::cl@block) [Space] tl + | Newline::Greaterthan::Spaces n::tl -> + assert(n>0); + loop (Newline::cl@block) [Spaces(n-1)] tl + + (* multi paragraph blockquotes with empty lines *) + | Newlines 0::Greaterthan::Space::tl -> + loop (Newlines 0::cl@block) [] tl + | Newlines 0::Greaterthan::Spaces 0::tl -> + loop (Newlines 0::cl@block) [Space] tl + | Newlines 0::Greaterthan::Spaces n::tl -> + assert(n>0); + loop (Newlines 0::cl@block) [Spaces(n-1)] tl + + | (Newlines _::_ as l) | ([] as l) -> fix(List.rev(cl@block)), l + | e::tl -> loop block (e::cl) tl + in + match loop [] [] lexemes with + | (Newline|Newlines _)::block, tl -> + if debug then + eprintf "(OMD) Omd_parser.emailstyle_quoting %S\n%!" + (L.string_of_tokens block); + Some((Blockquote(main_loop [] [] block)::r), [Newline], tl) + | _ -> + None + + + (* maybe a reference *) + let maybe_reference (main_loop:main_loop) rc r _p l = + assert_well_formed l; + (* this function is called when we know it's not a link although + it started with a '[' *) + (* So it could be a reference or a link definition. *) + let maybe_ref l = + let text, remains = read_until_cbracket ~bq:true l in + (* check that there is no ill-placed open bracket *) + if (try ignore(read_until_obracket ~bq:true text); true + with Premature_ending -> false) then + raise Premature_ending; (* <-- ill-placed open bracket *) + let blank, remains = read_until_obracket ~bq:true remains in + (* check that there are no unwanted characters between CB and OB. *) + if eat (let flag = ref true in + function (* allow only a space, multiple spaces, or a newline *) + | Newline -> !flag && (flag := false; true) + | (Space|Spaces _) -> !flag && (flag := false; true) + | _ -> false) blank <> [] then + raise Premature_ending (* <-- not a regular reference *) + else + match read_until_cbracket ~bq:true remains with + | [], remains -> + let fallback = extract_fallback main_loop remains (Obracket::l) in + let id = L.string_of_tokens text in (* implicit anchor *) + Some(((Ref(rc, id, id, fallback))::r), [Cbracket], remains) + | id, remains -> + let fallback = extract_fallback main_loop remains (Obracket::l) in + Some(((Ref(rc, L.string_of_tokens id, + L.string_of_tokens text, fallback))::r), + [Cbracket], remains) + in + let maybe_nonregular_ref l = + let text, remains = read_until_cbracket ~bq:true l in + (* check that there is no ill-placed open bracket *) + if (try ignore(read_until_obracket ~bq:true text); true + with Premature_ending -> false) then + raise Premature_ending; (* <-- ill-placed open bracket *) + let fallback = extract_fallback main_loop remains (Obracket::l) in + let id = L.string_of_tokens text in (* implicit anchor *) + Some(((Ref(rc, id, id, fallback))::r), [Cbracket], remains) + in + let maybe_def l = + match read_until_cbracket ~bq:true l with + | _, [] -> raise Premature_ending + | id, (Colon::(Space|Spaces _)::remains) + | id, (Colon::remains) -> + begin + match + fsplit + ~f:(function + | (Space|Spaces _|Newline|Newlines _):: _ as l -> Split([], l) + | _::_ -> Continue + | [] -> Split([],[])) + remains + with + | None | Some([], _) -> raise Premature_ending + | Some(url, remains) -> + let title, remains = + match + eat + (function | (Space|Spaces _|Newline|Newlines _) -> true + | _ -> false) + remains + with + | Doublequotes(0)::tl -> [], tl + | Doublequote::tl -> read_until_dq ~bq:true tl + | Quotes(0)::tl -> [], tl + | Quote::tl -> read_until_q ~bq:true tl + | Oparenthesis::tl-> read_until_cparenth ~bq:true tl + | l -> [], l + in + let url = + let url = L.string_of_tokens url in + if String.length url > 2 && url.[0] = '<' + && url.[String.length url - 1] = '>' then + String.sub url 1 (String.length url - 2) + else + url + in + rc#add_ref (L.string_of_tokens id) (L.string_of_tokens title) url; + Some(r, [Newline], remains) + end + | _ -> raise Premature_ending + in + try + maybe_ref l + with | Premature_ending | NL_exception -> + try + maybe_def l + with + | Premature_ending | NL_exception -> + try + maybe_nonregular_ref l + with + | Premature_ending | NL_exception -> + None + + + (** maybe a link *) + let maybe_link (main_loop:main_loop) r _p l = + if debug then eprintf "(OMD) # maybe_link\n"; + assert_well_formed l; + let read_url name l = + if debug then + eprintf "(OMD) # maybe_link>read_url %S\n" (L.string_of_tokens l); + try + let l_cp, r_cp = + read_until_cparenth ~no_nl:true ~bq:false l + in + if debug then eprintf "(OMD) maybe_link >> l_cp=%S r_cp=%S\n%!" + (L.string_of_tokens l_cp) + (L.string_of_tokens r_cp); + try + let l_dq, r_dq = + read_until_dq ~no_nl:true ~bq:false l + in + if debug then eprintf "(OMD) maybe_link >> l_dq=%S r_dq=%S\n%!" + (L.string_of_tokens l_dq) + (L.string_of_tokens r_dq); + (* maybe title *) + if List.length l_cp > List.length l_dq then (* title *) + begin + if debug then eprintf "(OMD) maybe_link >> title\n%!"; + let url = + match List.rev l_dq with + | (Newline|Space|Spaces _)::(Newline|Space|Spaces _)::tl + | (Newline|Space|Spaces _)::tl -> + L.string_of_tokens (List.rev tl) + | _ -> + L.string_of_tokens l_dq + in + let title, rest = read_until_dq ~no_nl:false ~bq:false r_dq in + let rest = snd(read_until_cparenth rest) in + let title = L.string_of_tokens title in + Some(Url(url, name, title) :: r, [Cparenthesis], rest) + end + else (* no title *) + raise Premature_ending + with NL_exception | Premature_ending -> (* no title *) + begin + if debug then eprintf "(OMD) maybe_link >> no title\n%!"; + let url = match List.rev l_cp with + | (Newline|Space|Spaces _)::(Newline|Space|Spaces _)::tl + | (Newline|Space|Spaces _)::tl -> List.rev tl + | _ -> l_cp + in + let title, rest = [], r_cp in + let url = L.string_of_tokens url in + let title = L.string_of_tokens title in + Some(Url(url, name, title) :: r, [Cparenthesis], rest) + end + with NL_exception | Premature_ending -> + None + in + let read_name l = + (* it's not really the "name" of a URL but what + corresponds to the inner HTML of an HTML 'A' tag *) + if debug then eprintf "(OMD) # maybe_link> read_name\n"; + try + match read_until_cbracket ~bq:true l with + | name, (Oparenthesis::tl) -> + read_url (main_loop [] [Obracket] name) (eat_blank tl) + | name, (Oparenthesiss 0::tl) -> + read_url (main_loop [] [Obracket] name) (Oparenthesis::tl) + | name, (Oparenthesiss n::tl) -> + read_url (main_loop [] [Obracket] name) (Oparenthesiss(n-1)::tl) + | _ -> + None + with Premature_ending | NL_exception -> None + in + read_name l + + + let has_paragraphs l = + (* Has at least 2 consecutive newlines. *) + List.exists (function Newlines _ -> true | _ -> false) l + + let parse_list (main_loop:main_loop) r _p l = + assert_well_formed l; + if debug then begin + eprintf "(OMD) parse_list r=(%s) p=(%s) l=(%s)\n%!" + "" (* (Omd_backend.sexpr_of_md (List.rev r)) *) + "" (* (destring_of_tl p) *) + (L.destring_of_tokens ~limit:40 l); + end; + let module UO = struct type ordered = O | U end in + let open UO in + if debug then + eprintf "(OMD) parse_list: l=(%s)\n%!" (L.destring_of_tokens l); + let end_of_item (indent:int) l : tok split_action = match l with + | [] -> + Split([],[]) + | Newlines 0 :: ((Spaces n) :: Greaterthan :: (Space | Spaces _) :: _ + as s) -> + assert(n>=0); + if n+2 = indent+4 then (* blockquote *) + match unindent (n+2) (Newline::s) with + | Newline::block, rest -> + Continue_with(List.rev(Newlines(1)::block), rest) + | Newlines n::block, rest -> + Continue_with(List.rev(Newlines(n+2)::block), rest) + | block, rest -> + Continue_with(Newlines 0::block, rest) + else if n+2 >= indent+8 then (* code inside item *) + match unindent (indent+4) (Newline::s) with + | Newline::block, rest -> + Continue_with(List.rev(Newlines(1)::block), rest) + | Newlines n::block, rest -> + Continue_with(List.rev(Newlines(n+2)::block), rest) + | block, rest -> + Continue_with(Newlines 0::block, rest) + else + Split([], l) + | Newlines 0 :: (Spaces n :: _ as s) -> + assert(n>=0); + if n+2 >= indent+8 then (* code inside item *) + match unindent (indent+4) (Newline::s) with + | Newline::block, rest -> + Continue_with(List.rev(Newlines(0)::block), rest) + | Newlines n::block, rest -> + Continue_with(List.rev(Newlines(n+1)::block), rest) + | block, rest -> + Continue_with(Newline::block, rest) + else if n+2 >= indent+4 then (* new paragraph inside item *) + match unindent (indent+4) (Newline::s) with + | Newline::block, rest -> + Continue_with(List.rev(Newlines(1)::block), rest) + | Newlines n::block, rest -> + Continue_with(List.rev(Newlines(n+2)::block), rest) + | block, rest -> + Continue_with(Newlines 0::block, rest) + else + Split([], l) + | (Newlines _) :: _ -> (* n > 0 *) + (* End of item, stop *) + Split([], l) + | Newline :: + ( + ((Space|Spaces _) :: (Star|Minus|Plus) :: (Space|Spaces _):: _) + | ((Space|Spaces _) :: Number _ :: Dot :: (Space|Spaces _) :: _) + | ((Star|Minus|Plus) :: (Space|Spaces _):: _) + | (Number _ :: Dot :: (Space|Spaces _) :: _) + as tl) -> + Split([Newline], tl) + | Newline :: (Space | Spaces _) :: Newline :: tl -> + (* A line with spaces shouldn't interfere here, + which is about exactly 2 consecutive newlines, + so we rewrite the head of the lexing stream. *) + Continue_with([], Newlines 0 :: tl) + | Newline :: (Space | Spaces _) :: (Newlines _) :: _ -> + (* A line with spaces shouldn't interfere here, + which is about at least 3 consecutive newlines, + so we stop. *) + Split([], l) + | Newline :: (Spaces _ as s) :: tl -> + Continue_with + ([s; + Tag("parse_list/remember spaces", + object + method parser_extension r p = + function Spaces _::tl -> Some(r,p,Space::tl) + | _ -> None + method to_string = "" + end); + Newline], + tl) + | Newline :: (Space as s) :: tl -> + Continue_with + ([s; + Tag("parse_list/remember space", + object + method parser_extension r p = + function (Space|Spaces _)::tl -> Some(r,p,Space::tl) + | _ -> None + method to_string = "" + end); + Newline], + tl) + | _::_ -> + Continue + in + let rev_to_t l = + assert_well_formed l; + (* Newlines at the end of items have no meaning (except to end the + item which is expressed by the constructor already). *) + let l = match l with (Newline | Newlines _) :: tl -> tl | _ -> l in + main_loop [] [Newline] (List.rev l) + in + let add (sublist:element) items = + if debug then eprintf "(OMD) add\n%!"; + match items with + | [] -> assert false + | (O,indents,item)::tl -> + (O,indents,(item@[sublist]))::tl + | (U,indents,item)::tl -> + (U,indents,(item@[sublist]))::tl + in + let make_up ~p items : Omd_representation.element = + if debug then eprintf "(OMD) make_up p=%b\n%!" p; + let items = List.rev items in + match items with + | (U,_,_item)::_ -> + if p then + Ulp(List.map (fun (_,_,i) -> i) items) + else + Ul(List.map (fun (_,_,i) -> i) items) + | (O,_,_item)::_ -> + if p then + Olp(List.map (fun (_,_,i) -> i) items) + else + Ol(List.map (fun (_,_,i) -> i) items) + | [] -> + failwith "make_up called with []" (* assert false *) + in + let rec list_items ~p indents items l = + if debug then eprintf "(OMD) list_items: p=%b l=(%s)\n%!" + p (L.destring_of_tokens l); + match l with + (* no more list items *) + | [] -> + make_up ~p items, l + (* more list items *) + (* new unordered items *) + | (Star|Minus|Plus)::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item 0) tl with + | None -> + make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + if debug then + eprintf "(OMD) (2346) new_item=%S\n%!" + (L.destring_of_tokens new_item); + match indents with + | [] -> + assert(items = []); + list_items ~p [0] ((U,[0], rev_to_t new_item)::items) rest + | 0::_ -> + list_items ~p indents ((U,indents,rev_to_t new_item)::items) rest + | _::_ -> + make_up ~p items, l + end + | Space::(Star|Minus|Plus)::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item 1) tl with + | None -> make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + match indents with + | [] -> + assert(items = []); + list_items ~p [1] ((U,[1],rev_to_t new_item)::items) rest + | 1::_ -> + list_items ~p indents ((U,indents,rev_to_t new_item)::items) rest + | i::_ -> + if i > 1 then + make_up ~p items, l + else (* i < 1 : new sub list*) + let sublist, remains = + list_items ~p (1::indents) + [(U,1::indents,rev_to_t new_item)] rest + in + list_items ~p indents (add sublist items) remains + end + | Spaces n::(Star|Minus|Plus)::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item (n+2)) tl with + | None -> + make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + match indents with + | [] -> + if debug then + eprintf "(OMD) spaces[] l=(%S)\n%!" (L.string_of_tokens l); + assert(items = []); (* a�e... listes mal form�es ?! *) + list_items ~p [n+2] ((U,[n+2],rev_to_t new_item)::items) rest + | i::_ -> + if debug then eprintf "(OMD) spaces(%d::_) n=%d l=(%S)\n%!" + i n (L.string_of_tokens l); + if i = n + 2 then + let items = (U,indents,rev_to_t new_item) :: items in + list_items ~p indents items rest + else if i < n + 2 then + let sublist, remains = + list_items ~p ((n+2)::indents) + [(U,(n+2)::indents,rev_to_t new_item)] + rest + in + list_items ~p indents (add sublist items) remains + else (* i > n + 2 *) + make_up ~p items, l + end + (* new ordered items *) + | Number _::Dot::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item 0) tl with + | None -> + make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + assert_well_formed new_item; + match indents with + | [] -> + assert(items = []); + list_items ~p [0] ((O,[0],rev_to_t new_item)::items) rest + | 0::_ -> + list_items ~p indents ((O,indents,rev_to_t new_item)::items) rest + | _::_ -> + make_up ~p items, l + end + | Space::Number _::Dot::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item 1) tl with + | None -> make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + match indents with + | [] -> + assert(items = []); + list_items ~p [1] ((O,[1],rev_to_t new_item)::items) rest + | 1::_ -> + list_items ~p indents ((O,indents,rev_to_t new_item)::items) rest + | i::_ -> + if i > 1 then + make_up ~p items, l + else (* i < 1 : new sub list*) + let sublist, remains = + list_items ~p (1::indents) + [(O,1::indents,rev_to_t new_item)] rest + in + list_items ~p:p indents (add sublist items) remains + end + | Spaces n::Number _::Dot::(Space|Spaces _)::tl -> + begin + match fsplit_rev ~f:(end_of_item (n+2)) tl with + | None -> + make_up ~p items, l + | Some(new_item, rest) -> + let p = p || has_paragraphs new_item in + match indents with + | [] -> + if debug then eprintf "(OMD) spaces[] l=(%S)\n%!" + (L.string_of_tokens l); + assert(items = []); (* a�e... listes mal form�es ?! *) + list_items ~p [n+2] ((O,[n+2],rev_to_t new_item)::items) rest + | i::_ -> + if debug then eprintf "(OMD) spaces(%d::_) n=%d l=(%S)\n%!" + i n (L.string_of_tokens l); + if i = n + 2 then + list_items ~p indents ((O,indents,rev_to_t new_item)::items) + rest + else if i < n + 2 then + let sublist, remains = + list_items ~p + ((n+2)::indents) + [(O,(n+2)::indents,rev_to_t new_item)] + rest + in + list_items ~p:p indents (add sublist items) remains + else (* i > n + 2 *) + make_up ~p items, l + end + (* *) + | Newlines 0::((Star|Minus|Plus)::(Space|Spaces _)::_ as l) + | Newlines 0::(Number _::Dot::(Space|Spaces _)::_ as l) + | Newlines 0::((Space|Spaces _)::Star::(Space|Spaces _)::_ as l) + | Newlines 0::((Space|Spaces _)::Number _::Dot::(Space|Spaces _)::_ as l) + -> + list_items ~p:true indents items l + | _ -> + if debug then + begin + let rec string_of_items items = + match items with + | [] -> "" + | (O,indent::_,item)::tl -> + sprintf "(O,i=%d,%S)" (indent) (Omd_backend.html_of_md item) + ^ string_of_items tl + | (U,indent::_,item)::tl -> + sprintf "(U,i=%d,%S)" (indent) (Omd_backend.html_of_md item) + ^ string_of_items tl + | _ -> "(weird)" + in + eprintf "(OMD) NALI parse_list: l=(%S) items=%s\n%!" + (L.string_of_tokens l) (string_of_items items) + end; + (* not a list item *) + make_up ~p items, l + in + match list_items ~p:false [] [] l with + | rp, l -> + rp::r, [Newline], l + + + + let icode ?(default_lang=default_lang) r _p l = + assert_well_formed l; + (* indented code: returns (r,p,l) where r is the result, p is the + last thing read, l is the remains *) + let dummy_tag = Tag("dummy_tag", + object + method to_string = "" + method parser_extension = fun _r _p _l -> None + end) in + let accu = Buffer.create 64 in + let rec loop s tl = match s, tl with + | (Newline|Newlines _ as p), (Space|Spaces(0|1))::_ -> + (* 1, 2 or 3 spaces. *) + (* -> Return what's been found as code because what follows isn't. *) + Code_block(default_lang, Buffer.contents accu) :: r, [p], tl + | (Newline|Newlines _ as p), Spaces(n)::tl -> + assert(n>0); + (* At least 4 spaces, it's still code. *) + Buffer.add_string accu (L.string_of_token p); + loop + (if n >= 4 then Spaces(n-4) else if n = 3 then Space else dummy_tag) + tl + | (Newline|Newlines _ as p), (_not_spaces::_ as tl) -> (* stop *) + Code_block(default_lang, Buffer.contents accu) :: r, [p], tl + (* -> Return what's been found as code because it's no more code. *) + | p, e::tl -> + Buffer.add_string accu (L.string_of_token p); + (* html entities are to be converted later! *) + loop e tl + | p, [] -> + Buffer.add_string accu (L.string_of_token p); + Code_block(default_lang, Buffer.contents accu)::r, [p], [] + in + match l with + | Spaces n::tl -> + if n >= 4 then + Some(loop (Spaces(n-4)) tl) + else if n = 3 then + Some(loop Space tl) + else Some(loop dummy_tag tl) + | _ -> assert false + + + (* Returns [(r,p,l)] where [r] is the result, [p] is the last thing + read, and [l] is what remains. *) + let spaces_at_beginning_of_line main_loop default_lang n r previous lexemes = + assert_well_formed lexemes; + assert (n > 0); + if n <= 3 then ( + match lexemes with + | (Star|Minus|Plus) :: (Space|Spaces _) :: _ -> + (* unordered list *) + parse_list main_loop r [] (L.make_space n::lexemes) + | (Number _)::Dot::(Space|Spaces _)::_tl -> + (* ordered list *) + parse_list main_loop r [] (L.make_space n::lexemes) + | [] + | (Newline|Newlines _) :: _ -> (* blank line, skip spaces *) + r, previous, lexemes + | _::_ -> + Text (" ")::r, previous, lexemes + ) + else ( (* n>=4, blank line or indented code *) + match lexemes with + | [] | (Newline|Newlines _) :: _ -> r, previous, lexemes + | _ -> + match + icode ~default_lang r [Newline] (L.make_space n :: lexemes) + with + | Some(r,p,l) -> r,p,l + | None -> + if debug then + eprintf "(OMD) Omd_parser.icode or \ + Omd_parser.main_loop is broken\n%!"; + assert false + ) + + let spaces_not_at_beginning_of_line ?(html=false) n r lexemes = + assert_well_formed lexemes; + assert (n > 0); + if n = 1 then + (Text " "::r), [Space], lexemes + else ( + match lexemes with + | Newline :: tl when not html -> + if debug then + eprintf + "(OMD) 2 or more spaces before a newline, eat the newline\n%!"; + Br::r, [Spaces(n-2)], tl + | Newlines k :: tl when not html -> + if debug then + eprintf + "(OMD) 2 or more spaces before a newline, eat 1 newline"; + let newlines = if k = 0 then Newline else Newlines(k-1) in + Br::r, [Spaces(n-2)], newlines :: tl + | _ -> + assert (n>1); + (Text (String.make n ' ')::r), [Spaces(n-2)], lexemes + ) + + + let maybe_autoemail r _p l = + assert_well_formed l; + match l with + | Lessthan::tl -> + begin + match + fsplit ~excl:(function (Newline|Newlines _|Space|Spaces _) :: _-> true + | [] -> true + | _ -> false) + ~f:(function At::tl -> Split([],tl) | _ -> Continue) + tl + with + | None -> None + | Some(left, right) -> + match + fsplit + ~excl:(function + | (Newline|Newlines _|Space|Spaces _) :: _-> true + | [] -> true + | _ -> false) + ~f:(function Greaterthan::tl -> Split([],tl) + | Greaterthans 0::tl -> Split([],Greaterthan::tl) + | Greaterthans n::tl -> Split([],Greaterthans(n-1)::tl) + | _ -> Continue) + right + with + | None -> None + | Some(domain, tl) -> + let email = L.string_of_tokens left + ^ "@" ^ L.string_of_tokens domain in + Some(Url("mailto:"^email,[Text email],"")::r,[Greaterthan],tl) + end + | _ -> failwith "Omd_parser.maybe_autoemail: wrong use of the function." + + let is_hex s = + String.length s > 1 + && (s.[0] = 'X' || s.[0] = 'x') + && (let rec loop i = + i = String.length s + || + (match s.[i] with + | '0' .. '9' | 'A' .. 'F' | 'a' .. 'f' -> + loop (succ i) + | _ -> false) + in loop 1) + + let mediatypetextomd : string list ref = ref [] + + let filter_text_omd_rev l = + let rec loop b r = function + | [] -> if b then r else l + | ("media:type", Some "text/omd")::tl -> + loop true r tl + | e::tl -> + loop b (e::r) tl + in + loop false [] l + + exception Orphan_closing of string * l * l + + let rec main_impl_rev ~html (r:r) (previous:p) (lexemes:l) = + (* if debug then eprintf "(OMD) main_impl_rev html=%b\n%!" html; *) + assert_well_formed lexemes; + if debug then + eprintf "(OMD) main_impl_rev html=%b r=%s p=(%s) l=(%s)\n%!" + html + (Omd_backend.sexpr_of_md (List.rev r)) + (L.destring_of_tokens previous) + (L.destring_of_tokens lexemes); + match previous, lexemes with + (* no more to process *) + | _, [] -> + (* return the result (/!\ it has to be reversed as some point) *) + r + + (* Tag: tag system $\cup$ high-priority extension mechanism *) + | _, Tag(_name, e) :: tl -> + begin match e#parser_extension r previous tl with + | Some(r, p, l) -> + main_impl_rev ~html r p l + | None -> + main_impl_rev ~html r previous tl + end + + (* HTML comments *) + | _, (Lessthan as t)::(Exclamation::Minuss 0::_c as tl) -> + begin + let f = function + | (Minuss _ as m)::(Greaterthan|Greaterthans _ as g)::tl -> + Split([g;m], tl) + | _ -> + Continue + in + match fsplit ~f:f lexemes with + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + | Some (comments, new_tl) -> + let r = Html_comment(L.string_of_tokens comments) :: r in + main_impl_rev ~html r [Newline] new_tl + end + + (* email-style quoting / blockquote *) + | ([]|[Newline|Newlines _]), Greaterthan::(Space|Spaces _)::_ -> + begin + match + emailstyle_quoting main_loop r previous (Newline::lexemes) + with + | Some(r,p,l) -> main_impl_rev ~html r p l + | None -> + if debug then + eprintf "(OMD) Omd_parser.emailstyle_quoting or \ + Omd_parser.main_loop is broken\n%!"; + assert false + end + + (* email-style quoting, with lines starting with spaces! *) + | ([]|[Newline|Newlines _]), (Space|Spaces(0|1) as s) + :: Greaterthan :: (Space|Spaces _)::_ -> + (* It's 1, 2 or 3 spaces, not more because it wouldn't mean + quoting anymore but code. *) + begin + let new_r, _p, rest = + let foo, rest = + match unindent (L.length s) (Newline::lexemes) with + | (Newline|Newlines _)::foo, rest -> foo, rest + | res -> res + in + match + emailstyle_quoting main_loop [] previous (Newline::foo) + with + | Some(new_r, p, []) -> new_r, p, rest + | _ -> + if debug then + eprintf "(OMD) Omd_parser.emailstyle_quoting or \ + Omd_parser.main_loop is broken\n%!"; + assert false + in + main_impl_rev ~html (new_r@r) [Newline] rest + end + + (* minus *) + | ([]|[Newline|Newlines _]), + (Minus|Minuss _ as t) :: ((Space|Spaces _)::_ as tl) -> + (* maybe hr *) + begin match hr_m lexemes with + | None -> (* no hr, so it could be a list *) + begin match t with + | Minus -> (* it's a list *) + let md, new_p, new_l = + parse_list main_loop r [] lexemes + in + main_impl_rev ~html md new_p new_l + | _ -> (* not a list *) + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + end + | Some l -> (* hr *) + main_impl_rev ~html (Hr::r) [Newline] l + end + | ([]|[Newline|Newlines _]), (Minus|Minuss _ as t)::tl -> + begin match hr_m lexemes with + | None -> (* no hr, and it's not a list either + because it's not followed by spaces *) + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + | Some l -> (* hr *) + main_impl_rev ~html (Hr::r) [Newline] l + end + + (* hashes *) + | ([]|[(Newline|Newlines _)]), + (Hashs n as t) :: ((Space|Spaces _) :: ttl as tl) + | ([]|[(Newline|Newlines _)]), + (Hashs n as t) :: (ttl as tl) -> (* hash titles *) + if n <= 4 then + match read_title main_loop (n+2) r previous ttl with + | Some(r, p, l) -> main_impl_rev ~html r p l + | None -> + if debug then + eprintf "(OMD) Omd_parser.read_title or \ + Omd_parser.main_loop is broken\n%!"; + assert false + else + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + | ([]|[(Newline|Newlines _)]), Hash :: (Space|Spaces _) :: tl + | ([]|[(Newline|Newlines _)]), Hash :: tl -> (* hash titles *) + begin match read_title main_loop 1 r previous tl with + | Some(r, p, l) -> main_impl_rev ~html r p l + | None -> + if debug then + eprintf "(OMD) Omd_parser.read_title or \ + Omd_parser.main_loop is broken\n%!"; + assert false + end + | _, (Hash|Hashs _ as t) :: tl -> (* hash -- no title *) + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + + (* spaces after a newline: could lead to hr *) + | ([]|[Newline|Newlines _]), ((Space|Spaces _) as sp) :: tl -> + begin match hr tl with + | None -> + (* No [Hr], but maybe [Ul], [Ol], code,... *) + let n = L.length sp in + let r, p, l = + spaces_at_beginning_of_line main_loop default_lang n r previous tl in + main_impl_rev ~html r p l + | Some tl -> + main_impl_rev ~html (Hr::r) [Newline] tl + end + + (* spaces anywhere *) + | _, ((Space|Spaces _) as t) :: tl -> + (* too many cases to be handled here *) + let n = L.length t in + let r, p, l = spaces_not_at_beginning_of_line ~html n r tl in + main_impl_rev ~html r p l + + (* underscores *) + | _, (Underscore as t) :: tl -> (* one "orphan" underscore, or emph *) + (match uemph_or_bold 1 tl with + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + | Some(x, new_tl) -> + main_impl_rev ~html (Emph(main_impl ~html [] [t] x) :: r) [t] new_tl + ) + | _, (Underscores((0|1) as n) as t) :: tl -> + (* 2 or 3 "orphan" underscores, or emph/bold *) + (match uemph_or_bold (n+2) tl with + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + | Some(x, new_tl) -> + if n = 0 then (* 1 underscore *) + main_impl_rev ~html (Bold(main_impl ~html [] [t] x) :: r) [t] new_tl + else (* 2 underscores *) + main_impl_rev ~html (Emph([Bold(main_impl ~html [] [t] x)]) :: r) [t] new_tl + ) + + (* enumerated lists *) + | ([]|[Newline|Newlines _]), (Number _) :: Dot :: (Space|Spaces _) :: _tl -> + let md, new_p, new_l = + parse_list main_loop r [] lexemes + in + main_impl_rev ~html md new_p new_l + + (* plus *) + | ([]|[(Newline|Newlines _)]), Plus :: (Space|Spaces _) :: _ -> + let md, new_p, new_l = + parse_list main_loop r [] lexemes + in + main_impl_rev ~html md new_p new_l + + (* stars *) + | ([]|[(Newline|Newlines _)]), Star :: (Space|Spaces _) :: _ -> + (* maybe hr or new list *) + begin match hr_s lexemes with + | Some l -> + main_impl_rev ~html (Hr::r) [Newline] l + | None -> + let md, new_p, new_l = + parse_list main_loop r [] lexemes + in + main_impl_rev ~html md new_p new_l + end + | ([]|[(Newline|Newlines _)]), Stars _ :: _ when hr_s lexemes <> None -> + (* hr *) + (match hr_s lexemes with + | Some l -> main_impl_rev ~html (Hr::r) [Newline] l + | None -> assert false + ) + | ([]|[(Newline|Newlines _)]), (Star as t) :: tl -> (* maybe hr *) + begin match hr_s lexemes with + | Some l -> + main_impl_rev ~html (Hr::r) [Newline] l + | None -> + (match semph_or_bold 1 tl with + | Some(x, new_tl) -> + main_impl_rev ~html (Emph(main_impl ~html [] [t] x) :: r) [t] new_tl + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + ) + end + | _, (Star as t) :: tl -> (* one "orphan" star, or emph // can't be hr *) + (match semph_or_bold 1 tl with + | Some(x, new_tl) -> + main_impl_rev ~html (Emph(main_impl ~html [] [t] x) :: r) [t] new_tl + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + | _, (Stars((0|1) as n) as t) :: tl -> + (* 2 or 3 "orphan" stars, or emph/bold *) + (match semph_or_bold (n+2) tl with + | Some(x, new_tl) -> + if n = 0 then + main_impl_rev ~html (Bold(main_impl ~html [] [t] x) :: r) [t] new_tl + else + main_impl_rev ~html (Emph([Bold(main_impl ~html [] [t] x)]) :: r) [t] new_tl + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + + (* backslashes *) + | _, Backslash :: (Newline as t) :: tl -> (* \\n *) + main_impl_rev ~html (Br :: r) [t] tl + | _, Backslash :: Newlines 0 :: tl -> (* \\n\n\n\n... *) + main_impl_rev ~html (Br :: r) [Backslash; Newline] (Newline :: tl) + | _, Backslash :: Newlines n :: tl -> assert (n >= 0); (* \\n\n\n\n... *) + main_impl_rev ~html (Br :: r) [Backslash; Newline] + (Newlines (n-1) :: tl) + | _, Backslash :: (Backquote as t) :: tl -> (* \` *) + main_impl_rev ~html (Text ("`") :: r) [t] tl + | _, Backslash :: Backquotes 0 :: tl -> (* \````... *) + main_impl_rev ~html (Text ("`") :: r) [Backslash; Backquote] (Backquote :: tl) + | _, Backslash :: Backquotes n :: tl -> assert (n >= 0); (* \````... *) + main_impl_rev ~html (Text ("`") :: r) [Backslash; Backquote] + (Backquotes (n-1) :: tl) + | _, Backslash :: (Star as t) :: tl -> (* \* *) + main_impl_rev ~html (Text ("*") :: r) [t] tl + | _, Backslash :: Stars 0 :: tl -> (* \****... *) + main_impl_rev ~html (Text ("*") :: r) [Backslash; Star] (Star :: tl) + | _, Backslash :: Stars n :: tl -> assert (n >= 0); (* \****... *) + main_impl_rev ~html (Text ("*") :: r) [Backslash; Star] (Stars (n-1) :: tl) + | _, Backslash :: (Underscore as t) :: tl -> (* \_ *) + main_impl_rev ~html (Text ("_") :: r) [t] tl + | _, Backslash :: Underscores 0 :: tl -> (* \___... *) + main_impl_rev ~html (Text ("_") :: r) [Backslash; Underscore] (Underscore :: tl) + | _, Backslash :: Underscores n :: tl -> assert (n >= 0); (* \___... *) + main_impl_rev ~html (Text ("_") :: r) [Backslash; Underscore] + (Underscores (n-1) :: tl) + | _, Backslash :: (Obrace as t) :: tl -> (* \{ *) + main_impl_rev ~html (Text ("{") :: r) [t] tl + | _, Backslash :: Obraces 0 :: tl -> (* \{{{... *) + main_impl_rev ~html (Text ("{") :: r) [Backslash; Obrace] (Obrace :: tl) + | _, Backslash :: Obraces n :: tl -> assert (n >= 0); (* \{{{... *) + main_impl_rev ~html (Text ("{") :: r) [Backslash; Obrace] (Obraces (n-1) :: tl) + | _, Backslash :: (Cbrace as t) :: tl -> (* \} *) + main_impl_rev ~html (Text ("}") :: r) [t] tl + | _, Backslash :: Cbraces 0 :: tl -> (* \}}}... *) + main_impl_rev ~html (Text ("}") :: r) [Backslash; Cbrace] (Cbrace :: tl) + | _, Backslash :: Cbraces n :: tl -> assert (n >= 0); (* \}}}... *) + main_impl_rev ~html (Text ("}") :: r) [Backslash; Cbrace] (Cbraces (n-1) :: tl) + | _, Backslash :: (Obracket as t) :: tl -> (* \[ *) + main_impl_rev ~html (Text ("[") :: r) [t] tl + | _, Backslash :: Obrackets 0 :: tl -> (* \[[[... *) + main_impl_rev ~html (Text ("[") :: r) [Backslash; Obracket] (Obracket :: tl) + | _, Backslash :: Obrackets n :: tl -> assert (n >= 0); (* \[[[... *) + main_impl_rev ~html (Text ("[") :: r) [Backslash; Obracket] (Obrackets (n-1) :: tl) + | _, Backslash :: (Cbracket as t) :: tl -> (* \} *) + main_impl_rev ~html (Text ("]") :: r) [t] tl + | _, Backslash :: Cbrackets 0 :: tl -> (* \}}}... *) + main_impl_rev ~html (Text ("]") :: r) [Backslash; Cbracket] (Cbracket :: tl) + | _, Backslash :: Cbrackets n :: tl -> assert (n >= 0); (* \}}}... *) + main_impl_rev ~html (Text ("]") :: r) [Backslash; Cbracket] (Cbrackets (n-1) :: tl) + | _, Backslash :: (Oparenthesis as t) :: tl -> (* \( *) + main_impl_rev ~html (Text ("(") :: r) [t] tl + | _, Backslash :: Oparenthesiss 0 :: tl -> (* \(((... *) + main_impl_rev ~html (Text ("(") :: r) [Backslash; Oparenthesis] (Oparenthesis :: tl) + | _, Backslash :: Oparenthesiss n :: tl -> assert (n >= 0); (* \(((... *) + main_impl_rev ~html (Text ("(") :: r) [Backslash; Oparenthesis] + (Oparenthesiss (n-1) :: tl) + | _, Backslash :: (Cparenthesis as t) :: tl -> (* \) *) + main_impl_rev ~html (Text (")") :: r) [t] tl + | _, Backslash :: Cparenthesiss 0 :: tl -> (* \)))... *) + main_impl_rev ~html (Text (")") :: r) [Backslash; Cparenthesis] + (Cparenthesis :: tl) + | _, Backslash :: Cparenthesiss n :: tl -> assert (n >= 0); (* \)))... *) + main_impl_rev ~html (Text (")") :: r) [Backslash; Cparenthesis] + (Cparenthesiss (n-1) :: tl) + | _, Backslash :: (Plus as t) :: tl -> (* \+ *) + main_impl_rev ~html (Text ("+") :: r) [t] tl + | _, Backslash :: Pluss 0 :: tl -> (* \+++... *) + main_impl_rev ~html (Text ("+") :: r) [Backslash; Plus] (Plus :: tl) + | _, Backslash :: Pluss n :: tl -> assert (n >= 0); (* \+++... *) + main_impl_rev ~html (Text ("+") :: r) [Backslash; Plus] (Pluss (n-1) :: tl) + | _, Backslash :: (Minus as t) :: tl -> (* \- *) + main_impl_rev ~html (Text ("-") :: r) [t] tl + | _, Backslash :: Minuss 0 :: tl -> (* \---... *) + main_impl_rev ~html (Text ("-") :: r) [Backslash; Minus] (Minus :: tl) + | _, Backslash :: Minuss n :: tl -> assert (n >= 0); (* \---... *) + main_impl_rev ~html (Text ("-") :: r) [Backslash; Minus] (Minuss (n-1) :: tl) + | _, Backslash :: (Dot as t) :: tl -> (* \. *) + main_impl_rev ~html (Text (".") :: r) [t] tl + | _, Backslash :: Dots 0 :: tl -> (* \....... *) + main_impl_rev ~html (Text (".") :: r) [Backslash; Dot] (Dot :: tl) + | _, Backslash :: Dots n :: tl -> assert (n >= 0); (* \....... *) + main_impl_rev ~html (Text (".") :: r) [Backslash; Dot] (Dots (n-1) :: tl) + | _, Backslash :: (Exclamation as t) :: tl -> (* \! *) + main_impl_rev ~html (Text ("!") :: r) [t] tl + | _, Backslash :: Exclamations 0 :: tl -> (* \!!!... *) + main_impl_rev ~html (Text ("!") :: r) [Backslash; Exclamation] (Exclamation :: tl) + | _, Backslash :: Exclamations n :: tl -> assert (n >= 0); (* \!!!... *) + main_impl_rev ~html (Text ("!") :: r) [Backslash; Exclamation] + (Exclamations (n-1) :: tl) + | _, Backslash :: (Hash as t) :: tl -> (* \# *) + main_impl_rev ~html (Text ("#") :: r) [t] tl + | _, Backslash :: Hashs 0 :: tl -> (* \###... *) + main_impl_rev ~html (Text ("#") :: r) [Backslash; Hash] (Hash :: tl) + | _, Backslash :: Hashs n :: tl -> assert (n >= 0); (* \###... *) + main_impl_rev ~html (Text ("#") :: r) [Backslash; Hash] (Hashs (n-1) :: tl) + | _, Backslash :: (Greaterthan as t) :: tl -> (* \> *) + main_impl_rev ~html (Text (">") :: r) [t] tl + | _, Backslash :: Greaterthans 0 :: tl -> (* \>>>... *) + main_impl_rev ~html (Text (">") :: r) [Backslash; Greaterthan] (Greaterthan :: tl) + | _, Backslash :: Greaterthans n :: tl -> assert (n >= 0); (* \>>>... *) + main_impl_rev ~html (Text (">") :: r) [Backslash; Greaterthan] + (Greaterthans (n-1) :: tl) + | _, Backslash :: (Lessthan as t) :: tl -> (* \< *) + main_impl_rev ~html (Text ("<") :: r) [t] tl + | _, Backslash :: Lessthans 0 :: tl -> (* \<<<... *) + main_impl_rev ~html (Text ("<") :: r) [Backslash; Lessthan] (Lessthan :: tl) + | _, Backslash :: Lessthans n :: tl -> assert (n >= 0); (* \<<<... *) + main_impl_rev ~html (Text ("<") :: r) [Backslash; Lessthan] + (Lessthans (n-1) :: tl) + | _, (Backslashs 0 as t) :: tl -> (* \\\\... *) + main_impl_rev ~html (Text ("\\") :: r) [t] tl + | _, (Backslashs n as t) :: tl -> (* \\\\... *) + if n mod 2 = 0 then + main_impl_rev ~html (Text(String.make ((n+2)/2) '\\') :: r) [t] tl + else + main_impl_rev ~html (Text(String.make ((n+2)/2) '\\') :: r) [t] (Backslash :: tl) + | _, Backslash::[] -> + main_impl_rev ~html (Text "\\" :: r) [] [] + | _, Backslash::tl -> + main_impl_rev ~html (Text "\\" :: r) [Backslash] tl + + (* < *) + | _, (Lessthan|Lessthans _ as t) + :: (Word("http"|"https"|"ftp"|"ftps"|"ssh"|"afp"|"imap") as w) + :: Colon::Slashs(n)::tl -> + (* "semi-automatic" URLs *) + let rec read_url accu = function + | (Newline|Newlines _)::_tl -> + None + | Greaterthan::tl -> + let url = + (L.string_of_token w) ^ "://" + ^ (if n = 0 then "" else String.make (n-1) '/') + ^ L.string_of_tokens (List.rev accu) + in Some(url, tl) + | x::tl -> + read_url (x::accu) tl + | [] -> + None + in + begin match read_url [] tl with + | Some(url, new_tl) -> + let r = + match t with + | Lessthans 0 -> Text "<" :: r + | Lessthans n -> Text(String.make (n+1) '<') :: r + | _ -> r + in + main_impl_rev ~html (Url(url,[Text url],"")::r) [] new_tl + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + end + + + (* Word(w) *) + | _, Word w::tl -> + main_impl_rev ~html (Text w :: r) [Word w] tl + + (* newline at the end *) + | _, [Newline] -> + NL::r + + (* named html entity *) + | _, Ampersand::((Word w::((Semicolon|Semicolons _) as s)::tl) as tl2) -> + if StringSet.mem w htmlcodes_set then + begin match s with + | Semicolon -> + main_impl_rev ~html (Raw("&"^w^";")::r) [s] tl + | Semicolons 0 -> + main_impl_rev ~html (Raw("&"^w^";")::r) [s] (Semicolon::tl) + | Semicolons n -> + main_impl_rev ~html (Raw("&"^w^";")::r) [s] (Semicolons(n-1)::tl) + | _ -> assert false + end + else + main_impl_rev ~html (Raw("&")::r) [] tl2 + + (* digit-coded html entity *) + | _, Ampersand::((Hash::Number w::((Semicolon|Semicolons _) as s)::tl) + as tl2) -> + if String.length w <= 4 then + begin match s with + | Semicolon -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] tl + | Semicolons 0 -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] (Semicolon::tl) + | Semicolons n -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] (Semicolons(n-1)::tl) + | _ -> assert false + end + else + main_impl_rev ~html (Raw("&")::r) [] tl2 + + (* maybe hex digit-coded html entity *) + | _, Ampersand::((Hash::Word w::((Semicolon|Semicolons _) as s)::tl) + as tl2) when is_hex w -> + if String.length w <= 4 then + begin match s with + | Semicolon -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] tl + | Semicolons 0 -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] (Semicolon::tl) + | Semicolons n -> + main_impl_rev ~html (Raw("&#"^w^";")::r) [s] (Semicolons(n-1)::tl) + | _ -> assert false + end + else + main_impl_rev ~html (Raw("&")::r) [] tl2 + + + (* Ampersand *) + | _, Ampersand::tl -> + main_impl_rev ~html (Raw("&")::r) [Ampersand] tl + + (* 2 Ampersands *) + | _, Ampersands(0)::tl -> + main_impl_rev ~html (Raw("&")::r) [] (Ampersand::tl) + + (* Several Ampersands (more than 2) *) + | _, Ampersands(n)::tl -> + main_impl_rev ~html (Raw("&")::r) [] (Ampersands(n-1)::tl) + + (* backquotes *) + | _, (Backquote|Backquotes _ as t)::tl -> + begin match bcode ~default_lang r previous lexemes with + | Some(r, p, l) -> main_impl_rev ~html r p l + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + end + + (* HTML *) + (*
and
with or without space(s) *) + | _, (Lessthan::Word("br"|"hr" as w)::Slash + ::(Greaterthan|Greaterthans _ as g)::tl) + | _, (Lessthan::Word("br"|"hr" as w)::(Space|Spaces _)::Slash + ::(Greaterthan|Greaterthans _ as g)::tl) -> + begin match g with + | Greaterthans 0 -> + main_impl_rev ~html (Raw("<"^w^" />")::r) [Greaterthan] (Greaterthan::tl) + | Greaterthans n -> + main_impl_rev ~html (Raw("<"^w^" />")::r) [Greaterthan] + (Greaterthans(n-1)::tl) + | _ -> + main_impl_rev ~html (Raw("<"^w^" />")::r) [Greaterthan] tl + end + + (* awaited orphan html closing tag *) + | _, Lessthan::Slash::Word(w)::(Greaterthan|Greaterthans _ as g)::tl + when !mediatypetextomd <> [] -> + raise (Orphan_closing(w, + lexemes, + (match g with + | Greaterthans 0 -> Greaterthan::tl + | Greaterthans n -> Greaterthans(n-1)::tl + | _ -> tl))) + + (* block html *) + | ([] | [Newline|Newlines _|Tag("HTMLBLOCK", _)]), + (Lessthan as t) + ::((Word(tagnametop) as w) + ::((Space|Spaces _|Greaterthan|Greaterthans _) + ::_ as html_stuff) as tlx) -> + if StringSet.mem tagnametop inline_htmltags_set then + main_impl_rev ~html r [Word ""] lexemes + else if not (blind_html || StringSet.mem tagnametop htmltags_set) then + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tlx + | Some(r, p, l) -> main_impl_rev ~html r p l + end + else + let read_html() = + let module T = struct + type t = + | Awaiting of string + | Open of string + type interm = + | HTML of string * (string * string option) list * interm list + | FTOKENS of L.t + | RTOKENS of L.t + | MD of Omd_representation.t + let rec md_of_interm_list html l = + let md_of_interm_list ?(html=html) l = + md_of_interm_list html l + in + match l with + | [] -> [] + | HTML(t, a, c)::tl -> + ( + let f_a = filter_text_omd_rev a in + if f_a != a then + Html_block + (t, + f_a, + make_paragraphs + (md_of_interm_list ~html:false (List.rev c))) + :: md_of_interm_list tl + else + Html_block + (t, f_a, md_of_interm_list ~html:true (List.rev c)) + :: md_of_interm_list tl + ) + | MD md::tl -> + md@md_of_interm_list tl + | RTOKENS t1::FTOKENS t2::tl -> + md_of_interm_list (FTOKENS(List.rev_append t1 t2)::tl) + | RTOKENS t1::RTOKENS t2::tl -> + md_of_interm_list + (FTOKENS(List.rev_append t1 (List.rev t2))::tl) + | FTOKENS t1::FTOKENS t2::tl -> + md_of_interm_list (FTOKENS(t1@t2)::tl) + | FTOKENS t :: tl -> + if html then + Raw(L.string_of_tokens t) :: md_of_interm_list tl + else + main_loop ~html [] [Word ""] t + @ md_of_interm_list tl + | RTOKENS t :: tl -> + md_of_interm_list (FTOKENS(List.rev t) :: tl) + let md_of_interm_list l = md_of_interm_list true l + let string_of_tagstatus tagstatus = + let b = Buffer.create 64 in + List.iter (function + | Open t -> bprintf b "{B/Open %s}" t + | Awaiting t -> bprintf b "{B/Awaiting %s}" t + ) tagstatus; + Buffer.contents b + end in + let add_token_to_body x body = + match body with + | T.RTOKENS r :: body -> T.RTOKENS(x::r)::body + | _ -> T.RTOKENS[x] :: body + in + let rec loop (body:T.interm list) attrs tagstatus tokens = + if debug then + eprintf "(OMD) 3333 BHTML loop body=%S tagstatus=%S %S\n%!" + (Omd_backend.sexpr_of_md(T.md_of_interm_list body)) + (T.string_of_tagstatus tagstatus) + (L.destring_of_tokens tokens); + match tokens with + | [] -> + begin + match tagstatus with + | [] -> Some(body, tokens) + | T.Open t :: _ when StringSet.mem t html_void_elements -> + Some(body, tokens) + | _ -> + if debug then + eprintf "(OMD) 3401 BHTML Not enough to read\n%!"; + None + end + | Lessthans n::tokens -> + begin match tagstatus with + | T.Awaiting _ :: _ -> None + | _ -> + if debug then eprintf "(OMD) 3408 BHTML loop\n%!"; + loop + (add_token_to_body + (if n = 0 then Lessthan else Lessthans(n-1)) + body) + attrs tagstatus (Lessthan::tokens) + end + (* self-closing tags *) + | Slash::Greaterthan::tokens -> + begin match tagstatus with + | T.Awaiting(tagname) :: tagstatus + when StringSet.mem tagname html_void_elements -> + loop [T.HTML(tagname, attrs, [])] [] tagstatus tokens + | _ -> + if debug then eprintf "(OMD) 3419 BHTML loop\n%!"; + loop + (add_token_to_body + Slash + (add_token_to_body + Greaterthan + body)) + attrs tagstatus tokens + end + (* closing the tag opener *) + | Lessthan::Slash::(Word(tagname) as w) + ::(Greaterthan|Greaterthans _ as g)::tokens -> + begin match tagstatus with + | T.Open t :: _ when t = tagname -> + if debug then + eprintf "(OMD) 3375 BHTML properly closing %S\n%!" t; + Some(body, + (match g with + | Greaterthans 0 -> Greaterthan :: tokens + | Greaterthans n -> Greaterthans(n-1) :: tokens + | _ -> tokens)) + | T.Open t :: _ -> + if debug then + eprintf "(OMD) 3379 BHTML wrongly closing %S with %S 1\n%!" + t tagname; + loop (T.RTOKENS[g;w;Slash;Lessthan]::body) + [] tagstatus tokens + | T.Awaiting t :: _ -> + if debug then + eprintf "(OMD) 3383 BHTML wrongly closing %S with %S 2\n%!" + t tagname; + if !mediatypetextomd <> [] then + raise + (Orphan_closing(t, + lexemes, + (match g with + | Greaterthans 0 -> + Greaterthan::tokens + | Greaterthans n -> + Greaterthans(n-1)::tokens + | _ -> tokens))) + else + None + | [] -> + if debug then + eprintf "(OMD) BHTML wrongly closing %S 3\n%!" tagname; + None + end + (* tag *) + | Lessthan::(Word(tagname) as word)::tokens + when + blind_html + || StringSet.mem tagname htmltags_set + -> + if debug then + eprintf "(OMD) 3489 BHTML tagname && StringSet.mem t html_void_elements -> + None + | T.Awaiting _ :: _ -> None + | _ -> + if attrs <> [] then + begin + if debug then + eprintf "(OMD) 3496 BHTML tag %S but attrs <> []\n%!" + tagname; + None + end + else + begin + if debug then + eprintf "(OMD) 3421 BHTML tag %S, tagstatus=%S, \ + attrs=[], tokens=%S\n%!" + tagname (T.string_of_tagstatus tagstatus) + (L.destring_of_tokens tokens); + match + loop [] [] (T.Awaiting tagname::tagstatus) tokens + with + | None -> + if debug then eprintf "(OMD) 3489 BHTML loop\n%!"; + loop + (add_token_to_body + word + (add_token_to_body + Lessthan + body)) + attrs tagstatus tokens + | Some(b, tokens) -> + if debug then begin + eprintf "(OMD) 3433 BHTML tagstatus=%S tokens=%S\n%!" + (T.string_of_tagstatus tagstatus) + (L.string_of_tokens tokens) + end; + Some(b@body, tokens) + end + end + (* end of opening tag *) + | Greaterthan::tokens -> + begin match tagstatus with + | T.Awaiting t :: tagstatus -> + if List.mem ("media:type", Some "text/omd") attrs then + ( + mediatypetextomd := t :: !mediatypetextomd; + try + ignore(main_impl_rev ~html [] [] tokens); + if debug then + eprintf "(OMD) 3524 BHTML closing tag not found \ + in %S\n%!" (L.destring_of_tokens tokens); + warn + (sprintf + "Closing tag `%s' not found for text/omd zone." + t); + mediatypetextomd := List.tl !mediatypetextomd; + None + with Orphan_closing(tagname, delimiter, after) -> + let before = + let rec f r = function + | Lessthans n as e :: tl -> + begin match delimiter with + | Lessthan::_ -> + if Lessthan::tl = delimiter then + List.rev + (if n = 0 then + Lessthan::r + else + Lessthans(n-1)::r) + else + f (e::r) tl + | _ -> + if tl == delimiter || tl = delimiter then + List.rev r + else + f (e::r) tl + end + | e::tl as l -> + if l == delimiter || l = delimiter then + List.rev r + else if tl == delimiter || tl = delimiter then + List.rev (e::r) + else + f (e::r) tl + | [] -> List.rev r + in + f [] tokens + in + if debug then + eprintf "(OMD) 3552 BHTML tokens=%s delimiter=%s \ + after=%s before=%s (tagname=t)=%b\n%!" + (L.destring_of_tokens tokens) + (L.destring_of_tokens delimiter) + (L.destring_of_tokens after) + (L.destring_of_tokens before) + (tagname = t); + (match !mediatypetextomd with + | _ :: tl -> mediatypetextomd := tl + | [] -> assert false); + if tagname = t then + loop + [T.HTML + (t, + attrs, + [T.MD + (main_impl ~html [] [] + (tag_setext main_loop before))])] + [] + tagstatus + after + else + None + ) + else + begin + if debug then eprintf "(OMD) 3571 BHTML loop\n%!"; + match loop body [] (T.Open t::tagstatus) tokens with + | None -> + if debug then + eprintf "(OMD) 3519 BHTML \ + Couldn't find an closing tag for %S\n%!" + t; + None + | Some(body, l) -> + if debug then + eprintf "(OMD) 3498 BHTML Found a closing tag %s\n%!" t; + match tagstatus with + | _ :: _ -> + loop [T.HTML(t, attrs, body)] [] tagstatus l + | [] -> + Some([T.HTML(t, attrs, body)], l) + end + | T.Open _t :: _ -> + if debug then + eprintf + "(OMD) 3591 BHTML Some `>` isn't for an opening tag\n%!"; + loop (add_token_to_body Greaterthan body) + attrs tagstatus tokens + | [] -> + if debug then + eprintf "(OMD) 3542 BHTML tagstatus=[]\n%!"; + None + end + + (* maybe attribute *) + | (Colon|Colons _|Underscore|Underscores _|Word _ as t)::tokens + | (Space|Spaces _) + ::(Colon|Colons _|Underscore|Underscores _|Word _ as t) + ::tokens + when (match tagstatus with + | T.Awaiting _ :: _ -> true + | _ -> false) -> + begin + let module Attribute_value = struct + type t = Empty of name | Named of name | Void + and name = string + end in + let open Attribute_value in + let rec extract_attribute accu = function + | (Space | Spaces _ | Newline) :: tokens-> + Empty(L.string_of_tokens(List.rev accu)), tokens + | (Greaterthan|Greaterthans _) :: _ as tokens-> + Empty(L.string_of_tokens(List.rev accu)), tokens + | Equal :: tokens -> + Named(L.string_of_tokens(List.rev accu)), tokens + | Colon | Colons _ | Underscore | Underscores _ | Word _ + | Number _ | Minus | Minuss _ | Dot | Dots _ as t :: tokens -> + extract_attribute (t::accu) tokens + | tokens -> Void, tokens + in + match extract_attribute [t] tokens with + | Empty attributename, tokens -> + (* attribute with no explicit value *) + if debug then eprintf "(OMD) 3628 BHTML loop\n%!"; + loop body ((attributename, None)::attrs) tagstatus tokens + | Named attributename, tokens -> + begin match tokens with + | Quotes 0 :: tokens -> + if debug then + eprintf "(OMD) 3661 BHTML empty attribute 1 %S\n%!" + (L.string_of_tokens tokens); + loop body ((attributename, Some "")::attrs) + tagstatus tokens + | Quote :: tokens -> + begin + if debug then + eprintf "(OMD) 3668 BHTML non empty attribute 1 %S\n%!" + (L.string_of_tokens tokens); + match + fsplit + ~excl:(function + | Quotes _ :: _ -> true + | _ -> false) + ~f:(function + | Quote::tl -> Split([], tl) + | _ -> Continue) + tokens + with + | None -> None + | Some(at_val, tokens) -> + if debug then eprintf "(OMD) 3654 BHTML loop\n%!"; + loop body ((attributename, + Some(L.string_of_tokens at_val)) + ::attrs) tagstatus tokens + end + | Doublequotes 0 :: tokens -> + begin + if debug then + eprintf "(OMD) 3690 BHTML empty attribute 2 %S\n%!" + (L.string_of_tokens tokens); + loop body ((attributename, Some "")::attrs) + tagstatus tokens + end + | Doublequote :: tokens -> + begin + if debug then + eprintf "(OMD) 3698 BHTML non empty attribute 2 %S\n%!" + (L.string_of_tokens tokens); + match fsplit + ~excl:(function + | Doublequotes _ :: _ -> true + | _ -> false) + ~f:(function + | Doublequote::tl -> Split([], tl) + | _ -> Continue) + tokens + with + | None -> None + | Some(at_val, tokens) -> + if debug then + eprintf "(OMD) 3622 BHTML %s=%S %s\n%!" + attributename + (L.string_of_tokens at_val) + (L.destring_of_tokens tokens); + loop body ((attributename, + Some(L.string_of_tokens at_val)) + ::attrs) tagstatus tokens + end + | _ -> None + end + | Void, _ -> None + end + + | x::tokens as dgts + when (match tagstatus with T.Open _ :: _ -> true | _ -> false) -> + begin + if debug then + eprintf "(OMD) 3620 BHTML general %S\n%!" + (L.string_of_tokens dgts); + loop (add_token_to_body x body) attrs tagstatus tokens + end + | (Newline | Space | Spaces _) :: tokens + when + (match tagstatus with T.Awaiting _ :: _ -> true | _ -> false) -> + begin + if debug then eprintf "(OMD) 3737 BHTML spaces\n%!"; + loop body attrs tagstatus tokens + end + | (Newlines _ as x) :: tokens + when + (match tagstatus with T.Awaiting _ :: _ -> true | _ -> false) -> + begin + if debug then eprintf "(OMD) 3827 BHTML newlines\n%!"; + warn "there are empty lines in what may be an HTML block"; + loop (add_token_to_body x body) attrs tagstatus tokens + end + | _ -> + if debug then + eprintf "(OMD) 3742 BHTML fallback with \ + tokens=%s and tagstatus=%s\n%!" + (L.destring_of_tokens tokens) + (match tagstatus with + | [] -> "None" + | T.Awaiting _ :: _ -> "Awaiting" + | T.Open _ :: _ -> "Open (can't be)"); + (match tagstatus with + | [] -> Some(body, tokens) + | T.Awaiting tag :: _ -> + warn (sprintf "expected to read an open HTML tag (%s), \ + but found nothing" tag); + None + | T.Open tag :: _ -> + warn (sprintf "expected to find the closing HTML tag for %s, \ + but found nothing" tag); + None) + in + if debug then eprintf "(OMD) 3408 BHTML loop\n%!"; + match loop [] [] [] lexemes with + | Some(h, rest) -> + Some(T.md_of_interm_list h, rest) + | None -> None + in + begin match read_html() with + | Some(h, rest) -> + main_impl_rev ~html (h@r) [Tag("HTMLBLOCK", empty_extension)] rest + | None -> + let text = L.string_of_token t in + main_impl_rev ~html (Text(text ^ tagnametop)::r) [w] html_stuff + end + (* / end of block HTML. *) + + + (* inline HTML *) + | _, + (Lessthan as t) + ::((Word(tagnametop) as w) + ::((Space|Spaces _|Greaterthan|Greaterthans _) + ::_ as html_stuff) as tlx) -> + if (strict_html && not(StringSet.mem tagnametop inline_htmltags_set)) + || not(blind_html || StringSet.mem tagnametop htmltags_set) + then + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tlx + | Some(r, p, l) -> main_impl_rev ~html r p l + end + else + let read_html() = + let module T = struct + type t = + | Awaiting of string + | Open of string + type interm = + | HTML of string * (string * string option) list * interm list + | TOKENS of L.t + | MD of Omd_representation.t + let rec md_of_interm_list = function + | [] -> [] + | HTML(t, a, c)::tl -> + Html(t, a, md_of_interm_list(List.rev c))::md_of_interm_list tl + | MD md::tl -> md @ md_of_interm_list tl + | TOKENS t1::TOKENS t2::tl -> + md_of_interm_list (TOKENS(t1@t2)::tl) + | TOKENS t :: tl -> + main_impl ~html [] [Word ""] (t) + @ md_of_interm_list tl + let string_of_tagstatus tagstatus = + let b = Buffer.create 64 in + List.iter (function + | Open t -> bprintf b "{I/Open %s}" t + | Awaiting t -> bprintf b "{I/Awaiting %s}" t + ) tagstatus; + Buffer.contents b + end in + let add_token_to_body x body = + T.TOKENS[x]::body + in + let rec loop (body:T.interm list) attrs tagstatus tokens = + if debug then + eprintf "(OMD) 3718 loop tagstatus=(%s) %s\n%!" + (* eprintf "(OMD) 3718 loop tagstatus=(%s) body=(%s) %s\n%!" *) + (T.string_of_tagstatus tagstatus) + (* (Omd_backend.sexpr_of_md(T.md_of_interm_list body)) *) + (L.destring_of_tokens tokens); + match tokens with + | [] -> + begin + match tagstatus with + | [] -> Some(body, tokens) + | T.Open(t)::_ when StringSet.mem t html_void_elements -> + Some(body, tokens) + | _ -> + if debug then + eprintf "(OMD) Not enough to read for inline HTML\n%!"; + None + end + | Lessthans n::tokens -> + begin match tagstatus with + | T.Awaiting _ :: _ -> None + | _ -> + loop + (add_token_to_body + (if n = 0 then Lessthan else Lessthans(n-1)) + body) + attrs tagstatus (Lessthan::tokens) + end + (* self-closing tags *) + | Slash::Greaterthan::tokens -> + begin match tagstatus with + | T.Awaiting(tagname)::tagstatus + when StringSet.mem tagname html_void_elements -> + loop [T.HTML(tagname, attrs, [])] [] tagstatus tokens + | _ -> + loop (T.TOKENS[Greaterthan;Slash]::body) + attrs tagstatus tokens + end + (* multiple newlines are not to be seen in inline HTML *) + | Newlines _ :: _ -> + if debug then eprintf "(OMD) Multiple lines in inline HTML\n%!"; + (match tagstatus with + | [] -> Some(body, tokens) + | _ -> warn "multiple newlines in inline HTML"; None) + (* maybe code *) + | (Backquote | Backquotes _ as b)::tl -> + begin match tagstatus with + | T.Awaiting _ :: _ -> + if debug then + eprintf "(OMD) maybe code in inline HTML: no code\n%!"; + None + | [] -> + if debug then + eprintf "(OMD) maybe code in inline HTML: none\n%!"; + None + | T.Open _ :: _ -> + if debug then + eprintf "(OMD) maybe code in inline HTML: let's try\n%!"; + begin match bcode [] [Space] tokens with + | Some (((Code _::_) as c), _p, l) -> + if debug then + eprintf "(OMD) maybe code in inline HTML: \ + confirmed\n%!"; + loop (T.MD c::body) [] tagstatus l + | _ -> + if debug then + eprintf "(OMD) maybe code in inline HTML: failed\n%!"; + loop (T.TOKENS[b]::body) [] tagstatus tl + end + end + (* closing the tag *) + | Lessthan::Slash::(Word(tagname) as w) + ::(Greaterthan|Greaterthans _ as g)::tokens -> + begin match tagstatus with + | T.Open t :: _ when t = tagname -> + if debug then + eprintf "(OMD) 4136 properly closing %S tokens=%s\n%!" + t (L.string_of_tokens tokens); + Some(body, + (match g with + | Greaterthans 0 -> Greaterthan :: tokens + | Greaterthans n -> Greaterthans(n-1) :: tokens + | _ -> tokens)) + | T.Open t :: _ -> + if debug then + eprintf "(OMD) 4144 \ + wrongly closing %S with %S 1\n%!" t tagname; + loop (T.TOKENS[g;w;Slash;Lessthan]::body) [] tagstatus tokens + | T.Awaiting t :: _ -> + if debug then + eprintf "(OMD) 4149 \ + wrongly closing %S with %S 2\n%!" t tagname; + None + | [] -> + if debug then + eprintf "(OMD) 4154 \ + wrongly closing nothing with %S 3\n%!" + tagname; + None + end + (* tag *) + | Lessthan::(Word(tagname) as word)::tokens + when + blind_html + || (strict_html && StringSet.mem tagname inline_htmltags_set) + || (not strict_html && StringSet.mem tagname htmltags_set) + -> + if debug then eprintf "(OMD) <%s...\n%!" tagname; + begin match tagstatus with + | T.Open(t) :: _ + when t <> tagname && StringSet.mem t html_void_elements -> + None + | T.Awaiting _ :: _ -> None + | _ -> + begin + if debug then + eprintf "(OMD) 3796 tag %s, attrs=[]\n%!" tagname; + match loop [] [] (T.Awaiting tagname::tagstatus) tokens + with + | None -> + loop (T.TOKENS[word;Lessthan]::body) + attrs tagstatus tokens + | Some(b,tokens) -> + Some(b@body, tokens) + end + end + (* end of opening tag *) + | Greaterthan::tokens -> + if debug then + eprintf "(OMD) 4185 end of opening tag tokens=%s \ + tagstatus=%s\n%!" + (L.string_of_tokens tokens) + (T.string_of_tagstatus tagstatus); + begin match tagstatus with + | T.Awaiting t :: tagstatus as ts -> + begin match loop body [] (T.Open t::tagstatus) tokens with + | None -> + if debug then + eprintf "(OMD) 4186 \ + Couldn't find an closing tag for %S\n%!" + t; + None + | Some(b, tokens) -> + if debug then + eprintf + "(OMD) 4192 Found a closing tag %s ts=%s \ + tokens=%s\n%!" + t + (T.string_of_tagstatus ts) + (L.string_of_tokens tokens); + match tagstatus with + | [] -> + Some(T.HTML(t, attrs, b)::body, tokens) + | _ -> + (* Note: we don't care about the value of + [attrs] here because in we have a + [tagstatus] matches [T.Open _ :: _] and + there's a corresponding filter that will + take care of attrs that will take care of + it. *) + loop (T.HTML(t, attrs, b)::body) [] tagstatus tokens + end + | T.Open _t :: _ -> + if debug then + eprintf + "(OMD) Turns out an `>` isn't for an opening tag\n%!"; + loop (T.TOKENS[Greaterthan]::body) attrs tagstatus tokens + | [] -> + if debug then + eprintf "(OMD) 4202 tagstatus=[]\n%!"; + None + end + + (* maybe attribute *) + | (Colon|Colons _|Underscore|Underscores _|Word _ as t)::tokens + | (Space|Spaces _) + ::(Colon|Colons _|Underscore|Underscores _|Word _ as t) + ::tokens + when (match tagstatus with + | T.Awaiting _ :: _ -> true + | _ -> false) -> + begin + let module Attribute_value = struct + type t = Empty of name | Named of name | Void + and name = string + end in + let open Attribute_value in + let rec extract_attribute accu = function + | (Space | Spaces _ | Newline) :: tokens-> + Empty(L.string_of_tokens(List.rev accu)), tokens + | (Greaterthan|Greaterthans _) :: _ as tokens-> + Empty(L.string_of_tokens(List.rev accu)), tokens + | Equal :: tokens -> + Named(L.string_of_tokens(List.rev accu)), tokens + | Colon | Colons _ | Underscore | Underscores _ | Word _ + | Number _ | Minus | Minuss _ | Dot | Dots _ as t :: tokens -> + extract_attribute (t::accu) tokens + | tokens -> Void, tokens + in + match extract_attribute [t] tokens with + | Empty attributename, tokens -> + (* attribute with no explicit value *) + loop body ((attributename, None)::attrs) tagstatus tokens + | Named attributename, tokens -> + begin match tokens with + | Quotes 0 :: tokens -> + if debug then + eprintf "(OMD) (IHTML) empty attribute 1 %S\n%!" + (L.string_of_tokens tokens); + loop body ((attributename, Some "")::attrs) tagstatus tokens + | Quote :: tokens -> + begin + if debug then + eprintf "(OMD) (IHTML) non empty attribute 1 %S\n%!" + (L.string_of_tokens tokens); + match + fsplit + ~excl:(function + | Quotes _ :: _ -> true + | _ -> false) + ~f:(function + | Quote::tl -> Split([], tl) + | _ -> Continue) + tokens + with + | None -> None + | Some(at_val, tokens) -> + loop body ((attributename, + Some(L.string_of_tokens at_val)) + ::attrs) tagstatus tokens + end + | Doublequotes 0 :: tokens -> + begin + if debug then + eprintf "(OMD) (IHTML) empty attribute 2 %S\n%!" + (L.string_of_tokens tokens); + loop body ((attributename, Some "")::attrs) tagstatus tokens + end + | Doublequote :: tokens -> + begin + if debug then + eprintf "(OMD) (IHTML) non empty attribute 2 %S\n%!" + (L.string_of_tokens tokens); + match fsplit + ~excl:(function + | Doublequotes _ :: _ -> true + | _ -> false) + ~f:(function + | Doublequote::tl -> Split([], tl) + | _ -> Continue) + tokens + with + | None -> None + | Some(at_val, tokens) -> + if debug then + eprintf "(OMD) (3957) %s=%S %s\n%!" attributename + (L.string_of_tokens at_val) + (L.destring_of_tokens tokens); + loop body ((attributename, + Some(L.string_of_tokens at_val)) + ::attrs) tagstatus tokens + end + | _ -> None + end + | Void, _ -> None + end + + | Backslash::x::tokens + when (match tagstatus with T.Open _ :: _ -> true | _ -> false) -> + loop (T.TOKENS[Backslash;x]::body) attrs tagstatus tokens + | Backslashs(n)::x::tokens + when (match tagstatus with T.Open _ :: _ -> true | _ -> false) + && n mod 2 = 1 -> + loop (T.TOKENS[Backslashs(n);x]::body) attrs tagstatus tokens + + | x::tokens + when (match tagstatus with T.Open _ :: _ -> true | _ -> false) -> + begin + if debug then + eprintf "(OMD) (4161) general %S\n%!" + (L.string_of_tokens (x::tokens)); + loop (T.TOKENS[x]::body) attrs tagstatus tokens + end + | (Newline | Space | Spaces _) :: tokens + when + (match tagstatus with T.Awaiting _ :: _ -> true | _ -> false) -> + begin + if debug then eprintf "(OMD) (4289) spaces\n%!"; + loop body attrs tagstatus tokens + end + | _ -> + if debug then + eprintf "(OMD) (4294) \ + fallback with tokens=%s and tagstatus=%s\n%!" + (L.destring_of_tokens tokens) + (T.string_of_tagstatus tagstatus); + (match tagstatus with + | [] -> Some(body, tokens) + | T.Awaiting tag :: _ -> + warn (sprintf "expected to read an open HTML tag (%s), \ + but found nothing" tag); + None + | T.Open tag :: _ -> + warn (sprintf "expected to find the closing HTML tag for %s, \ + but found nothing" tag); + None) + in match loop [] [] [] lexemes with + | Some(html, rest) -> + Some(T.md_of_interm_list html, rest) + | None -> None + in + begin match read_html() with + | Some(h, rest) -> + main_impl_rev ~html (h@r) [Greaterthan] rest + | None -> + let text = L.string_of_token t in + main_impl_rev ~html (Text(text ^ tagnametop)::r) [w] html_stuff + end + (* / end of inline HTML. *) + + (* < : emails *) + | _, (Lessthan as t)::tl -> + begin match maybe_autoemail r previous lexemes with + | Some(r,p,l) -> main_impl_rev ~html r p l + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + end + + (* line breaks *) + | _, Newline::tl -> + main_impl_rev ~html (NL::r) [Newline] tl + | _, Newlines _::tl -> + main_impl_rev ~html (NL::NL::r) [Newline] tl + + (* [ *) + | _, (Obracket as t)::tl -> + begin match maybe_link main_loop r previous tl with + | Some(r, p, l) -> main_impl_rev ~html r p l + | None -> + match maybe_reference main_loop rc r previous tl with + | Some(r, p, l) -> main_impl_rev ~html r p l + | None -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + end + + (* img *) + | _, (Exclamation|Exclamations _ as t) + ::Obracket::Cbracket::Oparenthesis::tl -> + (* image insertion with no "alt" *) + (* ![](/path/to/img.jpg) *) + (try + begin + let b, tl = read_until_cparenth ~bq:true ~no_nl:false tl in + (* new lines there are allowed *) + let r (* updated result *) = match t with + | Exclamations 0 -> Text "!" :: r + | Exclamations n -> Text(String.make (n+1) '!') :: r + | _ -> r in + match + try Some(read_until_space ~bq:false ~no_nl:true b) + with Premature_ending -> None + with + | Some(url, tls) -> + let title, _should_be_empty_list = + read_until_dq ~bq:true (snd (read_until_dq ~bq:true tls)) in + let url = L.string_of_tokens url in + let title = L.string_of_tokens title in + main_impl_rev ~html (Img("", url, title) :: r) [Cparenthesis] tl + | None -> + let url = L.string_of_tokens b in + main_impl_rev ~html (Img("", url, "") :: r) [Cparenthesis] tl + end + with + | NL_exception -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + + (* img ref *) + | _, (Exclamation as t) + ::Obracket::Cbracket::Obracket::tl -> + (* ref image insertion with no "alt" *) + (* ![][ref] *) + (try + let id, tl = read_until_cbracket ~bq:true ~no_nl:true tl in + let fallback = extract_fallback main_loop tl lexemes in + let id = L.string_of_tokens id in + main_impl_rev ~html (Img_ref(rc, id, "", fallback) :: r) [Cbracket] tl + with NL_exception -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + + + (* img *) + | _, (Exclamation|Exclamations _ as t)::Obracket::tl -> + (* image insertion with "alt" *) + (* ![Alt text](/path/to/img.jpg "Optional title") *) + (try + match read_until_cbracket ~bq:true tl with + | alt, Oparenthesis::ntl -> + (try + let alt = L.string_of_tokens alt in + let path_title, rest = + read_until_cparenth ~bq:true ~no_nl:false ntl in + let path, title = + try + read_until_space ~bq:true ~no_nl:true path_title + with Premature_ending -> path_title, [] in + let title, nothing = + if title <> [] then + read_until_dq ~bq:true (snd(read_until_dq ~bq:true title)) + else [], [] in + if nothing <> [] then + raise NL_exception; (* caught right below *) + let r = + match t with + | Exclamations 0 -> Text "!" :: r + | Exclamations n -> Text(String.make (n+1) '!') :: r + | _ -> r in + let path = L.string_of_tokens path in + let title = L.string_of_tokens title in + main_impl_rev ~html (Img(alt, path, title) :: r) [Cparenthesis] rest + with + | NL_exception + (* if NL_exception was raised, then fall back to "text" *) + | Premature_ending -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + ) + | alt, Obracket::Word(id)::Cbracket::ntl + | alt, Obracket::(Space|Spaces _)::Word(id)::Cbracket::ntl + | alt, Obracket::(Space|Spaces _)::Word(id)::(Space|Spaces _) + ::Cbracket::ntl + | alt, Obracket::Word(id)::(Space|Spaces _)::Cbracket::ntl -> + let fallback = extract_fallback main_loop ntl lexemes in + let alt = L.string_of_tokens alt in + main_impl_rev ~html (Img_ref(rc, id, alt, fallback)::r) [Cbracket] ntl + | alt, Obracket::((Newline|Space|Spaces _|Word _|Number _)::_ + as ntl) -> + (try + match read_until_cbracket ~bq:true ~no_nl:false ntl with + | [], _rest -> raise Premature_ending + | id, rest -> + let fallback = extract_fallback main_loop rest lexemes in + let id = L.string_of_tokens id in + let alt = L.string_of_tokens alt in + main_impl_rev ~html (Img_ref(rc, id, alt, fallback)::r) + [Cbracket] + rest + with + | Premature_ending + | NL_exception -> + begin match maybe_extension extensions r previous lexemes with + | None -> + main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + | _ -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + with + | Premature_ending -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + ) + + | _, + (At|Bar|Caret|Cbrace|Colon|Comma|Cparenthesis|Cbracket|Dollar + |Dot|Doublequote|Exclamation|Equal|Minus|Obrace|Oparenthesis + |Percent|Plus|Question|Quote|Semicolon|Slash|Tab|Tilde + |Greaterthan as t)::tl + -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + | _, (Number _ as t):: tl -> + begin match maybe_extension extensions r previous lexemes with + | None -> main_impl_rev ~html (Text(L.string_of_token t)::r) [t] tl + | Some(r, p, l) -> main_impl_rev ~html r p l + end + + | _, (Ats _ | Bars _ | Carets _ | Cbraces _ | Cbrackets _ | Colons _ + | Commas _ | Cparenthesiss _ | Dollars _ | Dots _ | Doublequotes _ + | Equals _ | Exclamations _ | Greaterthans _ | Lessthans _ + | Minuss _ | Obraces _ | Obrackets _ | Oparenthesiss _ + | Percents _ | Pluss _ | Questions _ | Quotes _ | Semicolons _ + | Slashs _ | Stars _ | Tabs _ | Tildes _ | Underscores _ as tk) + :: tl -> + begin match maybe_extension extensions r previous lexemes with + | None -> + let tk0, tks = L.split_first tk in + let text = L.string_of_token tk0 in + main_impl_rev ~html (Text text :: r) [tk0] (tks :: tl) + | Some(r, p, l) -> + main_impl_rev ~html r p l + end + + + and main_impl ~html (r:r) (previous:p) (lexemes:l) = + (* if debug then eprintf "(OMD) main_impl html=%b\n%!" html; *) + assert_well_formed lexemes; + List.rev (main_loop_rev ~html r previous lexemes) + + and main_loop ?(html=false) (r:r) (previous:p) (lexemes:l) = + main_impl ~html r previous lexemes + + and main_loop_rev ?(html=false) (r:r) (previous:p) (lexemes:l) = + main_impl_rev ~html r previous lexemes + + + let main_parse lexemes = + main_loop [] [] (tag_setext main_loop lexemes) + + let parse lexemes = + main_parse lexemes + +end + +let default_parse ?(extensions=[]) ?(default_lang="") lexemes = + let e = extensions and d = default_lang in + let module E = Default_env(Unit) in + let module M = + Make(struct + include E + let extensions = e + let default_lang = d + end) + in + M.main_parse lexemes + diff --git a/analysis/src/vendor/omd/omd_parser.mli b/analysis/src/vendor/omd/omd_parser.mli new file mode 100644 index 000000000..e8174bb48 --- /dev/null +++ b/analysis/src/vendor/omd/omd_parser.mli @@ -0,0 +1,379 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +(** Beware: the functions in this module may raise exceptions! If you + use them, you should be careful. *) + + +type r = Omd_representation.t +(** accumulator (beware, reversed tokens) *) + +and p = Omd_representation.tok list +(** context information: previous elements *) + +and l = Omd_representation.tok list +(** tokens to parse *) + +and main_loop = + ?html:bool -> + r -> (* accumulator (beware, reversed tokens) *) + p -> (* info: previous elements *) + l -> (* tokens to parse *) + Omd_representation.t (* final result *) +(** most important loop, which has to be given as an argument *) + + +val default_parse : + ?extensions:Omd_representation.extensions -> ?default_lang:string -> l + -> Omd_representation.t +(** Translate tokens to Markdown representation. + + @param lang language for blocks of code where it was not specified. + Default: [""]. +*) + +module type Env = +sig + val rc: Omd_representation.ref_container + (** reference container *) + val extensions : Omd_representation.extensions + (** list of parser extensions *) + val default_lang : string + (** default language for code blocks *) + val gh_uemph_or_bold_style : bool + (** flag: bold/emph using using underscores is by default + github-style, which means that underscores inside words are + left as underscore, rather than special characters, because + it's more convenient. However it is also less expressive + because then you can't bold/emph a part of a word. You might + want to set this flag to false. *) + val blind_html : bool + (** flag: if true, will not check whether a used HTML tag actually + exists in HTML. *) + val strict_html : bool + (** flag: if true, will only accept known inline HTML tags in inline HTML. *) + val warning : bool + (** flag: if true, will output warnings *) + val warn_error : bool + (** flag: if true, will convert warnings to errors *) +end + +module Default_env : functor (Unit: sig end) -> Env + +module Make : functor (Env : Env) -> +sig + + val rc: Omd_representation.ref_container + (** reference container *) + val extensions : Omd_representation.extensions + (** list of parser extensions *) + val default_lang : string + (** default language for code blocks *) + val gh_uemph_or_bold_style : bool + (** flag: bold/emph using using underscores is by default + github-style, which means that underscores inside words are + left as underscore, rather than special characters, because + it's more convenient. However it is also less expressive + because then you can't bold/emph a part of a word. You might + want to set this flag to false. *) + val blind_html : bool + (** flag: if true, will not check whether a used HTML tag actually + exists in HTML. *) + val strict_html : bool + (** flag: if true, will only accept known inline HTML tags in inline HTML. *) + + + val htmlcodes_set : Omd_utils.StringSet.t + (** set of known HTML codes *) + + val inline_htmltags_set : Omd_utils.StringSet.t + (** set of known inline HTML tags *) + + val htmltags_set : Omd_utils.StringSet.t + (** All known HTML tags *) + + val unindent_rev : + int -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + (** [unindent_rev n l] returns the same couple as [unindent n l] + except that the first element (which is a list) is reversed. + This function is used for lists. *) + + val unindent : + int -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + (** [unindent n l] returns [(unindented, rest)] where [unindented] is + the consecutive lines of [l] that are indented with at least [n] + spaces, and de-indented by [n] spaces. If [l] starts with a line + that is indented by less than [n] spaces, then it returns [([], l)]. + + + (* This function is used for lists, so it does not require [n] *) + (* spaces on every single line, but only on some specific ones of them. *) + + This function is used for lists and blockquotes. + + *) + + (* val unindent_strict_rev : *) + (* int -> *) + (* Omd_representation.tok list -> *) + (* Omd_representation.tok list * Omd_representation.tok list *) + (* (\** [unindent_strict_rev n l] returns the same couple as [unindent n l] *) + (* except that the first element (which is a list) is reversed. *) + (* This function is used for blockquotes. *\) *) + + (* val unindent_strict : *) + (* int -> *) + (* Omd_representation.tok list -> *) + (* Omd_representation.tok list * Omd_representation.tok list *) + (* (\** [unindent_strict n l] returns [(unindented, rest)] where [unindented] is *) + (* the consecutive lines of [l] that are indented with at least [n] *) + (* spaces, and de-indented by [n] spaces. If [l] starts with a line *) + (* that is indented by less than [n] spaces, then it returns [([], l)]. *) + (* This function is used for blockquotes. *) + (* *\) *) + + + + val is_blank : Omd_representation.tok list -> bool + (** [is_blank l] returns [true] if [l] only contains blanks, which are + spaces and newlines. *) + + val semph_or_bold : + int -> + Omd_representation.tok list -> + (Omd_representation.tok list * Omd_representation.tok list) option + (** [semph_or_bold n l] returns [None] if [l] doesn't start with + a bold/emph phrase (marked using stars), else it returns [Some(x,y)] + where [x] is the emph and/or bold phrase at the beginning of [l] + and [y] is the rest of [l]. *) + + val sm_uemph_or_bold : + int -> + Omd_representation.tok list -> + (Omd_representation.tok list * Omd_representation.tok list) option + (** [sm_uemph_or_bold n l] returns [None] if [l] doesn't start with + a bold/emph phrase (marked using underscores), else it returns [Some(x,y)] + where [x] is the emph and/or bold phrase at the beginning of [l] + and [y] is the rest of [l]. *) + + val gh_uemph_or_bold : + int -> + Omd_representation.tok list -> + (Omd_representation.tok list * Omd_representation.tok list) option + (** [gh_uemph_or_bold n l] returns [None] if [l] doesn't start with + a bold/emph phrase (marked using underscores), else it returns [Some(x,y)] + where [x] is the emph and/or bold phrase at the beginning of [l] + and [y] is the rest of [l]. *) + + val uemph_or_bold : + int -> + Omd_representation.tok list -> + (Omd_representation.tok list * Omd_representation.tok list) option + (** [uemph_or_bold n l] returns [None] if [l] doesn't start with a + bold/emph phrase (marked using underscores), else it returns + [Some(x,y)] where [x] is the emph and/or bold phrase at the + beginning of [l] and [y] is the rest of [l]. N.B. if + [!gh_uemph_or_bold_style] then in Github style (i.e., underscores + inside words are considered as underscores). *) + + val eat_blank : Omd_representation.tok list -> Omd_representation.tok list + (** [eat_blank l] returns [l] where all blanks at the beginning of the + list have been removed (it stops removing as soon as it meets an element + that is not a blank). Blanks are spaces and newlines only. *) + + val tag__maybe_h1 : main_loop -> Omd_representation.tok + (** [tag__maybe_h1 main_loop] is a tag that is injected everywhere that + might preceed a H1 title. It needs [main_loop] as argument because + it is used to parse the contents of the titles. *) + + val tag__maybe_h2 : main_loop -> Omd_representation.tok + (** [tag__maybe_h2 main_loop] is the same as [tag__maybe_h1 main_loop] + but for H2. *) + + val tag__md : Omd_representation.t -> Omd_representation.tok + (** [tag__md md] encapsulates [md] to make it a value of type [tok]. + Its purpose is to inject some pre-parsed markdown (i.e., [md] of type [t]) + in a yet-to-parse token stream of type [tok]. *) + + val tag_setext : + main_loop -> Omd_representation.tok list -> Omd_representation.tok list + (** Tag used for the lines that *might* be titles using setext-style. *) + + + val hr_m : l -> l option + (** [hr_m l] returns [Some nl] where [nl] is the remaining of [l] if [l] + contains a horizontal rule "drawn" with dashes. If there's no HR, then + returns [None].*) + + val hr_s : l -> l option + (** [hr_s l] is the same as [hr_m l] but for horizontal rules + "drawn" with stars instead. *) + + exception NL_exception + exception Premature_ending + + val read_until_gt : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_lt : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_cparenth : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_oparenth : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_dq : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_q : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_obracket : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_cbracket : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_space : + ?bq:bool -> + ?no_nl:bool -> + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + val read_until_newline : + Omd_representation.tok list -> + Omd_representation.tok list * Omd_representation.tok list + (** [read_until_...] are functions that read from a token list + and return two token lists: the first one is the tokens read + until a specific token is met, and the second one is the remainder. + The particularity of these functions is that they do consider + backslash-escaped characters and closing characters. + For instance, [read_until_gt "1 < 2 > 3 > 4"] returns + ["1 < 2 > 3 ", " 4"]: note that the ">" before " 4" has disappeared + and that [read_until_gt] takes a [tok list] (not a string) and + returns a couple of [tok list] (not a couple of strings), the + string notation is used here for concision. + + Until otherwise noted, those functions do *not* consider + backquote-trapped sections. + For instance, [read_until_gt "1 < 2 > 3 `>` 4"] + returns ["1 < 2 > 3 `", "` 4"]. + If you use these functions, you should make sure that they + do what you think they do (i.e., do look at the code). + + If the expected characters are not found, the exception + [Premature_ending] is raised. For instance, + [read_until_gt "1 < > 3"] raises [Premature_ending]. + + If [no_nl] is [true] (default value for [no_nl] is [false]) + and ['\n'] occurs before the splitting character, + then [NL_exception] is raised. + *) + + + val read_title : main_loop -> int -> r -> p -> l -> (r * p * l) option + (** [read_title main_loop n r p l] returns [Some(r,p,l)] + if it succeeds, [None] otherwise. + + [read_title main_loop n r p l] expects to read a [n]-level + hash-declared title from [l], where the hashes have *already* + been *removed*. If [n] is not between 1 and 6 (included), then + it returns [None]. + + [main_loop] is used to parse the contents of the title. + + [r] and [p] are the classical "result" and "previous" parameters. + *) + + val maybe_extension : + Omd_representation.extensions -> + r -> p -> l -> (r * p * l) option + (** [maybe_extension e r p l] returns [None] if there is no extension or + if extensions haven't had any effect, returns [Some(nr, np, nl)] if + at least one extension has applied successfully. *) + + val emailstyle_quoting : main_loop -> r -> p -> l -> (r * p * l) option + (** [emailstyle_quoting main_loop r p l] returns [Some(r,p,l)] with + [r] being the updated result, [p] being the last parsed token + and [l] being the remaining tokens to parse. If [emailstyle_quoting] + fails, then it returns [None], in which case its user is advise + to investigate why it returns [None] because there's possibly a + real problem. *) + + val maybe_reference : + main_loop -> + Omd_representation.ref_container -> r -> p -> l -> (r * p * l) option + (** [maybe_reference] tries to parse a reference, a reference definition or + a github-style short reference (e.g., [foo] as a shortcut for [foo][]), + and returns [Some(r,p,l)] if it succeeds, [None] otherwise. *) + + val maybe_link : main_loop -> r -> p -> l -> (r * p * l) option + (** [maybe_link] tries to parse a link, + and returns [Some(r,p,l)] if it succeeds, [None] otherwise. *) + + + val parse_list : main_loop -> r -> p -> l -> r * p * l + (** [parse_list main_loop r p l] parses a list from [l]. + + ***Important property*** + It is considered in Omd that a sub-list is always more indented than + the item that contains it (so, 2 items with different indentations cannot + have the direct same parent). + *) + + val make_paragraphs : Omd_representation.t -> Omd_representation.t + (** Since [Omd_parser.parse] doesn't build paragraphs, if you want + Markdown-style paragraphs, you need to apply this function to + the result of [Omd_parser.parse]. *) + + + val bcode : + ?default_lang:Omd_representation.name -> + r -> p -> l -> (r * p * l) option + (** [bcode default_lang r p l] + tries to parse some code that's delimited by backquotes, + and returns [Some(r,p,l)] if it succeeds, [None] otherwise. + *) + + val icode : + ?default_lang:Omd_representation.name -> + r -> p -> l -> (r * p * l) option + (** [icode default_lang r p l] + tries to parse some code that's delimited by space indentation. + It should always return [Some(r,p,l)], if it returns [None] + it means that it's been misused or there's a bug. + *) + + + val main_loop_rev : ?html:bool -> r -> p -> l -> r + val main_loop : ?html:bool -> r -> p -> l -> Omd_representation.t + val main_parse : Omd_representation.tok list -> Omd_representation.t + val parse : Omd_representation.tok list -> Omd_representation.t + +end + diff --git a/analysis/src/vendor/omd/omd_representation.ml b/analysis/src/vendor/omd/omd_representation.ml new file mode 100644 index 000000000..be4e795de --- /dev/null +++ b/analysis/src/vendor/omd/omd_representation.ml @@ -0,0 +1,491 @@ +open Omd_utils +open Printf + +(** references, instances created in [Omd_parser.main_parse] and + accessed in the [Omd_backend] module. *) +module R = Map.Make(String) +class ref_container : object + val mutable c : (string * string) R.t + method add_ref : R.key -> string -> string -> unit + method get_ref : R.key -> (string * string) option + method get_all : (string * (string * string)) list + end = object + val mutable c = R.empty + val mutable c2 = R.empty + + method get_all = R.bindings c + + method add_ref name title url = + c <- R.add name (url, title) c; + let ln = String.lowercase_ascii name in + if ln <> name then c2 <- R.add ln (url, title) c2 + + method get_ref name = + try + let r = + try R.find name c + with Not_found -> + let ln = String.lowercase_ascii name in + try R.find ln c + with Not_found -> + R.find ln c2 + in Some r + with Not_found -> + None +end + +type element = + | H1 of t + | H2 of t + | H3 of t + | H4 of t + | H5 of t + | H6 of t + | Paragraph of t + | Text of string + | Emph of t + | Bold of t + | Ul of t list + | Ol of t list + | Ulp of t list + | Olp of t list + | Code of name * string + | Code_block of name * string + | Br + | Hr + | NL + | Url of href * t * title + | Ref of ref_container * name * string * fallback + | Img_ref of ref_container * name * alt * fallback + | Html of name * (string * string option) list * t + | Html_block of name * (string * string option) list * t + | Html_comment of string + | Raw of string + | Raw_block of string + | Blockquote of t + | Img of alt * src * title + | X of + < name : string; + to_html : ?indent:int -> (t -> string) -> t -> string option; + to_sexpr : (t -> string) -> t -> string option; + to_t : t -> t option > +and fallback = < to_string : string ; to_t : t > +and name = string +and alt = string +and src = string +and href = string +and title = string +and t = element list + + +let rec loose_compare t1 t2 = match t1,t2 with + | H1 e1::tl1, H1 e2::tl2 + | H2 e1::tl1, H2 e2::tl2 + | H3 e1::tl1, H3 e2::tl2 + | H4 e1::tl1, H4 e2::tl2 + | H5 e1::tl1, H5 e2::tl2 + | H6 e1::tl1, H6 e2::tl2 + | Emph e1::tl1, Emph e2::tl2 + | Bold e1::tl1, Bold e2::tl2 + | Blockquote e1::tl1, Blockquote e2::tl2 + | Paragraph e1::tl1, Paragraph e2::tl2 + -> + (match loose_compare e1 e2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + + | Ul e1::tl1, Ul e2::tl2 + | Ol e1::tl1, Ol e2::tl2 + | Ulp e1::tl1, Ulp e2::tl2 + | Olp e1::tl1, Olp e2::tl2 + -> + (match loose_compare_lists e1 e2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + + | (Code _ as e1)::tl1, (Code _ as e2)::tl2 + | (Br as e1)::tl1, (Br as e2)::tl2 + | (Hr as e1)::tl1, (Hr as e2)::tl2 + | (NL as e1)::tl1, (NL as e2)::tl2 + | (Html _ as e1)::tl1, (Html _ as e2)::tl2 + | (Html_block _ as e1)::tl1, (Html_block _ as e2)::tl2 + | (Raw _ as e1)::tl1, (Raw _ as e2)::tl2 + | (Raw_block _ as e1)::tl1, (Raw_block _ as e2)::tl2 + | (Html_comment _ as e1)::tl1, (Html_comment _ as e2)::tl2 + | (Img _ as e1)::tl1, (Img _ as e2)::tl2 + | (Text _ as e1)::tl1, (Text _ as e2)::tl2 + -> + (match compare e1 e2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + + | Code_block(l1,c1)::tl1, Code_block(l2,c2)::tl2 + -> + (match compare l1 l2, String.length c1 - String.length c2 with + | 0, 0 -> + (match compare c1 c2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + | 0, 1 -> + (match compare c1 (c2^"\n") with + | 0 -> loose_compare tl1 tl2 + | i -> i) + | 0, -1 -> + (match compare (c1^"\n") c2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + | i, _ -> i + ) + + | Url (href1, t1, title1)::tl1, Url (href2, t2, title2)::tl2 + -> + (match compare href1 href2 with + | 0 -> (match loose_compare t1 t2 with + | 0 -> (match compare title1 title2 with + | 0 -> loose_compare tl1 tl2 + | i -> i) + | i -> i) + | i -> i) + + | Ref (ref_container1, name1, x1, fallback1)::tl1, + Ref (ref_container2, name2, x2, fallback2)::tl2 + | Img_ref (ref_container1, name1, x1, fallback1)::tl1, + Img_ref (ref_container2, name2, x2, fallback2)::tl2 + -> + (match compare (name1, x1) (name2, x2) with + | 0 -> + let cff = + if fallback1#to_string = fallback2#to_string then + 0 + else + loose_compare (fallback1#to_t) (fallback2#to_t) + in + if cff = 0 then + match + compare (ref_container1#get_all) (ref_container2#get_all) + with + | 0 -> loose_compare tl1 tl2 + | i -> i + else + cff + | i -> i) + + | X e1::tl1, X e2::tl2 -> + (match compare (e1#name) (e2#name) with + | 0 -> (match compare (e1#to_t) (e2#to_t) with + | 0 -> loose_compare tl1 tl2 + | i -> i) + | i -> i) + | X _::_, _ -> 1 + | _, X _::_ -> -1 + | _ -> compare t1 t2 + +and loose_compare_lists l1 l2 = + match l1, l2 with + | [], [] -> 0 + | e1::tl1, e2::tl2 -> + (match loose_compare e1 e2 with + | 0 -> loose_compare_lists tl1 tl2 + | i -> i) + | _, [] -> 1 + | _ -> -1 + + +type tok = (* Cs(n) means (n+2) times C *) +| Ampersand +| Ampersands of int +| At +| Ats of int +| Backquote +| Backquotes of int +| Backslash +| Backslashs of int +| Bar +| Bars of int +| Caret +| Carets of int +| Cbrace +| Cbraces of int +| Colon +| Colons of int +| Comma +| Commas of int +| Cparenthesis +| Cparenthesiss of int +| Cbracket +| Cbrackets of int +| Dollar +| Dollars of int +| Dot +| Dots of int +| Doublequote +| Doublequotes of int +| Exclamation +| Exclamations of int +| Equal +| Equals of int +| Greaterthan +| Greaterthans of int +| Hash +| Hashs of int +| Lessthan +| Lessthans of int +| Minus +| Minuss of int +| Newline +| Newlines of int +| Number of string +| Obrace +| Obraces of int +| Oparenthesis +| Oparenthesiss of int +| Obracket +| Obrackets of int +| Percent +| Percents of int +| Plus +| Pluss of int +| Question +| Questions of int +| Quote +| Quotes of int +| Semicolon +| Semicolons of int +| Slash +| Slashs of int +| Space +| Spaces of int +| Star +| Stars of int +| Tab +| Tabs of int +| Tilde +| Tildes of int +| Underscore +| Underscores of int +| Word of string +| Tag of name * extension + +and extension = < + parser_extension : + t -> tok list -> tok list -> ((t * tok list * tok list) option); + to_string : string +> + +type extensions = extension list + +let empty_extension = object + method parser_extension _r _p _l = None + method to_string = "" +end + +let rec normalise_md l = + if debug then + eprintf "(OMD) normalise_md\n%!"; + let rec loop = function + | [NL;NL;NL;NL;NL;NL;NL;] + | [NL;NL;NL;NL;NL;NL;] + | [NL;NL;NL;NL;NL;] + | [NL;NL;NL;NL;] + | [NL;NL;NL;] + | [NL;NL] + | [NL] -> [] + | [] -> [] + | NL::NL::NL::tl -> loop (NL::NL::tl) + | Text t1::Text t2::tl -> loop (Text(t1^t2)::tl) + | NL::(((Paragraph _|H1 _|H2 _|H3 _|H4 _|H5 _|H6 _ + |Code_block _|Ol _|Ul _|Olp _|Ulp _)::_) as tl) -> loop tl + | Paragraph[Text " "]::tl -> loop tl + | Paragraph[]::tl -> loop tl + | Paragraph(p)::tl -> Paragraph(loop p)::loop tl + | H1 v::tl -> H1(loop v)::loop tl + | H2 v::tl -> H2(loop v)::loop tl + | H3 v::tl -> H3(loop v)::loop tl + | H4 v::tl -> H4(loop v)::loop tl + | H5 v::tl -> H5(loop v)::loop tl + | H6 v::tl -> H6(loop v)::loop tl + | Emph v::tl -> Emph(loop v)::loop tl + | Bold v::tl -> Bold(loop v)::loop tl + | Ul v::tl -> Ul(List.map loop v)::loop tl + | Ol v::tl -> Ol(List.map loop v)::loop tl + | Ulp v::tl -> Ulp(List.map loop v)::loop tl + | Olp v::tl -> Olp(List.map loop v)::loop tl + | Blockquote v::tl -> Blockquote(loop v)::loop tl + | Url(href,v,title)::tl -> Url(href,(loop v),title)::loop tl + | Text _ + | Code _ + | Code_block _ + | Br + | Hr + | NL + | Ref _ + | Img_ref _ + | Html _ + | Html_block _ + | Html_comment _ + | Raw _ + | Raw_block _ + | Img _ + | X _ as v::tl -> v::loop tl + in + let a = loop l in + let b = loop a in + if a = b then + a + else + normalise_md b + +let rec visit f = function + | [] -> [] + | Paragraph v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Paragraph(visit f v)::visit f tl + end + | H1 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H1(visit f v)::visit f tl + end + | H2 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H2(visit f v)::visit f tl + end + | H3 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H3(visit f v)::visit f tl + end + | H4 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H4(visit f v)::visit f tl + end + | H5 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H5(visit f v)::visit f tl + end + | H6 v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> H6(visit f v)::visit f tl + end + | Emph v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Emph(visit f v)::visit f tl + end + | Bold v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Bold(visit f v)::visit f tl + end + | Ul v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Ul(List.map (visit f) v)::visit f tl + end + | Ol v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Ol(List.map (visit f) v)::visit f tl + end + | Ulp v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Ulp(List.map (visit f) v)::visit f tl + end + | Olp v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Olp(List.map (visit f) v)::visit f tl + end + | Blockquote v as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Blockquote(visit f v)::visit f tl + end + | Url(href,v,title) as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Url(href,visit f v,title)::visit f tl + end + | Text _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Code _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Code_block _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Ref _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Img_ref _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Html _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Html_block _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Html_comment _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Raw _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Raw_block _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Img _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | X _ as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> e::visit f tl + end + | Br as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Br::visit f tl + end + | Hr as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> Hr::visit f tl + end + | NL as e::tl -> + begin match f e with + | Some(l) -> l@visit f tl + | None -> NL::visit f tl + end + + diff --git a/analysis/src/vendor/omd/omd_representation.mli b/analysis/src/vendor/omd/omd_representation.mli new file mode 100644 index 000000000..6f0474e92 --- /dev/null +++ b/analysis/src/vendor/omd/omd_representation.mli @@ -0,0 +1,188 @@ + +module R : Map.S with type key = string + +class ref_container : + object + val mutable c : (string * string) R.t + method add_ref : R.key -> string -> string -> unit + method get_ref : R.key -> (string * string) option + method get_all : (string * (string * string)) list + end +type element = + | H1 of t + | H2 of t + | H3 of t + | H4 of t + | H5 of t + | H6 of t + | Paragraph of t + | Text of string + | Emph of t + | Bold of t + | Ul of t list + | Ol of t list + | Ulp of t list + | Olp of t list + | Code of name * string + | Code_block of name * string + | Br + | Hr + | NL + | Url of href * t * title + | Ref of ref_container * name * string * fallback + | Img_ref of ref_container * name * alt * fallback + | Html of name * (string * string option) list * t + | Html_block of name * (string * string option) list * t + | Html_comment of string + | Raw of string + | Raw_block of string + | Blockquote of t + | Img of alt * src * title + | X of + < name : string; + to_html : ?indent:int -> (t -> string) -> t -> string option; + to_sexpr : (t -> string) -> t -> string option; + to_t : t -> t option > +and fallback = < to_string : string ; to_t : t > +and name = string +and alt = string +and src = string +and href = string +and title = string +and t = element list + +type tok = + Ampersand (* one & *) + | Ampersands of int (* [Ampersands(n)] is (n+2) consecutive occurrences of & *) + | At (* @ *) + | Ats of int (* @@.. *) + | Backquote (* ` *) + | Backquotes of int (* ``.. *) + | Backslash (* \\ *) + | Backslashs of int (* \\\\.. *) + | Bar (* | *) + | Bars of int (* ||.. *) + | Caret (* ^ *) + | Carets of int (* ^^.. *) + | Cbrace (* } *) + | Cbraces of int (* }}.. *) + | Colon (* : *) + | Colons of int (* ::.. *) + | Comma (* , *) + | Commas of int (* ,,.. *) + | Cparenthesis (* ) *) + | Cparenthesiss of int (* )).. *) + | Cbracket (* ] *) + | Cbrackets of int (* ]].. *) + | Dollar (* $ *) + | Dollars of int (* $$.. *) + | Dot (* . *) + | Dots of int (* .... *) + | Doublequote (* \034 *) + | Doublequotes of int (* \034\034.. *) + | Exclamation (* ! *) + | Exclamations of int (* !!.. *) + | Equal (* = *) + | Equals of int (* ==.. *) + | Greaterthan (* > *) + | Greaterthans of int (* >>.. *) + | Hash (* # *) + | Hashs of int (* ##.. *) + | Lessthan (* < *) + | Lessthans of int (* <<.. *) + | Minus (* - *) + | Minuss of int (* --.. *) + | Newline (* \n *) + | Newlines of int (* \n\n.. *) + | Number of string + | Obrace (* { *) + | Obraces of int (* {{.. *) + | Oparenthesis (* ( *) + | Oparenthesiss of int (* ((.. *) + | Obracket (* [ *) + | Obrackets of int (* [[.. *) + | Percent (* % *) + | Percents of int (* %%.. *) + | Plus (* + *) + | Pluss of int (* ++.. *) + | Question (* ? *) + | Questions of int (* ??.. *) + | Quote (* ' *) + | Quotes of int (* ''.. *) + | Semicolon (* ; *) + | Semicolons of int (* ;;.. *) + | Slash (* / *) + | Slashs of int (* //.. *) + | Space (* *) + | Spaces of int (* .. *) + | Star (* * *) + | Stars of int (* **.. *) + | Tab (* \t *) + | Tabs of int (* \t\t.. *) + | Tilde (* ~ *) + | Tildes of int (* ~~.. *) + | Underscore (* _ *) + | Underscores of int (* __.. *) + | Word of string + | Tag of name * extension +(** Lexer's tokens. If you want to use the parser with an extended + lexer, you may use the constructor [Tag] to implement + the parser's extension. In the parser, [Tag] is used (at least) + 3 times in order to represent metadata or to store data. + + The integers carried by constructors means that the represented + character appears (n+2) times. So, [Ampersand(0)] is "&&". + Notably, this allows to use the property that in the match + case [Ampersand _ ->], we know there are at least 2 ampersands. + This is particularly useful for some characters, such as newlines + and spaces. It's not useful for all of them indeed but it has + been designed this way for the sake of uniformity (one doesn't + want to know by heart which constructor have that "at least 2" + property and which haven't). +*) + +and extension = < + parser_extension : t -> tok list -> tok list -> ((t * tok list * tok list) option); + to_string : string +> +(** - [parser_extension] is a method that takes the current state of the + parser's data and returns None if nothing has been changed, + otherwise it returns the new state. The current state of the + parser's data is [(r, p, l)] where [r] is the result so far, [p] + is the list of the previous tokens (it's typically empty or + contains information on how many newlines we've just seen), and + [l] is the remaining tokens to parse. + - and [to_string] is a method that returns directly a string + representation of the object (it's normal if it returns the + empty string). *) + +type extensions = extension list +(** One must use this type to extend the parser. It's a list of + functions of type [extension]. They are processed in order (the + head is applied first), so be careful about it. If you use it + wrong, it will behave wrong. *) + +val empty_extension : extension +(** An empty extension *) + +val loose_compare : t -> t -> int +(** [loose_compare t1 t2] returns [0] if [t1] and [t2] + are equivalent, otherwise it returns another number. *) + +val normalise_md : t -> t +(** [normalise_md md] returns a copy of [md] where some elements + have been factorized. *) + +val visit : (element -> t option) -> t -> t +(** visitor for structures of type t: [visit f md] will return a new + potentially altered copy of [md] that has been created by the + visit of [md] by [f]. + + The function [f] takes each [element] (from [md]) and returns + [Some t] if it has effectively been applied to [element], and + [None] otherwise. When it returns [Some t], [t] replaces [element] + in the copy of [md], and when it returns [None], either [element] + is copied as it is in the copy of [md] or a visited version is + copied instead (well, that depends on if [element] has elements + inside of it or not). +*) diff --git a/analysis/src/vendor/omd/omd_types.ml b/analysis/src/vendor/omd/omd_types.ml new file mode 100644 index 000000000..40582dcfa --- /dev/null +++ b/analysis/src/vendor/omd/omd_types.ml @@ -0,0 +1,44 @@ + +type name = string +type url = string +type title = string +type alt = string + +type 'a el = + [ `Text of string + | `Br + | `Emph of 'a + | `Bold of 'a + | `Url of url * 'a * title + | `Img of url * alt * title + | `Code of name * string + | `Html of string + | `Comment of string + ] + +type phrasing_no_NL = phrasing_no_NL el list + +type phrasing = [phrasing el | `NL] list + +type reference + +type flow = + [ phrasing el + | `H1 of phrasing_no_NL + | `H2 of phrasing_no_NL + | `H3 of phrasing_no_NL + | `H4 of phrasing_no_NL + | `H5 of phrasing_no_NL + | `H6 of phrasing_no_NL + | `Hr + | `Paragraph of phrasing + | `Code_block of name * string + | `Html_block of string + | `Ul of t + | `Ol of t + | `Quote of t + | `Ref of reference + | `Img_ref of reference + ] + +and t = flow list diff --git a/analysis/src/vendor/omd/omd_utils.ml b/analysis/src/vendor/omd/omd_utils.ml new file mode 100644 index 000000000..3919fd4d7 --- /dev/null +++ b/analysis/src/vendor/omd/omd_utils.ml @@ -0,0 +1,310 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013/2014 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +open Printf + +let debug = + let _DEBUG = + try + Some(Sys.getenv "DEBUG") + with _ -> None + and _OMD_DEBUG = + try + Some(Sys.getenv "OMD_DEBUG") + with _ -> None + in + match _DEBUG, _OMD_DEBUG with + | _, Some "false" -> + false + | Some _, None -> + eprintf "omd: debug mode activated because DEBUG is set, \ + you can deactivate the mode by unsetting DEBUG \ + or by setting OMD_DEBUG to the string \"false\".\n%!"; + true + | None, None -> + false + | _, Some _ -> + eprintf "omd: debug mode activated because OMD_DEBUG is set + to a value that isn't the string \"false\".\n%!"; + true + +exception Error of string + +let warn ?(we=false) msg = + if we then + raise (Error msg) + else + eprintf "(OMD) Warning: %s\n%!" msg + + +let trackfix = + try + ignore(Sys.getenv "OMD_FIX"); + eprintf "omd: tracking mode activated: token list are very often checked, \ + it might take a *very* long time if your input is large.\n%!"; + true + with Not_found -> + false + +let _ = if debug then Printexc.record_backtrace true + +let raise = + if debug then + (fun e -> + eprintf "(OMD) Exception raised: %s\n%!" (Printexc.to_string e); + raise e) + else + Pervasives.raise + +module StringSet : sig + include Set.S with type elt = string + val of_list : elt list -> t +end = struct + include Set.Make(String) + let of_list l = List.fold_left (fun r e -> add e r) empty l +end + + +type 'a split = 'a list -> 'a split_action +and 'a split_action = + | Continue + | Continue_with of 'a list * 'a list + | Split of 'a list * 'a list + + +let fsplit_rev ?(excl=(fun _ -> false)) ~(f:'a split) l + : ('a list * 'a list) option = + let rec loop accu = function + | [] -> + begin + match f [] with + | Split(left, right) -> Some(left@accu, right) + | Continue_with(left, tl) -> loop (left@accu) tl + | Continue -> None + end + | e::tl as l -> + if excl l then + None + else match f l with + | Split(left, right) -> Some(left@accu, right) + | Continue_with(left, tl) -> loop (left@accu) tl + | Continue -> loop (e::accu) tl + in loop [] l + +let fsplit ?(excl=(fun _ -> false)) ~f l = + match fsplit_rev ~excl:excl ~f:f l with + | None -> None + | Some(rev, l) -> Some(List.rev rev, l) + +let id_of_string ids s = + let n = String.length s in + let out = Buffer.create 0 in + (* Put [s] into [b], replacing non-alphanumeric characters with dashes. *) + let rec loop started i = + if i = n then () + else + match s.[i] with + | 'a' .. 'z' | 'A' .. 'Z' | '0' .. '9' as c -> + Buffer.add_char out c ; + loop true (i + 1) + (* Don't want to start with dashes. *) + | _ when not started -> + loop false (i + 1) + | _ -> + Buffer.add_char out '-' ; + loop false (i + 1) + in + loop false 0 ; + let s' = Buffer.contents out in + if s' = "" then "" + else + (* Find out the index of the last character in [s'] that isn't a dash. *) + let last_trailing = + let rec loop i = + if i < 0 || s'.[i] <> '-' then i + else loop (i - 1) + in + loop (String.length s' - 1) + in + (* Trim trailing dashes. *) + ids#mangle @@ String.sub s' 0 (last_trailing + 1) + +(* only convert when "necessary" *) +let htmlentities ?(md=false) s = + let module Break = struct exception Break end in + let b = Buffer.create 64 in + let rec loop i = + if i = String.length s then + () + else + let () = + match s.[i] with + | ( '0' .. '9' | 'a' .. 'z' | 'A' .. 'Z' ) as c -> Buffer.add_char b c + | '"' -> Buffer.add_string b """ + | '\'' -> Buffer.add_string b "'" + | '&' -> + if md then + begin + try + let () = match s.[i+1] with + | '#' -> + let rec ff j = + match s.[j] with + | '0' .. '9' -> ff (succ j) + | ';' -> () + | _ -> raise Break.Break + in + ff (i+2) + | 'A' .. 'Z' | 'a' .. 'z' -> + let rec ff j = + match s.[j] with + | 'A' .. 'Z' | 'a' .. 'z' -> ff (succ j) + | ';' -> () + | _ -> raise Break.Break + in + ff (i+2) + | _ -> raise Break.Break + in + Buffer.add_string b "&" + with _ -> Buffer.add_string b "&" + end + else + Buffer.add_string b "&" + | '<' -> Buffer.add_string b "<" + | '>' -> Buffer.add_string b ">" + | c -> Buffer.add_char b c + in loop (succ i) + in + loop 0; + Buffer.contents b + + +let minimalize_blanks s = + let l = String.length s in + let b = Buffer.create l in + let rec loop f i = + if i = l then + Buffer.contents b + else + match s.[i] with + | ' ' | '\t' | '\n' -> + loop true (succ i) + | _c -> + if Buffer.length b > 0 && f then + Buffer.add_char b ' '; + loop false (succ i) + in loop false 0 + +let rec eat f = function + | [] -> [] + | e::tl as l -> if f e then eat f tl else l + + +let rec extract_html_attributes (html:string) = + let cut_on_char_from s i c = + match String.index_from s i c with + | 0 -> "", String.sub s 1 (String.length s - 1) + | j -> String.sub s i (j-i), String.sub s (j+1) (String.length s - (j+1)) + in + let remove_prefix_spaces s = + if s = "" then + s + else if s.[0] <> ' ' then + s + else + let rec loop i = + if i = String.length s then + String.sub s i (String.length s - i) + else + match s.[i] with + | ' ' -> loop (i+1) + | _ -> String.sub s i (String.length s - i) + in loop 1 + in + let remove_suffix_spaces s = + if s = "" then + s + else if s.[String.length s - 1] <> ' ' then + s + else + let rec loop i = + match s.[i] with + | ' ' -> loop (i-1) + | _ -> String.sub s 0 (i+1) + in loop (String.length s - 1) + in + let rec loop s res i = + if i = String.length s then + res + else + match + try + Some (take_attribute s i) + with Not_found -> None + with + | Some (((_,_) as a), new_s) -> + loop new_s (a::res) 0 + | None -> res + and take_attribute s i = + let name, after_eq = cut_on_char_from s i '=' in + let name = remove_suffix_spaces name in + let after_eq = remove_prefix_spaces after_eq in + let value, rest = cut_on_char_from after_eq 1 after_eq.[0] in + (name,value), remove_prefix_spaces rest + in + if (* Has it at least one attribute? *) + try String.index html '>' < String.index html ' ' + with Not_found -> true + then + [] + else + match html.[1] with + | '<' | ' ' -> + extract_html_attributes + (remove_prefix_spaces (String.sub html 1 (String.length html - 1))) + | _ -> + try + let html = snd (cut_on_char_from html 0 ' ') in + loop (String.sub html 0 (String.index html '>')) [] 0 + with Not_found -> [] + +let extract_inner_html (html:string) = + let cut_on_char_from s i c = + match String.index_from s i c with + | 0 -> "", String.sub s 1 (String.length s - 1) + | j -> String.sub s i (j-i), String.sub s (j+1) (String.length s - (j+1)) + in + let rcut_on_char_from s i c = + match String.rindex_from s i c with + | 0 -> "", String.sub s 1 (String.length s - 1) + | j -> String.sub s 0 j, String.sub s (j+1) (String.length s - (j+1)) + in + let _, p = cut_on_char_from html 0 '>' in + let r, _ = rcut_on_char_from p (String.length p - 1) '<' in + r + + +let html_void_elements = StringSet.of_list [ + "img"; + "input"; + "link"; + "meta"; + "br"; + "hr"; + "source"; + "wbr"; + "param"; + "embed"; + "base"; + "area"; + "col"; + "track"; + "keygen"; +] + +let ( @ ) l1 l2 = + List.rev_append (List.rev l1) l2 diff --git a/analysis/src/vendor/omd/omd_utils.mli b/analysis/src/vendor/omd/omd_utils.mli new file mode 100644 index 000000000..7c36c3d03 --- /dev/null +++ b/analysis/src/vendor/omd/omd_utils.mli @@ -0,0 +1,118 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013/2014 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +val debug : bool +(** Equals [true] if the environment variable DEBUG is set, + or if the environment variable OMD_DEBUG is set to a string + that is not ["false"]. *) + +val trackfix : bool + +exception Error of string + +val raise : exn -> 'a +(** Same as [Pervasives.raise] except if [debug] equals true, + in which case it prints a trace on stderr before raising the exception. *) + +val warn : ?we:bool -> string -> unit +(** [warn we x] prints a warning with the message [x] if [we] is true, + else raises [Omd_utils.Error x]. *) + +module StringSet : + sig + include Set.S with type elt = string + val of_list : elt list -> t + end +(** Set of [string]. Cf. documentation of {!Set.S} *) + +type 'a split = 'a list -> 'a split_action +(** Type of a split function *) + +and 'a split_action = + (** Don't split yet *) + | Continue + + (** Don't split yet but continue with those two lists instead of default *) + | Continue_with of 'a list * 'a list + + (** Do split with this split scheme *) + | Split of 'a list * 'a list +(** Type of a split action *) + + +val fsplit_rev : + ?excl:('a list -> bool) -> + f:'a split -> 'a list -> ('a list * 'a list) option +(** [fsplit_rev ?excl ~f l] returns [Some(x,y)] where [x] is the + **reversed** list of the consecutive elements of [l] that obey the + split function [f]. + Note that [f] is applied to a list of elements and not just an + element, so that [f] can look farther in the list when applied. + [f l] returns [Continue] if there're more elements to consume, + [Continue_with(left,right)] if there's more elements to consume + but we want to choose what goes to the left part and what remains + to process (right part), and returns [Split(left,right)] if + the splitting is decided. + When [f] is applied to an empty list, if it returns [Continue] + then the result will be [None]. + + If [excl] is given, then [excl] is applied before [f] is, to check + if the splitting should be stopped right away. When the split + fails, it returns [None]. *) + + +val fsplit : + ?excl:('a list -> bool) -> + f:'a split -> 'a list -> ('a list * 'a list) option +(** [fsplit ?excl ~f l] returns [Some(List.rev x, y)] + if [fsplit ?excl ~f l] returns [Some(x,y)], else it returns [None]. *) + +val id_of_string : < mangle : string -> string; .. > -> string -> string +(** [id_of_string ids id] returns a mangled version of [id], using the + method [ids#mangle]. If you don't need mangling, you may use + [object method mangle x = x end] for [ids]. However, the name + [ids] also means that your object should have knowledge of all IDs + it has issued, in order to avoid collision. This is why + [id_of_string] asks for an object rather than "just a + function". *) + +val htmlentities : ?md:bool -> string -> string +(** [htmlentities s] returns a new string in which html-significant + characters have been converted to html entities. For instance, + "" is converted to "<Foo&Bar>". *) + +val minimalize_blanks : string -> string +(** [minimalize_blanks s] returns a copy of [s] in which the first and last + characters are never blank, and two consecutive blanks never happen. *) + + +val eat : ('a -> bool) -> 'a list -> 'a list +(** [eat f l] returns [l] where elements satisfying [f] have been removed, + but it stops removing as soon as one element doesn't satisfy [f]. *) + + +val extract_html_attributes : string -> (string * string) list +(** Takes some HTML and returns the list of attributes of the first + HTML tag. + Notes: + * Doesn't check the validity of HTML tags or attributes. + * Doesn't support backslash escaping. + * Attribute names are delimited by the space and equal characters. + * Attribute values are either delimited by the double quote + or the simple quote character. +*) + +val extract_inner_html : string -> string +(** Takes an HTML node and returns the contents of the node. + If it's not given a node, it returns something rubbish. +*) + +val html_void_elements : StringSet.t +(** HTML void elements *) + +val ( @ ) : 'a list -> 'a list -> 'a list +(** Tail-recursive version of [Pervasives.(@)]. *) diff --git a/analysis/src/vendor/omd/omd_xtxt.ml b/analysis/src/vendor/omd/omd_xtxt.ml new file mode 100644 index 000000000..68b49f66a --- /dev/null +++ b/analysis/src/vendor/omd/omd_xtxt.ml @@ -0,0 +1,28 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +(* xtxt = eXTernal eXTension *) + +(* let extensions = ref [] *) + +(* let get () = *) +(* !extensions *) + +(* let register e = *) +(* extensions := e :: !extensions *) + +(* let set es = extensions := es *) + +(* let activate ... *) + +(* (\* let deactivate ... *\) *) + +(* priority (integer?) *) +(* pre-extension *) +(* post-extension *) + + diff --git a/analysis/src/vendor/omd/omd_xtxt.mli b/analysis/src/vendor/omd/omd_xtxt.mli new file mode 100644 index 000000000..9c1d8749b --- /dev/null +++ b/analysis/src/vendor/omd/omd_xtxt.mli @@ -0,0 +1,9 @@ +(***********************************************************************) +(* omd: Markdown frontend in OCaml *) +(* (c) 2013 by Philippe Wang *) +(* Licence : ISC *) +(* http://www.isc.org/downloads/software-support-policy/isc-license/ *) +(***********************************************************************) + +(** xtxt = eXTernal eXTension *) + diff --git a/analysis/src/vendor/res_outcome_printer/res_comment.ml b/analysis/src/vendor/res_outcome_printer/res_comment.ml new file mode 100644 index 000000000..bdcd7e563 --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_comment.ml @@ -0,0 +1,72 @@ +type style = + | SingleLine + | MultiLine + +let styleToString s = match s with + | SingleLine -> "SingleLine" + | MultiLine -> "MultiLine" + +type t = { + txt: string; + style: style; + loc: Location.t; + mutable prevTokEndPos: Lexing.position; +} + +let loc t = t.loc +let txt t = t.txt +let prevTokEndPos t = t.prevTokEndPos + +let setPrevTokEndPos t pos = + t.prevTokEndPos <- pos + +let isSingleLineComment t = match t.style with + | SingleLine -> true + | MultiLine -> false + +let toString t = + Format.sprintf + "(txt: %s\nstyle: %s\nlines: %d-%d)" + t.txt + (styleToString t.style) + t.loc.loc_start.pos_lnum + t.loc.loc_end.pos_lnum + +let makeSingleLineComment ~loc txt = { + txt; + loc; + style = SingleLine; + prevTokEndPos = Lexing.dummy_pos; +} + +let makeMultiLineComment ~loc txt = { + txt; + loc; + style = MultiLine; + prevTokEndPos = Lexing.dummy_pos; +} + +let fromOcamlComment ~loc ~txt ~prevTokEndPos = { + txt; + loc; + style = MultiLine; + prevTokEndPos = prevTokEndPos +} + +let trimSpaces s = + let len = String.length s in + if len = 0 then s + else if String.unsafe_get s 0 = ' ' || String.unsafe_get s (len - 1) = ' ' then ( + let i = ref 0 in + while !i < len && (String.unsafe_get s !i) = ' ' do + incr i + done; + let j = ref (len - 1) in + while !j >= !i && (String.unsafe_get s !j) = ' ' do + decr j + done; + if !j >= !i then + (String.sub [@doesNotRaise]) s !i (!j - !i + 1) + else + "" + ) else s \ No newline at end of file diff --git a/analysis/src/vendor/res_outcome_printer/res_comment.mli b/analysis/src/vendor/res_outcome_printer/res_comment.mli new file mode 100644 index 000000000..7fdaa0459 --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_comment.mli @@ -0,0 +1,17 @@ +type t + +val toString: t -> string + +val loc: t -> Location.t +val txt: t -> string +val prevTokEndPos: t -> Lexing.position + +val setPrevTokEndPos: t -> Lexing.position -> unit + +val isSingleLineComment: t -> bool + +val makeSingleLineComment: loc:Location.t -> string -> t +val makeMultiLineComment: loc:Location.t -> string -> t +val fromOcamlComment: + loc:Location.t -> txt:string -> prevTokEndPos:Lexing.position -> t +val trimSpaces: string -> string \ No newline at end of file diff --git a/analysis/src/vendor/res_outcome_printer/res_doc.ml b/analysis/src/vendor/res_outcome_printer/res_doc.ml new file mode 100644 index 000000000..63a9a731c --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_doc.ml @@ -0,0 +1,356 @@ +module MiniBuffer = Res_minibuffer + +type mode = Break | Flat + +type lineStyle = + | Classic (* fits? -> replace with space *) + | Soft (* fits? -> replaced with nothing *) + | Hard (* always included, forces breaks in parents *) + (* always included, forces breaks in parents, but doesn't increase indentation + use case: template literals, multiline string content *) + | Literal + +type t = + | Nil + | Text of string + | Concat of t list + | Indent of t + | IfBreaks of {yes: t; no: t; mutable broken: bool} (* when broken is true, treat as the yes branch *) + | LineSuffix of t + | LineBreak of lineStyle + | Group of {mutable shouldBreak: bool; doc: t} + | CustomLayout of t list + | BreakParent + +let nil = Nil +let line = LineBreak Classic +let hardLine = LineBreak Hard +let softLine = LineBreak Soft +let literalLine = LineBreak Literal +let text s = Text s + +(* Optimization. We eagerly collapse and reduce whatever allocation we can *) +let rec _concat acc l = + match l with + | Text s1 :: Text s2 :: rest -> Text (s1 ^ s2) :: _concat acc rest + | Nil :: rest -> _concat acc rest + | Concat l2 :: rest -> _concat (_concat acc rest) l2 (* notice the order here *) + | x :: rest -> + let rest1 = _concat acc rest in + if rest1 == rest then l else x :: rest1 + | [] -> acc + +let concat l = Concat(_concat [] l) + +let indent d = Indent d +let ifBreaks t f = IfBreaks {yes = t; no = f; broken = false} +let lineSuffix d = LineSuffix d +let group d = Group {shouldBreak = false; doc = d} +let breakableGroup ~forceBreak d = Group {shouldBreak = forceBreak; doc = d} +let customLayout gs = CustomLayout gs +let breakParent = BreakParent + +let space = Text " " +let comma = Text "," +let dot = Text "." +let dotdot = Text ".." +let dotdotdot = Text "..." +let lessThan = Text "<" +let greaterThan = Text ">" +let lbrace = Text "{" +let rbrace = Text "}" +let lparen = Text "(" +let rparen = Text ")" +let lbracket = Text "[" +let rbracket = Text "]" +let question = Text "?" +let tilde = Text "~" +let equal = Text "=" +let trailingComma = ifBreaks comma nil +let doubleQuote = Text "\"" + +let propagateForcedBreaks doc = + let rec walk doc = match doc with + | Text _ | Nil | LineSuffix _ -> + false + | BreakParent -> + true + | LineBreak (Hard | Literal) -> + true + | LineBreak (Classic | Soft) -> + false + | Indent children -> + let childForcesBreak = walk children in + childForcesBreak + | IfBreaks ({yes = trueDoc; no = falseDoc} as ib) -> + let falseForceBreak = walk falseDoc in + if falseForceBreak then + let _ = walk trueDoc in + ib.broken <- true; + true + else + let forceBreak = walk trueDoc in + forceBreak + | Group ({shouldBreak = forceBreak; doc = children} as gr) -> + let childForcesBreak = walk children in + let shouldBreak = forceBreak || childForcesBreak in + gr.shouldBreak <- shouldBreak; + shouldBreak + | Concat children -> + List.fold_left (fun forceBreak child -> + let childForcesBreak = walk child in + forceBreak || childForcesBreak + ) false children + | CustomLayout children -> + (* When using CustomLayout, we don't want to propagate forced breaks + * from the children up. By definition it picks the first layout that fits + * otherwise it takes the last of the list. + * However we do want to propagate forced breaks in the sublayouts. They + * might need to be broken. We just don't propagate them any higher here *) + let _ = walk (Concat children) in + false + in + let _ = walk doc in + () + +(* See documentation in interface file *) +let rec willBreak doc = match doc with + | LineBreak (Hard | Literal) | BreakParent | Group {shouldBreak = true} -> true + | Group {doc} | Indent doc | CustomLayout (doc::_) -> willBreak doc + | Concat docs -> List.exists willBreak docs + | IfBreaks {yes; no} -> willBreak yes || willBreak no + | _ -> false + +let join ~sep docs = + let rec loop acc sep docs = + match docs with + | [] -> List.rev acc + | [x] -> List.rev (x::acc) + | x::xs -> loop (sep::x::acc) sep xs + in + concat(loop [] sep docs) + +let fits w stack = + let width = ref w in + let result = ref None in + + let rec calculate indent mode doc = + match mode, doc with + | _ when result.contents != None -> () + | _ when width.contents < 0 -> result := Some false + | _, Nil + | _, LineSuffix _ + | _, BreakParent -> () + | _, Text txt -> width := width.contents - (String.length txt) + | _, Indent doc -> calculate (indent + 2) mode doc + | Flat, LineBreak Hard + | Flat, LineBreak Literal -> result := Some true + | Flat, LineBreak Classic -> width := width.contents - 1 + | Flat, LineBreak Soft -> () + | Break, LineBreak _ -> result := Some true + | _, Group {shouldBreak = true; doc} -> calculate indent Break doc + | _, Group {doc} -> calculate indent mode doc + | _, IfBreaks {yes = breakDoc; broken = true} -> calculate indent mode breakDoc + | Break, IfBreaks {yes = breakDoc} -> calculate indent mode breakDoc + | Flat, IfBreaks {no = flatDoc} -> calculate indent mode flatDoc + | _, Concat docs -> calculateConcat indent mode docs + | _, CustomLayout (hd::_) -> + (* TODO: if we have nested custom layouts, what we should do here? *) + calculate indent mode hd + | _, CustomLayout [] -> () + and calculateConcat indent mode docs = + if result.contents == None then ( + match docs with + | [] -> () + | doc::rest -> + calculate indent mode doc; + calculateConcat indent mode rest + ) + in + let rec calculateAll stack = + match result.contents, stack with + | Some r, _ -> r + | None, [] -> !width >= 0 + | None, (indent, mode, doc)::rest -> + calculate indent mode doc; + calculateAll rest + in + calculateAll stack + +let toString ~width doc = + propagateForcedBreaks doc; + let buffer = MiniBuffer.create 1000 in + + let rec process ~pos lineSuffices stack = + match stack with + | ((ind, mode, doc) as cmd)::rest -> + begin match doc with + | Nil | BreakParent -> + process ~pos lineSuffices rest + | Text txt -> + MiniBuffer.add_string buffer txt; + process ~pos:(String.length txt + pos) lineSuffices rest + | LineSuffix doc -> + process ~pos ((ind, mode, doc)::lineSuffices) rest + | Concat docs -> + let ops = List.map (fun doc -> (ind, mode, doc)) docs in + process ~pos lineSuffices (List.append ops rest) + | Indent doc -> + process ~pos lineSuffices ((ind + 2, mode, doc)::rest) + | IfBreaks {yes = breakDoc; broken = true} -> + process ~pos lineSuffices ((ind, mode, breakDoc)::rest) + | IfBreaks {yes = breakDoc; no = flatDoc} -> + if mode = Break then + process ~pos lineSuffices ((ind, mode, breakDoc)::rest) + else + process ~pos lineSuffices ((ind, mode, flatDoc)::rest) + | LineBreak lineStyle -> + if mode = Break then ( + begin match lineSuffices with + | [] -> + if lineStyle = Literal then ( + MiniBuffer.add_char buffer '\n'; + process ~pos:0 [] rest + ) else ( + MiniBuffer.flush_newline buffer; + MiniBuffer.add_string buffer (String.make ind ' ' [@doesNotRaise]); + process ~pos:ind [] rest + ) + | _docs -> + process ~pos:ind [] (List.concat [List.rev lineSuffices; cmd::rest]) + end + ) else (* mode = Flat *) ( + let pos = match lineStyle with + | Classic -> MiniBuffer.add_string buffer " "; pos + 1 + | Hard -> MiniBuffer.flush_newline buffer; 0 + | Literal -> MiniBuffer.add_char buffer '\n'; 0 + | Soft -> pos + in + process ~pos lineSuffices rest + ) + | Group {shouldBreak; doc} -> + if shouldBreak || not (fits (width - pos) ((ind, Flat, doc)::rest)) then + process ~pos lineSuffices ((ind, Break, doc)::rest) + else + process ~pos lineSuffices ((ind, Flat, doc)::rest) + | CustomLayout docs -> + let rec findGroupThatFits groups = match groups with + | [] -> Nil + | [lastGroup] -> lastGroup + | doc::docs -> + if (fits (width - pos) ((ind, Flat, doc)::rest)) then + doc + else + findGroupThatFits docs + in + let doc = findGroupThatFits docs in + process ~pos lineSuffices ((ind, Flat, doc)::rest) + end + | [] -> + begin match lineSuffices with + | [] -> () + | suffices -> + process ~pos:0 [] (List.rev suffices) + end + in + process ~pos:0 [] [(0, Flat, doc)]; + MiniBuffer.contents buffer + + +let debug t = + let rec toDoc = function + | Nil -> text "nil" + | BreakParent -> text "breakparent" + | Text txt -> text ("text(\"" ^ txt ^ "\")") + | LineSuffix doc -> group( + concat [ + text "linesuffix("; + indent ( + concat [line; toDoc doc] + ); + line; + text ")" + ] + ) + | Concat [] -> text "concat()" + | Concat docs -> group( + concat [ + text "concat("; + indent ( + concat [ + line; + join ~sep:(concat [text ","; line]) + (List.map toDoc docs) ; + ] + ); + line; + text ")" + ] + ) + | CustomLayout docs -> group( + concat [ + text "customLayout("; + indent ( + concat [ + line; + join ~sep:(concat [text ","; line]) + (List.map toDoc docs) ; + ] + ); + line; + text ")" + ] + ) + | Indent doc -> + concat [ + text "indent("; + softLine; + toDoc doc; + softLine; + text ")"; + ] + | IfBreaks {yes = trueDoc; broken = true} -> toDoc trueDoc + | IfBreaks {yes = trueDoc; no = falseDoc} -> + group( + concat [ + text "ifBreaks("; + indent ( + concat [ + line; + toDoc trueDoc; + concat [text ","; line]; + toDoc falseDoc; + ] + ); + line; + text ")" + ] + ) + | LineBreak break -> + let breakTxt = match break with + | Classic -> "Classic" + | Soft -> "Soft" + | Hard -> "Hard" + | Literal -> "Liteal" + in + text ("LineBreak(" ^ breakTxt ^ ")") + | Group {shouldBreak; doc} -> + group( + concat [ + text "Group("; + indent ( + concat [ + line; + text ("{shouldBreak: " ^ (string_of_bool shouldBreak) ^ "}"); + concat [text ","; line]; + toDoc doc; + ] + ); + line; + text ")" + ] + ) + in + let doc = toDoc t in + toString ~width:10 doc |> print_endline + [@@live] diff --git a/analysis/src/vendor/res_outcome_printer/res_doc.mli b/analysis/src/vendor/res_outcome_printer/res_doc.mli new file mode 100644 index 000000000..031afbaf6 --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_doc.mli @@ -0,0 +1,63 @@ +type t + +val nil: t +val line: t +val hardLine: t +val softLine: t +val literalLine: t +val text: string -> t +val concat: t list -> t +val indent: t -> t +val ifBreaks: t -> t -> t +val lineSuffix: t -> t +val group: t -> t +val breakableGroup: forceBreak : bool -> t -> t +(* `customLayout docs` will pick the layout that fits from `docs`. + * This is a very expensive computation as every layout from the list + * will be checked until one fits. *) +val customLayout: t list -> t +val breakParent: t +val join: sep: t -> t list -> t + +val space: t +val comma: t +val dot: t +val dotdot: t +val dotdotdot: t +val lessThan: t +val greaterThan: t +val lbrace: t +val rbrace: t +val lparen: t +val rparen: t +val lbracket: t +val rbracket: t +val question: t +val tilde: t +val equal: t +val trailingComma: t +val doubleQuote: t [@@live] + +(* + * `willBreak doc` checks whether `doc` contains forced line breaks. + * This is more or less a "workaround" to make the parent of a `customLayout` break. + * Forced breaks are not propagated through `customLayout`; otherwise we would always + * get the last layout the algorithm tries… + * This might result into some weird layouts: + * [fn(x => { + * let _ = x + * }), fn(y => { + * let _ = y + * }), fn(z => { + * let _ = z + * })] + * The `[` and `]` would be a lot better broken out. + * Although the layout of `fn(x => {...})` is correct, we need to break its parent (the array). + * `willBreak` can be used in this scenario to check if the `fn…` contains any forced breaks. + * The consumer can then manually insert a `breakParent` doc, to manually propagate the + * force breaks from bottom to top. + *) +val willBreak: t -> bool + +val toString: width: int -> t -> string +val debug: t -> unit [@@live] diff --git a/analysis/src/vendor/res_outcome_printer/res_minibuffer.ml b/analysis/src/vendor/res_outcome_printer/res_minibuffer.ml new file mode 100644 index 000000000..174b5ec6a --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_minibuffer.ml @@ -0,0 +1,50 @@ +type t = { + mutable buffer : bytes; + mutable position : int; + mutable length : int; +} + +let create n = + let n = if n < 1 then 1 else n in + let s = (Bytes.create [@doesNotRaise]) n in + {buffer = s; position = 0; length = n} + +let contents b = (Bytes.sub_string [@doesNotRaise]) b.buffer 0 b.position + +(* Can't be called directly, don't add to the interface *) +let resize_internal b more = + let len = b.length in + let new_len = ref len in + while b.position + more > !new_len do new_len := 2 * !new_len done; + if !new_len > Sys.max_string_length then begin + if b.position + more <= Sys.max_string_length + then new_len := Sys.max_string_length + end; + let new_buffer = (Bytes.create [@doesNotRaise]) !new_len in + (* PR#6148: let's keep using [blit] rather than [unsafe_blit] in + this tricky function that is slow anyway. *) + Bytes.blit b.buffer 0 new_buffer 0 b.position [@doesNotRaise]; + b.buffer <- new_buffer; + b.length <- !new_len + +let add_char b c = + let pos = b.position in + if pos >= b.length then resize_internal b 1; + Bytes.unsafe_set b.buffer pos c; + b.position <- pos + 1 + +let add_string b s = + let len = String.length s in + let new_position = b.position + len in + if new_position > b.length then resize_internal b len; + Bytes.blit_string s 0 b.buffer b.position len [@doesNotRaise]; + b.position <- new_position + +(* adds newline and trims all preceding whitespace *) +let flush_newline b = + let position = ref (b.position) in + while (Bytes.unsafe_get b.buffer (!position - 1)) = ' ' && !position >= 0 do + position := !position - 1; + done; + b.position <- !position; + add_char b '\n' diff --git a/analysis/src/vendor/res_outcome_printer/res_minibuffer.mli b/analysis/src/vendor/res_outcome_printer/res_minibuffer.mli new file mode 100644 index 000000000..0a2bffa53 --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_minibuffer.mli @@ -0,0 +1,6 @@ +type t +val add_char : t -> char -> unit +val add_string : t -> string -> unit +val contents : t -> string +val create : int -> t +val flush_newline : t -> unit diff --git a/analysis/src/vendor/res_outcome_printer/res_outcome_printer.ml b/analysis/src/vendor/res_outcome_printer/res_outcome_printer.ml new file mode 100644 index 000000000..7300bef4e --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_outcome_printer.ml @@ -0,0 +1,1148 @@ +(* For the curious: the outcome printer is a printer to print data + * from the outcometree.mli file in the ocaml compiler. + * The outcome tree is used by: + * - ocaml's toplevel/repl, print results/errors + * - super errors, print nice errors + * - editor tooling, e.g. show type on hover + * + * In general it represent messages to show results or errors to the user. *) + +module Doc = Res_doc +module Token = Res_token + +(* checks if ident contains "arity", like in "arity1", "arity2", "arity3" etc. *) +let isArityIdent ident = + if String.length ident >= 6 then + (String.sub [@doesNotRaise]) ident 0 5 = "arity" + else + false + +type identifierStyle = + | ExoticIdent + | NormalIdent + +let classifyIdentContent ~allowUident txt = + let len = String.length txt in + let rec go i = + if i == len then NormalIdent + else + let c = String.unsafe_get txt i in + if i == 0 && not ( + (allowUident && (c >= 'A' && c <= 'Z')) || + (c >= 'a' && c <= 'z') || c = '_') then + ExoticIdent + else if not ( + (c >= 'a' && c <= 'z') + || (c >= 'A' && c <= 'Z') + || c = '\'' + || c = '_' + || (c >= '0' && c <= '9')) + then + ExoticIdent + else + go (i + 1) + in + if Token.isKeywordTxt txt then + ExoticIdent + else + go 0 + +let printIdentLike ~allowUident txt = + match classifyIdentContent ~allowUident txt with + | ExoticIdent -> Doc.concat [ + Doc.text "\\\""; + Doc.text txt; + Doc.text"\"" + ] + | NormalIdent -> Doc.text txt + +let printPolyVarIdent txt = + match classifyIdentContent ~allowUident:true txt with + | ExoticIdent -> Doc.concat [ + Doc.text "\""; + Doc.text txt; + Doc.text"\"" + ] + | NormalIdent -> Doc.text txt + + (* ReScript doesn't have parenthesized identifiers. + * We don't support custom operators. *) + let parenthesized_ident _name = true + + (* TODO: better allocation strategy for the buffer *) + let escapeStringContents s = + let len = String.length s in + let b = Buffer.create len in + for i = 0 to len - 1 do + let c = (String.get [@doesNotRaise]) s i in + if c = '\008' then ( + Buffer.add_char b '\\'; + Buffer.add_char b 'b'; + ) else if c = '\009' then ( + Buffer.add_char b '\\'; + Buffer.add_char b 't'; + ) else if c = '\010' then ( + Buffer.add_char b '\\'; + Buffer.add_char b 'n'; + ) else if c = '\013' then ( + Buffer.add_char b '\\'; + Buffer.add_char b 'r'; + ) else if c = '\034' then ( + Buffer.add_char b '\\'; + Buffer.add_char b '"'; + ) else if c = '\092' then ( + Buffer.add_char b '\\'; + Buffer.add_char b '\\'; + ) else ( + Buffer.add_char b c; + ); + done; + Buffer.contents b + + (* let rec print_ident fmt ident = match ident with + | Outcometree.Oide_ident s -> Format.pp_print_string fmt s + | Oide_dot (id, s) -> + print_ident fmt id; + Format.pp_print_char fmt '.'; + Format.pp_print_string fmt s + | Oide_apply (id1, id2) -> + print_ident fmt id1; + Format.pp_print_char fmt '('; + print_ident fmt id2; + Format.pp_print_char fmt ')' *) + + let rec printOutIdentDoc ?(allowUident=true) (ident : Outcometree.out_ident) = + match ident with + | Oide_ident s -> printIdentLike ~allowUident s + | Oide_dot (ident, s) -> Doc.concat [ + printOutIdentDoc ident; + Doc.dot; + Doc.text s; + ] + | Oide_apply (call, arg) ->Doc.concat [ + printOutIdentDoc call; + Doc.lparen; + printOutIdentDoc arg; + Doc.rparen; + ] + + let printOutAttributeDoc (outAttribute: Outcometree.out_attribute) = + Doc.concat [ + Doc.text "@"; + Doc.text outAttribute.oattr_name; + ] + + let printOutAttributesDoc (attrs: Outcometree.out_attribute list) = + match attrs with + | [] -> Doc.nil + | attrs -> + Doc.concat [ + Doc.group ( + Doc.join ~sep:Doc.line (List.map printOutAttributeDoc attrs) + ); + Doc.line; + ] + + let rec collectArrowArgs (outType: Outcometree.out_type) args = + match outType with + | Otyp_arrow (label, argType, returnType) -> + let arg = (label, argType) in + collectArrowArgs returnType (arg::args) + | _ as returnType -> + (List.rev args, returnType) + + let rec collectFunctorArgs (outModuleType: Outcometree.out_module_type) args = + match outModuleType with + | Omty_functor (lbl, optModType, returnModType) -> + let arg = (lbl, optModType) in + collectFunctorArgs returnModType (arg::args) + | _ -> + (List.rev args, outModuleType) + + let rec printOutTypeDoc (outType: Outcometree.out_type) = + match outType with + | Otyp_abstract | Otyp_open -> Doc.nil + | Otyp_variant (nonGen, outVariant, closed, labels) -> + (* bool * out_variant * bool * (string list) option *) + let opening = match (closed, labels) with + | (true, None) -> (* [#A | #B] *) Doc.softLine + | (false, None) -> + (* [> #A | #B] *) + Doc.concat [Doc.greaterThan; Doc.line] + | (true, Some []) -> + (* [< #A | #B] *) + Doc.concat [Doc.lessThan; Doc.line] + | (true, Some _) -> + (* [< #A | #B > #X #Y ] *) + Doc.concat [Doc.lessThan; Doc.line] + | (false, Some _) -> + (* impossible!? ocaml seems to print ?, see oprint.ml in 4.06 *) + Doc.concat [Doc.text "?"; Doc.line] + in + Doc.group ( + Doc.concat [ + if nonGen then Doc.text "_" else Doc.nil; + Doc.lbracket; + Doc.indent ( + Doc.concat [ + opening; + printOutVariant outVariant + ] + ); + begin match labels with + | None | Some [] -> Doc.nil + | Some tags -> + Doc.group ( + Doc.concat [ + Doc.space; + Doc.join ~sep:Doc.space ( + List.map (fun lbl -> printIdentLike ~allowUident:true lbl) tags + ) + ] + ) + end; + Doc.softLine; + Doc.rbracket; + ] + ) + | Otyp_alias (typ, aliasTxt) -> + Doc.concat [ + printOutTypeDoc typ; + Doc.text " as '"; + Doc.text aliasTxt + ] + | Otyp_constr ( + Oide_dot (Oide_dot (Oide_ident "Js", "Fn") , "arity0"), (* Js.Fn.arity0 *) + [Otyp_constr (Oide_ident ident, [])] (* int or unit or string *) + ) -> + (* Js.Fn.arity0 -> (.) => int*) + Doc.concat [ + Doc.text "(.) => "; + Doc.text ident; + ] + | Otyp_constr ( + Oide_dot (Oide_dot (Oide_ident "Js", "Fn") , ident), (* Js.Fn.arity2 *) + [(Otyp_arrow _) as arrowType] (* (int, int) => int *) + ) when isArityIdent ident -> + (* Js.Fn.arity2<(int, int) => int> -> (. int, int) => int*) + printOutArrowType ~uncurried:true arrowType + | Otyp_constr (outIdent, []) -> + printOutIdentDoc ~allowUident:false outIdent + | Otyp_manifest (typ1, typ2) -> + Doc.concat [ + printOutTypeDoc typ1; + Doc.text " = "; + printOutTypeDoc typ2; + ] + | Otyp_record record -> + printRecordDeclarationDoc ~inline:true record + | Otyp_stuff txt -> Doc.text txt + | Otyp_var (ng, s) -> Doc.concat [ + Doc.text ("'" ^ (if ng then "_" else "")); + Doc.text s + ] + | Otyp_object (fields, rest) -> printObjectFields fields rest + | Otyp_class _ -> Doc.nil + | Otyp_attribute (typ, attribute) -> + Doc.group ( + Doc.concat [ + printOutAttributeDoc attribute; + Doc.line; + printOutTypeDoc typ; + ] + ) + (* example: Red | Blue | Green | CustomColour(float, float, float) *) + | Otyp_sum constructors -> + printOutConstructorsDoc constructors + + (* example: {"name": string, "age": int} *) + | Otyp_constr ( + (Oide_dot ((Oide_ident "Js"), "t")), + [Otyp_object (fields, rest)] + ) -> printObjectFields fields rest + + (* example: node *) + | Otyp_constr (outIdent, args) -> + let argsDoc = match args with + | [] -> Doc.nil + | args -> + Doc.concat [ + Doc.lessThan; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutTypeDoc args + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.greaterThan; + ] + in + Doc.group ( + Doc.concat [ + printOutIdentDoc outIdent; + argsDoc; + ] + ) + | Otyp_tuple tupleArgs -> + Doc.group ( + Doc.concat [ + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutTypeDoc tupleArgs + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + | Otyp_poly (vars, outType) -> + Doc.group ( + Doc.concat [ + Doc.join ~sep:Doc.space ( + List.map (fun var -> Doc.text ("'" ^ var)) vars + ); + Doc.dot; + Doc.space; + printOutTypeDoc outType; + ] + ) + | Otyp_arrow _ as typ -> + printOutArrowType ~uncurried:false typ + | Otyp_module (_modName, _stringList, _outTypes) -> + Doc.nil + + and printOutArrowType ~uncurried typ = + let (typArgs, typ) = collectArrowArgs typ [] in + let args = Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map (fun (lbl, typ) -> + if lbl = "" then + printOutTypeDoc typ + else + Doc.group ( + Doc.concat [ + Doc.text ("~" ^ lbl ^ ": "); + printOutTypeDoc typ + ] + ) + ) typArgs + ) in + let argsDoc = + let needsParens = match typArgs with + | _ when uncurried -> true + | [_, (Otyp_tuple _ | Otyp_arrow _)] -> true + (* single argument should not be wrapped *) + | ["", _] -> false + | _ -> true + in + if needsParens then + Doc.group ( + Doc.concat [ + if uncurried then Doc.text "(. " else Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + args; + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + else args + in + Doc.concat [ + argsDoc; + Doc.text " => "; + printOutTypeDoc typ; + ] + + + and printOutVariant variant = match variant with + | Ovar_fields fields -> (* (string * bool * out_type list) list *) + Doc.join ~sep:Doc.line ( + (* + * [< | #T([< u2]) & ([< u2]) & ([< u1])] --> no ampersand + * [< | #S & ([< u2]) & ([< u2]) & ([< u1])] --> ampersand + *) + List.mapi (fun i (name, ampersand, types) -> + let needsParens = match types with + | [(Outcometree.Otyp_tuple _)] -> false + | _ -> true + in + Doc.concat [ + if i > 0 then + Doc.text "| " + else + Doc.ifBreaks (Doc.text "| ") Doc.nil; + Doc.group ( + Doc.concat [ + Doc.text "#"; + printPolyVarIdent name; + match types with + | [] -> Doc.nil + | types -> + Doc.concat [ + if ampersand then Doc.text " & " else Doc.nil; + Doc.indent ( + Doc.concat [ + Doc.join ~sep:(Doc.concat [Doc.text " &"; Doc.line]) + (List.map (fun typ -> + let outTypeDoc = printOutTypeDoc typ in + if needsParens then + Doc.concat [Doc.lparen; outTypeDoc; Doc.rparen] + else + outTypeDoc + ) types) + ]; + ); + ] + ] + ) + ] + ) fields + ) + | Ovar_typ typ -> printOutTypeDoc typ + + and printObjectFields fields rest = + let dots = match rest with + | Some non_gen -> Doc.text ((if non_gen then "_" else "") ^ "..") + | None -> Doc.nil + in + Doc.group ( + Doc.concat [ + Doc.lbrace; + dots; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map (fun (lbl, outType) -> Doc.group ( + Doc.concat [ + Doc.text ("\"" ^ lbl ^ "\": "); + printOutTypeDoc outType; + ] + )) fields + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rbrace; + ] + ) + + + and printOutConstructorsDoc constructors = + Doc.group ( + Doc.indent ( + Doc.concat [ + Doc.line; + Doc.join ~sep:Doc.line ( + List.mapi (fun i constructor -> + Doc.concat [ + if i > 0 then Doc.text "| " else Doc.ifBreaks (Doc.text "| ") Doc.nil; + printOutConstructorDoc constructor; + ] + ) constructors + ) + ] + ) + ) + + and printOutConstructorDoc (name, args, gadt) = + let gadtDoc = match gadt with + | Some outType -> + Doc.concat [ + Doc.text ": "; + printOutTypeDoc outType + ] + | None -> Doc.nil + in + let argsDoc = match args with + | [] -> Doc.nil + | [Otyp_record record] -> + (* inline records + * | Root({ + * mutable value: 'value, + * mutable updatedTime: float, + * }) + *) + Doc.concat [ + Doc.lparen; + Doc.indent ( + printRecordDeclarationDoc ~inline:true record; + ); + Doc.rparen; + ] + | _types -> + Doc.indent ( + Doc.concat [ + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutTypeDoc args + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + in + Doc.group ( + Doc.concat [ + Doc.text name; + argsDoc; + gadtDoc + ] + ) + + and printRecordDeclRowDoc (name, mut, arg) = + Doc.group ( + Doc.concat [ + if mut then Doc.text "mutable " else Doc.nil; + printIdentLike ~allowUident:false name; + Doc.text ": "; + printOutTypeDoc arg; + ] + ) + + and printRecordDeclarationDoc ~inline rows = + let content = Doc.concat [ + Doc.lbrace; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printRecordDeclRowDoc rows + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rbrace; + ] in + if not inline then + Doc.group content + else content + + let printOutType fmt outType = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutTypeDoc outType)) + + let printTypeParameterDoc (typ, (co, cn)) = + Doc.concat [ + if not cn then Doc.text "+" else if not co then Doc.text "-" else Doc.nil; + if typ = "_" then Doc.text "_" else Doc.text ("'" ^ typ) + ] + + + let rec printOutSigItemDoc (outSigItem : Outcometree.out_sig_item) = + match outSigItem with + | Osig_class _ | Osig_class_type _ -> Doc.nil + | Osig_ellipsis -> Doc.dotdotdot + | Osig_value valueDecl -> + Doc.group ( + Doc.concat [ + printOutAttributesDoc valueDecl.oval_attributes; + Doc.text ( + match valueDecl.oval_prims with | [] -> "let " | _ -> "external " + ); + Doc.text valueDecl.oval_name; + Doc.text ":"; + Doc.space; + printOutTypeDoc valueDecl.oval_type; + match valueDecl.oval_prims with + | [] -> Doc.nil + | primitives -> Doc.indent ( + Doc.concat [ + Doc.text " ="; + Doc.line; + Doc.group ( + Doc.join ~sep:Doc.line (List.map (fun prim -> Doc.text ("\"" ^ prim ^ "\"")) primitives) + ) + ] + ) + ] + ) + | Osig_typext (outExtensionConstructor, _outExtStatus) -> + printOutExtensionConstructorDoc outExtensionConstructor + | Osig_modtype (modName, Omty_signature []) -> + Doc.concat [ + Doc.text "module type "; + Doc.text modName; + ] + | Osig_modtype (modName, outModuleType) -> + Doc.group ( + Doc.concat [ + Doc.text "module type "; + Doc.text modName; + Doc.text " = "; + printOutModuleTypeDoc outModuleType; + ] + ) + | Osig_module (modName, Omty_alias ident, _) -> + Doc.group ( + Doc.concat [ + Doc.text "module "; + Doc.text modName; + Doc.text " ="; + Doc.line; + printOutIdentDoc ident; + ] + ) + | Osig_module (modName, outModType, outRecStatus) -> + Doc.group ( + Doc.concat [ + Doc.text ( + match outRecStatus with + | Orec_not -> "module " + | Orec_first -> "module rec " + | Orec_next -> "and" + ); + Doc.text modName; + Doc.text " = "; + printOutModuleTypeDoc outModType; + ] + ) + | Osig_type (outTypeDecl, outRecStatus) -> + (* TODO: manifest ? *) + let attrs = match outTypeDecl.otype_immediate, outTypeDecl.otype_unboxed with + | false, false -> Doc.nil + | true, false -> + Doc.concat [Doc.text "@immediate"; Doc.line] + | false, true -> + Doc.concat [Doc.text "@unboxed"; Doc.line] + | true, true -> + Doc.concat [Doc.text "@immediate @unboxed"; Doc.line] + in + let kw = Doc.text ( + match outRecStatus with + | Orec_not -> "type " + | Orec_first -> "type rec " + | Orec_next -> "and " + ) in + let typeParams = match outTypeDecl.otype_params with + | [] -> Doc.nil + | _params -> Doc.group ( + Doc.concat [ + Doc.lessThan; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printTypeParameterDoc outTypeDecl.otype_params + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.greaterThan; + ] + ) + in + let privateDoc = match outTypeDecl.otype_private with + | Asttypes.Private -> Doc.text "private " + | Public -> Doc.nil + in + let kind = match outTypeDecl.otype_type with + | Otyp_open -> Doc.concat [ + Doc.text " = "; + privateDoc; + Doc.text ".."; + ] + | Otyp_abstract -> Doc.nil + | Otyp_record record -> Doc.concat [ + Doc.text " = "; + privateDoc; + printRecordDeclarationDoc ~inline:false record; + ] + | typ -> Doc.concat [ + Doc.text " = "; + printOutTypeDoc typ + ] + in + let constraints = match outTypeDecl.otype_cstrs with + | [] -> Doc.nil + | _ -> Doc.group ( + Doc.indent ( + Doc.concat [ + Doc.hardLine; + Doc.join ~sep:Doc.line (List.map (fun (typ1, typ2) -> + Doc.group ( + Doc.concat [ + Doc.text "constraint "; + printOutTypeDoc typ1; + Doc.text " ="; + Doc.space; + printOutTypeDoc typ2; + ] + ) + ) outTypeDecl.otype_cstrs) + ] + ) + ) in + Doc.group ( + Doc.concat [ + attrs; + Doc.group ( + Doc.concat [ + attrs; + kw; + printIdentLike ~allowUident:false outTypeDecl.otype_name; + typeParams; + kind + ] + ); + constraints + ] + ) + + and printOutModuleTypeDoc (outModType : Outcometree.out_module_type) = + match outModType with + | Omty_abstract -> Doc.nil + | Omty_ident ident -> printOutIdentDoc ident + (* example: module Increment = (M: X_int) => X_int *) + | Omty_functor _ -> + let (args, returnModType) = collectFunctorArgs outModType [] in + let argsDoc = match args with + | [_, None] -> Doc.text "()" + | args -> + Doc.group ( + Doc.concat [ + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map (fun (lbl, optModType) -> Doc.group ( + Doc.concat [ + Doc.text lbl; + match optModType with + | None -> Doc.nil + | Some modType -> Doc.concat [ + Doc.text ": "; + printOutModuleTypeDoc modType; + ] + ] + )) args + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + in + Doc.group ( + Doc.concat [ + argsDoc; + Doc.text " => "; + printOutModuleTypeDoc returnModType + ] + ) + | Omty_signature [] -> Doc.nil + | Omty_signature signature -> + Doc.breakableGroup ~forceBreak:true ( + Doc.concat [ + Doc.lbrace; + Doc.indent ( + Doc.concat [ + Doc.line; + printOutSignatureDoc signature; + ] + ); + Doc.softLine; + Doc.rbrace; + ] + ) + | Omty_alias _ident -> Doc.nil + + and printOutSignatureDoc (signature : Outcometree.out_sig_item list) = + let rec loop signature acc = + match signature with + | [] -> List.rev acc + | Outcometree.Osig_typext(ext, Oext_first) :: items -> + (* Gather together the extension constructors *) + let rec gather_extensions acc items = + match items with + Outcometree.Osig_typext(ext, Oext_next) :: items -> + gather_extensions + ((ext.oext_name, ext.oext_args, ext.oext_ret_type) :: acc) + items + | _ -> (List.rev acc, items) + in + let exts, items = + gather_extensions + [(ext.oext_name, ext.oext_args, ext.oext_ret_type)] + items + in + let te = + { Outcometree.otyext_name = ext.oext_type_name; + otyext_params = ext.oext_type_params; + otyext_constructors = exts; + otyext_private = ext.oext_private } + in + let doc = printOutTypeExtensionDoc te in + loop items (doc::acc) + | item::items -> + let doc = printOutSigItemDoc item in + loop items (doc::acc) + in + match loop signature [] with + | [doc] -> doc + | docs -> + Doc.breakableGroup ~forceBreak:true ( + Doc.join ~sep:Doc.line docs + ) + + and printOutExtensionConstructorDoc (outExt : Outcometree.out_extension_constructor) = + let typeParams = match outExt.oext_type_params with + | [] -> Doc.nil + | params -> + Doc.group( + Doc.concat [ + Doc.lessThan; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) (List.map + (fun ty -> Doc.text (if ty = "_" then ty else "'" ^ ty)) + params + + ) + ] + ); + Doc.softLine; + Doc.greaterThan; + ] + ) + + in + Doc.group ( + Doc.concat [ + Doc.text "type "; + printIdentLike ~allowUident:false outExt.oext_type_name; + typeParams; + Doc.text " += "; + Doc.line; + if outExt.oext_private = Asttypes.Private then + Doc.text "private " + else + Doc.nil; + printOutConstructorDoc + (outExt.oext_name, outExt.oext_args, outExt.oext_ret_type) + ] + ) + + and printOutTypeExtensionDoc (typeExtension : Outcometree.out_type_extension) = + let typeParams = match typeExtension.otyext_params with + | [] -> Doc.nil + | params -> + Doc.group( + Doc.concat [ + Doc.lessThan; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) (List.map + (fun ty -> Doc.text (if ty = "_" then ty else "'" ^ ty)) + params + + ) + ] + ); + Doc.softLine; + Doc.greaterThan; + ] + ) + + in + Doc.group ( + Doc.concat [ + Doc.text "type "; + printIdentLike ~allowUident:false typeExtension.otyext_name; + typeParams; + Doc.text " += "; + if typeExtension.otyext_private = Asttypes.Private then + Doc.text "private " + else + Doc.nil; + printOutConstructorsDoc typeExtension.otyext_constructors; + ] + ) + + let printOutSigItem fmt outSigItem = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutSigItemDoc outSigItem)) + + let printOutSignature fmt signature = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutSignatureDoc signature)) + + let validFloatLexeme s = + let l = String.length s in + let rec loop i = + if i >= l then s ^ "." else + match (s.[i] [@doesNotRaise]) with + | '0' .. '9' | '-' -> loop (i+1) + | _ -> s + in loop 0 + + let floatRepres f = + match classify_float f with + | FP_nan -> "nan" + | FP_infinite -> + if f < 0.0 then "neg_infinity" else "infinity" + | _ -> + let float_val = + let s1 = Printf.sprintf "%.12g" f in + if f = (float_of_string [@doesNotRaise]) s1 then s1 else + let s2 = Printf.sprintf "%.15g" f in + if f = (float_of_string [@doesNotRaise]) s2 then s2 else + Printf.sprintf "%.18g" f + in validFloatLexeme float_val + + let rec printOutValueDoc (outValue : Outcometree.out_value) = + match outValue with + | Oval_array outValues -> + Doc.group ( + Doc.concat [ + Doc.lbracket; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutValueDoc outValues + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rbracket; + ] + ) + | Oval_char c -> Doc.text ("'" ^ (Char.escaped c) ^ "'") + | Oval_constr (outIdent, outValues) -> + Doc.group ( + Doc.concat [ + printOutIdentDoc outIdent; + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutValueDoc outValues + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + | Oval_ellipsis -> Doc.text "..." + | Oval_int i -> Doc.text (Format.sprintf "%i" i) + | Oval_int32 i -> Doc.text (Format.sprintf "%lil" i) + | Oval_int64 i -> Doc.text (Format.sprintf "%LiL" i) + | Oval_nativeint i -> Doc.text (Format.sprintf "%nin" i) + | Oval_float f -> Doc.text (floatRepres f) + | Oval_list outValues -> + Doc.group ( + Doc.concat [ + Doc.text "list["; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutValueDoc outValues + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rbracket; + ] + ) + | Oval_printer fn -> + let fmt = Format.str_formatter in + fn fmt; + let str = Format.flush_str_formatter () in + Doc.text str + | Oval_record rows -> + Doc.group ( + Doc.concat [ + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map (fun (outIdent, outValue) -> Doc.group ( + Doc.concat [ + printOutIdentDoc outIdent; + Doc.text ": "; + printOutValueDoc outValue; + ] + ) + ) rows + ); + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + | Oval_string (txt, _sizeToPrint, _kind) -> + Doc.text (escapeStringContents txt) + | Oval_stuff txt -> Doc.text txt + | Oval_tuple outValues -> + Doc.group ( + Doc.concat [ + Doc.lparen; + Doc.indent ( + Doc.concat [ + Doc.softLine; + Doc.join ~sep:(Doc.concat [Doc.comma; Doc.line]) ( + List.map printOutValueDoc outValues + ) + ] + ); + Doc.trailingComma; + Doc.softLine; + Doc.rparen; + ] + ) + (* Not supported by ReScript *) + | Oval_variant _ -> Doc.nil + + let printOutExceptionDoc exc outValue = + match exc with + | Sys.Break -> Doc.text "Interrupted." + | Out_of_memory -> Doc.text "Out of memory during evaluation." + | Stack_overflow -> + Doc.text "Stack overflow during evaluation (looping recursion?)." + | _ -> + Doc.group ( + Doc.indent( + Doc.concat [ + Doc.text "Exception:"; + Doc.line; + printOutValueDoc outValue; + ] + ) + ) + + let printOutPhraseSignature signature = + let rec loop signature acc = + match signature with + | [] -> List.rev acc + | (Outcometree.Osig_typext(ext, Oext_first), None)::signature -> + (* Gather together extension constructors *) + let rec gather_extensions acc items = + match items with + | (Outcometree.Osig_typext(ext, Oext_next), None)::items -> + gather_extensions + ((ext.oext_name, ext.oext_args, ext.oext_ret_type)::acc) + items + | _ -> (List.rev acc, items) + in + let exts, signature = + gather_extensions + [(ext.oext_name, ext.oext_args, ext.oext_ret_type)] + signature + in + let te = + { Outcometree.otyext_name = ext.oext_type_name; + otyext_params = ext.oext_type_params; + otyext_constructors = exts; + otyext_private = ext.oext_private } + in + let doc = printOutTypeExtensionDoc te in + loop signature (doc::acc) + | (sigItem, optOutValue)::signature -> + let doc = match optOutValue with + | None -> + printOutSigItemDoc sigItem + | Some outValue -> + Doc.group ( + Doc.concat [ + printOutSigItemDoc sigItem; + Doc.text " = "; + printOutValueDoc outValue; + ] + ) + in + loop signature (doc::acc) + in + Doc.breakableGroup ~forceBreak:true ( + Doc.join ~sep:Doc.line (loop signature []) + ) + + let printOutPhraseDoc (outPhrase : Outcometree.out_phrase) = + match outPhrase with + | Ophr_eval (outValue, outType) -> + Doc.group ( + Doc.concat [ + Doc.text "- : "; + printOutTypeDoc outType; + Doc.text " ="; + Doc.indent ( + Doc.concat [ + Doc.line; + printOutValueDoc outValue; + ] + ) + ] + ) + | Ophr_signature [] -> Doc.nil + | Ophr_signature signature -> printOutPhraseSignature signature + | Ophr_exception (exc, outValue) -> + printOutExceptionDoc exc outValue + + let printOutPhrase fmt outPhrase = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutPhraseDoc outPhrase)) + + let printOutModuleType fmt outModuleType = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutModuleTypeDoc outModuleType)) + + let printOutTypeExtension fmt typeExtension = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutTypeExtensionDoc typeExtension)) + + let printOutValue fmt outValue = + Format.pp_print_string fmt + (Doc.toString ~width:80 (printOutValueDoc outValue)) + + + + + +(* Not supported in ReScript *) +(* Oprint.out_class_type *) + let setup = lazy begin + Oprint.out_value := printOutValue; + Oprint.out_type := printOutType; + Oprint.out_module_type := printOutModuleType; + Oprint.out_sig_item := printOutSigItem; + Oprint.out_signature := printOutSignature; + Oprint.out_type_extension := printOutTypeExtension; + Oprint.out_phrase := printOutPhrase + end + diff --git a/analysis/src/vendor/res_outcome_printer/res_token.ml b/analysis/src/vendor/res_outcome_printer/res_token.ml new file mode 100644 index 000000000..a14491dd6 --- /dev/null +++ b/analysis/src/vendor/res_outcome_printer/res_token.ml @@ -0,0 +1,213 @@ +module Comment = Res_comment + +type t = + | Open + | True | False + | Character of char + | Int of {i: string; suffix: char option} + | Float of {f: string; suffix: char option} + | String of string + | Lident of string + | Uident of string + | As + | Dot | DotDot | DotDotDot + | Bang + | Semicolon + | Let + | And + | Rec + | Underscore + | SingleQuote + | Equal | EqualEqual | EqualEqualEqual + | Bar + | Lparen + | Rparen + | Lbracket + | Rbracket + | Lbrace + | Rbrace + | Colon + | Comma + | Eof + | Exception + | Backslash [@live] + | Forwardslash | ForwardslashDot + | Asterisk | AsteriskDot | Exponentiation + | Minus | MinusDot + | Plus | PlusDot | PlusPlus | PlusEqual + | ColonGreaterThan + | GreaterThan + | LessThan + | LessThanSlash + | Hash | HashEqual + | Assert + | Lazy + | Tilde + | Question + | If | Else | For | In | While | Switch + | When + | EqualGreater | MinusGreater + | External + | Typ + | Private + | Mutable + | Constraint + | Include + | Module + | Of + | Land | Lor + | Band (* Bitwise and: & *) + | BangEqual | BangEqualEqual + | LessEqual | GreaterEqual + | ColonEqual + | At | AtAt + | Percent | PercentPercent + | Comment of Comment.t + | List + | TemplateTail of string + | TemplatePart of string + | Backtick + | BarGreater + | Try + | Import + | Export + +let precedence = function + | HashEqual | ColonEqual -> 1 + | Lor -> 2 + | Land -> 3 + | Equal | EqualEqual | EqualEqualEqual | LessThan | GreaterThan + | BangEqual | BangEqualEqual | LessEqual | GreaterEqual | BarGreater -> 4 + | Plus | PlusDot | Minus | MinusDot | PlusPlus -> 5 + | Asterisk | AsteriskDot | Forwardslash | ForwardslashDot -> 6 + | Exponentiation -> 7 + | MinusGreater -> 8 + | Dot -> 9 + | _ -> 0 + +let toString = function + | Open -> "open" + | True -> "true" | False -> "false" + | Character c -> "character '" ^ (Char.escaped c) ^ "'" + | String s -> "string \"" ^ s ^ "\"" + | Lident str -> str + | Uident str -> str + | Dot -> "." | DotDot -> ".." | DotDotDot -> "..." + | Int {i} -> "int " ^ i + | Float {f} -> "Float: " ^ f + | Bang -> "!" + | Semicolon -> ";" + | Let -> "let" + | And -> "and" + | Rec -> "rec" + | Underscore -> "_" + | SingleQuote -> "'" + | Equal -> "=" | EqualEqual -> "==" | EqualEqualEqual -> "===" + | Eof -> "eof" + | Bar -> "|" + | As -> "as" + | Lparen -> "(" | Rparen -> ")" + | Lbracket -> "[" | Rbracket -> "]" + | Lbrace -> "{" | Rbrace -> "}" + | ColonGreaterThan -> ":>" + | Colon -> ":" + | Comma -> "," + | Minus -> "-" | MinusDot -> "-." + | Plus -> "+" | PlusDot -> "+." | PlusPlus -> "++" | PlusEqual -> "+=" + | Backslash -> "\\" + | Forwardslash -> "/" | ForwardslashDot -> "/." + | Exception -> "exception" + | Hash -> "#" | HashEqual -> "#=" + | GreaterThan -> ">" + | LessThan -> "<" + | LessThanSlash -> " "*" | AsteriskDot -> "*." | Exponentiation -> "**" + | Assert -> "assert" + | Lazy -> "lazy" + | Tilde -> "tilde" + | Question -> "?" + | If -> "if" + | Else -> "else" + | For -> "for" + | In -> "in" + | While -> "while" + | Switch -> "switch" + | When -> "when" + | EqualGreater -> "=>" | MinusGreater -> "->" + | External -> "external" + | Typ -> "type" + | Private -> "private" + | Constraint -> "constraint" + | Mutable -> "mutable" + | Include -> "include" + | Module -> "module" + | Of -> "of" + | Lor -> "||" + | Band -> "&" | Land -> "&&" + | BangEqual -> "!=" | BangEqualEqual -> "!==" + | GreaterEqual -> ">=" | LessEqual -> "<=" + | ColonEqual -> ":=" + | At -> "@" | AtAt -> "@@" + | Percent -> "%" | PercentPercent -> "%%" + | Comment c -> "Comment" ^ (Comment.toString c) + | List -> "list{" + | TemplatePart text -> text ^ "${" + | TemplateTail text -> "TemplateTail(" ^ text ^ ")" + | Backtick -> "`" + | BarGreater -> "|>" + | Try -> "try" + | Import -> "import" + | Export -> "export" + +let keywordTable = function +| "and" -> And +| "as" -> As +| "assert" -> Assert +| "constraint" -> Constraint +| "else" -> Else +| "exception" -> Exception +| "export" -> Export +| "external" -> External +| "false" -> False +| "for" -> For +| "if" -> If +| "import" -> Import +| "in" -> In +| "include" -> Include +| "lazy" -> Lazy +| "let" -> Let +| "list{" -> List +| "module" -> Module +| "mutable" -> Mutable +| "of" -> Of +| "open" -> Open +| "private" -> Private +| "rec" -> Rec +| "switch" -> Switch +| "true" -> True +| "try" -> Try +| "type" -> Typ +| "when" -> When +| "while" -> While +| _ -> raise Not_found +[@@raises Not_found] + +let isKeyword = function + | And | As | Assert | Constraint | Else | Exception | Export + | External | False | For | If | Import | In | Include | Land | Lazy + | Let | List | Lor | Module | Mutable | Of | Open | Private | Rec + | Switch | True | Try | Typ | When | While -> true + | _ -> false + +let lookupKeyword str = + try keywordTable str with + | Not_found -> + match str.[0] [@doesNotRaise] with + | 'A'..'Z' -> Uident str + | _ -> Lident str + +let isKeywordTxt str = + try let _ = keywordTable str in true with + | Not_found -> false + +let catch = Lident "catch" diff --git a/analysis/test.sh b/analysis/test.sh new file mode 100755 index 000000000..5a2fa163e --- /dev/null +++ b/analysis/test.sh @@ -0,0 +1,19 @@ +function exp { + echo "$(dirname $1)/expected/$(basename $1).txt" +} + +for file in tests/src/*.res; do + ../server/analysis_binaries/current-platform.exe test $file &> $(exp $file) +done + +warningYellow='\033[0;33m' +successGreen='\033[0;32m' +reset='\033[0m' + +diff=$(git ls-files --modified tests/src/expected) +if [[ $diff = "" ]]; then + printf "${successGreen}✅ No unstaged tests difference.${reset}\n" +else + printf "${warningYellow}⚠️ There are unstaged differences in tests/! Did you break a test?\n${diff}\n${reset}" + exit 1 +fi diff --git a/analysis/tests/bsconfig.json b/analysis/tests/bsconfig.json new file mode 100644 index 000000000..a65991b94 --- /dev/null +++ b/analysis/tests/bsconfig.json @@ -0,0 +1,6 @@ +{ + "name": "test", + "sources": "src", + "bs-dependencies": ["reason-react"], + "reason": { "react-jsx": 3 } +} diff --git a/analysis/tests/package-lock.json b/analysis/tests/package-lock.json new file mode 100644 index 000000000..223207d01 --- /dev/null +++ b/analysis/tests/package-lock.json @@ -0,0 +1,47 @@ +{ + "name": "tests", + "lockfileVersion": 2, + "requires": true, + "packages": { + "": { + "dependencies": { + "reason-react": "^0.9.1" + }, + "devDependencies": { + "rescript": "9.1.1" + } + }, + "node_modules/reason-react": { + "version": "0.9.1", + "license": "MIT", + "peerDependencies": { + "bs-platform": "^7.1.1", + "react": "^16.8.1", + "react-dom": "^16.8.1" + } + }, + "node_modules/rescript": { + "version": "9.1.1", + "dev": true, + "hasInstallScript": true, + "license": "SEE LICENSE IN LICENSE", + "bin": { + "bsb": "bsb", + "bsc": "bsc", + "bsrefmt": "bsrefmt", + "bstracing": "lib/bstracing", + "rescript": "rescript" + } + } + }, + "dependencies": { + "reason-react": { + "version": "0.9.1", + "requires": {} + }, + "rescript": { + "version": "9.1.1", + "dev": true + } + } +} diff --git a/analysis/tests/package.json b/analysis/tests/package.json new file mode 100644 index 000000000..79294d08b --- /dev/null +++ b/analysis/tests/package.json @@ -0,0 +1,13 @@ +{ + "dependencies": { + "reason-react": "^0.9.1" + }, + "devDependencies": { + "rescript": "9.1.1" + }, + "scripts": { + "build": "bsb -make-world", + "start": "bsb -make-world -w", + "clean": "bsb -clean" + } +} diff --git a/analysis/tests/src/Auto.res b/analysis/tests/src/Auto.res new file mode 100644 index 000000000..c33d1614b --- /dev/null +++ b/analysis/tests/src/Auto.res @@ -0,0 +1,4 @@ +open! Belt + +let m = List.map +// ^hov \ No newline at end of file diff --git a/analysis/tests/src/Complete.res b/analysis/tests/src/Complete.res new file mode 100644 index 000000000..8b3bd744f --- /dev/null +++ b/analysis/tests/src/Complete.res @@ -0,0 +1,57 @@ +module MyList = Belt.List +//^com MyList.m +//^com Array. +//^com Array.m + + +module Dep: { + @ocaml.doc("Some doc comment") @deprecated("Use customDouble instead") + let customDouble: int => int +} = { + let customDouble = foo => foo * 2 +} + +//^com let cc = Dep.c + +module Lib = { + let foo = (~age, ~name) => name ++ string_of_int(age) + let next = (~number=0, ~year) => number + year +} + +//^com let x = foo(~ + +//^com [1,2,3]->m + +//^com "abc"->toU + +let op = Some(3) + +//^com op->e + +module ForAuto = { + type t = int + let abc = (x:t, _y:int) => x + let abd = (x:t, _y:int) => x +} + +let fa:ForAuto.t = 34 +//^com fa-> + +//^com "hello"->Js.Dict.u + +module O = { + module Comp = { + @react.component + let make = (~first="", ~zoo=3, ~second) => + React.string(first ++ second ++ string_of_int(zoo)) + } +} + +let zzz = 11 + +//^com let comp = int +} = { + let customDouble = foo => foo * 2 +} + +module D = Dep +// ^hov + +let cd = D.customDouble +// ^hov + +module HoverInsideModuleWithComponent = { + let x = 2 // check that hover on x works +// ^hov + @react.component + let make = () => React.null +} + +@ocaml.doc("Doc comment for functionWithTypeAnnotation") +let functionWithTypeAnnotation : unit => int = () => 1 +// ^hov + +@react.component +let make = (~name) => React.string(name) +// ^hov + +@react.component +let make2 = (~name:string) => React.string(name) +// ^hov \ No newline at end of file diff --git a/analysis/tests/src/Jsx.res b/analysis/tests/src/Jsx.res new file mode 100644 index 000000000..d28ce76ec --- /dev/null +++ b/analysis/tests/src/Jsx.res @@ -0,0 +1,12 @@ +module M = { + @react.component + let make = (~first, ~fun="", ~second="") => React.string(first ++ fun++ second) +} + +let d = +// ^def + +//^com , 'a => 'b) => Belt.List.t<'b>\n```\n\n`map xs f`\n\nreturn the list obtained by applying `f` to each element of `xs`\n\n```ml\nmap [1;2] (fun x-> x + 1) = [3;4]\n```\n"} + diff --git a/analysis/tests/src/expected/Complete.res.txt b/analysis/tests/src/expected/Complete.res.txt new file mode 100644 index 000000000..355e25ab0 --- /dev/null +++ b/analysis/tests/src/expected/Complete.res.txt @@ -0,0 +1,456 @@ +Complete tests/src/Complete.res 0:2 +[{ + "label": "mapReverse", + "kind": 12, + "tags": [], + "detail": "(t<'a>, 'a => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "`mapReverse xs f`\n\nEquivalent to `reverse (map xs f)`\n\n```ml\nmapReverse [3;4;5] (fun x -> x * x) = [25;16;9];;\n```\n\n\n\nbelt_List.mli:299"} + }, { + "label": "makeBy", + "kind": 12, + "tags": [], + "detail": "(int, int => 'a) => t<'a>", + "documentation": {"kind": "markdown", "value": "`makeBy n f`\n\n- return a list of length `n` with element `i` initialized with `f i`\n\n\n- return the empty list if `n` is negative\n\n\n\n```ml\nmakeBy 5 (fun i -> i) = [0;1;2;3;4];;\nmakeBy 5 (fun i -> i * i) = [0;1;4;9;16];;\n```\n\n\n\nbelt_List.mli:127"} + }, { + "label": "make", + "kind": 12, + "tags": [], + "detail": "(int, 'a) => t<'a>", + "documentation": {"kind": "markdown", "value": "`make n v`\n\n- return a list of length `n` with each element filled with value `v`\n\n\n- return the empty list if `n` is negative\n\n\n\n```ml\nmake 3 1 = [1;1;1]\n```\n\n\n\nbelt_List.mli:115"} + }, { + "label": "mapReverse2U", + "kind": 12, + "tags": [], + "detail": "(t<'a>, t<'b>, (. 'a, 'b) => 'c) => t<'c>", + "documentation": {"kind": "markdown", "value": "\nbelt_List.mli:391"} + }, { + "label": "map", + "kind": 12, + "tags": [], + "detail": "(t<'a>, 'a => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "`map xs f`\n\nreturn the list obtained by applying `f` to each element of `xs`\n\n```ml\nmap [1;2] (fun x-> x + 1) = [3;4]\n```\n\n\n\nbelt_List.mli:222"} + }, { + "label": "mapWithIndexU", + "kind": 12, + "tags": [], + "detail": "(t<'a>, (. int, 'a) => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "\nbelt_List.mli:258"} + }, { + "label": "mapU", + "kind": 12, + "tags": [], + "detail": "(t<'a>, (. 'a) => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "\nbelt_List.mli:221"} + }, { + "label": "makeByU", + "kind": 12, + "tags": [], + "detail": "(int, (. int) => 'a) => t<'a>", + "documentation": {"kind": "markdown", "value": "\nbelt_List.mli:126"} + }, { + "label": "mapReverse2", + "kind": 12, + "tags": [], + "detail": "(t<'a>, t<'b>, ('a, 'b) => 'c) => t<'c>", + "documentation": {"kind": "markdown", "value": "`mapReverse2 xs ys f`\n\nequivalent to `reverse (zipBy xs ys f)`\n\n```ml\nmapReverse2 [1;2;3] [1;2] (+) = [4;2]\n```\n\n\n\nbelt_List.mli:392"} + }, { + "label": "mapWithIndex", + "kind": 12, + "tags": [], + "detail": "(t<'a>, (int, 'a) => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "`mapWithIndex xs f` applies `f` to each element of `xs`. Function `f` takes two arguments: the index starting from 0 and the element from `xs`.\n\n```ml\nmapWithIndex [1;2;3] (fun i x -> i + x) =\n[0 + 1; 1 + 2; 2 + 3 ]\n```\n\n\n\nbelt_List.mli:259"} + }, { + "label": "mapReverseU", + "kind": 12, + "tags": [], + "detail": "(t<'a>, (. 'a) => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "\nbelt_List.mli:298"} + }] + +Complete tests/src/Complete.res 1:2 +[{ + "label": "Floatarray", + "kind": 9, + "tags": [], + "detail": "module", + "documentation": {"kind": "markdown", "value": "\narray.mli:267"} + }, { + "label": "fold_left", + "kind": 12, + "tags": [], + "detail": "(('a, 'b) => 'a, 'a, array<'b>) => 'a", + "documentation": {"kind": "markdown", "value": "`Array.fold_left f x a` computes `f (... (f (f x a.(0)) a.(1)) ...) a.(n-1)`, where `n` is the length of the array `a`.\n\n\n\n\narray.mli:160"} + }, { + "label": "concat", + "kind": 12, + "tags": [], + "detail": "list> => array<'a>", + "documentation": {"kind": "markdown", "value": "Same as append, but concatenates a list of arrays.\n\n\n\n\narray.mli:95"} + }, { + "label": "mapi", + "kind": 12, + "tags": [], + "detail": "((int, 'a) => 'b, array<'a>) => array<'b>", + "documentation": {"kind": "markdown", "value": "Same as map, but the function is applied to the index of the element as first argument, and the element itself as second argument.\n\n\n\n\narray.mli:155"} + }, { + "label": "exists", + "kind": 12, + "tags": [], + "detail": "('a => bool, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "`Array.exists p [|a1; ...; an|]` checks if at least one element of the array satisfies the predicate `p`. That is, it returns `(p a1) || (p a2) || ... || (p an)`.\n\nSince: 4.03.0\n\n\narray.mli:197"} + }, { + "label": "for_all", + "kind": 12, + "tags": [], + "detail": "('a => bool, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "`Array.for_all p [|a1; ...; an|]` checks if all elements of the array satisfy the predicate `p`. That is, it returns `(p a1) && (p a2) && ... && (p an)`.\n\nSince: 4.03.0\n\n\narray.mli:191"} + }, { + "label": "copy", + "kind": 12, + "tags": [], + "detail": "array<'a> => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.copy a` returns a copy of `a`, that is, a fresh array containing the same elements as `a`.\n\n\n\n\narray.mli:107"} + }, { + "label": "iter2", + "kind": 12, + "tags": [], + "detail": "(('a, 'b) => unit, array<'a>, array<'b>) => unit", + "documentation": {"kind": "markdown", "value": "`Array.iter2 f a b` applies function `f` to all the elements of `a` and `b`. Raise `Invalid_argument` if the arrays are not the same size.\n\nSince: 4.03.0\n\n\narray.mli:174"} + }, { + "label": "to_list", + "kind": 12, + "tags": [], + "detail": "array<'a> => list<'a>", + "documentation": {"kind": "markdown", "value": "`Array.to_list a` returns the list of all the elements of `a`.\n\n\n\n\narray.mli:129"} + }, { + "label": "stable_sort", + "kind": 12, + "tags": [], + "detail": "(('a, 'a) => int, array<'a>) => unit", + "documentation": {"kind": "markdown", "value": "Same as sort, but the sorting algorithm is stable \\(i.e. elements that compare equal are kept in their original order\\) and not guaranteed to run in constant heap space.\n\nThe current implementation uses Merge Sort. It uses `n/2` words of heap space, where `n` is the length of the array. It is usually faster than the current implementation of sort.\n\n\n\n\narray.mli:243"} + }, { + "label": "iteri", + "kind": 12, + "tags": [], + "detail": "((int, 'a) => unit, array<'a>) => unit", + "documentation": {"kind": "markdown", "value": "Same as iter, but the function is applied with the index of the element as first argument, and the element itself as second argument.\n\n\n\n\narray.mli:145"} + }, { + "label": "memq", + "kind": 12, + "tags": [], + "detail": "('a, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "Same as mem, but uses physical equality instead of structural equality to compare array elements.\n\nSince: 4.03.0\n\n\narray.mli:208"} + }, { + "label": "map2", + "kind": 12, + "tags": [], + "detail": "(('a, 'b) => 'c, array<'a>, array<'b>) => array<'c>", + "documentation": {"kind": "markdown", "value": "`Array.map2 f a b` applies function `f` to all the elements of `a` and `b`, and builds an array with the results returned by `f`: `[| f a.(0) b.(0); ...; f a.(Array.length a - 1) b.(Array.length b - 1)|]`. Raise `Invalid_argument` if the arrays are not the same size.\n\nSince: 4.03.0\n\n\narray.mli:180"} + }, { + "label": "set", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int, 'a) => unit", + "documentation": {"kind": "markdown", "value": "`Array.set a n x` modifies array `a` in place, replacing element number `n` with `x`. You can also write `a.(n) <- x` instead of `Array.set a n x`.\n\nRaise `Invalid_argument \"index out of bounds\"` if `n` is outside the range 0 to `Array.length a - 1`.\n\n\n\n\narray.mli:30"} + }, { + "label": "make", + "kind": 12, + "tags": [], + "detail": "(int, 'a) => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.make n x` returns a fresh array of length `n`, initialized with `x`. All the elements of this new array are initially physically equal to `x` \\(in the sense of the `==` predicate\\). Consequently, if `x` is mutable, it is shared among all elements of the array, and modifying `x` through one of the array entries will modify all other entries at the same time.\n\nRaise `Invalid_argument` if `n < 0` or `n > Sys.max_array_length`. If the value of `x` is a floating-point number, then the maximum size is only `Sys.max_array_length / 2`.\n\n\n\n\narray.mli:38"} + }, { + "label": "make_float", + "kind": 12, + "tags": [], + "detail": "int => array", + "documentation": {"kind": "markdown", "value": "Deprecated: `Array.make_float` is an alias for create_float.\n\n\n\n\n\n\narray.mli:60"} + }, { + "label": "fold_right", + "kind": 12, + "tags": [], + "detail": "(('b, 'a) => 'a, array<'b>, 'a) => 'a", + "documentation": {"kind": "markdown", "value": "`Array.fold_right f a x` computes `f a.(0) (f a.(1) ( ... (f a.(n-1) x) ...))`, where `n` is the length of the array `a`.\n\n\n\n\narray.mli:165"} + }, { + "label": "sort", + "kind": 12, + "tags": [], + "detail": "(('a, 'a) => int, array<'a>) => unit", + "documentation": {"kind": "markdown", "value": "Sort an array in increasing order according to a comparison function. The comparison function must return 0 if its arguments compare as equal, a positive integer if the first is greater, and a negative integer if the first is smaller \\(see below for a complete specification\\). For example, compare is a suitable comparison function, provided there are no floating-point NaN values in the data. After calling `Array.sort`, the array is sorted in place in increasing order. `Array.sort` is guaranteed to run in constant heap space and \\(at most\\) logarithmic stack space.\n\nThe current implementation uses Heap Sort. It runs in constant stack space.\n\nSpecification of the comparison function: Let `a` be the array and `cmp` the comparison function. The following must be true for all x, y, z in a :\n\n- `cmp x y` \\> 0 if and only if `cmp y x` < 0\n\n\n- if `cmp x y` \\>= 0 and `cmp y z` \\>= 0 then `cmp x z` \\>= 0\n\n\n\nWhen `Array.sort` returns, `a` contains the same elements as before, reordered in such a way that for all i and j valid indices of `a` :\n\n- `cmp a.(i) a.(j)` \\>= 0 if and only if i \\>= j\n\n\n\n\n\n\narray.mli:217"} + }, { + "label": "length", + "kind": 12, + "tags": [], + "detail": "array<'a> => int", + "documentation": {"kind": "markdown", "value": "Return the length \\(number of elements\\) of the given array.\n\n\n\n\narray.mli:18"} + }, { + "label": "sub", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int, int) => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.sub a start len` returns a fresh array of length `len`, containing the elements number `start` to `start + len - 1` of array `a`.\n\nRaise `Invalid_argument \"Array.sub\"` if `start` and `len` do not designate a valid subarray of `a`; that is, if `start < 0`, or `len < 0`, or `start + len > Array.length a`.\n\n\n\n\narray.mli:98"} + }, { + "label": "of_list", + "kind": 12, + "tags": [], + "detail": "list<'a> => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.of_list l` returns a fresh array containing the elements of `l`.\n\n\n\n\narray.mli:132"} + }, { + "label": "iter", + "kind": 12, + "tags": [], + "detail": "('a => unit, array<'a>) => unit", + "documentation": {"kind": "markdown", "value": "`Array.iter f a` applies function `f` in turn to all the elements of `a`. It is equivalent to `f a.(0); f a.(1); ...; f a.(Array.length a - 1); ()`.\n\n\n\n\narray.mli:140"} + }, { + "label": "map", + "kind": 12, + "tags": [], + "detail": "('a => 'b, array<'a>) => array<'b>", + "documentation": {"kind": "markdown", "value": "`Array.map f a` applies function `f` to all the elements of `a`, and builds an array with the results returned by `f`: `[| f a.(0); f a.(1); ...; f a.(Array.length a - 1) |]`.\n\n\n\n\narray.mli:150"} + }, { + "label": "unsafe_get", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int) => 'a", + "documentation": {"kind": "markdown", "value": "\narray.mli:264"} + }, { + "label": "make_matrix", + "kind": 12, + "tags": [], + "detail": "(int, int, 'a) => array>", + "documentation": {"kind": "markdown", "value": "`Array.make_matrix dimx dimy e` returns a two-dimensional array \\(an array of arrays\\) with first dimension `dimx` and second dimension `dimy`. All the elements of this new matrix are initially physically equal to `e`. The element \\(`x,y`\\) of a matrix `m` is accessed with the notation `m.(x).(y)`.\n\nRaise `Invalid_argument` if `dimx` or `dimy` is negative or greater than max_array_length. If the value of `e` is a floating-point number, then the maximum size is only `Sys.max_array_length / 2`.\n\n\n\n\narray.mli:74"} + }, { + "label": "mem", + "kind": 12, + "tags": [], + "detail": "('a, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "`mem a l` is true if and only if `a` is equal to an element of `l`.\n\nSince: 4.03.0\n\n\narray.mli:203"} + }, { + "label": "get", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int) => 'a", + "documentation": {"kind": "markdown", "value": "`Array.get a n` returns the element number `n` of array `a`. The first element has number 0. The last element has number `Array.length a - 1`. You can also write `a.(n)` instead of `Array.get a n`.\n\nRaise `Invalid_argument \"index out of bounds\"` if `n` is outside the range 0 to `(Array.length a - 1)`.\n\n\n\n\narray.mli:21"} + }, { + "label": "append", + "kind": 12, + "tags": [], + "detail": "(array<'a>, array<'a>) => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.append v1 v2` returns a fresh array containing the concatenation of the arrays `v1` and `v2`.\n\n\n\n\narray.mli:91"} + }, { + "label": "unsafe_set", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int, 'a) => unit", + "documentation": {"kind": "markdown", "value": "\narray.mli:265"} + }, { + "label": "create_matrix", + "kind": 12, + "tags": [], + "detail": "(int, int, 'a) => array>", + "documentation": {"kind": "markdown", "value": "Deprecated: `Array.create_matrix` is an alias for make_matrix.\n\n\n\n\n\n\narray.mli:87"} + }, { + "label": "create_float", + "kind": 12, + "tags": [], + "detail": "int => array", + "documentation": {"kind": "markdown", "value": "`Array.create_float n` returns a fresh float array of length `n`, with uninitialized data.\n\nSince: 4.03\n\n\narray.mli:55"} + }, { + "label": "create", + "kind": 12, + "tags": [], + "detail": "(int, 'a) => array<'a>", + "documentation": {"kind": "markdown", "value": "Deprecated: `Array.create` is an alias for make.\n\n\n\n\n\n\narray.mli:51"} + }, { + "label": "init", + "kind": 12, + "tags": [], + "detail": "(int, int => 'a) => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.init n f` returns a fresh array of length `n`, with element number `i` initialized to the result of `f i`. In other terms, `Array.init n f` tabulates the results of `f` applied to the integers `0` to `n-1`.\n\nRaise `Invalid_argument` if `n < 0` or `n > Sys.max_array_length`. If the return type of `f` is `float`, then the maximum size is only `Sys.max_array_length / 2`.\n\n\n\n\narray.mli:64"} + }, { + "label": "fast_sort", + "kind": 12, + "tags": [], + "detail": "(('a, 'a) => int, array<'a>) => unit", + "documentation": {"kind": "markdown", "value": "Same as sort or stable_sort, whichever is faster on typical input.\n\n\n\n\narray.mli:253"} + }, { + "label": "fill", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int, int, 'a) => unit", + "documentation": {"kind": "markdown", "value": "`Array.fill a ofs len x` modifies the array `a` in place, storing `x` in elements number `ofs` to `ofs + len - 1`.\n\nRaise `Invalid_argument \"Array.fill\"` if `ofs` and `len` do not designate a valid subarray of `a`.\n\n\n\n\narray.mli:111"} + }, { + "label": "blit", + "kind": 12, + "tags": [], + "detail": "(array<'a>, int, array<'a>, int, int) => unit", + "documentation": {"kind": "markdown", "value": "`Array.blit v1 o1 v2 o2 len` copies `len` elements from array `v1`, starting at element number `o1`, to array `v2`, starting at element number `o2`. It works correctly even if `v1` and `v2` are the same array, and the source and destination chunks overlap.\n\nRaise `Invalid_argument \"Array.blit\"` if `o1` and `len` do not designate a valid subarray of `v1`, or if `o2` and `len` do not designate a valid subarray of `v2`.\n\n\n\n\narray.mli:118"} + }] + +Complete tests/src/Complete.res 2:2 +[{ + "label": "mapi", + "kind": 12, + "tags": [], + "detail": "((int, 'a) => 'b, array<'a>) => array<'b>", + "documentation": {"kind": "markdown", "value": "Same as map, but the function is applied to the index of the element as first argument, and the element itself as second argument.\n\n\n\n\narray.mli:155"} + }, { + "label": "memq", + "kind": 12, + "tags": [], + "detail": "('a, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "Same as mem, but uses physical equality instead of structural equality to compare array elements.\n\nSince: 4.03.0\n\n\narray.mli:208"} + }, { + "label": "map2", + "kind": 12, + "tags": [], + "detail": "(('a, 'b) => 'c, array<'a>, array<'b>) => array<'c>", + "documentation": {"kind": "markdown", "value": "`Array.map2 f a b` applies function `f` to all the elements of `a` and `b`, and builds an array with the results returned by `f`: `[| f a.(0) b.(0); ...; f a.(Array.length a - 1) b.(Array.length b - 1)|]`. Raise `Invalid_argument` if the arrays are not the same size.\n\nSince: 4.03.0\n\n\narray.mli:180"} + }, { + "label": "make", + "kind": 12, + "tags": [], + "detail": "(int, 'a) => array<'a>", + "documentation": {"kind": "markdown", "value": "`Array.make n x` returns a fresh array of length `n`, initialized with `x`. All the elements of this new array are initially physically equal to `x` \\(in the sense of the `==` predicate\\). Consequently, if `x` is mutable, it is shared among all elements of the array, and modifying `x` through one of the array entries will modify all other entries at the same time.\n\nRaise `Invalid_argument` if `n < 0` or `n > Sys.max_array_length`. If the value of `x` is a floating-point number, then the maximum size is only `Sys.max_array_length / 2`.\n\n\n\n\narray.mli:38"} + }, { + "label": "make_float", + "kind": 12, + "tags": [], + "detail": "int => array", + "documentation": {"kind": "markdown", "value": "Deprecated: `Array.make_float` is an alias for create_float.\n\n\n\n\n\n\narray.mli:60"} + }, { + "label": "map", + "kind": 12, + "tags": [], + "detail": "('a => 'b, array<'a>) => array<'b>", + "documentation": {"kind": "markdown", "value": "`Array.map f a` applies function `f` to all the elements of `a`, and builds an array with the results returned by `f`: `[| f a.(0); f a.(1); ...; f a.(Array.length a - 1) |]`.\n\n\n\n\narray.mli:150"} + }, { + "label": "make_matrix", + "kind": 12, + "tags": [], + "detail": "(int, int, 'a) => array>", + "documentation": {"kind": "markdown", "value": "`Array.make_matrix dimx dimy e` returns a two-dimensional array \\(an array of arrays\\) with first dimension `dimx` and second dimension `dimy`. All the elements of this new matrix are initially physically equal to `e`. The element \\(`x,y`\\) of a matrix `m` is accessed with the notation `m.(x).(y)`.\n\nRaise `Invalid_argument` if `dimx` or `dimy` is negative or greater than max_array_length. If the value of `e` is a floating-point number, then the maximum size is only `Sys.max_array_length / 2`.\n\n\n\n\narray.mli:74"} + }, { + "label": "mem", + "kind": 12, + "tags": [], + "detail": "('a, array<'a>) => bool", + "documentation": {"kind": "markdown", "value": "`mem a l` is true if and only if `a` is equal to an element of `l`.\n\nSince: 4.03.0\n\n\narray.mli:203"} + }] + +Complete tests/src/Complete.res 12:2 +[{ + "label": "customDouble", + "kind": 12, + "tags": [1], + "detail": "int => int", + "documentation": {"kind": "markdown", "value": "Deprecated: Use customDouble instead\n\nSome doc comment\n\n\nComplete.res:1"} + }] + +Complete tests/src/Complete.res 19:2 +[{ + "label": "age", + "kind": 4, + "tags": [], + "detail": "int", + "documentation": {"kind": "markdown", "value": "\nComplete.res:20"} + }, { + "label": "name", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": {"kind": "markdown", "value": "\nComplete.res:20"} + }] + +Complete tests/src/Complete.res 21:2 +[{ + "label": "Js.Array2.mapi", + "kind": 12, + "tags": [], + "detail": "(t<'a>, ('a, int) => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "\njs_array2.ml:127"} + }, { + "label": "Js.Array2.map", + "kind": 12, + "tags": [], + "detail": "(t<'a>, 'a => 'b) => t<'b>", + "documentation": {"kind": "markdown", "value": "\njs_array2.ml:126"} + }] + +Complete tests/src/Complete.res 23:2 +[{ + "label": "Js.String2.toUpperCase", + "kind": 12, + "tags": [], + "detail": "t => t", + "documentation": {"kind": "markdown", "value": "`toUpperCase str` converts `str` to upper case using the locale-insensitive case mappings in the Unicode Character Database. Notice that the conversion can expand the number of letters in the result; for example the German `\195\159` capitalizes to two `S`es in a row.\n\n```ml\ntoUpperCase \"abc\" = \"ABC\";;\ntoUpperCase {js|Stra\195\159e|js} = {js|STRASSE|js};;\ntoLowerCase {js|\207\128\207\130|js} = {js|\206\160\206\163|js};;\n```\n\n\n\njs_string2.ml:601"} + }] + +Complete tests/src/Complete.res 27:2 +[{ + "label": "Belt.Option.eqU", + "kind": 12, + "tags": [], + "detail": "(option<'a>, option<'b>, (. 'a, 'b) => bool) => bool", + "documentation": {"kind": "markdown", "value": "Uncurried version of `eq`\n\n\n\n\nbelt_Option.mli:154"} + }, { + "label": "Belt.Option.eq", + "kind": 12, + "tags": [], + "detail": "(option<'a>, option<'b>, ('a, 'b) => bool) => bool", + "documentation": {"kind": "markdown", "value": "`eq optValue1 optvalue2 predicate`\n\nEvaluates two optional values for equality with respect to a predicate function.\n\nIf both `optValue1` and `optValue2` are `None`, returns `true`.\n\nIf one of the arguments is `Some value` and the other is `None`, returns `false`\n\nIf arguments are `Some value1` and `Some value2`, returns the result of `predicate value1 value2`; the `predicate` function must return a `bool`\n\n```ml\nlet clockEqual = (fun a b -> a mod 12 = b mod 12);;\neq (Some 3) (Some 15) clockEqual = true;;\neq (Some 3) None clockEqual = false;;\neq None (Some 3) clockEqual = false;;\neq None None clockEqual = true;;\n```\n\n\n\nbelt_Option.mli:159"} + }] + +Complete tests/src/Complete.res 36:2 +[{ + "label": "ForAuto.abc", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": {"kind": "markdown", "value": "\nComplete.res:33"} + }, { + "label": "ForAuto.abd", + "kind": 12, + "tags": [], + "detail": "(t, int) => t", + "documentation": {"kind": "markdown", "value": "\nComplete.res:34"} + }] + +Complete tests/src/Complete.res 38:2 +[{ + "label": "unsafeGet", + "kind": 12, + "tags": [], + "detail": "(t<'a>, key) => 'a", + "documentation": {"kind": "markdown", "value": "`unsafeGet dict key` return the value if the `key` exists, otherwise an **undefined** value is returned. Must be used only when the existence of a key is certain. \\(i.e. when having called `keys` function previously.\n\n```ml\nArray.iter (fun key -> Js.log (Js_dict.unsafeGet dic key)) (Js_dict.keys dict) \n```\n\n\n\njs_dict.mli:42"} + }, { + "label": "unsafeDeleteKey", + "kind": 12, + "tags": [], + "detail": "(. t, string) => unit", + "documentation": {"kind": "markdown", "value": "Experimental internal function\n\n\n\n\njs_dict.mli:63"} + }] + +Complete tests/src/Complete.res 50:2 +[{ + "label": "zzz", + "kind": 12, + "tags": [], + "detail": "int", + "documentation": {"kind": "markdown", "value": "\nComplete.res:50"} + }] + +Complete tests/src/Complete.res 52:2 +[{ + "label": "key", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": {"kind": "markdown", "value": "\nComplete.res:53"} + }, { + "label": "zoo", + "kind": 4, + "tags": [], + "detail": "option", + "documentation": {"kind": "markdown", "value": "\nComplete.res:53"} + }] + diff --git a/analysis/tests/src/expected/Definition.res.txt b/analysis/tests/src/expected/Definition.res.txt new file mode 100644 index 000000000..61c640fb5 --- /dev/null +++ b/analysis/tests/src/expected/Definition.res.txt @@ -0,0 +1,12 @@ +Definition tests/src/Definition.res 2:8 +{"uri": "Definition.res", "range": {"start": {"line": 0, "character": 4}, "end": {"line": 0, "character": 6}}} + +Definition tests/src/Definition.res 10:23 +{"uri": "Definition.res", "range": {"start": {"line": 6, "character": 7}, "end": {"line": 6, "character": 13}}} + +Hover tests/src/Definition.res 14:14 +{"contents": "```rescript\n('a => 'b, list<'a>) => list<'b>\n```\n\n`List.map f [a1; ...; an]` applies function `f` to `a1, ..., an`, and builds the list `[f a1; ...; f an]` with the results returned by `f`. Not tail-recursive.\n\n"} + +Hover tests/src/Definition.res 18:14 +{"contents": "```rescript\n(Belt.List.t<'a>, 'a => 'b) => Belt.List.t<'b>\n```\n\n`map xs f`\n\nreturn the list obtained by applying `f` to each element of `xs`\n\n```ml\nmap [1;2] (fun x-> x + 1) = [3;4]\n```\n"} + diff --git a/analysis/tests/src/expected/Hover.res.txt b/analysis/tests/src/expected/Hover.res.txt new file mode 100644 index 000000000..8c36cff4e --- /dev/null +++ b/analysis/tests/src/expected/Hover.res.txt @@ -0,0 +1,27 @@ +Hover tests/src/Hover.res 0:4 +{"contents": "```rescript\nint\n```"} + +Hover tests/src/Hover.res 3:5 +{"contents": "```rescript\ntype t = (int, float)\n```"} + +Hover tests/src/Hover.res 6:7 +{"contents": "```rescript\nmodule Id = {\n type x = int\n}\n```"} + +Hover tests/src/Hover.res 19:11 +{"contents": "\nThis module is commented\n```rescript\nmodule Dep = {\n let customDouble: int => int\n}\n```"} + +Hover tests/src/Hover.res 22:11 +{"contents": "```rescript\nint => int\n```\n\nSome doc comment"} + +Hover tests/src/Hover.res 26:6 +{"contents": "```rescript\nint\n```"} + +Hover tests/src/Hover.res 33:4 +{"contents": "```rescript\nunit => int\n```\n\nDoc comment for functionWithTypeAnnotation"} + +Hover tests/src/Hover.res 37:13 +{"contents": "```rescript\nstring\n```"} + +Hover tests/src/Hover.res 41:13 +{"contents": "```rescript\nstring\n```"} + diff --git a/analysis/tests/src/expected/Jsx.res.txt b/analysis/tests/src/expected/Jsx.res.txt new file mode 100644 index 000000000..3dc179a82 --- /dev/null +++ b/analysis/tests/src/expected/Jsx.res.txt @@ -0,0 +1,27 @@ +Definition tests/src/Jsx.res 5:9 +{"uri": "Jsx.res", "range": {"start": {"line": 2, "character": 6}, "end": {"line": 2, "character": 10}}} + +Complete tests/src/Jsx.res 7:2 +[] + +Complete tests/src/Jsx.res 10:2 +[{ + "label": "key", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": {"kind": "markdown", "value": "\nJsx.res:11"} + }, { + "label": "first", + "kind": 4, + "tags": [], + "detail": "string", + "documentation": {"kind": "markdown", "value": "\nJsx.res:11"} + }, { + "label": "fun", + "kind": 4, + "tags": [], + "detail": "option", + "documentation": {"kind": "markdown", "value": "\nJsx.res:11"} + }] + diff --git a/package-lock.json b/package-lock.json index 87517e67b..1a67d568e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@typescript-eslint/parser": "^2.3.0", "eslint": "^6.4.0", "mocha": "^8.0.1", + "reanalyze": "^2.15.0", "typescript": "^3.9.4" }, "engines": { @@ -1838,6 +1839,16 @@ "node": ">=8.10.0" } }, + "node_modules/reanalyze": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/reanalyze/-/reanalyze-2.15.0.tgz", + "integrity": "sha512-FUN/pqgTKs5i+kzi9Mje5deahZHKniOQDyig5UseozDiK81eW77A4iRyN+3UsnontG6K6mAdUcXCU9NpEqZFug==", + "dev": true, + "hasInstallScript": true, + "bin": { + "reanalyze": "reanalyze.exe" + } + }, "node_modules/regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", @@ -3943,6 +3954,12 @@ "picomatch": "^2.2.1" } }, + "reanalyze": { + "version": "2.15.0", + "resolved": "https://registry.npmjs.org/reanalyze/-/reanalyze-2.15.0.tgz", + "integrity": "sha512-FUN/pqgTKs5i+kzi9Mje5deahZHKniOQDyig5UseozDiK81eW77A4iRyN+3UsnontG6K6mAdUcXCU9NpEqZFug==", + "dev": true + }, "regexpp": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-2.0.1.tgz", diff --git a/package.json b/package.json index a749949c6..ecf26b44a 100644 --- a/package.json +++ b/package.json @@ -112,6 +112,7 @@ "@typescript-eslint/parser": "^2.3.0", "eslint": "^6.4.0", "mocha": "^8.0.1", + "reanalyze": "^2.15.0", "typescript": "^3.9.4" }, "dependencies": { diff --git a/server/analysis_binaries/README.md b/server/analysis_binaries/README.md new file mode 100644 index 000000000..2927b24db --- /dev/null +++ b/server/analysis_binaries/README.md @@ -0,0 +1,2 @@ +We store the analysis binaries here. + diff --git a/server/src/RescriptEditorSupport.ts b/server/src/RescriptEditorSupport.ts index 0259ff055..1a54c88a0 100644 --- a/server/src/RescriptEditorSupport.ts +++ b/server/src/RescriptEditorSupport.ts @@ -1,101 +1,156 @@ import { fileURLToPath } from "url"; import { RequestMessage } from "vscode-languageserver"; -import { CompletionItem } from "vscode-languageserver-protocol"; +import { CompletionItem, Hover, Location } from "vscode-languageserver-protocol"; import * as utils from "./utils"; import * as path from "path"; import { execFileSync } from "child_process"; import fs from "fs"; -let binaryPath = path.join( +let binariesFolder = path.join( path.dirname(__dirname), - process.platform, - "rescript-editor-support.exe" + "analysis_binaries" +) + +// For local development and CI tests +let currentPlatformBinaryPath = path.join( + binariesFolder, + "current-platform.exe" +); +// Platform-specific production binaries manually downloaded from CI +let productionBinaryPath = path.join( + binariesFolder, + process.platform + ".exe" ); -export let binaryExists = fs.existsSync(binaryPath); +let findBinary = () => { + if (fs.existsSync(currentPlatformBinaryPath)) { + return currentPlatformBinaryPath + } else if (fs.existsSync(productionBinaryPath)) { + return productionBinaryPath + } else { + return null + } +} + +// export let binaryExists = fs.existsSync(binaryPath); + +// let findExecutable = (uri: string) => { +// let filePath = fileURLToPath(uri); +// let projectRootPath = utils.findProjectRootOfFile(filePath); +// if (projectRootPath == null || !binaryExists) { +// return null; +// } else { +// return { +// binaryPath: binaryPath, +// filePath: filePath, +// cwd: projectRootPath, +// }; +// } +// }; + +// type dumpCommandResult = { +// hover?: string; +// definition?: { uri?: string; range: any }; +// }; +// export function runDumpCommand(msg: RequestMessage): dumpCommandResult | null { +// let executable = findExecutable(msg.params.textDocument.uri); +// if (executable == null) { +// return null; +// } + +// let command = +// executable.filePath + +// ":" + +// msg.params.position.line + +// ":" + +// msg.params.position.character; + +// try { +// let stdout = execFileSync(executable.binaryPath, ["dump", command], { +// cwd: executable.cwd, +// }); +// let parsed = JSON.parse(stdout.toString()); +// if (parsed && parsed[0]) { +// return parsed[0]; +// } else { +// return null; +// } +// } catch (error) { +// // TODO: @cristianoc any exception possible? +// return null; +// } +// } -let findExecutable = (uri: string) => { - let filePath = fileURLToPath(uri); +export function runCompletionCommand( + msg: RequestMessage, + code: string +): CompletionItem[] | null { + let filePath = fileURLToPath(msg.params.textDocument.uri) let projectRootPath = utils.findProjectRootOfFile(filePath); - if (projectRootPath == null || !binaryExists) { + let binaryPath = findBinary(); + if (binaryPath == null || projectRootPath == null) { return null; - } else { - return { - binaryPath: binaryPath, - filePath: filePath, - cwd: projectRootPath, - }; } -}; + let tmpname = utils.createFileInTempDir(); + fs.writeFileSync(tmpname, code, { encoding: "utf-8" }); -type dumpCommandResult = { - hover?: string; - definition?: { uri?: string; range: any }; -}; -export function runDumpCommand(msg: RequestMessage): dumpCommandResult | null { - let executable = findExecutable(msg.params.textDocument.uri); - if (executable == null) { + try { + let stdout = execFileSync( + binaryPath, + ["complete", filePath, msg.params.position.line, msg.params.position.character, tmpname], + { cwd: projectRootPath } + ); + return JSON.parse(stdout.toString()); + } catch (error) { + // TODO: @cristianoc any exception possible? return null; + } finally { + fs.unlink(tmpname, () => null); } +} - let command = - executable.filePath + - ":" + - msg.params.position.line + - ":" + - msg.params.position.character; +export function runHoverCommand( + msg: RequestMessage, +): Hover | null { + let filePath = fileURLToPath(msg.params.textDocument.uri) + let projectRootPath = utils.findProjectRootOfFile(filePath); + let binaryPath = findBinary(); + if (binaryPath == null || projectRootPath == null) { + return null; + } try { - let stdout = execFileSync(executable.binaryPath, ["dump", command], { - cwd: executable.cwd, - }); - let parsed = JSON.parse(stdout.toString()); - if (parsed && parsed[0]) { - return parsed[0]; - } else { - return null; - } + let stdout = execFileSync( + binaryPath, + ["hover", filePath, msg.params.position.line, msg.params.position.character], + { cwd: projectRootPath } + ); + return JSON.parse(stdout.toString()); } catch (error) { // TODO: @cristianoc any exception possible? return null; } } -// TODO: the result will never be null soon when the updated binary syncs -export function runCompletionCommand( +export function runDefinitionCommand( msg: RequestMessage, - code: string -): CompletionItem[] | null { - let executable = findExecutable(msg.params.textDocument.uri); - if (executable == null) { +): Location | null { + let filePath = fileURLToPath(msg.params.textDocument.uri) + let projectRootPath = utils.findProjectRootOfFile(filePath); + let binaryPath = findBinary(); + if (binaryPath == null || projectRootPath == null) { return null; } - let tmpname = utils.createFileInTempDir(); - fs.writeFileSync(tmpname, code, { encoding: "utf-8" }); - - let command = - executable.filePath + - ":" + - msg.params.position.line + - ":" + - msg.params.position.character; try { let stdout = execFileSync( - executable.binaryPath, - ["complete", command, tmpname], - { cwd: executable.cwd } + binaryPath, + ["definition", filePath, msg.params.position.line, msg.params.position.character], + { cwd: projectRootPath } ); - let parsed = JSON.parse(stdout.toString()); - if (parsed && parsed[0]) { - return parsed; - } else { - return null; - } + return JSON.parse(stdout.toString()); } catch (error) { // TODO: @cristianoc any exception possible? return null; - } finally { - fs.unlink(tmpname, () => null); } } diff --git a/server/src/server.ts b/server/src/server.ts index 6407da2e8..6cd7b5d8f 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -10,6 +10,7 @@ import { DidOpenTextDocumentNotification, DidChangeTextDocumentNotification, DidCloseTextDocumentNotification, + Location, } from "vscode-languageserver-protocol"; import * as utils from "./utils"; import * as c from "./constants"; @@ -18,9 +19,7 @@ import { assert } from "console"; import { fileURLToPath } from "url"; import { ChildProcess } from "child_process"; import { - binaryExists, - runDumpCommand, - runCompletionCommand, + runCompletionCommand, runDefinitionCommand, runHoverCommand, } from "./RescriptEditorSupport"; // https://microsoft.github.io/language-server-protocol/specification#initialize @@ -296,11 +295,9 @@ function onMessage(msg: m.Message) { // TODO: incremental sync? textDocumentSync: v.TextDocumentSyncKind.Full, documentFormattingProvider: true, - hoverProvider: binaryExists, - definitionProvider: binaryExists, - completionProvider: binaryExists - ? { triggerCharacters: [".", ">", "@", "~"] } - : undefined, + hoverProvider: true, + definitionProvider: true, + completionProvider: { triggerCharacters: [".", ">", "@", "~"] }, }, }; let response: m.ResponseMessage = { @@ -351,11 +348,11 @@ function onMessage(msg: m.Message) { // type Hover = {contents: MarkedString | MarkedString[] | MarkupContent, range?: Range} result: null, }; - let result = runDumpCommand(msg); - if (result !== null && result.hover != null) { + let result = runHoverCommand(msg); + if (result !== null) { let hoverResponse: m.ResponseMessage = { ...emptyHoverResponse, - result: { contents: result.hover }, + result, }; send(hoverResponse); } else { @@ -371,14 +368,11 @@ function onMessage(msg: m.Message) { // error: code and message set in case an exception happens during the definition request. }; - let result = runDumpCommand(msg); - if (result !== null && result.definition != null) { + let result = runDefinitionCommand(msg); + if (result !== null) { let definitionResponse: m.ResponseMessage = { ...emptyDefinitionResponse, - result: { - uri: result.definition.uri || msg.params.textDocument.uri, - range: result.definition.range, - }, + result: result, }; send(definitionResponse); } else {