Compare commits

...

1108 Commits

Author SHA1 Message Date
Tony Garnock-Jones 73c6593f84 Python 0.994 docs 2024-02-05 23:01:01 +01:00
Tony Garnock-Jones a9e226f759 Bump python version to 0.994.0 2024-02-05 22:58:16 +01:00
Tony Garnock-Jones 33db0b8718 Publish
- @preserves/core@0.994.0
 - @preserves/schema-cli@0.994.0
 - @preserves/schema@0.994.0
2024-02-05 22:55:15 +01:00
Tony Garnock-Jones e923d87fa5 Switch from `#!` to `#:` for embedded values 2024-02-05 22:38:49 +01:00
Tony Garnock-Jones 83697b0e56 Update docs 2024-01-28 18:49:18 +01:00
Tony Garnock-Jones 1798e64615 Remove split-out Rust implementation 2024-01-28 15:18:08 +01:00
Tony Garnock-Jones be32f9b7c8 Remove single-precision floats from the implementations 2024-01-27 14:40:37 +01:00
Tony Garnock-Jones dc1b0ac54d Remove single-precision floats from the specs 2024-01-27 11:34:51 +01:00
Tony Garnock-Jones d579a0d607 Publish
- @preserves/schema-cli@0.992.5
 - @preserves/schema@0.992.5
2023-12-17 22:30:05 +13:00
Tony Garnock-Jones 7178fb0d9b Repair constructor generation; attach schemas 2023-12-17 22:29:09 +13:00
Tony Garnock-Jones 4c0bd3b9d7 Repair preserves-schemac 2023-12-17 11:12:04 +13:00
Tony Garnock-Jones b98f434ac9 Publish
- @preserves/core@0.992.4
 - @preserves/schema-cli@0.992.4
 - @preserves/schema@0.992.4
2023-12-17 11:06:15 +13:00
Tony Garnock-Jones 61cec52d46 preserves-schema-browser.js 2023-12-17 11:05:36 +13:00
Tony Garnock-Jones f6ddf0ca3b Interpreter 2023-12-17 11:05:25 +13:00
Tony Garnock-Jones 9c7770a54f Repair codegen for setof and dictof 2023-12-17 11:05:07 +13:00
Tony Garnock-Jones cd29602761 Host types 2023-12-17 11:02:53 +13:00
Tony Garnock-Jones c411e47d7f Corrections to preserves-schema host-type generation; extract host-type schema 2023-12-17 11:01:46 +13:00
Tony Garnock-Jones 897fc13054 Preserves ordering 2023-12-16 22:13:47 +13:00
Tony Garnock-Jones 9420cc7236 New EncodableDictionary/EncodableSet as intermediate steps between Flex- and Keyed- 2023-12-16 22:13:17 +13:00
Tony Garnock-Jones e0ef236001 Cosmetic: remove unused import 2023-12-16 09:38:00 +13:00
Tony Garnock-Jones 634b263ed2 Generalize constraints on keys in KeyedDictionary 2023-12-16 09:37:47 +13:00
Tony Garnock-Jones 2a6d0912b6 Avoid rollup step for schema-cli package 2023-12-16 07:46:44 +13:00
Tony Garnock-Jones 4a656dc929 Publish
- @preserves/core@0.992.3
 - @preserves/schema-cli@0.992.3
 - @preserves/schema@0.992.3
2023-11-26 00:00:54 +01:00
Tony Garnock-Jones 2532b42959 Make utf-8 decoding stricter in JavaScript 2023-11-26 00:00:27 +01:00
Tony Garnock-Jones b12d49739c Update base64 tests (oops) 2023-11-25 23:58:45 +01:00
Tony Garnock-Jones aea735bb4e Release independent packages
preserves@4.992.2

Generated by cargo-workspaces
2023-11-24 14:23:50 +01:00
Tony Garnock-Jones 9b71388817 Default to URL_SAFE base64 for writing 2023-11-24 14:23:30 +01:00
Tony Garnock-Jones b6ac046ba7 Publish
- @preserves/core@0.992.2
 - @preserves/schema-cli@0.992.2
 - @preserves/schema@0.992.2
2023-11-19 16:03:19 +01:00
Tony Garnock-Jones 7b3731a5e4 Remove superfluous generic parameter 2023-11-19 16:02:34 +01:00
Tony Garnock-Jones 185c233b2f Refactor base64 2023-11-19 16:02:18 +01:00
Tony Garnock-Jones 22b2f162bc Remove one of the sources of cyclic dependencies 2023-11-19 15:54:01 +01:00
Tony Garnock-Jones f664399a8c Fix (?) comment-dwim 2023-11-18 20:43:14 +01:00
Tony Garnock-Jones cd504becf7 Release independent packages
preserves-schema-macros@0.992.1

Generated by cargo-workspaces
2023-11-18 16:18:58 +01:00
Tony Garnock-Jones 48b2f06f8e Release independent packages
preserves-path@5.992.0
preserves-schema@5.992.0
preserves-schema-macros@0.992.0
preserves-tools@4.992.2

Generated by cargo-workspaces
2023-11-18 16:17:10 +01:00
Tony Garnock-Jones 284614eecb New `compile_preserves_schemas!` macro 2023-11-18 16:14:46 +01:00
Tony Garnock-Jones 114875b52f Release independent packages
preserves@4.992.1

Generated by cargo-workspaces
2023-11-14 00:50:18 +01:00
Tony Garnock-Jones 12a690b4b5 Tests for empty line comment, and fix in Rust 2023-11-14 00:49:58 +01:00
Tony Garnock-Jones f01cbf7443 Ah yes, doc latest updates require a tag 2023-11-10 18:08:47 +01:00
Tony Garnock-Jones 375cf291e0 Repair python package-data 2023-11-10 18:08:10 +01:00
Tony Garnock-Jones ab34971eef Uh, why didn't these get updated 2023-11-10 17:43:17 +01:00
Tony Garnock-Jones 441941fb19 Last vestiges of setup.py 2023-11-10 17:41:58 +01:00
Tony Garnock-Jones 586385c716 Switch to pyproject.toml 2023-11-10 17:38:21 +01:00
Tony Garnock-Jones b192313c94 str() for Symbol 2023-11-10 17:38:21 +01:00
Tony Garnock-Jones 3153dc7c62 More demo 2023-11-07 21:02:33 +01:00
Tony Garnock-Jones 5edcca1e7f Simpler and prettier 2023-11-07 20:54:01 +01:00
Tony Garnock-Jones 401e3973ee test-pexprs.rkt 2023-11-07 19:56:48 +01:00
Tony Garnock-Jones b0001e44cb Prettier 2023-11-07 12:29:21 +01:00
Tony Garnock-Jones 831f15099d Fixes and improvements wrt pexprs 2023-11-07 09:02:05 +01:00
Tony Garnock-Jones 782cbd73b2 write-pexpr (not quite right yet) 2023-11-05 20:43:23 +01:00
Tony Garnock-Jones 6e3950cbc5 pexprs.rkt 2023-11-04 16:10:08 +01:00
Tony Garnock-Jones cd4f8e410f Split apart text reader in prep for p-expressions 2023-11-04 14:02:40 +01:00
Tony Garnock-Jones d540ee6faf Make schema available from root python preserves module 2023-11-03 13:24:49 +01:00
Tony Garnock-Jones 0e43df1f9b Release independent packages
preserves-tools@4.992.1

Generated by cargo-workspaces
2023-11-03 13:07:27 +01:00
Tony Garnock-Jones 694d4e7ae7 Make `convert` the default subcommand for `preserves-tool` 2023-11-03 13:07:05 +01:00
Tony Garnock-Jones c5bec4ea76 Publish
- @preserves/core@0.992.1
 - @preserves/schema-cli@0.992.1
 - @preserves/schema@0.992.1
2023-11-03 10:32:36 +01:00
Tony Garnock-Jones 2e84614b3b Allow "sufficiently identifierlike" values in dictionaries too 2023-11-03 10:31:44 +01:00
Tony Garnock-Jones 071566b1e1 Simplify text reader by reusing Racket's float parser 2023-11-01 17:13:36 +01:00
Tony Garnock-Jones 8a8facc080 More tests 2023-11-01 17:09:10 +01:00
Tony Garnock-Jones d4d1919957 Fix boundary reading 2023-11-01 16:41:25 +01:00
Tony Garnock-Jones d0619cb164 Correct encoding functions for trailers 2023-11-01 15:42:25 +01:00
Tony Garnock-Jones 85b3e513f9 Fix bugs wrt pexpr trailers 2023-11-01 15:35:59 +01:00
Tony Garnock-Jones d638555239 Cosmetic rearrangement of cheatsheets 2023-11-01 15:30:23 +01:00
Tony Garnock-Jones ebd8b3f05b Cheatsheets for pexprs 2023-11-01 15:15:24 +01:00
Tony Garnock-Jones 39bfeedb54 New JavaScript packages 2023-11-01 14:50:56 +01:00
Tony Garnock-Jones 2bfd5f4b03 Release independent packages
preserves@4.992.0
preserves-path@4.992.0
preserves-schema@4.992.0
preserves-tools@4.992.0

Generated by cargo-workspaces
2023-11-01 14:48:37 +01:00
Tony Garnock-Jones 3313674f15 New python version 0.992.0 2023-11-01 14:47:11 +01:00
Tony Garnock-Jones d11ec61714 Restrict positions where commas may exist 2023-11-01 14:45:58 +01:00
Tony Garnock-Jones c89147dd6a Restrict usage of commas. Bump spec version to 0.992 2023-11-01 14:13:36 +01:00
Tony Garnock-Jones e0736e03c5 Nuance 2023-11-01 13:27:26 +01:00
Tony Garnock-Jones d7b983e140 This is way better 2023-11-01 13:06:15 +01:00
Tony Garnock-Jones 1a0772d39f Forgot groups! 2023-11-01 12:02:18 +01:00
Tony Garnock-Jones 9ae16b4561 Use records instead of dictionaries 2023-11-01 11:57:23 +01:00
Tony Garnock-Jones ae7555f6f3 Tweaks 2023-11-01 11:22:38 +01:00
Tony Garnock-Jones 63c124307b Empty records 2023-11-01 11:20:33 +01:00
Tony Garnock-Jones 1784024952 Release independent packages
preserves@4.991.1

Generated by cargo-workspaces
2023-11-01 00:13:46 +01:00
Tony Garnock-Jones 9ed9296fc0 Default to Document-style treatment of whitespace-before-EOF 2023-11-01 00:13:23 +01:00
Tony Garnock-Jones 03cb5ab02f Repair internal paths to preserves-schemac.js 2023-10-31 23:31:10 +01:00
Tony Garnock-Jones 5820755277 Another repair to preserves.el comment highlighting 2023-10-31 22:24:34 +01:00
Tony Garnock-Jones e269acebee Use # as comment in more places 2023-10-31 22:20:26 +01:00
Tony Garnock-Jones 334d9df81c Fix quick-reference cards 2023-10-31 22:17:24 +01:00
Tony Garnock-Jones ac34d3fa8e Repair and simplify comment highlighting in preserves.el 2023-10-31 21:53:54 +01:00
Tony Garnock-Jones 000c0ff2be Manually bump versions because lerna refuses to do it 2023-10-31 21:32:23 +01:00
Tony Garnock-Jones e4fdd26c71 New python docs 2023-10-31 21:29:13 +01:00
Tony Garnock-Jones 7fe4030dd5 Python package version 0.991.0 2023-10-31 21:28:03 +01:00
Tony Garnock-Jones 61f298503e Release independent packages
preserves@4.991.0
preserves-path@4.991.0
preserves-schema@4.991.0
preserves-tools@4.991.0

Generated by cargo-workspaces
2023-10-31 21:26:56 +01:00
Tony Garnock-Jones 2eb12a30d6 Bump spec version for hash-space comments 2023-10-31 21:22:49 +01:00
Tony Garnock-Jones fb63ac24b0 Merge branch 'main' into comment-syntax-hash-space 2023-10-31 21:15:41 +01:00
Tony Garnock-Jones ec03bdb45f Clarify lexicographical ordering 2023-10-31 20:00:18 +01:00
Tony Garnock-Jones 23e0e59daf Trailing comments 2023-10-31 19:32:06 +01:00
Tony Garnock-Jones c18e9dd1fe Tweaks 2023-10-31 18:06:05 +01:00
Tony Garnock-Jones a69444f085 preserves-expressions.md 2023-10-31 17:37:09 +01:00
Tony Garnock-Jones 982d916b61 Minor presentation tweak to make Sequence/Set/Dictionary line up in the grammar like it does in the abstract model definition 2023-10-31 17:34:37 +01:00
Tony Garnock-Jones 47b4c07268 Release independent packages
preserves@3.990.4

Generated by cargo-workspaces
2023-10-31 13:27:35 +01:00
Tony Garnock-Jones 8276a50552 Repair error in Rust integer width calculation 2023-10-31 13:27:03 +01:00
Tony Garnock-Jones cf50e00f80 Repair failing TS bigint tests 2023-10-31 12:53:54 +01:00
Tony Garnock-Jones c053102d07 Merge branch 'main' into comment-syntax-hash-space 2023-10-31 10:51:36 +01:00
Tony Garnock-Jones 1a2657fe33 Simplify and firm up test suite. Known failures: TS, wrt bigint 2023-10-30 17:28:33 +01:00
Tony Garnock-Jones cdc5295a72 Java quasi-Symbols 2023-10-30 10:46:11 +01:00
Tony Garnock-Jones 7c0fc8f358 Repairs after merge 2023-10-29 22:32:27 +01:00
Tony Garnock-Jones 1aedfe46b7 Merge branch 'main' into comment-syntax-hash-space 2023-10-29 22:22:40 +01:00
Tony Garnock-Jones e2e81a67c3 Fix up cheat sheets 2023-10-29 22:11:43 +01:00
Tony Garnock-Jones 9d1db2d71f Bump versions manually again 2023-10-29 21:47:06 +01:00
Tony Garnock-Jones eaa8963ead Update python docs 2023-10-29 21:41:24 +01:00
Tony Garnock-Jones 7958b9a6f2 Bump version 2023-10-29 21:37:44 +01:00
Tony Garnock-Jones 8c9f3b13ee Publish
- @preserves/core@0.990.1
 - @preserves/schema-cli@0.990.2
 - @preserves/schema@0.990.2
2023-10-29 21:34:42 +01:00
Tony Garnock-Jones b8fb7abab1 New tests and fixes for bad surrogate pair situations. Closes #43 2023-10-29 21:30:54 +01:00
Tony Garnock-Jones 9595872177 Test cases and implementation updates for delimiters following Boolean and SymbolOrNumber. Closes #47 2023-10-29 21:04:52 +01:00
Tony Garnock-Jones e8c0a2565e Repair errors exposed by parse tests in rust 2023-10-29 21:02:48 +01:00
Tony Garnock-Jones 755a8bc73b Enable parse tests in rust 2023-10-29 21:00:56 +01:00
Tony Garnock-Jones c30073c3f9 Repair errors surfaced by the new tests 2023-10-29 19:27:29 +01:00
Tony Garnock-Jones a14b2d49b7 Add parse tests 2023-10-29 19:11:16 +01:00
Tony Garnock-Jones 4869507b09 Enable text-parser tests in @preserves/core; fix a couple of errors 2023-10-29 18:12:12 +01:00
Tony Garnock-Jones c0df6c83a8 Cosmetic 2023-10-29 16:45:45 +01:00
Tony Garnock-Jones 2445ab4a5a Merge branch 'main' into comment-syntax-hash-space 2023-10-29 16:44:32 +01:00
Tony Garnock-Jones 5b8c07cb3f Introduce the notion of a "delimiter" to follow Boolean and SymbolOrNumber. 2023-10-29 15:55:19 +01:00
Tony Garnock-Jones 55deeea343 Correct schema spec and note different usages of @name. Closes #31. 2023-10-29 15:20:42 +01:00
Tony Garnock-Jones ee0f9a79fd Ignore leading/trailing/repeated slashes and ampersands. Closes #32. 2023-10-29 14:51:03 +01:00
Tony Garnock-Jones 49242bb4dc Use tables for implementations 2023-10-27 15:23:22 +02:00
Tony Garnock-Jones 3893b0e581 Link to Rust docs 2023-10-27 15:03:46 +02:00
Tony Garnock-Jones ed40b2198b Adjust whitespace 2023-10-27 14:12:57 +02:00
Tony Garnock-Jones 9c90efe36b Release independent packages
preserves-schema@3.990.3

Generated by cargo-workspaces
2023-10-27 14:11:52 +02:00
Tony Garnock-Jones e31f834d72 Update preserves-schema-rs docs 2023-10-27 14:11:38 +02:00
Tony Garnock-Jones af5de5b836 Release independent packages
preserves@3.990.2
preserves-schema@3.990.2

Generated by cargo-workspaces
2023-10-27 14:04:14 +02:00
Tony Garnock-Jones 8db860648b A little more documentation for preserves-schema 2023-10-27 14:03:53 +02:00
Tony Garnock-Jones f009920dd7 Progress on documenting preserves-schema 2023-10-27 13:26:52 +02:00
Tony Garnock-Jones a2cb071f01 Release independent packages
preserves@3.990.1
preserves-schema@3.990.1

Generated by cargo-workspaces
2023-10-27 11:44:22 +02:00
Tony Garnock-Jones 130e58a3e1 More documentation 2023-10-27 11:40:55 +02:00
Tony Garnock-Jones 4b40bf174d Document Preserves crate 2023-10-27 00:46:27 +02:00
Tony Garnock-Jones 01d3659216 Split out @preserves/schema-cli from @preserves/schema 2023-10-19 02:27:01 +02:00
Tony Garnock-Jones ccf277cddb Switch to "# "-prefixed text comment annotation syntax 2023-10-18 10:20:30 +02:00
Tony Garnock-Jones 4e471ed896 Tweak 2023-10-17 11:22:12 +02:00
Tony Garnock-Jones 0eabbd4257 Discuss unpaired surrogate code points on the why-not-json page 2023-10-17 11:18:50 +02:00
Tony Garnock-Jones 19a4edc36a Internal anchors 2023-10-17 10:57:33 +02:00
Tony Garnock-Jones 975fc05d8f Link QuickRef in nav 2023-10-17 10:55:11 +02:00
Tony Garnock-Jones 9cc537abf8 Feed back clarifications from the cheatsheet version of the text grammar 2023-10-17 10:40:29 +02:00
Tony Garnock-Jones 6a56dad886 Better responsiveness 2023-10-17 10:01:07 +02:00
Tony Garnock-Jones 7b4724f795 Another tweak 2023-10-17 02:24:14 +02:00
Tony Garnock-Jones 5b2db04a05 Tweak 2023-10-17 02:21:15 +02:00
Tony Garnock-Jones 3253c665d6 Fix specification of binchar 2023-10-17 02:18:52 +02:00
Tony Garnock-Jones a123630bb5 Fix unstyled width of gitlab logo 2023-10-17 02:18:28 +02:00
Tony Garnock-Jones 04e1c1b0b1 Fix cheatsheet style on small screens 2023-10-17 02:01:23 +02:00
Tony Garnock-Jones 71ead5abb7 Further small improvements 2023-10-17 01:34:21 +02:00
Tony Garnock-Jones 9f0217b22a Tiny tweak 2023-10-17 01:03:56 +02:00
Tony Garnock-Jones 0f08461ccc More tweaks and repairs 2023-10-17 01:00:54 +02:00
Tony Garnock-Jones 53e15bc46c Tweaks and repairs 2023-10-17 00:49:56 +02:00
Tony Garnock-Jones 3360e013a4 Work on cheatsheets 2023-10-17 00:31:10 +02:00
Tony Garnock-Jones 01a766c974 Fix examples; tweak opening text 2023-10-16 18:20:55 +02:00
Tony Garnock-Jones 5aee3af65a Update why-not-json 2023-10-16 15:31:25 +02:00
Tony Garnock-Jones 82f8bf1e0f Link to C and C++ impls 2023-10-15 16:07:45 +02:00
Tony Garnock-Jones bc1f4f0ae5 Manually bump versions because I can never figure out how cargo workspaces is supposed to work 2023-10-15 12:20:51 +02:00
Tony Garnock-Jones 82903af6c0 Update python docs 2023-10-15 12:12:22 +02:00
Tony Garnock-Jones 8ba216ff96 Python package version 0.990.0 2023-10-15 12:08:49 +02:00
Tony Garnock-Jones e3d67af17f Publish
- @preserves/core@0.990.0
 - @preserves/schema@0.990.0
2023-10-15 11:40:06 +02:00
Tony Garnock-Jones 22e4d0b272 Give a little room to move 2023-10-15 11:39:44 +02:00
Tony Garnock-Jones c649339ab5 Spec version 0.99.0 2023-10-15 11:38:18 +02:00
Tony Garnock-Jones 683ef11587 Tweak run-jekyll script 2023-10-15 11:18:28 +02:00
Tony Garnock-Jones 486528416d Simplify illustrative example 2023-10-15 01:21:59 +02:00
Tony Garnock-Jones 202d92d237 Rationale note about annotation binary syntax 2023-10-15 01:16:46 +02:00
Tony Garnock-Jones 5d0bf91d30 Repair varint() definition in cheatsheet 2023-10-15 01:04:01 +02:00
Tony Garnock-Jones f47786871c Tweak 2023-10-15 00:49:09 +02:00
Tony Garnock-Jones 3b89cbe880 Switch back to prefix-format binary annotations 2023-10-15 00:35:25 +02:00
Tony Garnock-Jones b7a2acf65b Update implementations for simplified binary syntax.
As part of this, I noticed a problem when updating the Rust
implementation. With the BF..84 design for annotations, if you are
wanting to skip annotations—for example, if you're parsing
incrementally, SAX-style, ignoring annotations—then with any
parenthesised form you have to remember to check for and skip something
after reading a term. This is awkward, so I want to revisit the design
of annotations and go back to the prefix-form (85..) of the
current-mainline design.
2023-10-14 21:29:09 +02:00
Tony Garnock-Jones 6cbfefc76d Simplify text further 2023-10-13 14:21:40 +02:00
Tony Garnock-Jones 0cdcc1d8e7 Merge branch 'main' into simplify-syntax 2023-10-13 14:04:39 +02:00
Tony Garnock-Jones 8edb657603 Preserves really uses Unicode scalar values, not code points. 2023-10-13 14:01:21 +02:00
Tony Garnock-Jones ad3da3896b Publish
- @preserves/core@0.22.1
 - @preserves/schema@0.22.4
2023-10-02 21:00:09 +02:00
Tony Garnock-Jones c395265c3b Generalize preserves formatter 2023-10-02 20:59:14 +02:00
Tony Garnock-Jones 930964ca05 cargo fmt 2023-07-21 18:15:30 +02:00
Tony Garnock-Jones 32ee76f7d8 Add rustfmt_skip to generated files 2023-07-21 18:14:47 +02:00
Tony Garnock-Jones 08e62fd681 Merge remote-tracking branch 'origin/mr/11' 2023-07-21 17:42:26 +02:00
Bruce Mitchener a618987545 preserves-schema: Remove useless `vec!`.
Since `decode_lit` takes a `&[u8]`, no `vec!` is needed here.
2023-07-17 11:59:43 +07:00
Bruce Mitchener acfacd636b preserves-tools: deps: Update to clap 3 release.
This was previously using a beta release. This helps move towards
updating to the current v4 release.
2023-07-17 11:46:48 +07:00
Tony Garnock-Jones 6f085a7192 Repair examples 2023-06-27 22:27:41 +02:00
Tony Garnock-Jones 775741f944 Update tagging scheme 2023-06-27 22:19:52 +02:00
Tony Garnock-Jones 5562add7ba Sketches of new Ref layouts 2023-06-27 21:25:19 +02:00
Tony Garnock-Jones 829ff5c3c5 Tweaks 2023-06-27 21:25:09 +02:00
Tony Garnock-Jones 9f556fd9e6 Experiment with zero-copy format 2023-06-24 00:49:12 +02:00
Tony Garnock-Jones 54b9bb6f25 Repair machineword reads 2023-06-23 22:34:38 +02:00
Tony Garnock-Jones ae02cff81d Fix annotations; progress on integers 2023-06-23 22:28:31 +02:00
Tony Garnock-Jones 3c76819687 Annotations draft 2023-06-23 16:54:19 +02:00
Tony Garnock-Jones 9c5368b69b Binary writer draft 2023-06-23 15:40:05 +02:00
Tony Garnock-Jones 60f23286e2 Fix EOF handling; BigNum 2023-06-23 09:07:19 +02:00
Tony Garnock-Jones 8788a72a8d Binary codec (not yet working) 2023-06-21 00:45:04 +02:00
Tony Garnock-Jones 3c8d9dcf98 Google Test C++ library 2023-06-19 23:53:23 +02:00
Tony Garnock-Jones 1f06ff9422 Publish
- @preserves/core@0.22.0
 - @preserves/schema@0.22.3
2023-06-16 12:45:18 +02:00
Tony Garnock-Jones ac6a138a00 forEachEmbedded 2023-06-16 12:44:22 +02:00
Tony Garnock-Jones 69010737b9 Begin sketch of C++ implementation 2023-06-12 23:16:47 +02:00
Tony Garnock-Jones fe12a8d775 Publish
- @preserves/schema@0.22.2
2023-06-09 16:46:07 +02:00
Tony Garnock-Jones b41c0125b0 Treat `arguments` as a JavaScript keyword too. 2023-06-09 16:44:34 +02:00
Tony Garnock-Jones 0081e290ca Publish
- @preserves/core@0.21.1
 - @preserves/schema@0.22.1
2023-05-12 11:26:47 +02:00
Tony Garnock-Jones 134de6989b parse, parseAll 2023-05-12 11:26:06 +02:00
Tony Garnock-Jones a167e97c00 ValueClass 2023-05-12 11:25:56 +02:00
Tony Garnock-Jones 7e088ef8b3 Remove .dir-locals.el (now that we have lsp-clients-typescript-prefer-use-project-ts-server) 2023-05-05 14:03:15 +02:00
Tony Garnock-Jones 10768bd83e Publish
- @preserves/core@0.21.0
 - @preserves/schema@0.22.0
2023-05-03 10:26:44 +03:00
Tony Garnock-Jones 741b664970 Make field numbers accessible 2023-05-03 10:22:47 +03:00
Tony Garnock-Jones b382b61bf6 Merges 2023-03-27 23:07:41 +02:00
Tony Garnock-Jones fe9d836942 Print ordering 2023-03-22 13:35:46 +01:00
Tony Garnock-Jones 18dd532df2 Fix anchor link scrolling behind navbar 2023-03-17 20:57:03 +01:00
Tony Garnock-Jones 8c28dfa3ae Cosmetic 2023-03-17 20:56:52 +01:00
Tony Garnock-Jones d53ca13cd7 Improve links a little 2023-03-17 15:45:19 +01:00
Tony Garnock-Jones 047ef7c521 Update 2023-03-17 15:25:44 +01:00
Tony Garnock-Jones 316c2dd407 Document preserves.schema 2023-03-17 15:24:53 +01:00
Tony Garnock-Jones 108119a351 Fancy nav, plus craiyon logo! 2023-03-17 12:02:27 +01:00
Tony Garnock-Jones 89b96f623d Slightly improve text in path spec 2023-03-17 10:38:43 +01:00
Tony Garnock-Jones f6032d899f Clarify 2023-03-17 10:35:53 +01:00
Tony Garnock-Jones fac24309aa Remove code snippets from heading (because otherwise it renders funny in the toc) 2023-03-17 10:34:02 +01:00
Tony Garnock-Jones b74f9a708b Always better to use quotes 2023-03-17 10:30:31 +01:00
Tony Garnock-Jones 1fc45a5365 Repair symlink 2023-03-17 10:29:44 +01:00
Tony Garnock-Jones d89896a6a7 Be cleverer about unreleased doc versions 2023-03-17 10:26:53 +01:00
Tony Garnock-Jones a86ff376c0 Initial rendering 2023-03-17 10:22:31 +01:00
Tony Garnock-Jones 8c033fa111 Document and repair path.py 2023-03-17 10:21:56 +01:00
Tony Garnock-Jones 04cc30d77f Link directly to latest version 2023-03-17 09:17:28 +01:00
Tony Garnock-Jones 0838d39187 Oops 2023-03-17 09:12:29 +01:00
Tony Garnock-Jones 86000bfbea Link to latest 2023-03-17 09:08:34 +01:00
Tony Garnock-Jones 2a7eaa5ae3 Versioned docs! 2023-03-17 09:07:53 +01:00
Tony Garnock-Jones f69906650e Link to python docs from main page 2023-03-17 00:33:11 +01:00
Tony Garnock-Jones 5314958086 Enable last-modified-date 2023-03-17 00:31:47 +01:00
Tony Garnock-Jones e96faede83 site_url, repo_url 2023-03-17 00:26:23 +01:00
Tony Garnock-Jones 9645c11051 serve-docs target 2023-03-17 00:22:24 +01:00
Tony Garnock-Jones 019cd35932 Bump version 2023-03-17 00:07:49 +01:00
Tony Garnock-Jones a4403cf6f3 More docs. That's the lot for python now except path and schema. 2023-03-17 00:07:27 +01:00
Tony Garnock-Jones c7687772b0 Update compare.py docs 2023-03-16 21:57:21 +01:00
Tony Garnock-Jones 514dc04920 Tweak 2023-03-16 21:43:47 +01:00
Tony Garnock-Jones a6c98159ce Another stab at long_description for python package 2023-03-16 21:42:25 +01:00
Tony Garnock-Jones 6bc3601e95 Don't bother with sitemap.xml.gz 2023-03-16 21:41:49 +01:00
Tony Garnock-Jones 47e579068a Fix long_description 2023-03-16 21:38:45 +01:00
Tony Garnock-Jones 6b2b266a8f python 0.17.1 2023-03-16 21:31:37 +01:00
Tony Garnock-Jones 2bad9a6bca README.md for python 2023-03-16 21:31:24 +01:00
Tony Garnock-Jones 2ab814333c Deploy docs 2023-03-16 21:19:42 +01:00
Tony Garnock-Jones e3e1dd4944 Better __init__.py docs 2023-03-16 21:04:37 +01:00
Tony Garnock-Jones 7cbd1a2813 Docs for preserves.binary; better encoding annotations with canonicalization 2023-03-16 20:55:49 +01:00
Tony Garnock-Jones 99258be1ef Fix doctest module loading to avoid double-creation of modules 2023-03-16 20:12:17 +01:00
Tony Garnock-Jones 031575ad82 Python preserves doctest runner, and mkdocs documentation stubs 2023-03-16 17:53:02 +01:00
Tony Garnock-Jones 98697e049d Add missing row to int example table 2023-03-15 20:50:49 +01:00
Tony Garnock-Jones 07b28ca042 Improvements 2023-03-15 17:15:26 +01:00
Tony Garnock-Jones d11f008705 Spec change proposal for #41 2023-03-15 16:24:02 +01:00
Tony Garnock-Jones 34f92c3870 Copy across easy wins from the wip branch 2023-03-15 15:18:46 +01:00
Tony Garnock-Jones 313b2c4271 Describe metaconventions for capitalization of various kinds of name. Fixes #11. 2023-03-15 15:14:33 +01:00
Andrew Elgert b1f9eb45d2 Compiler can load from file-like objects 2023-03-14 11:45:30 -04:00
Tony Garnock-Jones 8dc51d70e7 Publish
- @preserves/schema@0.21.10
2023-02-11 18:10:54 +01:00
Tony Garnock-Jones d027fc4ed6 Repair setof codegen 2023-02-11 18:10:36 +01:00
Tony Garnock-Jones 55d55d9ccf Publish
- @preserves/schema@0.21.9
2023-02-11 17:29:46 +01:00
Tony Garnock-Jones cd996bcbc7 Repair set schema parsing 2023-02-11 17:28:03 +01:00
Tony Garnock-Jones 26d65a4dd9 Release independent packages
preserves-schema@3.2.1

Generated by cargo-workspaces
2023-02-07 11:13:12 +01:00
Tony Garnock-Jones eef675cc0c Bump python version 2023-02-07 10:52:09 +01:00
Tony Garnock-Jones 156777cfa6 Publish
- @preserves/core@0.20.6
 - @preserves/schema@0.21.8
2023-02-07 10:50:12 +01:00
Tony Garnock-Jones 25f9f2c70c Bump main spec version 2023-02-07 10:47:50 +01:00
Tony Garnock-Jones eb28206b3e Bump schema spec version 2023-02-07 10:47:10 +01:00
Tony Garnock-Jones d73118fc77 Update implementations 2023-02-07 10:42:30 +01:00
Tony Garnock-Jones f5d7fde6f8 Update spec text 2023-02-07 10:35:03 +01:00
Tony Garnock-Jones 43312f5ead Repair syntax in schema.prs and regenerate schema.bin 2023-02-07 10:34:42 +01:00
Emery Hemingway 661e35a757 schema: order #f first in EmbeddedTypeName
Nim is taking the first option as the default initialization
value for Or types and #f is the default for EmbeddedTypeName.
2023-02-07 10:30:54 +01:00
Tony Garnock-Jones 23dbf3cb43 Repair multiple-glob common path usage. Closes #39 2023-02-04 17:07:16 +01:00
Tony Garnock-Jones 333503c772 Update dependencies 2023-02-04 11:58:51 +01:00
Tony Garnock-Jones 9559558831 Hack to allow external module paths to be symbols instead of strings 2023-02-02 21:56:12 +01:00
Tony Garnock-Jones c5974e0b00 Release independent packages
preserves-schema@3.2.0

Generated by cargo-workspaces
2023-01-31 15:08:45 +01:00
Tony Garnock-Jones b413d458f0 Include bundle (binary-encoded) in compiled output 2023-01-31 15:08:29 +01:00
Tony Garnock-Jones a51fc34700 Release independent packages
preserves-path@4.1.0
preserves-schema@3.1.0

Generated by cargo-workspaces
2023-01-28 22:39:11 +01:00
Tony Garnock-Jones 7f387d6d71 Support xrefs in schema compiler; offer Arc for languages 2023-01-28 22:38:14 +01:00
Tony Garnock-Jones acbc1c5fe5 Link to FeepingCreature's post 2023-01-23 10:36:44 +01:00
Tony Garnock-Jones 04d8201e08 Release independent packages
preserves@3.0.0
preserves-path@4.0.2
preserves-schema@3.0.3
preserves-tools@3.0.2

Generated by cargo-workspaces
2023-01-16 14:56:04 +01:00
Tony Garnock-Jones 5bee5fb247 Major bump to rust preserves because of the new numerics/symbol syntax 2023-01-16 14:55:08 +01:00
Tony Garnock-Jones 560267d0d4 Release independent packages
preserves-schema@3.0.2

Generated by cargo-workspaces
2023-01-08 13:13:00 +01:00
Tony Garnock-Jones fcf58f0dc0 Reader, it was *not* right. 2023-01-08 13:11:49 +01:00
Tony Garnock-Jones 502e83c26d Publish
- @preserves/core@0.20.5
 - @preserves/schema@0.21.7
2023-01-05 15:14:06 +01:00
Tony Garnock-Jones 340f809c55 isSequence 2023-01-05 15:13:23 +01:00
Tony Garnock-Jones a8d7fda89e merge-preserves 2022-11-28 22:38:23 +01:00
Tony Garnock-Jones 269ed2391a Repair text syntax for numbers and symbols. Closes #19/#36/#37/#38.
Numbers and (bare) Symbols are now disambiguated after reading, which
permits leading `+`, leading `0`, and a wider range of acceptable
Symbols.

Updates spec text, test cases, and implementations. Some ancillary fixes
to Python's comparison routines are also included.
2022-11-08 19:56:24 +01:00
Tony Garnock-Jones 351feba8d2 Benchmark profiling 2022-10-29 13:16:52 +02:00
Tony Garnock-Jones fd676e8f53 Allow ag to ignore scratch directory 2022-10-26 16:52:55 +02:00
Tony Garnock-Jones 0b94af3a52 Release independent packages
preserves@2.5.0

Generated by cargo-workspaces
2022-10-26 16:01:48 +02:00
Tony Garnock-Jones 52de025c21 Parsing and reading via TextReader/TextWriter 2022-10-26 16:00:48 +02:00
Tony Garnock-Jones a1043f406c It has moved beyond proposal really now 2022-10-26 13:35:22 +02:00
Tony Garnock-Jones b5e2eeae46 Release independent packages
preserves@2.4.1
preserves-path@4.0.1
preserves-schema@3.0.1
preserves-tools@3.0.1

Generated by cargo-workspaces
2022-10-26 13:31:17 +02:00
Tony Garnock-Jones 1804fa3918 Prepare to try out cargo-workspaces as release tool 2022-10-26 13:30:45 +02:00
Tony Garnock-Jones ad1aec3d89 Reject odd number of hexits rather than ignoring the lone trailing hexit 2022-10-26 12:49:49 +02:00
Tony Garnock-Jones fd121cfb4c Bump versions 2022-10-24 14:39:07 +02:00
Tony Garnock-Jones f88e3ca85c Unused arg 2022-10-24 14:38:51 +02:00
Tony Garnock-Jones be7b8e6477 Adjust quantification for Deserialize trait 2022-10-24 14:30:43 +02:00
Tony Garnock-Jones 703d5ac602 Merge branch 'readme' into 'main'
Update URL for Preserves-Nim

See merge request preserves/preserves!6
2022-10-18 07:24:49 +00:00
Emery Hemingway a15cb4fb53 Update URL for Preserves-Nim 2022-10-13 23:17:41 -05:00
Tony Garnock-Jones fbda60c96b Merge branch 'schema-notes' into 'main'
Note on ordering and default initialization

See merge request preserves/preserves!5
2022-08-23 13:28:31 +00:00
Tony Garnock-Jones 4b98acf8ba preserves-python version 0.16.1 2022-07-22 10:24:30 +02:00
Tony Garnock-Jones 3544bf18fa Ugh, python 3.7 doesn't have the PEP 570 "/" positional-only syntax. 2022-07-22 10:23:54 +02:00
Tony Garnock-Jones 4528100248 Update PDF generation 2022-06-18 21:51:09 +02:00
Tony Garnock-Jones 0feef9cc9e Clean up named links 2022-06-18 20:28:38 +02:00
Tony Garnock-Jones 7d3789e371 Split up spec! 2022-06-18 19:12:05 +02:00
Tony Garnock-Jones 1f495eef1e Repair typo that caused annotations not to work properly with fromJS 2022-06-16 10:10:38 +02:00
Tony Garnock-Jones 73ae8ddba3 NOTICE 2022-06-10 17:48:30 +02:00
Tony Garnock-Jones bb878bab46 Repair css 2022-06-10 16:05:35 +02:00
Tony Garnock-Jones b332f2668e Begin sketch of Preserves Schema semantics 2022-06-09 21:30:56 +02:00
Tony Garnock-Jones 5b01beb2f6 run-jekyll 2022-06-09 15:39:54 +02:00
Tony Garnock-Jones cd635477c6 Tweak 2022-06-09 15:37:29 +02:00
Tony Garnock-Jones 9ba8617952 Attempt to clarify Preserves Schema relationship to host-language types. 2022-06-09 14:59:53 +02:00
Tony Garnock-Jones c49b8673f7 READMEs for Schema implementations 2022-06-09 13:49:59 +02:00
Tony Garnock-Jones 0bc742d7a2 (cargo-release) version {{version}} 2022-06-08 16:27:04 +02:00
Tony Garnock-Jones aff94bbb2f python: bump version to 0.16.0 2022-06-08 16:23:56 +02:00
Tony Garnock-Jones 8b8da80f61 Explain why parsing failed 2022-06-08 16:22:10 +02:00
Tony Garnock-Jones d5a8440a9e Update docs 2022-06-08 16:13:17 +02:00
Tony Garnock-Jones 7fdf50b963 Support comma style option to preserves-tool 2022-06-08 16:10:33 +02:00
Tony Garnock-Jones 2e8d53c779 Another schema test 2022-06-07 21:59:05 +02:00
Tony Garnock-Jones 7ebe538c42 python: bump version to 0.15.0 2022-06-07 21:47:23 +02:00
Tony Garnock-Jones 34183a9519 Stub out some schema tests 2022-06-07 21:46:06 +02:00
Tony Garnock-Jones e019148065 Fix positional initializers for schema values, and allow keyword initializers 2022-06-07 21:45:56 +02:00
Tony Garnock-Jones c9c973ce9c Preserves-order for Python 2022-06-07 21:45:12 +02:00
Tony Garnock-Jones 00eb9e97b6 Implement count function 2022-06-06 15:28:11 +02:00
Tony Garnock-Jones 78fe5a2a85 Idea for preserves path count function 2022-06-06 14:27:47 +02:00
Tony Garnock-Jones 4ef1341883 Remove old import of distutils 2022-06-03 21:21:44 +02:00
Tony Garnock-Jones 2e50591946 python: Bump version 2022-06-03 21:14:24 +02:00
Tony Garnock-Jones c0d3f37905 python: merge(), and support for and-pattern unparsing 2022-06-03 21:13:59 +02:00
Tony Garnock-Jones 597e993c05 Support and-schemas in python 2022-06-03 16:50:03 +02:00
Tony Garnock-Jones 1a090ce5ff Note on comparisons 2022-06-01 14:43:33 +02:00
Tony Garnock-Jones 80e6e4aa3f Prepare for move away from preserves.gitlab.io/preserves toward preserves.dev 2022-05-24 13:32:54 +02:00
Tony Garnock-Jones 46a1228aa7 Remove front-page warning 2022-05-24 12:47:33 +02:00
Tony Garnock-Jones 0b21fc4a3f Publish
- @preserves/schema@0.21.6
2022-05-04 09:29:19 +02:00
Tony Garnock-Jones 8595034190 Repair schema distribution 2022-05-04 09:28:49 +02:00
Tony Garnock-Jones b45306db03 Distribute schema.prs 2022-05-04 09:27:04 +02:00
Tony Garnock-Jones 859a88adc2 Publish
- @preserves/schema@0.21.5
2022-05-03 17:13:05 +02:00
Tony Garnock-Jones e3a1be80e9 Repair preserves-schema-ts watch mode 2022-05-03 17:12:34 +02:00
Tony Garnock-Jones 5d24902de1 Publish
- @preserves/core@0.20.4
 - @preserves/schema@0.21.4
2022-04-27 22:08:34 +03:00
Tony Garnock-Jones 800e9ccf80 Repair stringification of nontrivial embedded values 2022-04-27 22:07:53 +03:00
Tony Garnock-Jones 197359ff22 Update docs 2022-03-08 12:27:28 +01:00
Tony Garnock-Jones 22a7cffd12 Correct names 2022-03-07 08:31:24 +01:00
Tony Garnock-Jones 9c71df9abb PRESERVES_COMPACT 2022-03-04 09:18:59 +01:00
Tony Garnock-Jones 4a41fac6ec Crude measurement; multiple input values 2022-03-04 09:18:43 +01:00
Tony Garnock-Jones daa6b8f931 Repair typos (thanks to Gregg Irwin for pointing these out) 2022-02-23 09:36:42 +01:00
Tony Garnock-Jones fad9111241 Utility for tagging 2022-02-11 11:57:29 +01:00
Tony Garnock-Jones 7f65f9ecb1 Add _ALL superclass for Enumeration subclasses, for @extend purposes 2022-02-11 11:56:08 +01:00
Tony Garnock-Jones 32544c544a Optional commas in pretty-printer 2022-02-11 11:38:29 +01:00
Tony Garnock-Jones 0f49b8b1b4 Python release 0.11.0 (canonicalizing binary encoder) 2022-02-09 15:05:55 +01:00
Tony Garnock-Jones a1f5a6ee13 Publish
- @preserves/core@0.20.3
 - @preserves/schema@0.21.3
2022-02-09 15:04:14 +01:00
Tony Garnock-Jones 356ff74058 Experimentally (?) use fromJS in stringify 2022-02-09 15:03:36 +01:00
Tony Garnock-Jones 21de8b799a Canonicalizing binary encoder 2022-02-09 14:59:50 +01:00
Tony Garnock-Jones 0596d877f8 Publish
- @preserves/core@0.20.2
 - @preserves/schema@0.21.2
2022-01-26 16:42:06 +01:00
Tony Garnock-Jones d9ee80093b Repair stringifyEmbeddedWrite 2022-01-26 16:41:16 +01:00
Tony Garnock-Jones a461657cde Publish
- @preserves/core@0.20.1
 - @preserves/schema@0.21.1
2022-01-26 16:03:33 +01:00
Tony Garnock-Jones 574209966b Better stringification 2022-01-26 16:03:13 +01:00
Tony Garnock-Jones c5094f8b8f Publish
- @preserves/core@0.20.0
 - @preserves/schema@0.21.0
2022-01-26 14:16:19 +01:00
Tony Garnock-Jones 0129901dab Javascript pretty-printer 2022-01-26 14:15:35 +01:00
Tony Garnock-Jones 6cd8cb2c37 Repair test 2022-01-24 13:41:58 +01:00
Tony Garnock-Jones bdbffad5d3 Update package.json script names 2022-01-24 13:39:19 +01:00
Tony Garnock-Jones 2c58066380 Publish
- @preserves/core@0.19.0
 - @preserves/schema@0.20.0
2022-01-24 13:01:17 +01:00
Tony Garnock-Jones f6999938f8 More ergonomic definition of variant name 2022-01-22 23:53:07 +01:00
Tony Garnock-Jones 382bf8d956 Expose reflection typedefs 2022-01-22 23:45:24 +01:00
Tony Garnock-Jones e4792ddccd Improve protocols for (de)coding JS/Preserves somewhat 2022-01-22 23:38:02 +01:00
Tony Garnock-Jones ae6eaa663e Expose a touch more static information, e.g. __as_preserve__ and __from_preserve__. 2022-01-22 23:25:16 +01:00
Tony Garnock-Jones d8615175a5 I guess I'm developing a convention for how these things should look now 2022-01-22 23:21:05 +01:00
Tony Garnock-Jones a98aa357d4 Repair command-line handling 2022-01-22 23:20:35 +01:00
Tony Garnock-Jones 3d1b151462 (cargo-release) version {{version}} 2022-01-19 14:27:14 +01:00
Tony Garnock-Jones 261395beaf `rec!` macro; access to `io::Write` inside `Writer`s 2022-01-19 14:25:52 +01:00
Tony Garnock-Jones 76ca8fe12b (cargo-release) version {{version}} 2022-01-14 15:49:36 +01:00
Tony Garnock-Jones 35e5d54683 Silence warning 2022-01-14 15:48:22 +01:00
Tony Garnock-Jones 8f7ef3ff69 Repair case-conversion of type names for names like "IOList" -> "IoList" 2022-01-14 15:48:10 +01:00
Tony Garnock-Jones c1077e2f35 Note on annotation ordering (spelling out what was already implicit in the text) 2022-01-14 15:28:54 +01:00
Tony Garnock-Jones 087b8d6130 Repair schemac invocations 2022-01-14 14:12:32 +01:00
Tony Garnock-Jones 9a718548c0 First steps toward a C library for Preserves 2022-01-14 00:27:18 +01:00
Tony Garnock-Jones 15765126e2 Repair typo 2022-01-13 23:30:59 +01:00
Tony Garnock-Jones 2721ce81c4 Repair lexicographic ordering among Bytes instances, which in turn repairs canonicalizing binary output, which in turn fixes the tests now there's just been a very long test name symbol added. 2022-01-13 23:30:28 +01:00
Tony Garnock-Jones 850678a80a New test of very long integer 2022-01-13 23:29:47 +01:00
Tony Garnock-Jones c3af4656c1 Version 0.10.2 2022-01-12 14:07:51 +01:00
Tony Garnock-Jones 61ef91a958 Allow schema encoding (auto-)escapes 2022-01-12 14:07:32 +01:00
Tony Garnock-Jones 8f99f14b92 Oops. Forgot to git add copies of test sample data 2022-01-12 12:27:01 +01:00
Tony Garnock-Jones f348701169 Avoid x.__preserve__(), preferring preserve(x) 2022-01-12 11:40:05 +01:00
Tony Garnock-Jones ed518a9c71 Version 0.10.0 2022-01-12 02:04:49 +01:00
Tony Garnock-Jones b70352bc85 Publish
- @preserves/core@0.18.2
 - @preserves/schema@0.19.7
2022-01-12 02:03:23 +01:00
Tony Garnock-Jones ddfd2e24a6 Indented text printing 2022-01-12 02:02:36 +01:00
Tony Garnock-Jones 3cc875bce3 Handle no-whitespace-after-a-number 2022-01-12 02:02:11 +01:00
Tony Garnock-Jones fb6a5caf38 Update LSP settings 2022-01-12 02:01:54 +01:00
Tony Garnock-Jones 50de00eac5 Version 0.9.0 2022-01-11 06:29:45 +01:00
Tony Garnock-Jones 8c4fedac0a Split out encoding/formatting unhandler error to method, for overridability 2022-01-11 06:29:18 +01:00
Tony Garnock-Jones 602dea1ac6 Rearrange tests to include test data. As part of this, remove tests for python2; from here out, python2 compatibility will likely rot. 2022-01-07 21:14:21 +01:00
Tony Garnock-Jones 275a5aa45b Bump patch number 2022-01-07 20:49:20 +01:00
Tony Garnock-Jones 39f9a2e5ef Include path.prb in the package, like schema.prb 2022-01-07 20:48:24 +01:00
Tony Garnock-Jones 21cfa3a1e6 Bump to 0.7.3 2021-12-17 23:25:39 +01:00
Tony Garnock-Jones 6c0ef47e03 Be more liberal about accepting either lists or tuples when parsing using a schema instance 2021-12-17 23:25:03 +01:00
Tony Garnock-Jones 550a524ca1 Publish
- @preserves/schema@0.19.6
2021-12-13 13:13:33 +01:00
Tony Garnock-Jones 944ac54414 Dumber but more reliable and more useful handling of schema input base directories 2021-12-13 12:52:24 +01:00
Tony Garnock-Jones bfa0b0bcb4 Publish
- @preserves/schema@0.19.5
2021-12-13 12:19:19 +01:00
Tony Garnock-Jones 3b1669389c Allow cross-references to other schemas when generating code 2021-12-13 12:18:12 +01:00
Tony Garnock-Jones 9442abe9bd Publish
- @preserves/schema@0.19.4
2021-12-12 23:53:07 +01:00
Tony Garnock-Jones fc84813704 Expose schema compiler's gentype.ts module 2021-12-12 23:52:45 +01:00
Tony Garnock-Jones 972085a59b Use GenericEmbedded for embedded schemas 2021-12-12 23:52:31 +01:00
Tony Garnock-Jones 8b7ea7c589 Publish
- @preserves/schema@0.19.3
2021-12-12 22:58:40 +01:00
Tony Garnock-Jones 80dd3cd6b4 Support access to "external" schema module via some dotted path within a TypeScript module 2021-12-12 22:58:00 +01:00
Tony Garnock-Jones ef6db841dd Publish
- @preserves/schema@0.19.2
2021-12-12 13:50:32 +01:00
Tony Garnock-Jones 282d3f798f Support references to node_modules modules on command line 2021-12-12 13:49:50 +01:00
Tony Garnock-Jones 874303186d Publish
- @preserves/core@0.18.1
 - @preserves/schema@0.19.1
2021-12-09 21:25:53 +01:00
Tony Garnock-Jones 2ff4e88822 Repair error in encoder contents extraction. 2021-12-09 21:25:27 +01:00
Tony Garnock-Jones 57c66f81b1 Publish
- @preserves/core@0.18.0
 - @preserves/schema@0.19.0
2021-12-09 18:38:49 +01:00
Tony Garnock-Jones 1e4bf0dc51 Export reflective information about schema-generated types 2021-12-09 18:36:45 +01:00
Tony Garnock-Jones 5a0fbeb1b5 Export JS Symbol, in case of aliasing in a schema 2021-12-09 18:35:17 +01:00
Tony Garnock-Jones f2d9b68329 Slightly nicer embedded-value printing 2021-12-09 18:34:54 +01:00
Tony Garnock-Jones 43f31b9a78 Repairs to tests 2021-12-08 14:06:16 +01:00
Tony Garnock-Jones 6576f30639 Refactoring of uncertain benefit: allow multiple function definitions at once 2021-12-03 14:42:49 +01:00
Tony Garnock-Jones e12d978915 Upgrade to TypeScript 4.5 2021-12-03 14:42:25 +01:00
Tony Garnock-Jones 4673de5db4 Publish
- @preserves/schema@0.18.0
2021-12-01 16:42:00 +01:00
Tony Garnock-Jones bb0d825cdf Fix bundling of schema.prb 2021-11-30 18:41:34 +01:00
Tony Garnock-Jones aadd83d715 Use bundled metaschema copy 2021-11-30 18:35:16 +01:00
Tony Garnock-Jones ed63d46348 Experimental schema interpreter and preserves-path support 2021-11-16 22:10:04 +01:00
Tony Garnock-Jones b38d180956 Targets for uniformish cross-build 2021-11-16 22:06:56 +01:00
Tony Garnock-Jones b7c9efc3a1 Explicitly select beta.2 of clap, since beta.5 isn't compiling for me 2021-11-12 13:35:37 +01:00
Emery Hemingway 4ae8c341c9 Note on ordering and default initialization 2021-11-11 21:23:03 +01:00
Emery Hemingway 2ff489d975 Initial Dhall functions
Add functions for representing the JSON subset of Preserves in the
Dhall configuration language.

https://dhall-lang.org/
2021-10-22 19:01:32 +02:00
Tony Garnock-Jones 1668fdc6dd Notes on identifiers and capitalization conventions 2021-10-14 13:11:40 +02:00
Tony Garnock-Jones 3a605e75d6 Better treatment of generics in TypeScript schema compiler, after the Rust implementation 2021-10-11 12:56:53 +02:00
Tony Garnock-Jones b2c3032e7a arm-binary target 2021-10-08 16:56:35 +02:00
Tony Garnock-Jones 668ac9f680 (cargo-release) version {{version}} 2021-10-04 14:32:51 +02:00
Tony Garnock-Jones ea83031a28 Notes on regex dialect 2021-10-04 14:29:39 +02:00
Tony Garnock-Jones 77c305a4cf into_string, into_bytestring, into_symbol 2021-10-04 14:28:56 +02:00
Tony Garnock-Jones 602cfb8800 Sure, POSIX ERE in the interim 2021-10-03 16:14:18 +02:00
Tony Garnock-Jones 5260f85952 Fix value_owned for ArcValue 2021-10-02 11:49:40 +02:00
Tony Garnock-Jones 55b02b9cff Specify RFC3339 convention further. 2021-10-02 11:48:53 +02:00
Tony Garnock-Jones 386c07628c XML Infoset convention 2021-09-30 14:46:46 +02:00
Tony Garnock-Jones 5fded03fa4 Alternative encoding of optional values in preserves-schema.md 2021-09-30 14:46:35 +02:00
Tony Garnock-Jones 0507ab2f38 Repair errors in preserves-schema.md 2021-09-30 14:46:19 +02:00
Tony Garnock-Jones b73e0c7025 (cargo-release) version {{version}} 2021-09-30 13:12:27 +02:00
Tony Garnock-Jones e2a4e3d6cb Inline the heck out of the binary codec for a ~5% speedup 2021-09-30 13:12:06 +02:00
Tony Garnock-Jones e31cf739df (cargo-release) version {{version}} 2021-09-30 12:45:19 +02:00
Tony Garnock-Jones 486a631e73 Add strip_annotations function and expose it in preserves-tool 2021-09-30 12:41:29 +02:00
Tony Garnock-Jones 29a882f953 Shrink commonest annotationless representation 2021-09-30 11:15:26 +02:00
Tony Garnock-Jones d1d52c2a30 Repair (?) warning 2021-09-29 12:12:48 +02:00
Tony Garnock-Jones 62d9236045 aarch64-binary target 2021-09-29 12:09:52 +02:00
Tony Garnock-Jones 1b4064b17c Release 2.0.0 2021-09-25 11:16:48 +02:00
Tony Garnock-Jones a72810c416 (cargo-release) version {{version}} 2021-09-20 15:39:02 +02:00
Tony Garnock-Jones b1ed29657e Make it so that any defined language can be used as placeholder to identity-(un)parse `NestedValue`s 2021-09-20 15:38:21 +02:00
Tony Garnock-Jones 9936ddb29d (cargo-release) version {{version}} 2021-09-20 14:33:39 +02:00
Tony Garnock-Jones 94e9fabc70 Make WalkState reusable; add lookup_definition 2021-09-20 14:33:05 +02:00
Tony Garnock-Jones f778325748 (cargo-release) version {{version}} 2021-09-19 20:58:03 +02:00
Tony Garnock-Jones b68b485af4 Better approach to intra-workspace dependencies, I hope 2021-09-19 20:57:38 +02:00
Tony Garnock-Jones 72a38cea7e Argh, update to naming convention used by cargo-release 2021-09-19 20:53:06 +02:00
Tony Garnock-Jones ccf01f5f24 Repair scoping of crate references in syntax macros 2021-09-19 20:50:13 +02:00
Tony Garnock-Jones 6fdda6636b Bump versions to 2.0.0-rc1 2021-09-19 16:48:00 +02:00
Tony Garnock-Jones 843c0c894f Include `Language`s from externally-referenced definitions 2021-09-19 16:43:46 +02:00
Tony Garnock-Jones e5a5130b56 Tighten 2021-09-17 12:30:31 +02:00
Tony Garnock-Jones a91ee3977f DRY for Language; prepare for factoring out language prerequisites 2021-09-17 12:27:10 +02:00
Tony Garnock-Jones ac8567731d DRY for _Value 2021-09-17 12:19:26 +02:00
Tony Garnock-Jones 6869a89291 Another step toward nested multi-Language support 2021-09-17 11:59:43 +02:00
Tony Garnock-Jones e78196c942 Value self-parsing happens with &() now, not () 2021-09-17 11:37:36 +02:00
Tony Garnock-Jones 15a27b4865 Better Parse/Unparse traits 2021-09-17 11:06:53 +02:00
Tony Garnock-Jones df4059ee7a Oops 2021-09-16 16:35:12 +02:00
Tony Garnock-Jones 8d587c0aaa Steps toward better cross-compilation-unit handling of Languages 2021-09-16 16:31:59 +02:00
Tony Garnock-Jones d872f7cf8a Limit NestedValue self-parse/unparse support to () rather than L 2021-09-15 21:13:03 +02:00
Tony Garnock-Jones 7bf5403353 Repair generated references to _Value::Embedded (was _Value::D) 2021-09-15 15:26:02 +02:00
Tony Garnock-Jones 9f98e1ef3b Remove unwanted code 2021-09-15 15:25:46 +02:00
Tony Garnock-Jones 06fc9aa017 NestedValue::D --> NestedValue::Embedded 2021-09-15 15:19:03 +02:00
Tony Garnock-Jones 997bea2836 Better name 2021-09-15 15:14:36 +02:00
Tony Garnock-Jones 28249b19f7 More flexible Literals-finding 2021-09-15 15:07:46 +02:00
Tony Garnock-Jones e56b62cfbb Fix multi-schema bundles after refactor 2021-09-15 14:03:33 +02:00
Tony Garnock-Jones 985a0b6795 Handle external types wrt has_embedded 2021-09-15 10:50:37 +02:00
Tony Garnock-Jones 352d8ba1b3 Massive refactor to be more flexible around embedded types 2021-09-15 10:43:31 +02:00
Tony Garnock-Jones 8127033407 Surprisingly, associated types might simplify drastically! 2021-09-14 22:56:09 +02:00
Tony Garnock-Jones 6348524542 Introduce TField::Any and TField::Embedded 2021-09-12 18:28:29 +02:00
Tony Garnock-Jones 87227b5623 Make whole Schema available when compiling 2021-09-12 18:08:56 +02:00
Tony Garnock-Jones c4afc49646 Value::from various references-to-atoms 2021-09-12 14:55:05 +02:00
Tony Garnock-Jones ef67347b8d Repair and-pattern reading 2021-09-11 02:53:46 +02:00
Tony Garnock-Jones aabe7b2623 Repair reading of simple but compound types within a simple dictionary pattern 2021-09-11 02:53:32 +02:00
Tony Garnock-Jones e43e85ce8e preserves::value::merge has to return Option 2021-09-11 02:52:49 +02:00
Tony Garnock-Jones 5f2a3e3eb8 Preserves value merge in Rust 2021-09-11 02:49:17 +02:00
Tony Garnock-Jones eda9979041 Owned destructors 2021-09-11 02:47:51 +02:00
Tony Garnock-Jones 4afc6d4c94 Repair error in merge 2021-09-10 15:47:16 +02:00
Tony Garnock-Jones d26e38ded0 Regenerate schema.rs 2021-09-05 13:16:29 +02:00
Tony Garnock-Jones 66e7af491f Update docs 2021-09-02 13:46:49 +02:00
Tony Garnock-Jones 18ac916899 preserves-path examples 2021-09-02 12:05:55 +02:00
Tony Garnock-Jones 00e31c0e29 (cargo-release) version {{version}} 2021-09-02 11:13:17 +02:00
Tony Garnock-Jones a9125874bf (cargo-release) version {{version}} 2021-08-28 17:02:28 +02:00
Tony Garnock-Jones 8550be0ba2 Refactor SignedInteger conversions to be more comprehensive and less repetitive 2021-08-28 17:01:59 +02:00
Tony Garnock-Jones 3cdf1f662e Reorganize support re-exports to support preservesless usage of preserves_schema 2021-08-28 17:01:36 +02:00
Tony Garnock-Jones 027966fb3b (cargo-release) version {{version}} 2021-08-27 17:17:39 +02:00
Tony Garnock-Jones f387f5e8c9 Value::bytestring, NestedValue::bytestring 2021-08-27 17:17:30 +02:00
Tony Garnock-Jones 0aded61071 Take advantage of NestedValue::symbol 2021-08-27 16:59:54 +02:00
Tony Garnock-Jones dc451ea7b4 (cargo-release) version {{version}} 2021-08-27 16:52:52 +02:00
Tony Garnock-Jones 3e56cf3d7e NestedValue::symbol 2021-08-27 16:52:11 +02:00
Tony Garnock-Jones 94fe6ad946 Update pre-commit hook and clone path.bin to address #30. 2021-08-25 09:24:24 +02:00
Tony Garnock-Jones 446b2ee5f7 (cargo-release) version {{version}} 2021-08-20 15:02:01 -04:00
Tony Garnock-Jones 62cd9ac78f Unquoted output format 2021-08-20 15:01:30 -04:00
Tony Garnock-Jones 8b5aa372b5 Release 0.7.0 2021-08-19 18:46:33 -04:00
Tony Garnock-Jones d408070fde Better debug printing 2021-08-18 22:57:06 -04:00
Tony Garnock-Jones 0f30522f19 Repair typo 2021-08-18 22:56:59 -04:00
Tony Garnock-Jones e0bc1b31b8 Better error reporting 2021-08-18 22:56:50 -04:00
Tony Garnock-Jones 6bb99b45c3 fold.py 2021-08-18 14:04:00 -04:00
Tony Garnock-Jones 5669f2aff1 Bump preserves version 2021-08-17 14:25:50 -04:00
Tony Garnock-Jones 0c693d8ece Python preserves-path implementation 2021-08-17 14:07:12 -04:00
Tony Garnock-Jones 82c66ec1c4 Embedded wrapper; preserve() function 2021-08-17 14:06:52 -04:00
Tony Garnock-Jones a5065955ca loads/dumps aliases 2021-08-17 08:05:38 -04:00
Tony Garnock-Jones 8afc8f1eae Python text codec 2021-08-17 08:04:38 -04:00
Tony Garnock-Jones 123b6222ca Split out modules 2021-08-16 19:24:50 -04:00
Tony Garnock-Jones cf192b634c SchemaEntity -> SchemaObject 2021-08-16 14:06:41 -04:00
Tony Garnock-Jones fff34b8d45 Simplify Namespace 2021-08-16 12:37:31 -04:00
Tony Garnock-Jones e2b27b619f Schema encode 2021-08-16 11:38:44 -04:00
Tony Garnock-Jones a2ca133983 load_schema_file, load metaschema 2021-08-16 11:18:54 -04:00
Tony Garnock-Jones e056394ca6 Tighten exceptions 2021-08-16 11:18:34 -04:00
Tony Garnock-Jones fc1d6afc28 Handle empty patterns specially 2021-08-15 23:51:07 -04:00
Tony Garnock-Jones 59bcced776 @extend decorator 2021-08-15 23:40:28 -04:00
Tony Garnock-Jones e45ff6b020 Small improvements 2021-08-15 23:30:46 -04:00
Tony Garnock-Jones abe60b3506 First steps to schema support for python 2021-08-15 22:57:09 -04:00
Tony Garnock-Jones 5c8bacd759 (cargo-release) version {{version}} 2021-08-12 17:43:48 -04:00
Tony Garnock-Jones 1b466aade7 Add --exclude-unchanged cargo-release flag, from my own feature branch 2021-08-12 17:43:34 -04:00
Tony Garnock-Jones 23329cd8f3 Let's try without dev versions 2021-08-12 15:53:50 -04:00
Tony Garnock-Jones 06a08631aa Plugins are now part of CompilerConfig 2021-08-12 15:28:24 -04:00
Tony Garnock-Jones eafc22fb1c Compiler plugins (hardcoded to begin with) 2021-08-12 15:23:37 -04:00
Tony Garnock-Jones ea75dc8f59 Avoid needless schemas/mod.rs file 2021-08-11 18:04:46 -04:00
Tony Garnock-Jones 5161e54e0d Publish
- @preserves/schema@0.17.0
2021-08-11 16:03:16 -04:00
Tony Garnock-Jones 7f8db08039 Manual bump deps 2021-08-10 12:56:12 -04:00
Tony Garnock-Jones cf94a95266 Put a symlink to path.bin in this crate, to allow cargo publish to work 2021-08-10 12:54:42 -04:00
Tony Garnock-Jones 3128f6da82 Grar, more manual bumping 2021-08-10 12:22:58 -04:00
Tony Garnock-Jones f1b4a4568b Try manual publishing 2021-08-10 12:20:50 -04:00
Tony Garnock-Jones d67bafcb30 (cargo-release) version {{version}} 2021-08-10 11:36:46 -04:00
Tony Garnock-Jones ebc609dfec Repair? versions 2021-08-10 11:33:16 -04:00
Tony Garnock-Jones 4f60845dc0 (cargo-release) version {{version}} 2021-08-10 11:27:40 -04:00
Tony Garnock-Jones 37ca3fd493 Use OUT_DIR 2021-08-10 11:19:12 -04:00
Tony Garnock-Jones e6d7e9c1b5 (cargo-release) start next development iteration {{next_version}} 2021-08-10 11:00:12 -04:00
Tony Garnock-Jones b11316e40b (cargo-release) version {{version}} 2021-08-10 11:00:12 -04:00
Tony Garnock-Jones c2167f1ee8 Prep for release 2021-08-10 10:59:03 -04:00
Tony Garnock-Jones 0821f6e3da Default to streaming, not collection 2021-08-10 08:43:33 -04:00
Tony Garnock-Jones e02ee00894 Fix conformance errors 2021-08-10 08:40:20 -04:00
Tony Garnock-Jones 32ebebec34 Move racket preserves-tool out of the way (in favour of the rust one) 2021-08-10 08:40:11 -04:00
Tony Garnock-Jones a0bf6ebf41 Various output formats 2021-08-08 18:04:49 -04:00
Tony Garnock-Jones 02420543f1 Treat Symbols more uniformly like Strings 2021-08-08 18:04:33 -04:00
Tony Garnock-Jones 7abd4a3d3a Split out submodules 2021-08-08 17:51:18 -04:00
Tony Garnock-Jones 2ee1c48fcd Rename real to ~real, and add ~int to match 2021-08-08 17:19:58 -04:00
Tony Garnock-Jones 965bda9f9e Add "real" 2021-08-08 17:08:32 -04:00
Tony Garnock-Jones 61af114d5f Much better: boolean operations on newly-introduced Predicates, rather than selectors generally 2021-08-08 16:01:29 -04:00
Tony Garnock-Jones 137cc63a97 Initial stab at preserves-path; repair error wrt EOF immediately following a number 2021-08-08 14:26:17 -04:00
Tony Garnock-Jones be10924118 Be less specific for regex dep 2021-08-05 15:58:02 +02:00
Tony Garnock-Jones 96707352e6 Quotation 2021-08-05 15:55:48 +02:00
Tony Garnock-Jones cfd9898b4d Option to escape spaces 2021-08-05 15:54:41 +02:00
Tony Garnock-Jones 661d96780d Yield Strings from TextWriter::encode* 2021-08-05 14:28:19 +02:00
Tony Garnock-Jones 87946abb63 Avoid egregious &'a mut R 2021-08-05 14:28:00 +02:00
Tony Garnock-Jones 96f5c9f434 Repair accidentally-dropped-buffer error 2021-08-04 15:49:57 +02:00
Tony Garnock-Jones b24aca8f0f Rust preserves-tools 2021-08-04 15:42:07 +02:00
Tony Garnock-Jones 3078396487 Conveniences for TextReader matching those existing for PackedReader 2021-08-04 15:41:23 +02:00
Tony Garnock-Jones d28901446d Decode text syntax from *byte* sources. 2021-08-03 16:26:40 +02:00
Tony Garnock-Jones e913951b91 TextWriter::encode for Vec<u8> 2021-08-02 21:49:40 +02:00
Tony Garnock-Jones e80d849f9a DomainUnparse is useless 2021-08-02 21:49:05 +02:00
Tony Garnock-Jones dcdfdb8dd9 Track boundaries in schema-emitted reader code 2021-08-02 20:55:44 +02:00
Tony Garnock-Jones 3176e5f8d0 Indented printing 2021-08-02 12:53:17 +02:00
Tony Garnock-Jones af1405e87a Use dtoa for better floating-point formatting 2021-08-02 12:14:07 +02:00
Tony Garnock-Jones 9d4e6998f2 TextReader/TextWriter 2021-08-02 11:42:48 +02:00
Tony Garnock-Jones 9b100ab9aa Tweak base64 variant handling 2021-08-02 11:41:45 +02:00
Tony Garnock-Jones 5fa8c32ba0 Cosmetic 2021-07-27 16:38:55 +02:00
Tony Garnock-Jones 61c6dfbc3e Bump Rust package versions 2021-07-22 16:56:42 +02:00
Tony Garnock-Jones 48412ae7ea Simplify by removing DelimitedStream 2021-07-21 22:48:00 +02:00
Tony Garnock-Jones 9b88db6790 Remove misleading prefix on branch readers 2021-07-21 22:34:02 +02:00
Tony Garnock-Jones cae254ef21 Experimental deserialization direct from Reader 2021-07-21 21:52:20 +02:00
Tony Garnock-Jones 6d9ed94065 Only write to files if they don't exist or their existing contents differs from the new contents 2021-07-14 11:53:12 +02:00
Tony Garnock-Jones 7712c6e0a9 More conversions for SignedInteger 2021-07-13 16:00:18 +02:00
Tony Garnock-Jones 423c9d0bba Better Debug for SignedInteger 2021-07-13 15:59:27 +02:00
Tony Garnock-Jones 6ffc34065f Value from usize/isize 2021-07-13 15:59:14 +02:00
Tony Garnock-Jones 932375fa49 NestedValue::domain() 2021-07-13 15:58:58 +02:00
Tony Garnock-Jones 7e3bf2ade5 Hex codec, for convenience 2021-07-13 15:58:28 +02:00
Tony Garnock-Jones 790782fc87 Hex and Base64 encodings in preserves-tool 2021-07-13 15:58:05 +02:00
Tony Garnock-Jones c527160e9d Repair silly mistake that made only one input processed 2021-07-06 20:52:03 +02:00
Tony Garnock-Jones 64593436a8 If T is DomainDecode, so is &mut T. 2021-07-05 20:24:40 +02:00
Tony Garnock-Jones 7c8a5c61ca foreach_embedded 2021-07-05 16:09:31 +02:00
Tony Garnock-Jones e9b5b3549c Convenience 2021-07-05 13:03:30 +02:00
Tony Garnock-Jones e30ade6ed3 Take more advantage of use std::io; and fix benches 2021-07-05 13:00:30 +02:00
Tony Garnock-Jones 7e76503779 Remove IOResult in favour of std::io::Result 2021-07-05 12:38:11 +02:00
Tony Garnock-Jones da08189dd4 Embedded-parameterised Writer 2021-07-05 12:34:29 +02:00
Tony Garnock-Jones 8f1a83e548 Avoidance of hardcoded use of IOValue in Embedded reading 2021-07-05 12:00:05 +02:00
Tony Garnock-Jones 41fe3c3440 A new approach to embedded-parameterised Reader 2021-07-04 17:16:13 +02:00
Tony Garnock-Jones 64ff818cd1 Fixes in handling of embeddeds 2021-07-03 23:11:41 +02:00
Tony Garnock-Jones 83d15a838e Remove mostly-useless Domain methods 2021-07-03 23:10:43 +02:00
Tony Garnock-Jones 0fb1ef4efd Repair error in schema include 2021-07-03 16:27:50 +02:00
Tony Garnock-Jones c04447d62a Fixes & tweaks wrt Domain/Embedded/Embeddable 2021-07-03 09:00:05 +02:00
Tony Garnock-Jones c7b252ca9d More TryFrom<&SignedInteger> implementations 2021-07-03 08:59:22 +02:00
Tony Garnock-Jones 6143ddc93d NestedValue new() method for easy wrapping 2021-07-03 08:59:03 +02:00
Tony Garnock-Jones c70035b044 Avoid name clash 2021-07-02 16:24:16 +02:00
Tony Garnock-Jones 297d1d39eb Domain -> Embedded/Embeddable 2021-07-02 08:00:37 +02:00
Tony Garnock-Jones 8b7baec26b PreDomain 2021-07-02 07:49:13 +02:00
Tony Garnock-Jones 23943f8b14 ModuleContextMode::TargetIOValue, ModuleContextMode::TargetAny 2021-07-02 07:48:52 +02:00
Tony Garnock-Jones 8cafcbcaf1 Bump rust versions for release 2021-06-30 16:19:27 +02:00
Tony Garnock-Jones 70990d2371 Better names for literal constants 2021-06-30 16:07:49 +02:00
Tony Garnock-Jones 7c4cf38110 snake_case for module names 2021-06-30 15:55:42 +02:00
Tony Garnock-Jones 569563a564 Better error handling; use domains correctly 2021-06-30 15:50:50 +02:00
Tony Garnock-Jones aea230b056 Switch back to parsing/unparsing from IOValue 2021-06-30 15:18:20 +02:00
Tony Garnock-Jones 00759673ce Arc at ref boundaries 2021-06-30 15:06:28 +02:00
Tony Garnock-Jones 5b9c4d29f6 impl Domain for Arc<D: Domain> 2021-06-30 15:05:57 +02:00
Tony Garnock-Jones 17d8d076ec make test target 2021-06-30 15:05:44 +02:00
Tony Garnock-Jones e882d5a4df Aftershocks of Float/Double changes 2021-06-30 10:14:29 +02:00
Tony Garnock-Jones fc8709706c Cleanups; make as_float etc return Float, add as_f32 etc 2021-06-30 10:10:38 +02:00
Tony Garnock-Jones 3156180601 IOValue -> _Any = ArcValue<_Ptr> 2021-06-30 09:53:32 +02:00
Tony Garnock-Jones f1d403a6a7 Fallible from_preserves 2021-06-30 09:52:16 +02:00
Tony Garnock-Jones d69787e5ee Module aliases and embedded pointer types 2021-06-29 23:19:22 +02:00
Tony Garnock-Jones c3bc678a46 Better compiler API 2021-06-29 22:32:35 +02:00
Tony Garnock-Jones dd9e190bed More metadata 2021-06-29 20:31:28 +02:00
Tony Garnock-Jones 01e8e2c279 Include both a path and a version 2021-06-29 20:30:21 +02:00
Tony Garnock-Jones e4392ea2d5 Bump rust preserves version 2021-06-29 20:27:11 +02:00
Tony Garnock-Jones aa1c983acc Unparsers 2021-06-29 16:54:29 +02:00
Tony Garnock-Jones 5c2d12971d Better idioms 2021-06-28 22:38:30 +02:00
Tony Garnock-Jones 9c4be54be1 Bootstrap Rust preserves-schema 2021-06-28 22:25:41 +02:00
Tony Garnock-Jones 7546ba29ad Avoid quite a bit of boxing 2021-06-28 17:26:41 +02:00
Tony Garnock-Jones c7dbbdc178 Small fixes 2021-06-28 17:08:44 +02:00
Tony Garnock-Jones 4144a90b9d Initial commit (several days' worth) 2021-06-28 16:35:45 +02:00
Tony Garnock-Jones 60d1be41a3 Rename NamedSimplePattern_ -> Binding 2021-06-25 10:25:26 +02:00
Tony Garnock-Jones fdb43f6292 Constrain names/identifiers in schemas to be lowest-common-denominator. 2021-06-25 09:45:07 +02:00
Tony Garnock-Jones da513a249e Allow void to be `->preserve`able 2021-06-17 15:26:43 +02:00
Tony Garnock-Jones f808e37e89 Fix very stupid mistake in order.rkt 2021-06-16 21:36:09 +02:00
Tony Garnock-Jones f12343e723 Add missing *->preserve coercions during unparsing 2021-06-10 10:01:04 +02:00
Tony Garnock-Jones 0d4d1e738c Coerce integers to floats/doubles during unparse 2021-06-09 20:02:06 +02:00
Tony Garnock-Jones feb6361029 I forgot embedded! 2021-06-08 16:15:53 +02:00
Tony Garnock-Jones 27002dfe7f Always use wrapper struct 2021-06-08 15:50:29 +02:00
Tony Garnock-Jones e5b6c46169 Time reader self-test 2021-06-08 15:34:33 +02:00
Tony Garnock-Jones 6cecf64df5 Move toward being user-facing rather than plugin-facing 2021-06-08 15:34:25 +02:00
Tony Garnock-Jones 7acf7c5b40 Generic-method based unparsing; prelude to generic pattern-formation 2021-06-08 15:26:32 +02:00
Tony Garnock-Jones 0bcb4e64ec Add struct ty-field for better structure in ty-records 2021-06-08 09:56:04 +02:00
Tony Garnock-Jones 30bcc1a50b Fix embedded (un)parsing to rely on the reader 2021-06-08 09:27:03 +02:00
Tony Garnock-Jones a4d61017d8 Repair error wrt `float` struct test 2021-06-08 09:26:35 +02:00
Tony Garnock-Jones ade9b0a0f1 map-embeddeds 2021-06-08 09:26:10 +02:00
Tony Garnock-Jones f93d329f48 Small optimization 2021-06-08 09:25:52 +02:00
Tony Garnock-Jones c05180c492 Some tool docs; improve help message of preserves-tool 2021-06-04 17:47:16 +02:00
Tony Garnock-Jones f14b902f24 Tweak to pre-commit hook 2021-06-04 15:52:32 +02:00
Tony Garnock-Jones 139f4ff08b Accept parameters in #lang preserves-schema lines 2021-06-03 23:23:22 +02:00
Tony Garnock-Jones 12e38ddd8f Repair minor typo in appendix 2021-06-03 14:39:31 +02:00
Tony Garnock-Jones 2c5ee2066b Link to Syrup and preserves-nim 2021-06-02 20:32:40 +02:00
Tony Garnock-Jones 53bd5a1a7e Escape schema names with ? instead of $ 2021-06-02 12:35:56 +02:00
Tony Garnock-Jones b968f77ff6 Provide fold-Schema-definitions and map-Schema-definitions 2021-06-02 12:35:33 +02:00
Tony Garnock-Jones bd68786f1c Schema compiler plugins 2021-06-02 06:56:44 +02:00
Tony Garnock-Jones 64696ac184 Publish
- @preserves/core@0.17.0
 - @preserves/schema@0.16.0
2021-06-01 16:54:23 +02:00
Tony Garnock-Jones 7683a64a5b Repair Racket schema reader 2021-06-01 16:53:21 +02:00
Tony Garnock-Jones 498c63ef67 Repair embedded reading; and preserve annotations in generic embedded values 2021-06-01 16:46:23 +02:00
Tony Garnock-Jones 9014a0ffb8 Publish
- @preserves/schema@0.15.0
2021-06-01 16:10:51 +02:00
Tony Garnock-Jones 8d96743d53 Redo schema embedded value patterns to use embedded syntax and an interface schema 2021-06-01 16:10:04 +02:00
Tony Garnock-Jones b23acdaf5a Default to text output from preserves-tool 2021-06-01 15:37:45 +02:00
Tony Garnock-Jones a44884d9f5 Tweaks 2021-05-31 11:48:52 +02:00
Tony Garnock-Jones 947b816a57 Improve error-handling in Racket schema reader 2021-05-28 20:06:46 +02:00
Tony Garnock-Jones b69c3a0894 Avoid name clashes with :parse-embedded and :embedded->preserves 2021-05-28 10:33:28 +02:00
Tony Garnock-Jones d7bf235813 Avoid use of void so much 2021-05-27 09:54:11 +02:00
Tony Garnock-Jones ab12c6535f Simpler embedded parsing/unparsing 2021-05-27 09:53:55 +02:00
Tony Garnock-Jones 534018e3a4 batch-compile 2021-05-27 09:52:58 +02:00
Tony Garnock-Jones ef7cea09bf JSON Preserves schema! 2021-05-27 00:28:57 +02:00
Tony Garnock-Jones 264c4b9d2e Repair integer parsing 2021-05-27 00:28:42 +02:00
Tony Garnock-Jones 87e816306d Better import failure error reporting 2021-05-26 23:27:55 +02:00
Tony Garnock-Jones 5470497aa2 Update gen/schema.rkt 2021-05-26 23:16:49 +02:00
Tony Garnock-Jones f90544d807 Code generator tool; handle module references and embeddeds 2021-05-26 23:15:49 +02:00
Tony Garnock-Jones cb88c587b6 Include a copy of samples.pr so that tests run OK on the buildserver 2021-05-26 21:11:45 +02:00
Tony Garnock-Jones e6efd03be7 Introduce explicit embedded wrapper 2021-05-26 21:09:06 +02:00
Tony Garnock-Jones 351cafddb4 Bump dates 2021-05-26 14:27:17 +02:00
Tony Garnock-Jones 90ce0a544d The Racket preserves package needs a local copy of schema.prs for the package-builder to work. Use a git hook to keep it in sync with the master copy 2021-05-26 14:27:03 +02:00
Tony Garnock-Jones 7ab12108e4 Inline reader module; add port->preserves 2021-05-26 13:56:37 +02:00
Tony Garnock-Jones ed3cd8de26 Repair typo 2021-05-25 23:01:16 +02:00
Tony Garnock-Jones e4f7219dc6 Publish
- @preserves/schema@0.14.1
2021-05-25 20:15:18 +02:00
Tony Garnock-Jones 98e2511fe1 #lang preserves-schema 2021-05-25 20:14:10 +02:00
Tony Garnock-Jones 3559cc679e Document inclusion in schemas 2021-05-25 20:13:18 +02:00
Tony Garnock-Jones 460529e1c7 Clean up language wrt invertibility/"bijection" 2021-05-25 20:13:02 +02:00
Tony Garnock-Jones badb059440 Note re DSL-to-AST translation 2021-05-25 14:37:44 +02:00
Tony Garnock-Jones 668d4e6271 Repair typo 2021-05-25 14:37:34 +02:00
Tony Garnock-Jones 394d10e6da Link preserves-schema to main page 2021-05-25 14:13:09 +02:00
Tony Garnock-Jones de7ac63b96 First stab at specification of Schema 2021-05-25 14:11:33 +02:00
Tony Garnock-Jones 52bc77c9d7 Publish
- @preserves/schema@0.14.0
2021-05-25 11:08:27 +02:00
Tony Garnock-Jones 49efc76580 Racket Preserves Schema reader implementation 2021-05-25 11:07:37 +02:00
Tony Garnock-Jones a24a5b19f5 Regenerate metaschema 2021-05-25 11:06:40 +02:00
Tony Garnock-Jones eeb84ad669 Sort emitted entries; add throwing parser variant 2021-05-25 11:06:30 +02:00
Tony Garnock-Jones 6c9071fd88 Optionally omit commas when writing preserves 2021-05-25 11:05:16 +02:00
Tony Garnock-Jones 8b2aa0fb87 preserve=? 2021-05-25 11:05:03 +02:00
Tony Garnock-Jones 5c644624c4 detect-preserve-syntax, file->preserves 2021-05-25 11:04:51 +02:00
Tony Garnock-Jones 163e338ce5 Name is already always supplied, no need to add it 2021-05-25 11:04:29 +02:00
Tony Garnock-Jones 4ed8fd2c92 Add "annotations" procedure 2021-05-25 11:02:36 +02:00
Tony Garnock-Jones 669d1b480d Name is already always supplied, no need to add it 2021-05-25 11:01:55 +02:00
Tony Garnock-Jones 7d06c7dce0 Repair dict "named" generation 2021-05-25 10:49:35 +02:00
Tony Garnock-Jones bdd699ae9f Deterministic hash ordering for Racket CompoundPattern-dict 2021-05-24 17:46:50 +02:00
Tony Garnock-Jones 33a80533fa Checker for Racket 2021-05-24 12:47:44 +02:00
Tony Garnock-Jones 1ca796e6aa Publish
- @preserves/schema@0.13.0
2021-05-24 12:05:44 +02:00
Tony Garnock-Jones 4914c8cd68 Move type/gentype back into compiler where it belongs 2021-05-24 12:05:02 +02:00
Tony Garnock-Jones 46d76dfca7 Redo bijection checking without using gentype 2021-05-24 12:03:04 +02:00
Tony Garnock-Jones 0db223ede8 More tests, and variant label duplicate check 2021-05-24 11:53:25 +02:00
Tony Garnock-Jones 52be118dc7 A few more tests 2021-05-24 11:32:29 +02:00
Tony Garnock-Jones a1fdddcf7b Simple tests; more exports 2021-05-24 11:27:46 +02:00
Tony Garnock-Jones e594d22d09 Publish
- @preserves/core@0.16.0
 - @preserves/schema@0.12.0
2021-05-24 10:40:50 +02:00
Tony Garnock-Jones aaee62044c Lerna for convenient package collection scripting 2021-05-24 10:39:23 +02:00
Tony Garnock-Jones 9bdfc4c3ab Split compute from report 2021-05-24 10:16:00 +02:00
Tony Garnock-Jones c4bfc0eefc Follow improvements through Racket schema impl 2021-05-24 10:10:13 +02:00
Tony Garnock-Jones 2559a4713f Much improved refactoring/repair of metaschema 2021-05-24 00:15:31 +02:00
Tony Garnock-Jones 1d6956fa55 Move gen-schema.rkt to gen/schema.rkt 2021-05-22 15:52:12 +02:00
Tony Garnock-Jones 9e6743abdc Autogenerate predicate for unions 2021-05-22 15:50:54 +02:00
Tony Garnock-Jones 43b776eb7f Switch to manipulating parsed form of metaschema 2021-05-22 15:47:13 +02:00
Tony Garnock-Jones ebab3fafc5 gen-schema.rkt (initial rendering) 2021-05-22 15:43:29 +02:00
Tony Garnock-Jones 986e7fa30d Update CI script 2021-05-21 21:58:31 +02:00
Tony Garnock-Jones b5405d80ec Update main branch 2021-05-21 21:56:59 +02:00
Tony Garnock-Jones 49cba14b4f Initial work on preserves-schema for Racket 2021-05-21 21:44:05 +02:00
Tony Garnock-Jones 1654ad4c80 Repair error reporting 2021-05-21 17:33:29 +02:00
Tony Garnock-Jones 2ddedc7673 v0.11.2 2021-05-21 17:28:48 +02:00
Tony Garnock-Jones dbd6c3cf53 Repair checker 2021-05-21 17:28:18 +02:00
Tony Garnock-Jones 2bde06f509 Add omitted checkBinding call 2021-05-21 16:14:58 +02:00
Tony Garnock-Jones 0f1ea4aa20 Further simplification 2021-05-21 16:01:04 +02:00
Tony Garnock-Jones 46ab6d90ec v0.11.1 2021-05-21 15:56:21 +02:00
Tony Garnock-Jones b5b4effeac Simplify unconverter generation even further 2021-05-21 15:56:01 +02:00
Tony Garnock-Jones 20b676df27 Move insufficient-information checking to read-time 2021-05-21 15:49:06 +02:00
Tony Garnock-Jones ecdb314366 Require at least two branches in "&" and "/" 2021-05-21 11:20:25 +02:00
Tony Garnock-Jones 7253d1507e v0.9.0 2021-05-20 22:36:07 +02:00
Tony Garnock-Jones 10380e451a Simplify schemas by allowing at most one of "&" or "/" in a definition 2021-05-20 22:34:20 +02:00
Tony Garnock-Jones 436b14e2fe v0.8.1 2021-05-17 15:13:08 +02:00
Tony Garnock-Jones cefc029f70 Repair egregious error in schema compiler 2021-05-17 15:12:39 +02:00
Tony Garnock-Jones e4a2503899 The Great Renaming: Pointer -> Embedded 2021-05-17 14:54:06 +02:00
Tony Garnock-Jones 6fc41ead6f v0.7.0 2021-04-25 10:44:36 +02:00
Tony Garnock-Jones 8e068fbdbf v0.14.0 2021-04-25 10:43:52 +02:00
Tony Garnock-Jones 8442718f96 Fix module cycles (largely by splitting PointerType in two) 2021-04-25 10:42:21 +02:00
Tony Garnock-Jones 854a2bc41c v0.6.0 2021-04-25 00:17:57 +02:00
Tony Garnock-Jones 638f8e026e v0.13.0 2021-04-25 00:16:38 +02:00
Tony Garnock-Jones 2d0e6255bd Embed pointer values into Values 2021-04-25 00:09:33 +02:00
Tony Garnock-Jones 58d2bf6f3a Make undeclared pointer type `any` instead of `never`, and take advantage of new explicit pointer wrappers 2021-04-24 23:49:37 +02:00
Tony Garnock-Jones 70ce961dd2 Mention Pointers in canonical-binary.md 2021-04-24 22:01:53 +02:00
Tony Garnock-Jones e5965fde83 Rename DefaultPointer -> GenericPointer 2021-04-24 22:01:16 +02:00
Tony Garnock-Jones 8c783dbc7d Explicit wrapper for Pointers 2021-04-24 21:59:52 +02:00
Tony Garnock-Jones aef970dc2d v0.5.1 2021-04-22 20:55:48 +02:00
Tony Garnock-Jones 8895d2b6a4 Fix rollup 2021-04-22 20:55:26 +02:00
Tony Garnock-Jones be6537f6d4 v0.5.0 2021-04-22 20:54:51 +02:00
Tony Garnock-Jones c54a17162d Bump dep 2021-04-22 20:54:28 +02:00
Tony Garnock-Jones 63cf5d1cf2 v0.12.0 2021-04-22 20:53:12 +02:00
Tony Garnock-Jones 9f9514a7e6 Handle pointer type nesting properly 2021-04-22 20:51:48 +02:00
Tony Garnock-Jones 825d208198 Introduce Cargo workspace in prep for schema 2021-04-19 22:28:59 +02:00
Tony Garnock-Jones b4d7af4322 v0.4.0 2021-04-01 22:02:46 +02:00
Tony Garnock-Jones e763174846 Bump 2021-04-01 22:02:27 +02:00
Tony Garnock-Jones e1e7904a87 v0.11.0 2021-04-01 22:02:08 +02:00
Tony Garnock-Jones 545e1bb6de Switch from yargs to commander 2021-04-01 21:57:49 +02:00
Tony Garnock-Jones 3ad56a5275 Enhance preserves-schemac to emit bundles 2021-04-01 21:12:11 +02:00
Tony Garnock-Jones 55fab35073 Schema Bundles 2021-04-01 20:31:08 +02:00
Tony Garnock-Jones 4d8618ce63 schema.bin 2021-03-23 19:48:37 +01:00
Tony Garnock-Jones 121bcc7a53 Refactor schema schema 2021-03-23 19:40:06 +01:00
Tony Garnock-Jones 94f6959ac8 Named instead of positional ctor args 2021-03-23 16:59:44 +01:00
Tony Garnock-Jones d64bb82c22 Fix preserves-schemac 2021-03-23 16:37:35 +01:00
Tony Garnock-Jones c23cbcc60c Fix error reporting; handle unit like record 2021-03-23 12:16:16 +01:00
Tony Garnock-Jones e187fb83b4 Regenerate to avoid warnings 2021-03-23 12:09:16 +01:00
Tony Garnock-Jones 4434f712b6 Fix fromPtr; avoid warnings 2021-03-23 12:08:58 +01:00
Tony Garnock-Jones d91924c72b Regenerate 2021-03-23 11:37:22 +01:00
Tony Garnock-Jones 7d8453a806 Unconverter 2021-03-23 11:36:55 +01:00
Tony Garnock-Jones 1d73289345 First attempt at a merge() utility 2021-03-23 11:36:11 +01:00
Tony Garnock-Jones 4ded3a484c Excise old decoder 2021-03-22 14:43:40 +01:00
Tony Garnock-Jones 87dda48083 Use $ instead of _ in tokens 2021-03-22 12:29:27 +01:00
Tony Garnock-Jones 4814790d8e Cut over to new representation 2021-03-22 12:13:34 +01:00
Tony Garnock-Jones 0304c2631b genctor.ts 2021-03-21 22:02:34 +01:00
Tony Garnock-Jones 889d38bbb8 Fix types. 2021-03-19 23:42:43 +01:00
Tony Garnock-Jones c2fe82e71d Comment 2021-03-19 23:32:47 +01:00
Tony Garnock-Jones 0970898065 Prepare for removing base environment: special support for any 2021-03-19 20:54:05 +01:00
Tony Garnock-Jones 1c07573178 Refactor type.ts 2021-03-18 22:51:00 +01:00
Tony Garnock-Jones c8b752a73b Refactor withCapture 2021-03-18 22:41:27 +01:00
Tony Garnock-Jones d372977023 Add Capture support; repair (redo) converter accordingly 2021-03-18 22:33:37 +01:00
Tony Garnock-Jones 084f54f869 Work on converter; split up compiler 2021-03-18 14:02:47 +01:00
Tony Garnock-Jones 98558b81f0 Full stack traces 2021-03-18 11:14:31 +01:00
Tony Garnock-Jones 5d2ee85b36 More general variant 2021-03-17 20:23:55 +01:00
Tony Garnock-Jones 3463cd4a65 Further schema schema fixes 2021-03-17 20:01:26 +01:00
Tony Garnock-Jones 5f71239130 Further refine schema schema 2021-03-17 19:17:19 +01:00
Tony Garnock-Jones 376e83acd0 Allow naming of `and` branches 2021-03-17 16:25:29 +01:00
Tony Garnock-Jones 306c7c2cae Support writing to stdout 2021-03-17 16:14:45 +01:00
Tony Garnock-Jones bacf310648 Refactor compiler before new approach 2021-03-17 15:59:46 +01:00
Tony Garnock-Jones 98346c61d5 More preparation for alternate compilation approach 2021-03-17 14:36:44 +01:00
Tony Garnock-Jones ca42ffe832 isCompound 2021-03-17 14:24:41 +01:00
Tony Garnock-Jones 05c7343983 Rename and export asLatin1 2021-03-17 12:56:49 +01:00
Tony Garnock-Jones e6f99ae2e1 Prepare for alternative compiler output 2021-03-17 12:38:11 +01:00
Tony Garnock-Jones b9019d03f1 Tighten schema definition to permit simpler codegen 2021-03-17 12:20:50 +01:00
Tony Garnock-Jones 178f528bf0 Rearrange Dictionary type parameters for improved Record type inference 2021-03-17 12:20:06 +01:00
Tony Garnock-Jones 8f2da8f8db A more... pragmatic DefaultPointer 2021-03-17 10:21:48 +01:00
Tony Garnock-Jones 12121128a6 Remove unused interpreter.ts 2021-03-17 10:00:06 +01:00
Tony Garnock-Jones 425e7dd5cb Rename samples.txt -> samples.pr 2021-03-17 09:08:07 +01:00
Tony Garnock-Jones 434279ab66 For some reason, leap doesn't work with the /dev/stdout version 2021-03-16 21:30:44 +01:00
Tony Garnock-Jones 7072f19407 Small updates 2021-03-16 14:36:46 +01:00
Tony Garnock-Jones adfb1822ac Simpler schema-schema 2021-03-14 22:59:53 +01:00
Tony Garnock-Jones 14be044092 preserves-schemac 2021-03-14 22:53:13 +01:00
Tony Garnock-Jones 5393308be4 Avoid unnecessary use of asLiteral 2021-03-14 21:13:40 +01:00
Tony Garnock-Jones 5afb1469f3 Faster generated decoders 2021-03-14 21:10:47 +01:00
Tony Garnock-Jones e078a71b30 Bump version 2021-03-12 20:52:52 +01:00
Tony Garnock-Jones 47da2e5308 v0.3.1 2021-03-12 20:46:43 +01:00
Tony Garnock-Jones 5e3ae0c18b Argh, fix dep version 2021-03-12 20:45:37 +01:00
Tony Garnock-Jones d6d4e830a3 v0.3.0 2021-03-12 20:44:44 +01:00
Tony Garnock-Jones 2311dbd245 v0.10.0 2021-03-12 20:44:03 +01:00
Tony Garnock-Jones d502249c53 Repair record decoders 2021-03-12 20:42:53 +01:00
Tony Garnock-Jones 1cc0325007 Different approach to pointer codec; support custom schema-driven decode 2021-03-12 20:41:35 +01:00
Tony Garnock-Jones 4ee9f99529 Inline single-use function 2021-03-12 11:33:49 +01:00
Tony Garnock-Jones 942fb79a2e Regenerate schema 2021-03-12 11:14:39 +01:00
Tony Garnock-Jones ce947c109a Cosmetic: rename function, rearrange functions 2021-03-12 11:11:00 +01:00
Tony Garnock-Jones 2b9eddfc79 Rename value::value to value::repr, the final clippyism 2021-03-12 11:04:23 +01:00
Tony Garnock-Jones d811032ac7 Fix up almost all reported clippyisms 2021-03-12 10:59:28 +01:00
Tony Garnock-Jones bddb111f87 v0.2.1 2021-03-11 23:11:47 +01:00
Tony Garnock-Jones a1db64422c v0.9.1 2021-03-11 23:11:25 +01:00
Tony Garnock-Jones cfa0a9caa3 .npmignore is required to not omit crucial build products 2021-03-11 23:11:06 +01:00
Tony Garnock-Jones fc88612414 v0.2.0 2021-03-11 23:02:35 +01:00
Tony Garnock-Jones c75aaf4b18 Includes in schema compiler 2021-03-11 23:02:18 +01:00
Tony Garnock-Jones 55e4222d68 v0.1.1 2021-03-11 19:14:57 +01:00
Tony Garnock-Jones 9a46a22fb6 Oops - wrong dependency version 2021-03-11 19:14:48 +01:00
Tony Garnock-Jones 86fc0acc92 v0.1.0 2021-03-11 19:12:27 +01:00
Tony Garnock-Jones f531eb347d v0.9.0 2021-03-11 19:11:46 +01:00
Tony Garnock-Jones 8ec5946696 yarnrc 2021-03-11 19:11:36 +01:00
Tony Garnock-Jones d9726a6878 Fix up rollup config for CLI compiler 2021-03-11 19:05:54 +01:00
Tony Garnock-Jones 8d7e7c6d95 Pointers; improved diagnostics 2021-03-11 17:59:40 +01:00
Tony Garnock-Jones c09032f609 Regenerate schema 2021-03-11 14:43:58 +01:00
Tony Garnock-Jones c8f564aea4 Position tracking in Reader; major driver improvements in schema compiler 2021-03-11 14:43:06 +01:00
Tony Garnock-Jones 94f6f9af9d Fix rollup config for CLI tool 2021-03-11 11:01:01 +01:00
Tony Garnock-Jones d932431d83 Improve compiler driver 2021-03-11 10:56:49 +01:00
Tony Garnock-Jones ba2c7e9978 Generate as*() alongside is*() 2021-03-11 09:25:17 +01:00
Tony Garnock-Jones a0d51fab4c Revised preserves.el instructions 2021-03-11 09:10:11 +01:00
Tony Garnock-Jones 87bb930020 Fix schema package.json for publish 2021-03-10 23:32:14 +01:00
Tony Garnock-Jones 929f916d1d Bump typescript version 2021-03-10 23:16:05 +01:00
Tony Garnock-Jones 932818145b Bootstrap schema schema 2021-03-10 23:15:53 +01:00
Tony Garnock-Jones 7c587f03d3 Allow tide to find the appropriate tsserver 2021-03-10 23:14:54 +01:00
Tony Garnock-Jones 036d42a73a Default to pointer type being never instead of object 2021-03-10 23:14:26 +01:00
Tony Garnock-Jones 754306aca3 Rearrange into two packages, using yarn workspaces 2021-03-10 19:24:20 +01:00
Tony Garnock-Jones 8187337187 Prepare for command-line schem compiler tool 2021-03-09 19:29:31 +01:00
Tony Garnock-Jones 447380218e More 2021-03-09 16:45:57 +01:00
Tony Garnock-Jones 62bab41bed First steps toward schema 2021-03-09 15:59:40 +01:00
Tony Garnock-Jones c27aa7579e Repair detection of "easily-printable" symbols 2021-03-08 15:31:50 +01:00
Tony Garnock-Jones e894d0dbbc Whitespace questions 2021-03-07 21:49:57 +01:00
Tony Garnock-Jones dc96f74075 Text syntax reader in Javascript implementation 2021-03-05 21:16:14 +01:00
Tony Garnock-Jones a05bf0cb7a Fix canonical encoding of Set and Dictionary 2021-03-05 21:14:39 +01:00
Tony Garnock-Jones 4022b76650 Move text stuff to text.ts 2021-03-04 22:42:40 +01:00
Tony Garnock-Jones 5412f8b9d0 Optimizations for reuse of a Decoder instance 2021-03-04 11:26:11 +01:00
Tony Garnock-Jones 550224e0b1 FlexMap getOrSet and getAndDelete 2021-03-03 19:14:58 +01:00
Tony Garnock-Jones eaff7b86d8 Repair remaining cyclic dependency 2021-03-02 22:54:58 +01:00
Tony Garnock-Jones 6d2120989b Fix *almost* all cyclic dependencies in js impl 2021-03-02 22:43:10 +01:00
Tony Garnock-Jones c8c027f762 Repair javascript integer codec 2021-03-02 16:38:54 +01:00
Tony Garnock-Jones 75790f237b Bump version and deps 2021-03-02 13:42:51 +01:00
Tony Garnock-Jones 1268c4f9bd Inline definition of Record<> to uncollapse Value<T> 2021-03-01 09:19:32 +01:00
Tony Garnock-Jones 481f866ada Improvements to Typed Records 2021-02-25 23:16:05 +01:00
Tony Garnock-Jones 993689356b Typed Records 2021-02-25 22:19:35 +01:00
Tony Garnock-Jones 074fc5db98 Loosen type of constructor, to allow patterns as well as values 2021-02-25 11:33:11 +01:00
Tony Garnock-Jones c46566e5a0 mapPointers can yield non-pointers 2021-02-24 20:40:49 +01:00
Tony Garnock-Jones 055b367764 unannotate 2021-02-24 20:40:39 +01:00
Tony Garnock-Jones a19a9d50c6 Tighter types now fromJS is explicit 2021-02-22 20:03:09 +01:00
Tony Garnock-Jones 4353d5280e Be explicit about conversions fromJS() 2021-02-22 20:00:15 +01:00
Tony Garnock-Jones a69297c3ba Folds 2021-02-17 20:55:22 +01:00
Tony Garnock-Jones 407e8778a1 Split out values.ts; this makes circular dependencies worse, so I will likely pull it all back together again soon 2021-02-17 16:52:01 +01:00
Tony Garnock-Jones 83b09d9406 Improve ergonomics of Double/Single and floatValue 2021-02-17 15:34:31 +01:00
Tony Garnock-Jones 95c04bd5d5 Float.unwrap 2021-01-29 16:06:00 +01:00
Tony Garnock-Jones 1743756097 Fix parameter ordering and defaults 2021-01-29 15:35:07 +01:00
Tony Garnock-Jones 013c5f4dae Bump Rust version 2021-01-29 13:53:49 +01:00
Tony Garnock-Jones 6fd06cec98 Require that domain toing and froing be total 2021-01-29 13:42:17 +01:00
Tony Garnock-Jones 532e811894 Introduce pointers 2021-01-29 12:03:28 +01:00
Tony Garnock-Jones 6bf49874b7 Correct testwatch script 2021-01-28 08:59:02 +01:00
Tony Garnock-Jones abca13e260 Idempotent Map/Set extensions 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 0bb61d260f Adjust packaging 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones a5d4098e29 Strict tsconfig 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones d8a041a647 Reuse buffers during encoding, where safe to do so; in particular, during simple canonicalization 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 320215dca0 Add missing toString on Preserves Set 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones b4d5334a1a Repair silly mistake 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 329cee7bd6 Much better duck typing 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 10351b5369 type: module in package.json 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 3f62d68bab union, intersect, subtract 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones b0ed7e914b Better collections; package types 2021-01-23 22:54:28 +01:00
Tony Garnock-Jones 55db55b42b Ignore dist/ 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones f7b7f29a3b Load index.js by default 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 98e981dccf Custom util inspectors for Set and Dictionary too 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 66cac324e0 Rollup 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 44f142d86b Update JavaScript implementation and port to TypeScript 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 2c5ed693f5 Tool works on multiple inputs now 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones a1a604aee8 Update Rust implementation 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones ca2276d268 Update python implementation 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 8459521db5 No more `StreamingTest`s 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 749747ca05 Remove #:read-annotations?, to ensure a consistent output format from the reader 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 77fd8e86bf Ah, of course a 0-byte integer has value 0 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 85fe7b3b07 Update Racket implementation 2021-01-23 22:54:27 +01:00
Tony Garnock-Jones 5d719c2c6f MUCH simpler binary format, inspired by Syrup; alterations to text format 2021-01-23 22:54:22 +01:00
Tony Garnock-Jones ccf4f97ed8 Repair CSS problem exposed by recent Firefox update 2020-12-28 21:52:47 +01:00
Tony Garnock-Jones 2391722a25 Closes #14 2020-12-28 09:43:24 +01:00
Tony Garnock-Jones 3c6bff6646 Note in TODO.md 2020-10-26 14:15:03 +01:00
Tony Garnock-Jones 137aa308e3 Update mocha dependency 2020-10-26 14:14:54 +01:00
Tony Garnock-Jones 3c059a573c Browser doesn't have util 2020-08-05 12:47:49 +02:00
Tony Garnock-Jones 8d3146cf30 Bump Rust version 2020-07-10 17:01:21 +02:00
Tony Garnock-Jones 7cc7bd783d Adjust internal trait API 2020-07-10 16:59:54 +02:00
Tony Garnock-Jones 9f83756931 Remove decoder/encoder; undoes performance regression 2020-07-08 15:44:06 +02:00
Tony Garnock-Jones 28101cc7d1 Refactor to extract packed format from general traits. Regression in encode performance? 2020-07-08 15:44:06 +02:00
Tony Garnock-Jones 3f76049f13 Drastically improve quoting.v - including a much better main theorem 2020-06-18 23:57:42 +02:00
Tony Garnock-Jones 95cdd84db4 Bump rust version 2020-06-17 13:43:11 +02:00
Tony Garnock-Jones 70a305458a Avoid some copying 2020-06-17 11:01:44 +02:00
Tony Garnock-Jones 24afca5d32 Avoid expensive setup 2020-06-17 11:01:31 +02:00
Tony Garnock-Jones b1c7fe8c04 Remove unnecessary Cow 2020-06-17 09:35:09 +02:00
Tony Garnock-Jones 046a2ad999 Better varint reader 2020-06-17 04:11:27 +02:00
Tony Garnock-Jones 6cfb474a6f Avoid running the slow benchmark tests during regular testing 2020-06-17 03:05:32 +02:00
Tony Garnock-Jones 89c3171758 Autodelete benches/testdata.bin 2020-06-17 02:42:44 +02:00
Tony Garnock-Jones fefa2af730 Bump rust version 2020-06-17 02:39:31 +02:00
Tony Garnock-Jones f2910eb8d0 Slim down the common case of no annotations 2020-06-17 02:39:01 +02:00
Tony Garnock-Jones 078e8dd4e8 Benchmark large collection of small items 2020-06-17 02:33:45 +02:00
Tony Garnock-Jones e4e6622074 Switch to iter_with_large_drop 2020-06-17 00:13:56 +02:00
Tony Garnock-Jones 21cc7595ae Benchmark various readers for decoding 2020-06-16 17:57:52 +02:00
Tony Garnock-Jones e01f960ddc Benchmarks and test factorization 2020-06-16 17:46:55 +02:00
Tony Garnock-Jones 275d9e73b1 Bump version 2020-06-15 16:04:41 +02:00
Tony Garnock-Jones bffbeb2f6e Smaller Record; also, use u128/i128 when possible 2020-06-15 16:03:19 +02:00
Tony Garnock-Jones 1f4adc5ba6 Oh cool, is_none() exists 2020-06-15 09:58:49 +02:00
Tony Garnock-Jones 83b7513fae Build all targets 2020-06-15 09:57:56 +02:00
Tony Garnock-Jones b9aba35d39 New release 2020-05-29 11:08:33 +02:00
Tony Garnock-Jones eb088aa491 Experimentation with extensibility, such as it is 2020-05-29 11:08:12 +02:00
Tony Garnock-Jones 22b76cb9b6 Consume all sequence items in direct deserialization, matching deserialization from a Value 2020-05-29 11:07:55 +02:00
Tony Garnock-Jones 65b5399dbe Make CompoundBody consumption past end idempotent 2020-05-29 10:58:40 +02:00
Tony Garnock-Jones d1a5389060 Allow f32 and f64 to deserialize into each other 2020-05-29 10:58:00 +02:00
Tony Garnock-Jones 0788edaaed Minor repairs to text 2020-05-29 10:00:11 +02:00
Tony Garnock-Jones c4f90ef86b Remove placeholders from spec and implementations 5/5
Update Racket implementation: remove placeholders; reject zero-length
streamed binary chunks.
2020-05-28 23:23:19 +02:00
Tony Garnock-Jones 83dce41092 Remove placeholders from spec and implementations 4/5
Update Python implementation: remove placeholders; reject zero-length
streamed binary chunks.
2020-05-28 23:23:19 +02:00
Tony Garnock-Jones 0b0709b615 Remove placeholders from spec and implementations 3/5
Update JS implementation: remove placeholders; reject zero-length
streamed binary chunks.
2020-05-28 23:23:19 +02:00
Tony Garnock-Jones b5f4c3a498 Remove placeholders from spec and implementations 2/5
Enormous refactoring in Rust implementation. Direct deserialization.
Zero-copy deserialization in some cases. Much faster.
2020-05-28 23:23:19 +02:00
Tony Garnock-Jones e7ef060695 Remove placeholders from spec and implementations 1/5
Update spec and test suite.
2020-05-28 23:23:19 +02:00
Tony Garnock-Jones dfc50fcaee Simplify code (and get back up to speed! ~150kHz) by insisting on IOValue for all codec operations 2020-05-26 11:01:33 +02:00
Tony Garnock-Jones c30154e0c8 Improve (!) safety at the expense of speed: 155kHz -> 115kHz :-/ 2020-05-26 09:08:17 +02:00
Tony Garnock-Jones b122d6e2e0 copy_via able to map domains 2020-05-25 20:11:54 +02:00
Tony Garnock-Jones 2d57d0001b Even more error cleanup 2020-05-25 16:21:18 +02:00
Tony Garnock-Jones 450b73a4d2 More error cleanup 2020-05-25 16:12:41 +02:00
Tony Garnock-Jones 3f5e431717 Clean up error situation 2020-05-25 15:40:01 +02:00
Tony Garnock-Jones 1011818be6 Split out general std::io::Error wrapper 2020-05-25 15:34:17 +02:00
Tony Garnock-Jones 94058b5ec2 Simplify again by moving away from excessive internal buffering 2020-05-25 15:29:54 +02:00
Tony Garnock-Jones f07d2e6a5d Simplify and speed up decoding 2020-05-25 15:00:58 +02:00
Tony Garnock-Jones 6ad1707870 First stab at factoring out Reader trait 2020-05-25 13:42:06 +02:00
Tony Garnock-Jones 92463c37d4 Less ownership in preserves::ser::Serializer 2020-05-24 21:17:41 +02:00
Tony Garnock-Jones fdae09b44c Tweak preserves::ser API 2020-05-24 14:40:36 +02:00
Tony Garnock-Jones 67bf47a5c9 Less safe but drastically more efficient raw-value (de)serialization 2020-05-24 13:47:42 +02:00
Tony Garnock-Jones bddb4331aa Direct serialization 2020-05-23 22:33:02 +02:00
Tony Garnock-Jones 0feff28d82 Integer ranges 2020-05-22 14:36:33 +02:00
Tony Garnock-Jones d29ec9ffc4 Avoid a few small allocations when decoding 2020-05-22 10:38:34 +02:00
Tony Garnock-Jones 316a772fec Change API: as_record() now takes Option<usize> 2020-05-18 16:42:59 +02:00
Tony Garnock-Jones 2a85e53acc Repair mistaken usage of String instead of Symbol 2020-05-18 16:42:34 +02:00
Tony Garnock-Jones a099fc9a3b Cosmetic: table whitespace 2020-05-18 09:56:16 +02:00
Tony Garnock-Jones 3eeaab375a More on autodetection 2020-05-18 09:55:57 +02:00
Tony Garnock-Jones af7c523513 Bump versions 2020-05-13 12:58:47 +02:00
Tony Garnock-Jones 8e0ab95d82 Autodetectability of binary vs text; documented test case schema a little 2020-05-13 12:56:13 +02:00
Tony Garnock-Jones ebbd268166 Toplevel test-all target 2020-05-13 12:56:13 +02:00
Tony Garnock-Jones 4272238048 An additional example 2020-05-13 12:50:12 +02:00
Tony Garnock-Jones d883ad9a75 Move racket implementation one subdirectory deeper, to allow package name inference to work 2020-04-27 21:07:20 +02:00
Tony Garnock-Jones 340f77cc3c Fix Racket package deps 2020-01-17 14:12:50 +01:00
Tony Garnock-Jones 46b220e042 LEB128 2019-11-22 10:27:59 -05:00
Tony Garnock-Jones f6bbe191ab Unconfuse preserves emacs-mode with muon emacs-mode 2019-11-03 14:24:02 +00:00
Tony Garnock-Jones 1054cd4219 Override syntax properties for angle brackets appearing in left/right single arrows 2019-11-01 12:00:55 +00:00
Tony Garnock-Jones 0832b94ce9 Optionally-canonicalizing binary output. 2019-10-24 14:35:34 +01:00
Tony Garnock-Jones 02c02b641f Bump rust version 2019-10-23 22:59:13 +01:00
Tony Garnock-Jones ab06cd465d Remove pointless footnote remark 2019-10-23 22:58:47 +01:00
Tony Garnock-Jones b1ca95c835 Customizable treatment of Domain during encoding 2019-10-23 22:58:36 +01:00
Tony Garnock-Jones 27fb961653 Allow embedding of domain-specific non-Preserves data within a Preserves Value 2019-10-22 22:36:01 +01:00
Tony Garnock-Jones 826fce6203 Bump rust version 2019-10-20 13:57:40 +01:00
Tony Garnock-Jones 91f48d08f9 Clip clip clip 2019-10-20 13:57:10 +01:00
Tony Garnock-Jones 488bfdc59c Fix clippyisms 2019-10-20 13:57:04 +01:00
Tony Garnock-Jones ccc3b5780b Bump rust version 2019-10-19 23:31:22 +01:00
Tony Garnock-Jones 6f03538956 Clippy told me not to implement Hash when deriving PartialEq. Fair enough 2019-10-19 23:31:00 +01:00
Tony Garnock-Jones 84f99fc471 New tests 2019-10-19 23:30:46 +01:00
Tony Garnock-Jones 4b957d8785 Advance index when reading multiple bytes as well as one at a time 2019-10-19 23:26:44 +01:00
Tony Garnock-Jones a00314d108 Refine error API 2019-10-15 20:44:19 +01:00
Tony Garnock-Jones d2a57c839c Exclude implementations/ from site 2019-10-15 19:01:05 +01:00
Tony Garnock-Jones 96679e8bf5 Bump Rust version to 0.2.0 2019-10-12 22:51:36 +01:00
Tony Garnock-Jones c95b000b7b annotations_mut, value_mut for PlainValue 2019-10-12 22:51:25 +01:00
Tony Garnock-Jones 9611c1f977 Delegate most numeric From implementations to the i128 one 2019-10-12 22:36:46 +01:00
Tony Garnock-Jones 623bbd0a71 annotations() returns slice rather than Vec ref 2019-10-12 22:36:18 +01:00
Tony Garnock-Jones 54d1e349b3 AValue -> PlainValue, RcValue and ArcValue 2019-10-12 21:54:20 +01:00
Tony Garnock-Jones f1b37ac9af First tentative step to allowing reuse of Value with direct containment, Rc and Arc 2019-10-12 15:40:35 +01:00
Tony Garnock-Jones cea42db3dc Tweak language per @dustyweb suggestion 2019-10-11 16:19:57 +01:00
Tony Garnock-Jones 2ff6cf400b Simplify 2019-10-09 15:58:02 +01:00
Tony Garnock-Jones 383824c5e0 CSS tweaks for mobile 2019-10-08 18:43:38 +01:00
Tony Garnock-Jones 0a537b73ed Tweak print stylesheet. 2019-10-08 14:45:58 +01:00
Tony Garnock-Jones ab1ef92334 Clarify Dictionary canonical form 2019-10-08 14:06:08 +01:00
Tony Garnock-Jones 5b0ad234fd This is not needed; instead, the general prohibition on non-shortest-varints applies 2019-10-08 13:55:12 +01:00
Tony Garnock-Jones ebcdbe9ee2 Further clarification. 2019-10-08 13:54:23 +01:00
Tony Garnock-Jones ba8875e1d7 Tweak 2019-10-08 13:49:21 +01:00
Tony Garnock-Jones 8aaec11635 Varints must be as short as possible to be canonical 2019-10-08 13:49:03 +01:00
Tony Garnock-Jones fe8c766d1e Canonical Form for Binary Syntax 2019-10-08 13:27:35 +01:00
Tony Garnock-Jones da2a6d44d1 Bump patch version 2019-09-20 21:43:18 +01:00
Tony Garnock-Jones f081470b1e panic -> unreachable 2019-09-20 21:43:08 +01:00
Tony Garnock-Jones 72edd1327a From<value::decoder::Error> for std::io::Error 2019-09-20 21:42:45 +01:00
Tony Garnock-Jones 8cdd67f5ba Mutable Read in Decoder 2019-09-20 21:42:09 +01:00
Tony Garnock-Jones 898a5d680d Expose encoder::Error 2019-09-19 21:49:46 +01:00
Tony Garnock-Jones 63cd853ba1 Rc -> Box 2019-09-19 21:35:00 +01:00
Tony Garnock-Jones 116b37a24f Ahhh 2019-09-19 21:34:40 +01:00
Tony Garnock-Jones 9af8ca8e97 More metadata 2019-09-17 11:42:13 +01:00
Tony Garnock-Jones ef3b88147e Codec; implement Rust tests; samples.txt in quasi-canonical serialized order 2019-09-17 11:27:44 +01:00
Tony Garnock-Jones 199ce1fdba invert_map (for placeholder en/decode conversion) 2019-09-17 09:57:44 +01:00
Tony Garnock-Jones 6775868272 Better errors; Symbol wrapper; proper identifier decoding; Test case struct deserialization works 2019-09-17 09:43:03 +01:00
Tony Garnock-Jones a548485b2e Rust serde glue (wip) 2019-09-17 00:58:44 +01:00
Tony Garnock-Jones 2349e80b30 Rust Encoder 2019-09-15 16:16:17 +01:00
Tony Garnock-Jones bec3a10d64 Annotation skipping; text-syntax-like debug printing (plus pretty printing for free!!) 2019-09-15 12:25:23 +01:00
Tony Garnock-Jones 365e41b798 New case 2019-09-15 11:18:06 +01:00
Tony Garnock-Jones 2fc1d42cd7 Bump versions 2019-09-11 14:31:08 +01:00
Tony Garnock-Jones 1be54a2b8f Beginnings of a Rust Preserves decoder 2019-09-09 22:08:26 +01:00
Tony Garnock-Jones 02e0eae396 New test 2019-09-09 21:08:31 +01:00
Tony Garnock-Jones d5cbe90235 Further steps toward learning enough Rust to produce an implementation 2019-09-09 14:46:53 +01:00
Tony Garnock-Jones 2dd2c329ed Another test case 2019-09-08 14:33:50 +01:00
Tony Garnock-Jones 5fe529825f New tests 2019-09-08 14:17:00 +01:00
Tony Garnock-Jones 9c6deecb55 Bump nyc version 2019-08-31 21:18:21 +01:00
Tony Garnock-Jones 07312954df Bump versions; update metadata 2019-08-31 21:15:59 +01:00
Tony Garnock-Jones 15d14cdafe Common tests for Python impl 2019-08-31 20:52:32 +01:00
Tony Garnock-Jones b2203b6f5b Remove unwanted code 2019-08-31 20:18:23 +01:00
Tony Garnock-Jones 85804a9e6d More specific check for DecodeError 2019-08-31 14:09:21 +01:00
Tony Garnock-Jones 7efce309f6 Add peel(); tweak strip; adjust tests to use accessors and peel 2019-08-31 08:27:07 +01:00
Tony Garnock-Jones e35c237c34 Update Python codec; first round of Python test updates 2019-08-30 22:53:01 +01:00
Tony Garnock-Jones 942fa30d9d Remove almost-duplicate code 2019-08-30 21:55:45 +01:00
Tony Garnock-Jones 64c7de832e SequenceStream not required in tests 2019-08-30 09:21:34 +01:00
Tony Garnock-Jones 04ecbe03e3 js: Update tests, Immutable.js; implement more tests; all tests pass 2019-08-29 21:07:17 +01:00
Tony Garnock-Jones 102cb93f26 samples.bin 2019-08-27 21:09:07 +01:00
Tony Garnock-Jones 738a47ce90 WIP 2019-08-26 21:03:32 +01:00
Tony Garnock-Jones 2b74100b2a Some coverage of read/write text syntax 2019-08-26 21:03:19 +01:00
Tony Garnock-Jones aa59846ee9 --atob, --btoa 2019-08-23 22:14:02 +01:00
Tony Garnock-Jones a34a4cd20e Stop reading symbols at @ 2019-08-23 22:09:30 +01:00
Tony Garnock-Jones a817fed40d preserves-tool 2019-08-23 22:05:30 +01:00
Tony Garnock-Jones f09067d719 Allow use of strip-annotations and peel-annotations as identifiers 2019-08-23 22:00:52 +01:00
Tony Garnock-Jones 6f4e06f6b9 Annotation inappropriate for ExpectedPlaceholderMapping 2019-08-23 09:58:30 +01:00
Tony Garnock-Jones a7147fa123 Remove old commented-out code 2019-08-22 21:14:53 +01:00
Tony Garnock-Jones 54b33f5f13 Heuristics for choosing base64 vs ascii-ish binary display 2019-08-22 20:59:37 +01:00
Tony Garnock-Jones 4d73ab6d69 Fix DecodeError runner 2019-08-22 15:31:58 +01:00
Tony Garnock-Jones aac73efd3a DecodeShort, DecodeError 2019-08-22 11:24:51 +01:00
Tony Garnock-Jones 36c5d92b73 Nondeterministic and Streaming tests; fixes 2019-08-22 11:20:58 +01:00
Tony Garnock-Jones adda505f45 Better record support; more test implementations; fixes 2019-08-22 09:57:57 +01:00
Tony Garnock-Jones e90a790963 First real running tests with the new design, and several concomitant fixes and new tests 2019-08-21 22:18:21 +01:00
Tony Garnock-Jones 27ac21bed1 Actually use current-value->placeholder 2019-08-21 21:20:26 +01:00
Tony Garnock-Jones 79af429b58 Fix annotation encoding 2019-08-20 22:34:29 +01:00
Tony Garnock-Jones 6783daa20d Make strip- and peel-annotations more useful with dictionaries 2019-08-20 22:30:44 +01:00
Tony Garnock-Jones e57fe62a48 peel-annotations 2019-08-20 22:30:30 +01:00
Tony Garnock-Jones fa5eaa6e39 Limited-depth strip-annotations 2019-08-20 22:24:24 +01:00
Tony Garnock-Jones 3f0ec34d49 Uniform annotations in syntax mode; WIP update binary codec 2019-08-20 20:44:07 +01:00
Tony Garnock-Jones e84b4c5780 New small test case 2019-08-20 20:33:14 +01:00
Tony Garnock-Jones 9de37d5df0 Note re annotations and compact values 2019-08-20 20:32:58 +01:00
Tony Garnock-Jones fd87f07ec0 Quasi-pretty-printed text output (indentation, basically) 2019-08-19 22:48:12 +01:00
Tony Garnock-Jones e7a528fc72 Acknowledge Racket influence 2019-08-19 21:14:46 +01:00
Tony Garnock-Jones 3609b121d5 Link to spec in tutorial 2019-08-18 23:07:54 +01:00
Tony Garnock-Jones f4beba8b7a Update TODO.md 2019-08-18 23:06:28 +01:00
Tony Garnock-Jones 389b74bf87 Tweak 2019-08-18 22:45:57 +01:00
Tony Garnock-Jones 8f205ea0ca Acknowledge influences 2019-08-18 22:42:23 +01:00
Tony Garnock-Jones d7daa7dd02 Note on wrapper for sizing 2019-08-18 22:42:17 +01:00
Tony Garnock-Jones 9f61059449 Proper layouting 2019-08-18 22:08:55 +01:00
Tony Garnock-Jones fc0ee42a98 Fix PDF generation 2019-08-18 17:52:11 +01:00
Tony Garnock-Jones 9064258dbc Split out inessential text from the spec 2019-08-18 17:51:26 +01:00
Tony Garnock-Jones 1bb7e1862e Fix byte string output for control characters 2019-08-18 17:40:44 +01:00
Tony Garnock-Jones 467da29c56 WIP: write-preserve, preserve->string 2019-08-18 17:34:42 +01:00
Tony Garnock-Jones 6221bdf5c7 Fancy quotes 2019-08-18 16:51:59 +01:00
Tony Garnock-Jones 3c676cb3de Avoid confusing dashes/numerics in symunicode at start of a symbol 2019-08-18 16:51:46 +01:00
Tony Garnock-Jones 7d2ffe640d Title, stylesheet link, highlighter config, and simple approximate highlighting 2019-08-18 16:39:53 +01:00
Christopher Lemmer Webber 1a84c3f609
Move from TUTORIAL.org -> TUTORIAL.md 2019-08-18 11:11:12 -04:00
Tony Garnock-Jones 40d474d456 Revert whitespace change 2019-08-18 15:16:36 +01:00
Tony Garnock-Jones 872ea562a2 Follow formatting convention 2019-08-18 15:15:11 +01:00
Tony Garnock-Jones 37a12b3595 Cosmetic: remove trailing whitespace 2019-08-18 14:26:36 +01:00
Tony Garnock-Jones 4074699766 Alternative debug annotation example, plus explanation of why I think it needs changed like this 2019-08-18 14:24:53 +01:00
Tony Garnock-Jones 8d022a332a Remark regarding annotation-reading modes 2019-08-18 14:24:24 +01:00
Tony Garnock-Jones e38f56c1c3 Comment out example of integer record labels, and explain why I'm uneasy about it 2019-08-18 14:24:02 +01:00
Tony Garnock-Jones 2db315d140 Cosmetic: spacing 2019-08-18 14:23:29 +01:00
Tony Garnock-Jones 95e9167d78 Use explicit Unknown record in example 2019-08-18 14:23:16 +01:00
Tony Garnock-Jones 3556c98346 Tweak to canonicalization section. 2019-08-18 14:22:55 +01:00
Tony Garnock-Jones 5e89bfb2ca Fix typos 2019-08-18 14:22:21 +01:00
Tony Garnock-Jones c7bae6eff8 tag -> label; slot -> field 2019-08-18 14:21:35 +01:00
Christopher Lemmer Webber 17368c8961 Correct total ordering and canonicalization 2019-08-18 13:56:23 +01:00
Christopher Lemmer Webber 90ed5bc6d7 Be more precise when talking about records (rather than saying objects) 2019-08-18 13:56:23 +01:00
Christopher Lemmer Webber bcd7dcba79 Typo fix: usually strings => usually symbols 2019-08-18 13:56:23 +01:00
Christopher Lemmer Webber ba62d998ca A few small nits fixed in the tutorial 2019-08-18 13:56:23 +01:00
Christopher Lemmer Webber dca049ce46 Add Preserves tutorial 2019-08-18 13:56:23 +01:00
Tony Garnock-Jones 0365fd8c36 Clarify no-duplicates in syntaxes. 2019-08-18 13:56:13 +01:00
Tony Garnock-Jones 0aa7218523 Another test case 2019-08-18 13:51:26 +01:00
Tony Garnock-Jones 8ffa9ce915 More tests 2019-08-18 13:42:26 +01:00
Tony Garnock-Jones 9974002cad Read angle-bracket delimited records in the Racket implementation 2019-08-18 13:40:59 +01:00
Christopher Lemmer Webber c9a624839f
recurd7 -> record7 2019-08-15 13:51:24 -04:00
Tony Garnock-Jones efd976ed90 Followup to previous commit 2019-08-12 00:09:13 +01:00
Tony Garnock-Jones 0f5f0630d2 Angle bracket S-exprs for Records! 2019-08-11 23:54:57 +01:00
Tony Garnock-Jones 74f9093c5e Link to gitlab pages 2019-08-11 16:52:29 +01:00
Tony Garnock-Jones 05789f77cd Aha! baseurl? 2019-08-11 16:26:38 +01:00
Tony Garnock-Jones c058a4ca71 Tweak jekyll setup 2019-08-11 16:19:08 +01:00
Tony Garnock-Jones 7520067318 Try the jekyll image 2019-08-11 16:07:15 +01:00
Tony Garnock-Jones 78681265ee .gitlab-ci.yml 2019-08-11 16:05:03 +01:00
Tony Garnock-Jones 423a3531f6 Improve clarity of simple example table. Closes #4. 2019-08-11 15:56:46 +01:00
Tony Garnock-Jones a737a0cf18 Spacing is actually required here :-/ 2019-08-11 15:25:43 +01:00
Tony Garnock-Jones 1cf1ab1707 New Q 2019-08-11 14:37:08 +01:00
Tony Garnock-Jones bd6517e154 Don't overmatch symbols 2019-08-11 14:37:08 +01:00
Tony Garnock-Jones 672ee83be0 Begin factoring out test suite for cross-implementation use; begin updating Racket implementation to match (not finished) 2019-08-11 14:37:08 +01:00
Tony Garnock-Jones 892df1634a LICENSE, NOTICE, README.md 2019-08-08 14:46:03 +01:00
Tony Garnock-Jones a9bed81556 Restore removed "Notes" heading 2019-07-14 14:09:19 -04:00
Tony Garnock-Jones f08e223cb0 Tweak 2019-07-13 22:26:29 -04:00
Tony Garnock-Jones e2b859e55d Major revision of binary syntax: placeholders; annotations; forbid empty format-C chunks 2019-07-13 22:20:22 -04:00
Tony Garnock-Jones d349e89ea4 Update TODOs 2019-07-11 12:34:55 -04:00
Tony Garnock-Jones 098cbe58ac hexchunk was a bad idea; introduce IOList instead 2019-07-11 12:34:47 -04:00
Tony Garnock-Jones 2b1e2f2fba TODO note re testing of NaNs 2019-07-10 21:52:29 -04:00
Tony Garnock-Jones 7933e34766 More on annotations 2019-07-10 21:52:04 -04:00
Tony Garnock-Jones c6700c2f2b Another stab at why Symbols are special 2019-07-04 05:58:15 -04:00
Tony Garnock-Jones 04868a2309 Be explicit about expected type 2019-07-04 05:47:32 -04:00
Tony Garnock-Jones 681bb9705f Symbols are actually special - without them there's a bootstrapping issue
This reverts commit df94b7a8c6.
2019-07-04 05:33:19 -04:00
Tony Garnock-Jones df94b7a8c6 Tweak 2019-07-03 19:36:07 -04:00
Tony Garnock-Jones 7861341951 Cosmetic. 2019-07-03 19:35:56 -04:00
Tony Garnock-Jones 4a70364eda Initial draft text re annotations 2019-07-04 00:33:37 +01:00
Tony Garnock-Jones 2c20de2730 Refine Racket Makefile 2019-07-01 21:47:21 +01:00
Tony Garnock-Jones 97cd007bab Remove comment parsing from Racket implementation 2019-07-01 21:47:12 +01:00
Tony Garnock-Jones 0d0124f004 Remove comments, in prep for annotations replacing them 2019-07-01 21:31:49 +01:00
Tony Garnock-Jones e2e4d23e23 Jekyll _config.yml 2019-06-30 23:43:51 +01:00
Tony Garnock-Jones eadd83d603 Some tests of IEEE754 totalOrder 2019-06-30 23:43:32 +01:00
Tony Garnock-Jones 5e8e462091 More notes 2019-06-30 22:45:10 +01:00
Tony Garnock-Jones e6e3057de3 Notes and TODOs 2019-06-30 22:43:52 +01:00
Tony Garnock-Jones 52cd074767 Update .gitignores 2019-06-30 19:01:43 +01:00
Tony Garnock-Jones 6960046263 Beginnings of Rust implementation 2019-06-29 23:02:27 +01:00
Tony Garnock-Jones 4d4cd6f417 toString for Single/Double 2019-06-24 10:44:58 +01:00
Tony Garnock-Jones 48f8024056 More webpack problems. Everything is called index.js apparently in webpack land 2019-06-14 15:52:54 +01:00
Tony Garnock-Jones 2bf10030c1 Use __filename instead of module.filename because webpack doesn't offer the latter 2019-06-14 15:46:31 +01:00
Tony Garnock-Jones 0e835bc6f9 Make boilerplate less gratuitous 2019-06-14 15:28:16 +01:00
Tony Garnock-Jones a2755a6c9b singletonmodule.js 2019-06-14 12:47:38 +01:00
Tony Garnock-Jones 48b800a7ce Fix RecordConstructorInfo.equals 2019-06-13 23:04:09 +01:00
Tony Garnock-Jones 5ab78e8281 Global symbols 2019-06-13 21:30:15 +01:00
Tony Garnock-Jones c1898ef73f Friendlier record accessors 2019-06-11 22:31:27 +01:00
Tony Garnock-Jones 5d9b03171d Bytes.toString now offers something reprish; fromUtf8 does what toString used to do 2019-06-07 13:14:07 +01:00
Tony Garnock-Jones 65866b9041 Bump version 2019-05-30 14:07:36 +01:00
Tony Garnock-Jones 320cb894f1 Friendlier accessors 2019-05-30 14:06:40 +01:00
Tony Garnock-Jones 921fe7dfd2 Tweaks 2019-04-26 08:33:43 +01:00
Tony Garnock-Jones 70888543fa Punctuation and keywords 2019-04-25 23:12:24 +01:00
Tony Garnock-Jones 6db903bd37 save-excursion during actual reindentation unless bolp 2019-04-25 20:07:31 +01:00
Tony Garnock-Jones 328f82acbc preserves-looking-at-syntax-p 2019-04-25 19:47:58 +01:00
Tony Garnock-Jones 5de9e31bdb preserves.el 2019-04-14 00:02:00 +12:00
Tony Garnock-Jones 0799fd3293 Support detection of short inputs, for e.g. incremental parsing use 2019-03-17 00:52:05 +00:00
Tony Garnock-Jones 1a2ad3201f Bump version 2018-12-08 14:04:52 +00:00
Tony Garnock-Jones d826aa9116 Repair errors that made Bytes (and Bytes.from) non-idempotent 2018-12-08 14:04:13 +00:00
Tony Garnock-Jones cbbd6ffd0c Tighten 2018-12-07 11:06:08 +00:00
Tony Garnock-Jones 8f20ae7a48 Simplify by using builtin list and a custom induction principle 2018-12-06 23:54:14 +00:00
Tony Garnock-Jones fdd7eb6e94 Explore quoting. 2018-12-06 21:25:51 +00:00
Tony Garnock-Jones b2a9b53b6c Bump python version 2018-11-26 13:25:47 +00:00
Tony Garnock-Jones 870da87350 Record.__getitem__ 2018-11-20 19:48:23 +00:00
Tony Garnock-Jones 1fd763cf56 Bump versions 2018-11-20 14:48:50 +00:00
Tony Garnock-Jones 87deddc879 RecordConstructorInfo for python 2018-11-19 16:55:22 +00:00
Tony Garnock-Jones 594b379a77 Ability to feed JS Decoder 2018-11-19 16:55:14 +00:00
Tony Garnock-Jones 28cf1d37a8 Makefile tweak 2018-11-18 17:00:20 +00:00
Tony Garnock-Jones ecdf123358 Decoder.try_next 2018-11-18 16:59:27 +00:00
Tony Garnock-Jones 10d8eb1b0a Notes on required packages 2018-11-18 16:35:09 +00:00
Tony Garnock-Jones f654280b88 Bump versions 2018-11-18 16:29:01 +00:00
Tony Garnock-Jones 639241c5a6 Fix error in format C decoding 2018-11-18 16:28:21 +00:00
Tony Garnock-Jones c9484ac9ac Float.unwrap 2018-11-16 00:10:45 +00:00
Tony Garnock-Jones 80c55e4f30 Repair error in Decoder.nextbytes 2018-11-15 23:40:48 +00:00
Tony Garnock-Jones ab8acf2154 Bump package.json 2018-11-15 23:18:42 +00:00
Tony Garnock-Jones f0a63fbb4c Many features and fixes discovered while switching Syndicate/js to Record 2018-11-15 23:18:16 +00:00
903 changed files with 278326 additions and 3539 deletions

6
.gitignore vendored
View File

@ -1,2 +1,8 @@
_site/
cheatsheet.pdf
preserves-expressions.pdf
preserves-binary.pdf
preserves-schema.pdf
preserves-text.pdf
preserves.pdf
scratch/

14
.gitlab-ci.yml Normal file
View File

@ -0,0 +1,14 @@
image: jekyll/jekyll:3.8
variables:
JEKYLL_ENV: production
pages:
script:
- jekyll build -d public
artifacts:
paths:
- public
only:
- main

View File

@ -0,0 +1 @@
<svg height="86" viewBox="0 0 76 86" width="76" xmlns="http://www.w3.org/2000/svg"><path d="m76 82v4h-76l.00080851-4zm-3-6v5h-70v-5zm-62.6696277-54 .8344146.4217275.4176066 6.7436084.4176065 10.9576581v10.5383496l-.4176065 13.1364492-.0694681 8.8498268-1.1825531.3523804h-4.17367003l-1.25202116-.3523804-.48627608-8.8498268-.41840503-13.0662957v-10.5375432l.41840503-11.028618.38167482-6.7798947.87034634-.3854412zm60.0004653 0 .8353798.4217275.4168913 6.7436084.4168913 10.9576581v10.5383496l-.4168913 13.1364492-.0686832 8.8498268-1.1835879.3523804h-4.1737047l-1.2522712-.3523804-.4879704-8.8498268-.4168913-13.0662957v-10.5375432l.4168913-11.028618.3833483-6.7798947.8697215-.3854412zm-42.000632 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2529447-.3523804-.4863246-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8688361-.3854412zm23 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2521462-.3523804-.4871231-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8696347-.3854412zm21.6697944-9v7h-70v-7zm-35.7200748-13 36.7200748 8.4088317-1.4720205 2.5911683h-70.32799254l-2.19998696-2.10140371z" fill="#2c2c2c" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.3 KiB

202
LICENSE Normal file
View File

@ -0,0 +1,202 @@
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "[]"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright [yyyy] [name of copyright owner]
Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.

View File

@ -1,2 +1,24 @@
preserves.pdf: preserves.md preserves.css
google-chrome --headless --disable-gpu --print-to-pdf=$@ http://localhost:4000/preserves.html
__ignored__ := $(shell ./setup.sh)
PDFS=\
preserves.pdf \
preserves-text.pdf \
preserves-binary.pdf \
preserves-schema.pdf \
preserves-expressions.pdf \
cheatsheet.pdf
all: $(PDFS)
clean:
rm -f $(PDFS)
%.pdf: %.md preserves.css
google-chrome --headless --disable-gpu --print-to-pdf=$@ \
http://localhost:4000/$*.html
test-all:
make -C tests
(cd implementations/javascript; npm test)
(cd implementations/python; make test)
(cd implementations/racket/preserves; make testonly)

2
NOTICE Normal file
View File

@ -0,0 +1,2 @@
Preserves: an Expressive Data Language
Copyright 2018-2022 Tony Garnock-Jones

93
README.md Normal file
View File

@ -0,0 +1,93 @@
---
projectpages: "https://gitlab.com/preserves/preserves"
projecttree: "https://gitlab.com/preserves/preserves/tree/main"
title: "Preserves: an Expressive Data Language"
no_site_title: true
---
This [repository]({{page.projectpages}}) contains a
[definition](preserves.html) and various implementations of *Preserves*, a
data model with associated serialization formats in many ways
comparable to JSON, XML, S-expressions, CBOR, ASN.1 BER, and so on.
## Core documents
### Preserves data model and serialization formats
Preserves is defined in terms of a syntax-neutral
[data model and semantics](preserves.html#semantics)
which all transfer syntaxes share. This allows trivial, completely
automatic, perfect-fidelity conversion between syntaxes.
- [Preserves specification](preserves.html):
- [Preserves semantics and data model](preserves.html#semantics),
- [Preserves textual syntax](preserves-text.html), and
- [Preserves machine-oriented binary syntax](preserves-binary.html)
- [Preserves tutorial](TUTORIAL.html)
- [Quick Reference for Preserves syntax](cheatsheet.html)
- [Canonical Form for Binary Syntax](canonical-binary.html)
- [Syrup](https://github.com/ocapn/syrup#pseudo-specification), a
hybrid binary/human-readable syntax for the Preserves data model
### Preserves schema and queries
- [Preserves Schema specification](preserves-schema.html)
- [Preserves Path specification](preserves-path.html)
## Implementations
#### Implementations of the data model, plus Preserves textual and binary transfer syntax
| Language[^pre-alpha-implementations] | Code | Package | Docs |
|--------------------------------------|------------------------------------------------------------------------------|--------------------------------------------------------------------------------|-------------------------------------------|
| Nim | [git.syndicate-lang.org](https://git.syndicate-lang.org/ehmry/preserves-nim) | | |
| Python | [preserves.dev]({{page.projecttree}}/implementations/python/) | [`pip install preserves`](https://pypi.org/project/preserves/) | [docs](python/latest/) |
| Racket | [preserves.dev]({{page.projecttree}}/implementations/racket/preserves/) | [`raco pkg install preserves`](https://pkgs.racket-lang.org/package/preserves) | |
| Rust | [preserves.dev](https://gitlab.com/preserves/preserves-rs/) | [`cargo add preserves`](https://crates.io/crates/preserves) | [docs](https://docs.rs/preserves/latest/) |
| Squeak Smalltalk | [SqueakSource](https://squeaksource.com/Preserves.html) | `Installer ss project: 'Preserves';`<br>`  install: 'Preserves'` | |
| TypeScript/JavaScript | [preserves.dev]({{page.projecttree}}/implementations/javascript/) | [`yarn add @preserves/core`](https://www.npmjs.com/package/@preserves/core) | |
[^pre-alpha-implementations]: Pre-alpha implementations also exist for
[C]({{page.projecttree}}/implementations/c/) and
[C++]({{page.projecttree}}/implementations/cpp/).
#### Implementations of the data model, plus Syrup transfer syntax
| Language | Code |
|------------|----------------------------------------------------------------------------------------------------------------------------------|
| Guile | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/guile/syrup.scm) |
| Haskell | [github.com/zenhack/haskell-preserves](https://github.com/zenhack/haskell-preserves) |
| JavaScript | [github.com/zarutian/agoric-sdk](https://github.com/zarutian/agoric-sdk/blob/zarutian/captp_variant/packages/captp/lib/syrup.js) |
| Python | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/python/syrup.py) |
| Racket | [github.com/ocapn/syrup](https://github.com/ocapn/syrup/blob/master/impls/racket/syrup/syrup.rkt) |
## Tools
### Preserves documents
- [preserves-tool](doc/preserves-tool.html), generic syntax translation and pretty-printing
### Preserves Schema documents and codegen
- [Tools for working with Preserves Schema](doc/schema-tools.html)
## Additional resources
- [Preserves Expressions (P-expressions, pexprs)](preserves-expressions.html)
- Some [conventions for common data types](conventions.html)
- [Open questions](questions.html); see also the
[issues list]({{page.projectpages}}/issues)
- [Why not Just Use JSON?](why-not-json.html)
## Contact
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
## Licensing
The contents of this repository are made available to you under the
[Apache License, version 2.0](LICENSE)
(<http://www.apache.org/licenses/LICENSE-2.0>), and are Copyright
2018-2022 Tony Garnock-Jones.
## Notes

365
TODO.md Normal file
View File

@ -0,0 +1,365 @@
---
---
TODO:
- consider a lead byte value used to wrap an encoded `Value` in a
size-counted wrapper? That way parsers can quickly skip nested
structure they're not interested in...
- https://github.com/uwiger/sext
- http://erlang.org/doc/reference_manual/expressions.html#term-comparisons;
in particular, see the non-lexicographic ordering on tuples (vs
lists).
- should there be a built-in (i.e. recommended) reference type for external data??
- if there were, it'd give IPLD-like characteristics to the thing from the get-go
- IRIs and mime-typed things are already in there so why not content-based addressing
- Check out https://hitchdev.com/strictyaml/, in particular the "Why
StrictYAML?" and "Design justifications" sections; perhaps borrow
elements of that structure for writing a comparison of Preserves
with other things
It is becoming VERY CLEAR that on-the-wire efficiency is... a
secondary concern. Perhaps revise the binary syntax to be less terse
and better for simple encoding and for term ordering,
canonicalization, quick indexing, etc.
- the indexing thing clashes with the term ordering thing
- maybe put the indexes at the end?? they could be optional
It might be nice to define some kind of jsonpath/xpath-like means of
naming a subterm within a Preserve. Record labels would be a kind of
assertion on the current node. Indexes and keys would be steps. It'd
be a lot like xpath I think; see also my `racket-xe` package.
- `<child>` - moves into direct children
- `<descendant-or-self>` - moves into direct and indirect children, including this node
- `<descendant>` - moves into direct and indirect children, excluding this node
- `<where[P*]>` - "where" clause, applies nested path, keeping nodes with submatches
- `<or[P*]>` - result of first non-empty `P` match
- `<at K>` - moves into direct children whose keys are `K` from
dictionaries, sequences or records; `K` should be a number for the
latter two
- `<label>` - moves into labels of records
- `<equals V>` - filters to only nodes that equal `V`
- `<isa T>` - filters to only nodes that are `T ∈
{boolean float double signed-integer string byte-string symbol record sequence set dictionary}`
Abbreviations:
/ = <child>
// = <descendant-or-self>
[P*] = <where[P*]>
Symbol = [<label> <equals Symbol>]
NonSymbolAtom = <at NonSymbolAtom>
# TODO
- explain why total order / comparison of values is important and/or useful
- what does having a total order unlock?
- explain why records are good (see below on yaml tags etc)
- hashability: comes from equivalence
- more examples
- over-8000er mountains
- yaml example from the top of https://camel.readthedocs.io/en/latest/yamlref.html
- having records with ANONYMOUS but ordered fields is good for easy
parsing in languages like C where you don't want to explicitly
search dictionaries of key/value mappings
- labels vs. yaml tags vs. annotations
- yaml tags are complex. they're relative uris, for the most part
anyway, except the local ones; they force interpretation rather
than being data, e.g. `!` forces a node to be interpreted as a
string, sequence, or map and `?` forces "tag resolution" aka
dwimming of scalar syntax. Labels here don't change how their
fields resolve at all.
- they're also used to specify particular host-language classes
and other objects.
!!python/none
!!python/bool
!!python/bytes
!!python/str
!!python/unicode
!!python/int
!!python/long
!!python/float
!!python/complex
!!python/list
!!python/tuple
!!python/dict
!!python/name:module.name
!!python/module:package.module
!!python/object:module.Cls
!!python/object/new:module.Cls
!!python/object/apply:module.f
!ruby/symbol
!ruby/sym (alias of the previous!)
!ruby/range
!ruby/regexp
!ruby/struct:StructTypeName
!ruby/object:Module::ClassName
!ruby/array:Module::ClassName (subtyping arrays! objects, not data)
!ruby/hash:Module::ClassName (subtyping hashes! objects, not data)
!perl/regexp
- yaml tag meanings are per-document or global. Labels aren't
really specified. Is this good or bad? Once there's a type
system, labels will become meaningful in a per-type context.
- yaml tags basically are meant to mean the type of the object
following. Labels are not: they are for distinguishing among
variants *within* a type. (In a unityped setting, this boils
down to the same thing at a different level; object-level vs
meta-level variants.)
- in some cases (ruby) a tag indicates a subclass: a
behavioural refinement of some *object* rather than a
structural extension of some *data*.
- yaml tags don't have intrinsic meaning: implementations are
allowed to complain if they don't recognise a tag. They also
affect how and whether an object can be used as a dict key;
labels, otoh, have intrinsic (trivial) meaning, and *any*
preserves value is allowed to be used as a dict key. YAML
documents then have implementation-specific meaning, but
Preserves have intrinsic meaning.
- yaml has schemas, holy shit, and there the tags really do
direct interpretation of values to a significant extent.
Preserves forces the application to do such interpretations:
the parser/reader won't do them for you.
- TODO: be clearer in the bit on "validity"
- yaml tags are URIs, and cannot be structured data
- annotations
- in brief: out-of-domain METADATA; implementation/metalevel, not domain/objectlevel
- comments are a good example: out-of-domain description about the
value, not part of the value itself
- uses:
- roundtripping config cf the approach taken by http://augeas.net/
- embedding trace information in messages
- provenance information
- stack information / distributed trace/continuation record
- remove comments once annotations are in!
- binary syntax: length-prefixing is good for pattern-matching,
because it allows you to reject terms based on arity without having
to scan the contents.
- hey so what about protobufs? the optional fields /
forward-and-backwards-compatibility thing is interesting.
- what about skipping e.g. lists? would need byte-length prefix
- When thinking about extensibility and forward/backward
compatibility, consider this:
<https://eighty-twenty.org/2016/09/18/gnome-flashback-patch>
- types, type-directed whitespace-sensitive parsing (oh hey it might
also lead to optimized binary parsers based on type?)
- Zephyr (here `*` is postfix Kleene star and `?` marks zero-or-one):
asdl_ty = Sum(identifier, field*, ctor, ctor*) ;; typename, common fields, at least one ctor, more ctors
| Product(identifier, field, field*) ;; ?? i guess a degenerate kind of sum??
ctor = Con(identifier, field*) ;; most like Preserves' record
field = Id(identifier, identifier?) ;; basic typename reference (?)
| Option(identifier, identifier?) ;; postfix `?`
| Sequence(identifier, identifier?) ;; postfix `*`
value = SumVal(identifier, value*, value*) ;; there are common fields
| ProductVal(value, value*)
| SequenceVal(value*)
| NoneVal
| SomeVal(value)
| PrimVal(prim)
prim = IntVal(int)
| IdentifierVal(identifier)
| StringVal(string)
- So then for us, where we have kind of union types more than
labelled sums:
- `<equals Value>`, `<lessthan Value>`, `<greaterthan Value>`
- must be equal to / less than / greater than this value
- maybe take `<range lo hi>` as primitive?
- no, because of infinitesimals
- `<regexp string>` ... etc? (Perhaps `<pattern regexpstring>`
is better) (Be sure to specify ECMA-262 dialect, with
restrictions a la JSON-schema
https://json-schema.org/latest/json-schema-validation.html#rfc.section.4.3)
- identifier naming a type definition
- some type definitions are builtin: `Boolean = <union <equals #true> <equals #false>>`
- some have to be primitive rather than builtin, like
`SignedInteger` or `Double`, because they have unboundedly
(or awkwardly) many inhabitants and the class above or
below them doesn't have a limit ordinal in the right place
- parameters/`forall`?
- `<record Type Type ...>` - first one is the label type
- `<list Type ...>` - heterogeneous list of specific types
- `<listof Type>` - homogeneous list
- `<setof Type>`
- `{ keyType: valueType, ... }` - heterogeneous dict
- wait, `{ keyLiteral: valueType, ... }` might be better - sugar for
`<dict [<equals keyLiteral> valueType] ...>`
- `<dict+ ...>` for when extra members are allowed
- what about optional members?
- `<dictof keyType valueType>` - homogeneous dict
- `<union Type ...>`
- empty union is uninhabited type(!)
- a kind of or
- `<and Type ...>`
- simultaneous constraints on type, for range, or for range-and-type
- a kind of intersection; parallel reduction
- `<interleave Type ...>` ?? maybe, if sequences are a thing?
Could be good for organizing key-value mappings in
dictionary-brackets, because unordered... and sets...
Sketching it out:
preserves_ty =
- Oh dear, actually this is very close to being just a pattern
language without the captures.
a1.a & b1.b = a1.(a & b1.b) + b1.(a1.a & b)
- Take two.
- `<== Value >`, `<|<| value>`, `<|>| value>`, `|<=|`, `|>=|`, `*eq` `*lt` `*gt` `*le` `*ge`
- `_` for discard, `<*discard>`
- scalar values not symbols beginning with `*` match themselves as if they were `==`-wrapped
- all the special things are records, possibly 0-ary, with labels symbols starting with `*`
except for `==` etc and `_` and `...`
- if you have to match a label like `*foo` it might clash, so match `<== *foo>` instead:
`<*foo 1 2 3>` ==> `<<== *foo> 1 2 3>`
- `<*int>` for `SignedInteger`, `<*string>`, `<*symbol>`,
`<*bytestring>`/`<*binary>`, `<*float>`, `<*double>`,
`<*bool>`
- `<*and Pattern ⋯>`
- `<*or Pattern ⋯>`
- `<*not Pattern>` ?
- `<Pattern Pattern ⋯>` - match record
- `[Pattern ⋯]` - match sequence
- `#set{Pattern}` - match set
- don't know how to match dictionaries yet
- view it as an interleave of its keyvalues
- `<*interleave Pattern ⋯>`?
- somehow allow specification of a keyvalue that is repeating, that is optional, etc
- `{Keypat:Valpat ⋯ <... Keypat>:<... Valpat>}` ??? eww?
- `<*group Pattern ⋯>` - sequence of values spliced into wider sequence?
- use literal `...` symbol (!) to mark repetition in a sequence:
`[<*string> ...]`
- could use literal `?` to mark optionality; or better perhaps `<*optional Pattern>`,
equivalent to `<*biased-choice Pattern <*group>>`; hmm, biased choice!
- could use `<*repeat lo hi>` or similar for counted repetition
- don't know how to write refs to other types yet! def labels starting with `*`?
<*def <*foo> <*or <*int> <*string>>>
<*foo>
<*def <*maybe a> <*or <nothing> <just a>>>
<*maybe <*int>>
- should those be relative URLs, or jsonpointer or something,
so can drag in types from the web?
- NOTE: No schema for indicating attachment of annotations?!?!?!
The YAML example:
database:
username: admin
password: foobar # TODO get prod passwords out of config
socket: /var/tmp/database.sock
options: {use_utf8: true}
memcached:
host: 10.0.0.99
workers:
- host: 10.0.0.101
port: 2301
- host: 10.0.0.102
port: 2302
Could be:
[ <Database [<Username "admin">
@<TODO "get prod passwords out of config"> <Password "foobar">
<Socket "/var/tmp/database.sock">
<Options [<UseUTF8>]>]>
<Memcached [<Host "10.0.0.99">]>
<Workers [<Worker "10.0.0.101" 2301>
<Worker "10.0.0.102" 2302>]> ]
Or
{
database: {
username: "admin",
@<TODO "get prod passwords out of config">
password: "foobar",
socket: "/var/tmp/database.sock",
options: #set{use_utf8}
},
memcached: {
host: "10.0.0.99"
},
workers: [ <Worker "10.0.0.101" 2301>
<Worker "10.0.0.102" 2302> ]
}
Its schema-sketch could be
[ <*interleave <Database [ <*interleave <Username <*string>>
<Password <*string>>
<*optional <Socket <*string>>>
<*optional <Options [<*option> ...]>>> ]>
<Memcached [ <Host <*ipv4>> ... ]>
<Workers [ <Worker <*ipv4> <*u16>> ... ]>> ]
(for the first variant) or
{
database: {
username: <*string>,
password: <*string>,
<*optional socket>: <*string>,
<*optional options>: #set{<*option>}
},
memcached: {
host: <*ipv4>
},
workers: [ <Worker <*ipv4> <*u16>> ... ]
}
Annotations will be allowed on any value; but also perhaps on a
key-value mapping pair?
{
@"I label the key" key: value
key @"I label the mapping": value
key: @"I label the value" value
}
??
Perhaps not.
The schema for the second YAML config sketch would allow the instance
to be written:
database:
username: admin
@<TODO "get prod passwords out of config">
password: foobar
socket: /var/tmp/database.sock
options: use_utf8
memcached:
host: 10.0.0.99
workers:
<Worker 10.0.0.101 2301>
<Worker 10.0.0.102 2302>

390
TUTORIAL.md Normal file
View File

@ -0,0 +1,390 @@
---
no_site_title: true
title: "Preserves: a tutorial"
---
By Christine Lemmer Webber and Tony Garnock-Jones
August 2019.
*This document, like Preserves itself, is released under*
*[version 2.0 of the Apache license](./LICENSE).*
<a id="org74bd8ba"></a>
# Overview
Preserves is a serialization system which supplies both a
human-readable textual and efficient binary syntax; converting between
the two is straightforward.
Preserves' human readable syntax is easy to read and should be mostly
familiar if you already know systems like JSON.
However, Preserves is more precisely specified than JSON, and also
has a clean extension mechanism.
This document is a tutorial; it does not get into all the details of
Preserves.
For that, see the [Preserves specification](preserves.html).
<a id="org0a73f91"></a>
# Preserves basics
<a id="orge8d9054"></a>
## Starting with the familiar
If you're familiar with JSON, Preserves looks fairly similar:
```
{"name": "Missy Rose",
"species": "Felis Catus",
"age": 13,
"foods": ["kibble", "cat treats", "tinned meat"]}
```
Preserves also has something we can use for debugging/development
information called "annotations"; they aren't actually read in as data
but we can use them for comments.
(They can also be used for other development tools and are not
restricted to strings; more on this later, but for now, we will stick
to the special comment annotation syntax.)
```
# I'm an annotation... basically a comment. Ignore me!
"I'm data! Don't ignore me!"
```
Preserves supports some data types you're probably already familiar
with from JSON, and which look fairly similar in the textual format:
```
# booleans
#t
#f
# various kinds of numbers:
42
123556789012345678901234567890
-10
13.5
# strings
"I'm feeling stringy!"
# sequences (lists)
["cat", "dog", "mouse", "goldfish"]
# dictionaries (hashmaps)
{"cat": "meow",
"dog": "woof",
"goldfish": "glub glub",
"mouse": "squeak"}
```
<a id="org270f7f4"></a>
## Going beyond JSON
We can observe a few differences from JSON already; it's possible to
*reliably* express integers of arbitrary length in Preserves, and booleans look a little
bit different.
A few more interesting differences:
```
# Preserves treats commas as whitespace, so these are the same
["cat", "dog", "mouse", "goldfish"]
["cat" "dog" "mouse" "goldfish"]
# We can use anything as keys in dictionaries, not just strings
{1: "the loneliest number",
["why", "was", 6, "afraid", "of", 7]: "because 7 8 9",
{"dictionaries": "as keys???"}: "well, why not?"}
```
Preserves technically provides various types of numbers:
```
# Signed Integers
42
-42
5907212309572059846509324862304968273468909473609826340
-5907212309572059846509324862304968273468909473609826340
# Doubles (Double-precision IEEE floats)
3.141592653589793
```
Preserves also provides some types that don't come in JSON.
`Symbols` are fairly interesting; they look a lot like strings but
really aren't meant to represent text as much as they are, well&#x2026; a
symbolic name.
Often they're meant to be used for something that has symbolic importance
to the program, but not textual importance (other than to guide the
programmer&#x2026; not unlike variable names).
```
# A symbol (NOT a string!)
JustASymbol
# You can do mixedCase or CamelCase too of course, pick your poison
# (but be consistent, for the sake of your collaborators!)
iAmASymbol
i-am-a-symbol
# A list of symbols
[GET, PUT, POST, DELETE]
# A symbol with spaces in it
|this is just one symbol believe it or not|
```
We can also add binary data, aka ByteStrings:
```
# Some binary data, base64 encoded
#[cGljdHVyZSBvZiBhIGNhdA==]
# Some other binary data, hexadecimal encoded
#x"616263"
# Same binary data as above, base64 encoded
#[YWJj]
```
What's neat about this is that we don't have to "pay the cost" of
base64 or hexadecimal encoding when we serialize this data to binary;
the length of the binary data is the length of the binary data.
Conveniently, Preserves also includes Sets, which are collections of
unique elements where ordering of items is unimportant.
```
#{flour, salt, water}
```
<a id="orgefafe56"></a>
## Canonicalization
This is a good time to mention that even though from a semantic
perspective sets and dictionaries do not carry information about the
ordering of their elements (and Preserves doesn't care what order we
enter them in for our hand-written-as-text Preserves documents),
[Preserves provides support for canonical ordering](canonical-binary.html)
when serializing.
In canonicalizing output mode, Preserves will always write out a given
value using exactly the same bytes, every time. This is important and
useful for many contexts, but especially for cryptographic signatures
and hashing.
```
# This hand-typed Preserves document...
{monkey: {"noise": "ooh-ooh",
"eats": #{"bananas", "berries"}}
cat: {"noise": "meow",
"eats": #{"kibble", "cat treats", "tinned meat"}}}
# Will always, always be written out in this order (except in
# binary, of course) when canonicalized:
{cat: {"eats": #{"cat treats", "kibble", "tinned meat"},
"noise": "meow"}
monkey: {"eats": #{"bananas", "berries"},
"noise": "ooh-ooh"}}
```
<a id="org0366627"></a>
## Defining our own types using Records
Finally, there is one more type that Preserves provides&#x2026; but in a
sense, it's a meta-type.
`Record` objects have a label and a series of arguments (or "fields").
For example, we can make a `Date` record:
```
<Date 2019 8 15>
```
In this example, the `Date` label is a symbol; 2019, 8, and 15 are the
year, month, and day fields respectively.
Why do we care about this?
We could instead just decide to encode our date data in a string,
like "2019-08-15".
A document using such a date structure might look like so:
```
{"name": "Gregor Samsa",
"description": "humanoid trapped in an insect body",
"born": "1915-10-04"}
```
Unfortunately, say our boss comes along and tells us that the people
doing data entry have complained that it isn't always possible to get
an exact date.
They would like to be able to type in what they know if they don't
know the date exactly.
This causes a problem.
Now we might have two kinds of entries:
```
# Exact date known
{"name": "Gregor Samsa",
"description": "humanoid trapped in an insect body",
"born": "1915-10-04"}
# Not sure about exact date...
{"name": "Gregor Samsa",
"description": "humanoid trapped in an insect body",
"born": "Sometime in October 1915? Or was that when he became an insect?"}
```
This is a mess.
We *could* just try parsing a regular expression to see if it "looks
like a date", but doing this kind of thing is prone to errors and weird
edge cases.
No, it's better to be able to have a separate type:
```
# Exact date known
{"name": "Gregor Samsa",
"description": "humanoid trapped in an insect body",
"born": <Date 1915 10 04>}
# Not sure about exact date...
{"name": "Gregor Samsa",
"description": "humanoid trapped in an insect body",
"born": <Unknown "Sometime in October 1915? Or was that when he became an insect?">}
```
Now we can distinguish the two.
We can make as many Record types as our program needs, though it is up
to our program to make sense of what these mean.
Since Preserves does not specify the `Date` itself, both the program
(or person) writing the Preserves document and the program reading it
need to have a mutual understanding of how many fields it has and what
the meaning the label signifies for it to be of use.
Still, there are plenty of interesting labels we can define.
Here is one for an "iri", a hyperlink:
```
<iri "https://dustycloud.org/blog/">
```
That's nice enough, but here's another interesting detail&#x2026; labels on
Records are usually symbols but aren't necessarily so.
They can also be strings or numbers or even dictionaries.
And very interestingly, they can also be other records:
```
< <iri "https://www.w3.org/ns/activitystreams#Note">
{"to": [<iri "https://chatty.example/ben/">],
"attributedTo": <iri "https://social.example/alyssa/">,
"content": "Say, did you finish reading that book I lent you?"} >
```
Do you see it? This Record's label is&#x2026; an `iri` Record!
The link here points to a more precise term saying that "this is a
note meant to be sent around in social networks".
It is considerably more precise than just using the string or symbol
"Note", which could be ambiguous.
(A social networking note? A footnote? A music note?)
While not all systems need this, this (partial) example hints at how
Preserves can also be used to coordinate meaning in larger, more
decentralized systems.
Likewise, it is also possible to annotate records with integers.
Languages like OCaml use integers instead of symbolic record labels
because their type systems ensure that it is never ambiguous what,
say, the label `23` means in any given context.
Allowing integer record labels lets Preserves directly express OCaml
data.
<a id="org1b72b96"></a>
## Annotations
Annotations are not strictly a necessary feature, but they are useful
in some circumstances.
We have previously shown them used as comments:
```
# I'm a comment!
"I am not a comment, I am data!"
```
Annotations annotate the values they precede.
It is possible to have multiple annotations on a value.
The hash-space (or hash-tab) comment syntax is syntactic sugar for the general
`@`-prefixed string annotation syntax.
```
# I am annotating this number
@"And so am I!"
42
```
As said, annotations are not really data.
They are merely meant for development tooling or debugging.
You have to explicitly ask for them when reading, and they wrap all
the values.
Many implementations will, in the same mode, also supply line number
and column information attached to each read value.
So what's the point of them then?
If annotations were just for comments, there would be indeed hardly any
point at all&#x2026; it would be simpler to just provide a comment syntax.
However, annotations can be used for more than just comments.
They can also be used for debugging or other development-tool-oriented
data.
For instance, here's a reply from an HTTP API service running in
"debug" mode annotated with the time it took to produce the reply and
the internal name of the server that produced the response:
```
@<ResponseTime <Milliseconds 64.4>>
@<BackendServer "humpty-dumpty.example.com">
<Success
<Employees [
<Employee "Alyssa P. Hacker"
#{<Role Programmer>, <Role Manager>}
<Date 2018, 1, 24>>
<Employee "Ben Bitdiddle"
#{<Role Programmer>}
<Date 2019, 2, 13>> ]>>
```
The annotations aren't related to the data requested, which is all
about "employees"; instead, they're about the systems that produced
the response.
You could say they're in the domain of "debugging" instead of the
domain of "employees".
<a id="org1924a0a"></a>
# Conclusions
We've covered the broad strokes of Preserves, but not everything that
is possible with it.
We leave it as an exercise to the reader to try reading these examples
into their languages (several libraries exist already) and writing them
out as binary objects.
But as we've seen, Preserves is a flexible system which comes with
well-defined, carefully specified built-in types, as well as a
meta-type which can be used as an extension point.
Happy preserving!

17
_config.yml Normal file
View File

@ -0,0 +1,17 @@
exclude: [implementations, scratch]
markdown: kramdown
highlighter: rouge
baseurl: "https://preserves.dev"
defaults:
-
scope:
path: ""
values:
layout: page
title: "Preserves"
version_date: "February 2024"
version: "0.994.0"

View File

@ -0,0 +1,12 @@
[
{"version":"0.994.0","title":"0.994.0","aliases":["latest"]},
{"version":"0.993.0","title":"0.993.0","aliases":[]},
{"version":"0.992.2","title":"0.992.2","aliases":[]},
{"version":"0.992.1","title":"0.992.1","aliases":[]},
{"version":"0.992.0","title":"0.992.0","aliases":[]},
{"version":"0.991.0","title":"0.991.0","aliases":[]},
{"version":"0.990.1","title":"0.990.1","aliases":[]},
{"version":"0.990.0","title":"0.990.0","aliases":[]},
{"version":"0.18.1","title":"0.18.1","aliases":[]},
{"version":"0.18.0","title":"0.18.0","aliases":[]}
]

View File

@ -0,0 +1,31 @@
For a value `V`, we write `«V»` for the binary encoding of `V`.
```text
«#f» = [0x80]
«#t» = [0x81]
«@W V» = [0x85] ++ «W» ++ «V»
«#:V» = [0x86] ++ «V»
«V» if V ∈ Double = [0x87, 0x08] ++ binary64(V)
«V» if V ∈ SignedInteger = [0xB0] ++ varint(|intbytes(V)|) ++ intbytes(V)
«V» if V ∈ String = [0xB1] ++ varint(|utf8(V)|) ++ utf8(V)
«V» if V ∈ ByteString = [0xB2] ++ varint(|V|) ++ V
«V» if V ∈ Symbol = [0xB3] ++ varint(|utf8(V)|) ++ utf8(V)
«<L F_1...F_m>» = [0xB4] ++ «L» ++ «F_1» ++...++ «F_m» ++ [0x84]
«[X_1...X_m]» = [0xB5] ++ «X_1» ++...++ «X_m» ++ [0x84]
«#{E_1...E_m}» = [0xB6] ++ «E_1» ++...++ «E_m» ++ [0x84]
«{K_1:V_1...K_m:V_m}» = [0xB7] ++ «K_1» ++ «V_1» ++...++ «K_m» ++ «V_m» ++ [0x84]
varint(n) = [n] if n < 128
[(n & 127) | 128] ++ varint(n >> 7) if n ≥ 128
intbytes(n) = the empty sequence if n = 0, otherwise signedBigEndian(n)
signedBigEndian(n) = [n & 255] if -128 ≤ n ≤ 127
signedBigEndian(n >> 8) ++ [n & 255] otherwise
```
The function `binary64(D)` yields the big-endian 8-byte IEEE 754 binary representation of `D`.

View File

@ -0,0 +1,51 @@
For a value <span class="postcard-grammar binarysyntax">*V*</span>, we write <span
class="postcard-grammar binarysyntax">«*V*»</span> for the binary encoding of <span
class="postcard-grammar binarysyntax">*V*</span>.
{:.postcard-grammar.binarysyntax}
«`#f`» | = | `80`
«`#t`» | = | `81`
{:.postcard-grammar.binarysyntax}
«`@`*W* *V*» | = | `85` «*W*» «*V*»
«`#:`*V*» | = | `86` «*V*»
{:.postcard-grammar.binarysyntax}
«*V*» | = | `87``08` **binary64**(*V*) | if *V* ∈ Double
{:.postcard-grammar.binarysyntax}
«*V*» | = | `B0` **varint**(&#124;**intbytes**(*V*)&#124;) **intbytes**(*V*) | if *V* ∈ SignedInteger
«*V*» | = | `B1` **varint**(&#124;**utf8**(*V*)&#124;) **utf8**(*V*) | if *V* ∈ String
«*V*» | = | `B2` **varint**(&#124;*V*&#124;) *V* | if *V* ∈ ByteString
«*V*» | = | `B3` **varint**(&#124;**utf8**(*V*)&#124;) **utf8**(*V*) | if *V* ∈ Symbol
{:.postcard-grammar.binarysyntax}
«`<`*L* *F*<sub>1</sub> ... *F*<sub>m</sub>`>`» | = | `B4` «*L*» «*F*<sub>1</sub>» ... «*F*<sub>m</sub>» `84`
«`[`*X*<sub>1</sub> ... *X*<sub>m</sub>`]`» | = | `B5` «*X*<sub>1</sub>» ... «*X*<sub>m</sub>» `84`
«`#{`*E*<sub>1</sub> ... *E*<sub>m</sub>`}`» | = | `B6` «*E*<sub>1</sub>» ... «*E*<sub>m</sub>» `84`
«`{`*K*<sub>1</sub>`:`*V*<sub>1</sub> ... *K*<sub>m</sub>`:`*V*<sub>m</sub>`}`» | = | `B7` «*K*<sub>1</sub>» «*V*<sub>1</sub>» ... «*K*<sub>m</sub>» «*V*<sub>m</sub>» `84`
{:.postcard-grammar.binarysyntax}
**varint**(*n*) | = | <span class="outputish">*n*</span> | if *n* &lt; 128
| | <span class="outputish">(*n* &amp; 127) &#124; 128</span> **varint**(*n* &gt;&gt; 7) | if *n* ≥ 128
{:.postcard-grammar.binarysyntax}
**intbytes**(*n*) | = | <span class="roman">the empty sequence if</span> *n* = 0<span class="roman">, otherwise</span> **signedBigEndian**(*n*)
{:.postcard-grammar.binarysyntax}
**signedBigEndian**(*n*) | = | <span class="outputish">*n* &amp; 255</span> | if 128 ≤ *n* ≤ 127
| | **signedBigEndian**(*n* &gt;&gt; 8) <span class="outputish">*n* &amp; 255</span> | otherwise
The function <span class="postcard-grammar binarysyntax">**binary64**(*D*)</span> yields the
big-endian 8-byte IEEE 754 binary representation of <span class="postcard-grammar
binarysyntax">*D*</span>.
<!--
Together, <span class="postcard-grammar binarysyntax">**div**</span> and <span
class="postcard-grammar binarysyntax">**mod**</span> give [Euclidean
division](https://en.wikipedia.org/wiki/Euclidean_division); that is, if
<span class="postcard-grammar binarysyntax">*n* **div** *d* = *q*</span> and
<span class="postcard-grammar binarysyntax">*n* **mod** *d* = *r*</span>, then
<span class="postcard-grammar binarysyntax">*n* = *dq* + *r*</span> and
<span class="postcard-grammar binarysyntax">0 ≤ *r* &lt; |d|</span>.
-->

View File

@ -0,0 +1,21 @@
The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves text syntax.
```text
Document := Expr* Trailer ws
Expr := ws (SimpleExpr | Punct)
SimpleExpr := Compound | Embedded | Annotated | Atom
Compound := Sequence | Record | Block | Group | Set
Punct := `,` | `;` | `:`+
Sequence := `[` Expr* Trailer ws `]`
Record := `<` Expr* Trailer ws `>`
Block := `{` Expr* Trailer ws `}`
Group := `(` Expr* Trailer ws `)`
Set := `#{` Expr* Trailer ws `}`
Trailer := (ws Annotation)*
Embedded := `#:` SimpleExpr
Annotated := Annotation SimpleExpr
Annotation := `@` SimpleExpr | `#` ((space | tab) linecomment) (cr | lf)
```

View File

@ -0,0 +1,23 @@
The definitions of `Atom`, `ws`, and `linecomment` are as given in the Preserves text syntax.
{:.postcard-grammar.textsyntax}
| *Document* | := | *Expr*<sup></sup> *Trailer* **ws**
| *Expr* | := | **ws** (*SimpleExpr* &#124; *Punct*)
| *SimpleExpr* | := | *Compound* &#124; *Embedded* &#124; *Annotated* &#124; *Atom*
| *Compound* | := | *Sequence* &#124; *Record* &#124; *Block* &#124; *Group* &#124; *Set*
| *Punct* | := | `,` &#124; `;` &#124; `:`<sup>+</sup>
{:.postcard-grammar.textsyntax}
| *Sequence* | := | `[` *Expr*<sup></sup> *Trailer* **ws** `]`
| *Record* | := | `<` *Expr*<sup></sup> *Trailer* **ws** `>`
| *Block* | := | `{` *Expr*<sup></sup> *Trailer* **ws** `}`
| *Group* | := | `(` *Expr*<sup></sup> *Trailer* **ws** `)`
| *Set* | := | `#{` *Expr*<sup></sup> *Trailer* **ws** `}`
{:.postcard-grammar.textsyntax}
| *Trailer* | := | (**ws** *Annotation*)<sup></sup>
{:.postcard-grammar.textsyntax}
| *Embedded* | := | `#:` *SimpleExpr*
| *Annotated* | := | *Annotation* *SimpleExpr*
| *Annotation* | := | `@` *SimpleExpr* &#124; `#` ((**space** &#124; **tab**) *linecomment*) (**cr** &#124; **lf**)

View File

@ -0,0 +1,48 @@
```text
Document := Value ws
Value := ws (Record | Collection | Embedded | Annotated | Atom)
Collection := Sequence | Dictionary | Set
Record := `<` Value+ ws `>`
Sequence := `[` (commas Value)* commas `]`
Set := `#{` (commas Value)* commas `}`
Dictionary := `{` (commas Value ws `:` Value)* commas `}`
commas := (ws `,`)* ws
Embedded := `#:` Value
Annotated := Annotation Value
Annotation := `@` Value | `#` ((space | tab) linecomment) (cr | lf)
Atom := Boolean | ByteString | String | QuotedSymbol | Symbol | Number
Boolean := `#t` | `#f`
ByteString := `#"` binchar* `"`
| `#x"` (ws hex hex)* ws `"`
| `#[` (ws base64char)* ws `]`
String := `"` («any unicode scalar except `\` or `"`» | escaped | `\"`)* `"`
QuotedSymbol := `|` («any unicode scalar except `\` or `|`» | escaped | `\|`)* `|`
Symbol := (`A`..`Z` | `a`..`z` | `0`..`9` | sympunct | symuchar)+
Number := Double | SignedInteger
Double := flt | `#xd"` (ws hex hex)8 ws `"`
SignedInteger := int
escaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\u` hex hex hex hex
binescaped := `\\` | `\/` | `\b` | `\f` | `\n` | `\r` | `\t` | `\x` hex hex
binchar := «any scalar ≥32 and ≤126, except `\` or `"`» | binescaped | `\"`
base64char := `A`..`Z` | `a`..`z` | `0`..`9` | `+` | `/` | `-` | `_` | `=`
sympunct := `~` | `!` | `$` | `%` | `^` | `&` | `*` | `?`
| `_` | `=` | `+` | `-` | `/` | `.`
symuchar := «any scalar value ≥128 whose Unicode category is
Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pc,
Pd, Po, Sc, Sm, Sk, So, or Co»
flt := int ( frac exp | frac | exp )
int := (`-`|`+`) (`0`..`9`)+
frac := `.` (`0`..`9`)+
exp := (`e`|`E`) (`-`|`+`) (`0`..`9`)+
hex := `A`..`F` | `a`..`f` | `0`..`9`
ws := (space | tab | cr | lf)*
delimiter := ws | `<` | `>` | `[` | `]` | `{` | `}`
| `#` | `:` | `"` | `|` | `@` | `;` | `,`
linecomment := «any unicode scalar except cr or lf»*
```

View File

@ -0,0 +1,47 @@
{:.postcard-grammar.textsyntax}
| *Document* | := | *Value* **ws** |
| *Value* | := | **ws** (*Record* &#124; *Collection* &#124; *Embedded* &#124; *Annotated* &#124; *Atom*) |
| *Collection* | := | *Sequence* &#124; *Dictionary* &#124; *Set* |
{:.postcard-grammar.textsyntax}
| *Record* | := | `<`*Value*<sup>+</sup> **ws**`>` |
| *Sequence* | := | `[`(**commas** *Value*)<sup></sup> **commas**`]` |
| *Set* | := | `#{`(**commas** *Value*)<sup></sup> **commas**`}` |
| *Dictionary* | := | `{` (**commas** *Value* **ws**`:`*Value*)<sup></sup> **commas**`}` |
| **commas** | := | (**ws** `,`)<sup></sup> **ws** |
{:.postcard-grammar.textsyntax}
| *Embedded* | := | `#:`*Value* |
| *Annotated* | := | *Annotation* *Value* |
| *Annotation* | := | `@`*Value* &#124;`#` ((**space** &#124; **tab**) *linecomment*) (**cr** &#124; **lf**) |
{:.postcard-grammar.textsyntax}
| *Atom* | := | *Boolean* &#124; *ByteString* &#124; *String* &#124; *QuotedSymbol* &#124; *Symbol* &#124; *Number* |
| *Boolean* | := | `#t`&#124;`#f` |
| *ByteString* | := | `#"`*binchar*<sup></sup> `"`&#124;`#x"` (**ws** *hex* *hex*)<sup></sup> **ws**`"`&#124;`#[` (**ws** *base64char*)<sup></sup> **ws**`]` |
| *String* | := | `"` (« any unicode scalar value except `\` or `"` » &#124; *escaped* &#124;`\"`)<sup>⋆</sup> `"` |
| *QuotedSymbol* | := | `|` (« any unicode scalar value except `\` or `|` » &#124; *escaped* &#124;`\|`)<sup>⋆</sup> `|` |
| *Symbol* | := | (`A`..`Z`&#124;`a`..`z`&#124;`0`..`9`&#124; *sympunct* &#124; *symuchar*)<sup>+</sup> |
| *Number* | := | *Double* &#124; *SignedInteger* |
| *Double* | := | *flt* &#124;`#xd"` (**ws** *hex* *hex*)<sup>8</sup> **ws**`"` |
| *SignedInteger* | := | *int* |
{:.postcard-grammar.textsyntax}
| *escaped* | := | `\\`&#124;`\/`&#124;`\b`&#124;`\f`&#124;`\n`&#124;`\r`&#124;`\t`&#124;`\u`*hex* *hex* *hex* *hex* |
| *binescaped* | := | `\\`&#124;`\/`&#124;`\b`&#124;`\f`&#124;`\n`&#124;`\r`&#124;`\t`&#124;`\x`*hex* *hex* |
| *binchar* | := | « any unicode scalar value ≥32 and ≤126, except `\` or `"` » &#124; *binescaped* &#124;`\"` |
| *base64char* | := | `A`..`Z`&#124;`a`..`z`&#124;`0`..`9`&#124;`+`&#124;`/`&#124;`-`&#124;`_`&#124;`=` |
| *sympunct* | := | `~`&#124;`!`&#124;`$`&#124;`%`&#124;`^`&#124;`&`&#124;`*`&#124;`?`&#124;`_`&#124;`=`&#124;`+`&#124;`-`&#124;`/`&#124;`.` |
| *symuchar* | := | « any scalar value ≥128 whose Unicode category is Lu, Ll, Lt, Lm, Lo, Mn, Mc, Me, Nd, Nl, No, Pc, Pd, Po, Sc, Sm, Sk, So, or Co » |
{:.postcard-grammar.textsyntax}
| *flt* | := | *int* ( *frac* *exp* &#124; *frac* &#124; *exp* ) |
| *int* | := | (`-`&#124;`+`) (`0`..`9`)<sup>+</sup> |
| *frac* | := | `.` (`0`..`9`)<sup>+</sup> |
| *exp* | := | (`e`&#124;`E`) (`-`&#124;`+`) (`0`..`9`)<sup>+</sup> |
| *hex* | := | `A`..`F`&#124;`a`..`f`&#124;`0`..`9` |
{:.postcard-grammar.textsyntax}
| **ws** | := | (**space** &#124; **tab** &#124; **cr** &#124; **lf**)<sup></sup> |
| **delimiter** | := | **ws** &#124; `<` &#124; `>` &#124; `[` &#124; `]` &#124; `{` &#124; `}` &#124; `#` &#124; `:` &#124; `"` &#124; `|` &#124; `@` &#124; `;` &#124; `,` |
| *linecomment* | := | « any unicode scalar value except **cr** or **lf** »<sup></sup> |

1
_includes/ia-logo.md Normal file
View File

@ -0,0 +1 @@
<svg height="86" viewBox="0 0 76 86" height="12" xmlns="http://www.w3.org/2000/svg"><path d="m76 82v4h-76l.00080851-4zm-3-6v5h-70v-5zm-62.6696277-54 .8344146.4217275.4176066 6.7436084.4176065 10.9576581v10.5383496l-.4176065 13.1364492-.0694681 8.8498268-1.1825531.3523804h-4.17367003l-1.25202116-.3523804-.48627608-8.8498268-.41840503-13.0662957v-10.5375432l.41840503-11.028618.38167482-6.7798947.87034634-.3854412zm60.0004653 0 .8353798.4217275.4168913 6.7436084.4168913 10.9576581v10.5383496l-.4168913 13.1364492-.0686832 8.8498268-1.1835879.3523804h-4.1737047l-1.2522712-.3523804-.4879704-8.8498268-.4168913-13.0662957v-10.5375432l.4168913-11.028618.3833483-6.7798947.8697215-.3854412zm-42.000632 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2529447-.3523804-.4863246-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8688361-.3854412zm23 0 .8344979.4217275.4176483 6.7436084.4176482 10.9576581v10.5383496l-.4176482 13.1364492-.0686764 8.8498268-1.1834698.3523804h-4.1740866l-1.2521462-.3523804-.4871231-8.8498268-.4168497-13.0662957v-10.5375432l.4168497-11.028618.38331-6.7798947.8696347-.3854412zm21.6697944-9v7h-70v-7zm-35.7200748-13 36.7200748 8.4088317-1.4720205 2.5911683h-70.32799254l-2.19998696-2.10140371z" fill="currentColor" fill-rule="evenodd"/></svg>

After

Width:  |  Height:  |  Size: 1.4 KiB

129
_includes/intbytes.rkt Normal file
View File

@ -0,0 +1,129 @@
#lang racket
(define (base-bytes v)
(define byte-count (cond [(zero? v) 0]
[else (define raw-bit-count (+ (integer-length v) 1))
(quotient (+ raw-bit-count 7) 8)]))
(for/list [(shift (in-range (* byte-count 8) 0 -8))]
(bitwise-bit-field v (- shift 8) shift)))
(define (mod n d) (modulo n d)) ;; sign equal to sign of d
(define (div n d) (/ (- n (mod n d)) d)) ;; sign equal to sign of n, or zero
;;---------------------------------------------------------------------------
;; One
;;
;; (define (intbytes* v)
;; (cond [(zero? v) '()]
;; [(= v -1) '()]
;; [else (append (intbytes* (div v 256)) (list (mod v 256)))]))
;;
;; (define (intbytes v)
;; (define bs (intbytes* v))
;; (define looks-negative (and (pair? bs) (>= (car bs) 128)))
;; (if (xor (negative? v) looks-negative)
;; (cons (if (negative? v) 255 0) bs)
;; bs))
;;---------------------------------------------------------------------------
;; Two
;;
;; (define (intbytes** v)
;; (cond [(zero? v) '()]
;; [else (append (intbytes** (quotient v 256)) (list (remainder v 256)))]))
;;
;; (define (intbytes* v)
;; (define bs (intbytes** v))
;; (if (and (pair? bs) (>= (car bs) 128))
;; (cons 0 bs)
;; bs))
;;
;; (define (intbytes v)
;; (if (negative? v)
;; (map (lambda (n) (- 255 n)) (intbytes* (- (+ v 1))))
;; (intbytes* v)))
;;---------------------------------------------------------------------------
;; Three
;;
;; (define (intbytes+ v)
;; (cond [(>= v 128) (append (intbytes+ (div v 256)) (list (mod v 256)))]
;; [else (list (mod v 256))]))
;;
;; (define (intbytes- v)
;; (cond [(< v -128) (append (intbytes- (div v 256)) (list (mod v 256)))]
;; [else (list (mod v 256))]))
;;
;; (define (intbytes v)
;; (cond [(negative? v) (intbytes- v)]
;; [(zero? v) '()]
;; [(positive? v) (intbytes+ v)]))
;;---------------------------------------------------------------------------
;; Four
;;
;; (define (intbytes* v)
;; (append (if (<= -128 v 127)
;; '()
;; (intbytes* (div v 256)))
;; (list (mod v 256))))
;;
;; (define (intbytes v)
;; (if (zero? v)
;; '()
;; (intbytes* v)))
;;---------------------------------------------------------------------------
;; Five
;;
;; (define (intbytes* v)
;; (if (<= -128 v 127)
;; (list (mod v 256))
;; (append (intbytes* (div v 256)) (list (mod v 256)))))
;;
;; (define (intbytes v)
;; (if (zero? v)
;; '()
;; (intbytes* v)))
;;---------------------------------------------------------------------------
;; Six
(define (intbytes* v)
(if (<= -128 v 127)
(list (bitwise-and v 255))
(append (intbytes* (arithmetic-shift v -8)) (list (bitwise-and v 255)))))
(define (intbytes v)
(if (zero? v)
'()
(intbytes* v)))
(define cases `(
(-257 (#xfe #xff))
(-256 (#xff #x00))
(-255 (#xff #x01))
(-129 (#xff #x7f))
(-128 (#x80))
(-127 (#x81))
(-2 (#xfe))
(-1 (#xff))
(0 ())
(1 (#x01))
(127 (#x7f))
(128 (#x00 #x80))
(255 (#x00 #xff))
(256 (#x01 #x00))
(32767 (#x7f #xff))
(32768 (#x00 #x80 #x00))
(65535 (#x00 #xff #xff))
(65536 (#x01 #x00 #x00))
))
(module+ test
(require rackunit)
(for [(c (in-list cases))]
(match-define (list input output) c)
(writeln (list input output (base-bytes input) (intbytes input)))
(check-equal? output (base-bytes input))
(check-equal? output (intbytes input))))

View File

@ -0,0 +1,8 @@
Python's strings, byte strings, integers, booleans, and double-precision floats stand directly
for their Preserves counterparts. Wrapper objects for
[Symbol][preserves.values.Symbol] complete the suite of atomic types.
Python's lists and tuples correspond to Preserves `Sequence`s, and dicts and sets to
`Dictionary` and `Set` values, respectively. Preserves `Record`s are represented by
[Record][preserves.values.Record] objects. Finally, embedded values are represented by
[Embedded][preserves.values.Embedded] objects.

View File

@ -0,0 +1,16 @@
Here are a few example values, written using the [text
syntax](https://preserves.dev/preserves-text.html):
Boolean : #t #f
Double : 1.0 10.4e3 -100.6
Integer : 1 0 -100
String : "Hello, world!\n"
ByteString : #"bin\x00str\x00" #[YmluAHN0cgA] #x"62696e0073747200"
Symbol : hello-world |hello world| = ! hello? || ...
Record : <label field1 field2 ...>
Sequence : [value1 value2 ...]
Set : #{value1 value2 ...}
Dictionary : {key1: value1 key2: value2 ...: ...}
Embedded : #:value
Commas are optional in sequences, sets, and dictionaries.

View File

@ -0,0 +1,17 @@
```text
Value = Atom
| Compound
| Embedded
Atom = Boolean
| Double
| SignedInteger
| String
| ByteString
| Symbol
Compound = Record
| Sequence
| Set
| Dictionary
```

View File

@ -0,0 +1,16 @@
A Preserves schema connects Preserves `Value`s to host-language data
structures. Each definition within a schema can be processed by a
compiler to produce
- a simple host-language *type definition*;
- a partial *parsing* function from `Value`s to instances of the
produced type; and
- a total *serialization* function from instances of the type to
`Value`s.
Every parsed `Value` retains enough information to always be able to
be serialized again, and every instance of a host-language data
structure contains, by construction, enough information to be
successfully serialized.

View File

@ -0,0 +1,12 @@
*Preserves* is a data model, with associated serialization formats.
It supports *records* with user-defined *labels*, embedded
*references*, and the usual suite of atomic and compound data types,
including *binary* data as a distinct type from text strings. Its
*annotations* allow separation of data from metadata such as comments,
trace information, and provenance information.
Preserves departs from many other data languages in defining how to
*compare* two values. Comparison is based on the data model, not on
syntax or on data structures of any particular implementation
language.

36
_layouts/page.html Normal file
View File

@ -0,0 +1,36 @@
---
layout: skeleton
extra_html_headers: >
<link rel="stylesheet" href="{{ site.baseurl }}/normalize.css">
<link rel="stylesheet" href="{{ site.baseurl }}/preserves.css">
---
<nav>
<div class="left">
<h1>
<a href="{{ site.baseurl }}/">
<span class="icon">
<img src="{{ site.baseurl }}/logo-64x64.png">
</span>
</a>
</h1>
</div>
<div class="middle">
<ul>
<li><a href="{{site.baseurl}}/preserves.html">Core</a>
<li><a href="{{site.baseurl}}/cheatsheet.html">QuickRef</a>
<li><a href="{{site.baseurl}}/preserves-schema.html">Schema</a>
<li><a href="{{site.baseurl}}/preserves-path.html">Path</a>
</ul>
</div>
<div class="right">
<a href="https://gitlab.com/preserves/preserves">
<span class="icon">
<img src="{{ site.baseurl }}/gitlab-logo-500.svg" height="64" alt="Gitlab logo">
</span>
</a>
</div>
</nav>
<main>
<h1>{{ page.title }}</h1>
{{ content }}
</main>

11
_layouts/redirect.html Normal file
View File

@ -0,0 +1,11 @@
---
layout: skeleton
extra_html_headers: |
<link rel="canonical" href="{{ site.baseurl }}{{ page.redirect_target }}">
<meta http-equiv="Refresh" content="0; URL={{ site.baseurl }}{{ page.redirect_target }}">
---
<main>
<h1>Redirecting</h1>
<p>Redirecting you to <a href="{{ site.baseurl }}{{ page.redirect_target }}">{{ page.redirect_target }}</a>.</p>
</main>

20
_layouts/skeleton.html Normal file
View File

@ -0,0 +1,20 @@
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=utf-8">
<title>{% unless page.no_site_title %}{{ site.title }}: {% endunless %}{{ page.title }}</title>
<link href="{{ site.baseurl }}/logo-64x64.png" rel="icon" />
<link href="{{ site.baseurl }}/logo-256x256.png" rel="shortcut icon" />
<meta name="author" content="Tony Garnock-Jones">
<meta name="viewport" content="width=device-width, initial-scale=1.0">{{
page.extra_html_headers | liquify }}{{
layout.extra_html_headers | liquify }}
</head>
<body>
{{ content }}
</body>
</html>

11
_plugins/liquify.rb Normal file
View File

@ -0,0 +1,11 @@
# http://stackoverflow.com/questions/14487110/include-jekyll-liquid-template-data-in-a-yaml-variable
module Jekyll
module LiquifyFilter
def liquify(input)
Liquid::Template.parse(input).render(@context)
end
end
end
Liquid::Template.register_filter(Jekyll::LiquifyFilter)

46
canonical-binary.md Normal file
View File

@ -0,0 +1,46 @@
---
title: "Canonical Form for Binary Syntax"
---
[spec]: preserves.html
When two `Value`s are written down in *canonical form*, comparing
their *syntax* for equivalence gives the same result as comparing them
*semantically* according to the equivalence defined in the
[Preserves specification][spec].[^equivalence-not-ordering]
[^equivalence-not-ordering]: However, canonical form does *not*
induce a match between lexicographic ordering on syntax and
semantic ordering [as specified][spec]. It *only* induces a
connection between equivalences.
That is, canonical forms are equal if and only if the encoded `Value`s
are equal.
This document specifies canonical form for the Preserves [machine-oriented
binary syntax](preserves-binary.html).
**Annotations.**
Annotations *MUST NOT* be present.
**Sets.**
The elements of a `Set` *MUST* be serialized sorted in ascending order
by comparing their canonical encoded binary representations.
**Dictionaries.**
The key-value pairs in a `Dictionary` *MUST* be serialized sorted in
ascending order by comparing the canonical encoded binary
representations of their keys.[^no-need-for-by-value]
[^no-need-for-by-value]: There is no need to order by (key, value)
pair, since a `Dictionary` has no duplicate keys.
**Other kinds of `Value`.**
There are no special canonicalization restrictions on
`SignedInteger`s, `String`s, `ByteString`s, `Symbol`s, `Boolean`s,
`Double`s, `Record`s, `Sequence`s, or `Embedded`s. The
constraints given for these `Value`s in the [specification][spec]
suffice to ensure canonicity.
<!-- Heading to visually offset the footnotes from the main document: -->
## Notes

25
cheatsheet-plaintext.md Normal file
View File

@ -0,0 +1,25 @@
---
no_site_title: true
title: "Preserves Quick Reference (Plaintext)"
---
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
{{ site.version_date }}. Version {{ site.version }}.
These are non-normative "reference card" definitions. See also the normative [semantics]({{
site.baseurl }}/preserves.html), [text syntax specification]({{ site.baseurl
}}/preserves-text.html), and [machine-oriented syntax specification]({{ site.baseurl
}}/preserves-binary.html), and the experimental [P-expressions definition]({{ site.baseurl
}}/preserves-expressions.html).
## <a id="binary"></a>Machine-Oriented Binary Syntax
{% include cheatsheet-binary-plaintext.md %}
## <a id="text"></a>Human-Oriented Text Syntax
{% include cheatsheet-text-plaintext.md %}
## <a id="pexprs"></a>P-expression Syntax
{% include cheatsheet-pexprs-plaintext.md %}

25
cheatsheet.md Normal file
View File

@ -0,0 +1,25 @@
---
no_site_title: true
title: "Preserves Quick Reference"
---
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
{{ site.version_date }}. Version {{ site.version }}.
These are non-normative "reference card" definitions. See also the normative [semantics]({{
site.baseurl }}/preserves.html), [text syntax specification]({{ site.baseurl
}}/preserves-text.html), and [machine-oriented syntax specification]({{ site.baseurl
}}/preserves-binary.html), and the experimental [P-expressions definition]({{ site.baseurl
}}/preserves-expressions.html).
## <a id="binary"></a>Machine-Oriented Binary Syntax
{% include cheatsheet-binary.md %}
## <a id="text"></a>Human-Oriented Text Syntax
{% include cheatsheet-text.md %}
## <a id="pexprs"></a>P-expression Syntax
{% include cheatsheet-pexprs.md %}

219
conventions.md Normal file
View File

@ -0,0 +1,219 @@
---
title: "Conventions for Common Data Types"
---
The `Value` data type is essentially an S-Expression, able to
represent semi-structured data over `ByteString`, `String`,
`SignedInteger` atoms and so on.[^why-not-spki-sexps]
[^why-not-spki-sexps]: Rivest's S-Expressions are in many ways
similar to Preserves. However, while they include binary data and
sequences, and an obvious equivalence for them exists, they lack
numbers *per se* as well as any kind of unordered structure such
as sets or maps. In addition, while “display hints” allow
labelling of binary data with an intended interpretation, they
cannot be attached to any other kind of structure, and the “hint”
itself can only be a binary blob.
However, users need a wide variety of data types for representing
domain-specific values such as various kinds of encoded and normalized
text, calendrical values, machine words, and so on.
Appropriately-labelled `Record`s denote these domain-specific data
types.[^why-dictionaries]
[^why-dictionaries]: Given `Record`'s existence, it may seem odd
that `Dictionary`, `Set`, `Double`, etc. are given special
treatment. Preserves aims to offer a useful basic equivalence
predicate to programmers, and so if a data type demands a special
equivalence predicate, as `Dictionary`, `Set` and `Double` all do,
then the type should be included in the base language. Otherwise,
it can be represented as a `Record` and treated separately.
`Boolean`, `String` and `Symbol` are seeming exceptions. The first
two merit inclusion because of their cultural importance, while
`Symbol`s are included to allow their use as `Record` labels.
Primitive `Symbol` support avoids a bootstrapping issue.
All of these conventions are optional. They form a layer atop the core
`Value` structure. Non-domain-specific tools do not in general need to
treat them specially.
**Validity.** Many of the labels we will describe in this section come
with side-conditions on the contents of labelled `Record`s. It is
possible to construct an instance of `Value` that violates these
side-conditions without ceasing to be a `Value` or becoming
unrepresentable. However, we say that such a `Value` is *invalid*
because it fails to honour the necessary side-conditions.
Implementations *SHOULD* allow two modes of working: one which
treats all `Value`s identically, without regard for side-conditions,
and one which enforces validity (i.e. side-conditions) when reading,
writing, or constructing `Value`s.
## Metaconventions.
By and large `Capitalized` and `CamelCase` identifiers refer to *types* or *schema definition
names* describing families of `Value`s, while `kebab-case`, `lisp-style` identifiers are used
for concrete symbols appearing in e.g. `Record` labels.
## IOLists.
Inspired by Erlang's notions of
[`iolist()` and `iodata()`](http://erlang.org/doc/reference_manual/typespec.html),
an `IOList` is any tree constructed from `ByteString`s and
`Sequence`s. Formally, an `IOList` is either a `ByteString` or a
`Sequence` of `IOList`s.
`IOList`s can be useful for
[vectored I/O](https://en.wikipedia.org/wiki/Vectored_I/O).
Additionally, the flexibility of `IOList` trees allows annotation of
interior portions of a tree.
## Comments.
`String` values used as annotations are conventionally interpreted as
comments. Special syntax exists for such string annotations, though
the usual `@`-prefixed annotation notation can also be used.
# I am a comment for the Dictionary
{
# I am a comment for the key
key: # I am a comment for the value
value
}
# I am a comment for this entire IOList, as are the next three lines.
#
# The previous line (containing only hash-newline) adds an empty
# string to the annotations attached to the entire IOList.
[
#x"00010203"
# I am a comment for the middle half of the IOList
# A second comment for the same portion of the IOList
@ # I am the first and only comment for the following comment
"A third (itself commented!) comment for the same part of the IOList"
[
# I am a comment for the following ByteString
#x"04050607"
#x"08090A0B"
]
#x"0C0D0E0F"
]
## MIME-type tagged binary data.
Many internet protocols use
[media types](https://tools.ietf.org/html/rfc6838) (a.k.a MIME types)
to indicate the format of some associated binary data. For this
purpose, we define `MIMEData` to be a record labelled `mime` with two
fields, the first being a `Symbol`, the media type, and the second
being a `ByteString`, the binary data.
While each media type may define its own rules for comparing
documents, we define ordering among `MIMEData` *representations* of
such media types following the general rules for ordering of
`Record`s.
**Examples.**
<mime application/octet-stream #"abcde">
<mime text/plain #"ABC">
<mime application/xml #"<xhtml/>">
<mime text/csv #"123,234,345">
## Unicode normalization forms.
Unicode defines multiple
[normalization forms](http://unicode.org/reports/tr15/) for text.
While no particular normalization form is required for `String`s,
users may need to unambiguously signal or require a particular
normalization form. A `NormalizedString` is a `Record` labelled with
`unicode-normalization` and having two fields, the first of which is a
`Symbol` specifying the normalization form used (e.g. `nfc`, `nfd`,
`nfkc`, `nfkd`), and the second of which is a `String` whose
underlying Unicode scalar value sequence *MUST* be normalized according to
the named normalization form.
## IRIs (URIs, URLs, URNs, etc.).
An `IRI` is a `Record` labelled with `iri` and having one field, a
`String` which is the IRI itself and which *MUST* be a valid absolute
or relative IRI.
## Machine words.
The definition of `SignedInteger` captures all integers. However, in
certain circumstances it can be valuable to assert that a number
inhabits a particular range, such as a fixed-width machine word.
A family of labels `i`*n* and `u`*n* for *n* ∈ {8,16,32,64,128} denote
*n*-bit-wide signed and unsigned range restrictions, respectively.
Records with these labels *MUST* have one field, a `SignedInteger`,
which *MUST* fall within the appropriate range. That is, to be valid,
- in `<i8 `*x*`>`, -128 <= *x* <= 127.
- in `<u8 `*x*`>`, 0 <= *x* <= 255.
- in `<i16 `*x*`>`, -32768 <= *x* <= 32767.
- etc.
## Anonymous Tuples and Unit.
A `Tuple` is a `Record` with label `tuple` and zero or more fields,
denoting an anonymous tuple of values.
The 0-ary tuple, `<tuple>`, denotes the empty tuple, sometimes called
“unit” or “void” (but *not* e.g. JavaScript's “undefined” value).
## Null and Undefined.
Tony Hoare's
“[billion-dollar mistake](https://en.wikipedia.org/wiki/Tony_Hoare#Apologies_and_retractions)”
can be represented with the 0-ary `Record` `<null>`. An “undefined”
value can be represented as `<undefined>`.
## Dates and Times.
Dates, times, moments, and timestamps can be represented with a
`Record` with label `rfc3339` having a single field, a `String`, which
*MUST* conform to one of the `full-date`, `partial-time`, `full-time`,
or `date-time` productions of [section 5.6 of RFC
3339](https://tools.ietf.org/html/rfc3339#section-5.6). (In
`date-time`, "T" and "Z" *MUST* be upper-case and "T" *MUST* be used;
a space separating the `full-date` and `full-time` *MUST NOT* be
used.)
## XML Infoset
[XML Infoset](https://www.w3.org/TR/2004/REC-xml-infoset-20040204/)
describes the semantics of XML - that is, the underlying information
contained in a document, independent of surface syntax.
A useful subset of XML Infoset, namely its Element Information Items
(omitting processing instructions, entities, entity references,
comments, namespaces, name prefixes, and base URIs), can be captured
with the [schema](preserves-schema.html)
Node = Text / Element .
Text = string .
Element =
/ @withAttributes
<<rec> @localName symbol [@attributes Attributes @children Node ...]>
/ @withoutAttributes
<<rec> @localName symbol @children [Node ...]> .
Attributes = { symbol: string ...:... } .
**Examples.**
<html
<h1 {class: "title"} "Hello World!">
<p
"I could swear I've seen markup like this somewhere before. "
"Perhaps it was "
<a {href: "https://docs.racket-lang.org/search/index.html?q=xexpr%3F"} "here">
"?"
>
<table
<tr <th> <th "Column 1"> <th "Column 2">>
<tr <th "Row 1"> <td 123> <td 234>>>
>
<!-- Heading to visually offset the footnotes from the main document: -->
## Notes

BIN
craiyon_111353.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 506 KiB

10
doc/demo.prs Normal file
View File

@ -0,0 +1,10 @@
version 1 .
JSON =
/ @string string
/ @integer int
/ @double double
/ @boolean JSONBoolean
/ @null =null
/ @array [JSON ...]
/ @object { string: JSON ...:... } .
JSONBoolean = =true / =false .

View File

@ -0,0 +1,46 @@
---
title: preserves-schema-rkt
---
The `preserves-schema-rkt` program reads
[Preserves Schema](../preserves-schema.html) DSL input files. For each
input file, it produces a Racket source file of the same name but
with `.rkt` in place of `.prs`.
Instead of using this tool, you may prefer to use `#lang
preserves-schema` to use Schema DSL syntax in an ordinary Racket
module source file.
## Installation
Install Racket. Then, `raco pkg install preserves`.
## Usage
usage: preserves-schema-rkt [ <option> ... ] [<input-glob>] ...
<option> is one of
--output <directory>
Output directory for modules (default: next to sources)
--stdout
Prints each module to stdout one after the other instead of writing them to files in the `--output` directory
--no-write-files
Disables generation of output to the filesystem
--base <directory>
Base directory for sources (default: common prefix)
* --module <namespace=path>
Additional Namespace=path import
* --plugin-lib <lib-path>, -l <lib-path>
Load compiler plugin library
* --plugin-file <rkt-file-path>, -f <rkt-file-path>
Load compiler plugin source file
--help, -h
Show this help
--
Do not treat any remaining argument as a switch (at this level)
* Asterisks indicate options allowed multiple times.
Multiple single-letter switches can be combined after
one `-`. For example, `-h-` is the same as `-h --`.

View File

@ -0,0 +1,74 @@
---
title: preserves-schema-rs
---
The `preserves-schema-rs` program reads
[Preserves Schema](../preserves-schema.html) AST input files (such as
are produced by [`preserves-schemac`]({% link doc/preserves-schemac.md
%})). It produces a collection of Rust source files providing parsers,
unparsers, and Rust data structures reflecting the definitions in the
inputs.
## Using the compiler from `build.rs` instead
You will usually not need to use the `preserves-schema-rs`
command-line program. Instead, access the preserves-schema compiler
API from your `build.rs`. The following example is taken from
[`build.rs` for the `preserves-path` crate](https://gitlab.com/preserves/preserves/-/blob/af5de5b836ffc51999db93797d1995ff677cf6f8/implementations/rust/preserves-path/build.rs):
use preserves_schema::compiler::*;
use std::io::Error;
use std::path::PathBuf;
fn main() -> Result<(), Error> {
let buildroot = PathBuf::from(std::env::var_os("OUT_DIR").unwrap());
let mut gen_dir = buildroot.clone();
gen_dir.push("src/schemas");
let mut c = CompilerConfig::new(gen_dir, "crate::schemas".to_owned());
let inputs = expand_inputs(&vec!["path.bin".to_owned()])?;
c.load_schemas_and_bundles(&inputs, &vec![])?;
compile(&c)
}
This approach also requires an `include!` from your main, hand-written
source tree. The following is a snippet from
[`preserves-path/src/lib.rs`](https://gitlab.com/preserves/preserves/-/blob/af5de5b836ffc51999db93797d1995ff677cf6f8/implementations/rust/preserves-path/src/lib.rs):
pub mod schemas {
include!(concat!(env!("OUT_DIR"), "/src/schemas/mod.rs"));
}
## Installation
The tool is
[written in Rust](https://crates.io/crates/preserves-schema).
[Install `cargo`.](https://doc.rust-lang.org/cargo/getting-started/installation.html)
Then, `cargo install preserves-schema`.
## Usage
preserves-schema 3.990.2
USAGE:
preserves-schema-rs [FLAGS] [OPTIONS] --output-dir <output-dir> --prefix <prefix>
[--] [input-glob]...
FLAGS:
-h, --help Prints help information
--rustfmt-skip
-V, --version Prints version information
OPTIONS:
--module <module>...
-o, --output-dir <output-dir>
-p, --prefix <prefix>
--support-crate <support-crate>
--xref <xref>...
ARGS:
<input-glob>...

View File

@ -0,0 +1,34 @@
---
title: preserves-schema-ts
---
The `preserves-schema-ts` program reads
[Preserves Schema](../preserves-schema.html) DSL input files. For each
input file, it produces a TypeScript source file of the same name but
with `.ts` in place of `.prs`.
## Installation
Install node.js v12 or newer. Then, `yarn global add @preserves/schema`.
## Usage
Usage: preserves-schema-ts [options] [input...]
Compile Preserves schema definitions to TypeScript
Arguments:
input Input directory, optionally with :<glob> on the end
Options:
--xref <glob> Cross-reference other textual Preserves schema definitions
(default: [])
--output <directory> Output directory for modules
--stdout Prints each module to stdout one after the other instead
of writing them to files in the `--output` directory
--core <path> Import path for @preserves/core
(default: "@preserves/core")
--watch Watch base directory for changes
--traceback Include stack traces in compiler errors
--module <namespace=path> Additional Namespace=path import (default: [])
-h, --help display help for command

135
doc/preserves-schemac.md Normal file
View File

@ -0,0 +1,135 @@
---
title: preserves-schemac
---
The `preserves-schemac` program reads
[Preserves Schema](../preserves-schema.html) DSL input files and
outputs a binary-syntax Preserves document conforming to the
[metaschema](https://gitlab.com/preserves/preserves/-/blob/main/schema/schema.prs).
It can either output single `Schema` records (corresponding to a
single input file), or a `Bundle` of `Schema`s (corresponding to a
directory tree of files).
## Installation
Install node.js v12 or newer. Then, `yarn global add @preserves/schema`.
## Usage
Usage: preserves-schemac [options] [input...]
Compile textual Preserves schema definitions to binary format
Arguments:
input Input directory, with optional ":glob" appended (defaults to ":**/*.prs")
Options:
--no-bundle Emit a single Schema instead of a schema Bundle
-h, --help display help for command
## Examples
### Single file (non-bundle)
Given a file [`demo.prs`](demo.prs) containing:
version 1 .
JSON =
/ @string string
/ @integer int
/ @double double
/ @boolean JSONBoolean
/ @null =null
/ @array [JSON ...]
/ @object { string: JSON ...:... } .
JSONBoolean = =true / =false .
running the following:
preserves-schemac --no-bundle .:demo.prs
will produce the following binary file on `stdout`:
00000000: b4b3 0673 6368 656d 61b7 b307 7665 7273 ...schema...vers
00000010: 696f 6e91 b30b 6465 6669 6e69 7469 6f6e ion...definition
00000020: 73b7 b304 4a53 4f4e b4b3 026f 72b5 b5b1 s...JSON...or...
00000030: 0673 7472 696e 67b4 b304 6174 6f6d b306 .string...atom..
00000040: 5374 7269 6e67 8484 b5b1 0769 6e74 6567 String.....integ
00000050: 6572 b4b3 0461 746f 6db3 0d53 6967 6e65 er...atom..Signe
00000060: 6449 6e74 6567 6572 8484 b5b1 0664 6f75 dInteger.....dou
00000070: 626c 65b4 b304 6174 6f6d b306 446f 7562 ble...atom..Doub
00000080: 6c65 8484 b5b1 0762 6f6f 6c65 616e b4b3 le.....boolean..
00000090: 0372 6566 b584 b30b 4a53 4f4e 426f 6f6c .ref....JSONBool
000000a0: 6561 6e84 84b5 b104 6e75 6c6c b4b3 036c ean.....null...l
000000b0: 6974 b304 6e75 6c6c 8484 b5b1 0561 7272 it..null.....arr
000000c0: 6179 b4b3 0573 6571 6f66 b4b3 0372 6566 ay...seqof...ref
000000d0: b584 b304 4a53 4f4e 8484 84b5 b106 6f62 ....JSON......ob
000000e0: 6a65 6374 b4b3 0664 6963 746f 66b4 b304 ject...dictof...
000000f0: 6174 6f6d b306 5374 7269 6e67 84b4 b303 atom..String....
00000100: 7265 66b5 84b3 044a 534f 4e84 8484 8484 ref....JSON.....
00000110: b30b 4a53 4f4e 426f 6f6c 6561 6eb4 b302 ..JSONBoolean...
00000120: 6f72 b5b5 b104 7472 7565 b4b3 036c 6974 or....true...lit
00000130: b304 7472 7565 8484 b5b1 0566 616c 7365 ..true.....false
00000140: b4b3 036c 6974 b305 6661 6c73 6584 8484 ...lit..false...
00000150: 8484 b30c 656d 6265 6464 6564 5479 7065 ....embeddedType
00000160: 8084 84 ...
Piping the output to [`preserves-tool`](./preserves-tool.html) to
pretty-print it produces:
<schema {
version: 1,
embeddedType: #f,
definitions: {
JSONBoolean: <or [
[
"true",
<lit true>
],
[
"false",
<lit false>
]
]>,
JSON: <or [
[
"string",
<atom String>
],
[
"integer",
<atom SignedInteger>
],
[
"double",
<atom Double>
],
[
"boolean",
<ref [] JSONBoolean>
],
[
"null",
<lit null>
],
[
"array",
<seqof <ref [] JSON>>
],
[
"object",
<dictof <atom String> <ref [] JSON>>
]
]>
}
}>
### Multiple file (bundle)
Given a directory tree containing multiple `*.prs` files, running
preserves-schemac .
will produce a binary `Bundle` on `stdout` containing one `Schema` for
each input file in the tree.

196
doc/preserves-tool.md Normal file
View File

@ -0,0 +1,196 @@
---
title: preserves-tool
---
The `preserves-tool` program is a swiss army knife for working with
Preserves documents.
```
preserves-tool 4.992.0
Swiss-army knife tool for working with Preserves data.
See https://preserves.dev/. If no subcommand is specified, the default
subcommand will be `convert`.
USAGE:
preserves-tool [OPTIONS]
preserves-tool <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-V, --version Print version information
OPTIONS FOR DEFAULT SUBCOMMAND convert:
[...]
SUBCOMMANDS:
completions
convert
help Print this message or the help of the given subcommand(s)
quote
```
## Installation
The tool is
[written in Rust](https://crates.io/crates/preserves-tools).
[Install `cargo`.](https://doc.rust-lang.org/cargo/getting-started/installation.html)
Then, `cargo install preserves-tools`.
## Subcommands
The tool includes three subcommands.
### `preserves-tool convert`, `preserves-tool`
This is the main tool, and is also the default if no subcommand is
explicitly specified. It can
- translate between the various Preserves text and binary document
syntaxes;
- strip annotations;
- pretty-print; and
- break down and filter documents using [preserves path](..{% link preserves-path.md %}) selectors.
#### Usage
preserves-tool-convert
USAGE:
preserves-tool convert [FLAGS] [OPTIONS]
OPTIONS:
--bundle <filename>
-c, --commas <COMMAS>
[default: none] [possible values: none, separating, terminating]
--collect
--escape-spaces
-h, --help
Print help information
-i, --input-format <INPUT_FORMAT>
[default: auto-detect] [possible values: auto-detect, text, binary]
--indent <on/off>
[default: on] [possible values: disabled, enabled]
--limit <LIMIT>
-o, --output-format <OUTPUT_FORMAT>
[default: text] [possible values: text, binary, unquoted]
--read-annotations <on/off>
[default: on] [possible values: disabled, enabled]
--select <SELECT_EXPR>
[default: *]
--select-output <SELECT_OUTPUT>
[default: sequence] [possible values: sequence, set]
--write-annotations <on/off>
[default: on] [possible values: disabled, enabled]
### `preserves-tool quote`
This subcommand reads chunks from standard input and outputs each one
as a Preserves `String`, `Symbol`, or `ByteString` using either the
text or binary Preserves surface syntax.
This is useful when writing shell scripts that interact with other
programs using Preserves as an interchange format.
It defaults to taking the entirety of standard input as a single large
chunk, but it can also work with newline- or `nul`-delimited chunks.
#### Usage
```
preserves-tool-quote
USAGE:
preserves-tool quote [OPTIONS] <SUBCOMMAND>
OPTIONS:
-h, --help Print help information
-o, --output-format <OUTPUT_FORMAT> [default: text] [possible values: text,
binary, unquoted]
SUBCOMMANDS:
byte-string
help Print this message or the help of the given subcommand(s)
string
symbol
```
```
preserves-tool-quote-string
USAGE:
preserves-tool quote string [OPTIONS]
OPTIONS:
--escape-spaces
-h, --help Print help information
--include-terminator
--input-terminator <INPUT_TERMINATOR> [default: eof] [possible values:
eof, newline, nul]
```
```
preserves-tool-quote-symbol
USAGE:
preserves-tool quote symbol [OPTIONS]
OPTIONS:
--escape-spaces
-h, --help Print help information
--include-terminator
--input-terminator <INPUT_TERMINATOR> [default: eof] [possible values:
eof, newline, nul]
```
```
preserves-tool-quote-byte-string
USAGE:
preserves-tool quote byte-string
OPTIONS:
-h, --help Print help information
```
### `preserves-tool completions`
This subcommand outputs Bash completion code to stdout, for sourcing
at shell startup time.
#### Usage
Add the following to your `.profile` or similar:
eval "$(preserves-tool completions bash 2>/dev/null)"
Multiple shell dialects are supported (courtesy of
[`clap`](https://crates.io/crates/clap)):
```
preserves-tool-completions
USAGE:
preserves-tool completions <SHELL>
ARGS:
<SHELL> [possible values: bash, elvish, fish, powershell, zsh]
OPTIONS:
-h, --help Print help information
```

10
doc/schema-tools.md Normal file
View File

@ -0,0 +1,10 @@
---
title: Tools for working with Preserves Schema
---
A number of tools for working with [Preserves Schema](..{% link preserves-schema.md %}) exist:
- [preserves-schemac](preserves-schemac.html), generic Schema reader and linter
- [preserves-schema-rkt](preserves-schema-rkt.html), Racket code generator
- [preserves-schema-rs](preserves-schema-rs.html), Rust code generator
- [preserves-schema-ts](preserves-schema-ts.html), TypeScript code generator

42
git-hooks/pre-commit Executable file
View File

@ -0,0 +1,42 @@
#!/bin/sh
set -e
exec 1>&2
failed=
cmp_and_fail() {
if ! cmp "$1" "$2"
then
failed=failed
fi
}
COMMAND=cmp_and_fail
if [ "$1" = "--fix" ];
then
COMMAND=cp
fi
# https://gitlab.com/preserves/preserves/-/issues/30
#
# So it turns out that Racket's git-checkout mechanism pays attention
# to portions of the tree outside the package of interest, which is
# totally fair enough!
#
# But it means we can't use updir-containing symlinks anywhere in the
# repository if we want to have a Racket-installable package as well,
# *even if* the non-Racket implementation concerned is OK with
# updir-containing symlinks.
# Ensure that various copies of schema.prs, schema.bin, path.bin,
# samples.pr and samples.bin are in fact identical.
${COMMAND} path/path.bin implementations/python/preserves/path.prb
${COMMAND} schema/schema.bin implementations/python/preserves/schema.prb
${COMMAND} schema/schema.prs implementations/racket/preserves/preserves-schema/schema.prs
${COMMAND} tests/samples.bin implementations/python/tests/samples.bin
${COMMAND} tests/samples.pr implementations/python/tests/samples.pr
${COMMAND} tests/samples.pr implementations/racket/preserves/preserves/tests/samples.pr
[ -z "$failed" ]

1
gitlab-logo-200.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 990 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}.cls-4{fill:#fff;}</style></defs><g id="LOGO"><path class="cls-1" d="M302,174.37l-.21-.56-21.2-55.3a5.5,5.5,0,0,0-2.18-2.63,5.6,5.6,0,0,0-8.41,3.2l-14.31,43.81H197.74l-14.31-43.81a5.61,5.61,0,0,0-8.41-3.2,5.5,5.5,0,0,0-2.18,2.63l-21.19,55.31-.22.55a39.36,39.36,0,0,0,13.06,45.49l.08.06.18.14L197,244.23l16,12.09,9.72,7.35a6.57,6.57,0,0,0,7.92,0l9.72-7.35,16-12.09,32.48-24.31.09-.07A39.36,39.36,0,0,0,302,174.37Z"/><path class="cls-2" d="M302,174.37l-.21-.56a71.5,71.5,0,0,0-28.5,12.82l-46.55,35.2,29.64,22.4,32.48-24.31.09-.07A39.36,39.36,0,0,0,302,174.37Z"/><path class="cls-3" d="M197,244.23l16,12.09,9.72,7.35a6.57,6.57,0,0,0,7.92,0l9.72-7.35,16-12.09-29.64-22.4Z"/><path class="cls-2" d="M180.14,186.63a71.44,71.44,0,0,0-28.49-12.81l-.22.55a39.36,39.36,0,0,0,13.06,45.49l.08.06.18.14L197,244.23l29.66-22.4Z"/><path class="cls-4" d="M428.92,171.51H451.7c-3.8-24.22-24.77-41.09-52.06-41.09-32.29,0-56.52,23.74-56.52,63.5,0,39.05,23.14,63.27,57.18,63.27,30.55,0,52.42-19.65,52.42-51.46V190.91H402.65v17.47h28.44c-.36,17.6-12.11,28.74-30.67,28.74-20.66,0-34.82-15.48-34.82-43.44,0-27.78,14.4-43.2,34.34-43.2C414.82,150.48,425,158.43,428.92,171.51Z"/><path class="cls-4" d="M467.78,255.5h21.81V163H467.78Zm11-107.2c6.93,0,12.59-5.31,12.59-11.81s-5.66-11.87-12.59-11.87-12.65,5.3-12.65,11.87S471.75,148.3,478.74,148.3Z"/><path class="cls-4" d="M554.9,163H536.64V140.78H514.83V163H501.7v16.87h13.13v51.46c-.12,17.41,12.54,26,28.92,25.49a44.29,44.29,0,0,0,12.84-2.17l-3.68-17.06a26.57,26.57,0,0,1-6.38.85c-5.49,0-9.89-1.93-9.89-10.73V179.82H554.9Z"/><path class="cls-4" d="M571.78,255.5h76.7V236.76H594.14V132.1H571.78Z"/><path class="cls-4" d="M690.26,257.37c14.52,0,23.19-6.81,27.17-14.58h.72V255.5h21V193.56c0-24.46-19.94-31.81-37.6-31.81-19.46,0-34.4,8.67-39.22,25.54l20.37,2.9c2.16-6.33,8.31-11.75,19-11.75,10.13,0,15.67,5.18,15.67,14.28v.36c0,6.26-6.57,6.57-22.9,8.31-17.95,1.93-35.12,7.29-35.12,28.14C659.29,247.73,672.6,257.37,690.26,257.37Zm7.17-16c-9.1,0-15.61-4.16-15.61-12.17,0-8.38,7.29-11.87,17-13.26,5.73-.78,17.18-2.23,20-4.51v10.9C718.88,232.6,710.56,241.34,697.43,241.34Z"/><path class="cls-4" d="M755.21,255.5h21.45V240.92h1.26c3.44,6.75,10.61,16.21,26.52,16.21,21.81,0,38.14-17.3,38.14-47.78,0-30.85-16.81-47.6-38.2-47.6-16.33,0-23.14,9.82-26.46,16.51H777V132.1H755.21Zm21.39-46.27c0-18,7.71-29.59,21.75-29.59,14.52,0,22,12.35,22,29.59s-7.59,30-22,30C784.43,239.23,776.6,227.18,776.6,209.23Z"/></g></svg>

After

Width:  |  Height:  |  Size: 2.5 KiB

1
gitlab-logo-500.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#e24329;}.cls-2{fill:#fc6d26;}.cls-3{fill:#fca326;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-2" d="M282.83,170.73l-.27-.69a88.3,88.3,0,0,0-35.15,15.8L190,229.25c19.55,14.79,36.57,27.64,36.57,27.64l40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/><path class="cls-3" d="M153.43,256.89l19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91S209.55,244,190,229.25C170.45,244,153.43,256.89,153.43,256.89Z"/><path class="cls-2" d="M132.58,185.84A88.19,88.19,0,0,0,97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82s17-12.85,36.57-27.64Z"/></g></svg>

After

Width:  |  Height:  |  Size: 1.0 KiB

1
gitlab-logo-700.svg Normal file
View File

@ -0,0 +1 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 380 380"><defs><style>.cls-1{fill:#fff;}</style></defs><g id="LOGO"><path class="cls-1" d="M282.83,170.73l-.27-.69-26.14-68.22a6.81,6.81,0,0,0-2.69-3.24,7,7,0,0,0-8,.43,7,7,0,0,0-2.32,3.52l-17.65,54H154.29l-17.65-54A6.86,6.86,0,0,0,134.32,99a7,7,0,0,0-8-.43,6.87,6.87,0,0,0-2.69,3.24L97.44,170l-.26.69a48.54,48.54,0,0,0,16.1,56.1l.09.07.24.17,39.82,29.82,19.7,14.91,12,9.06a8.07,8.07,0,0,0,9.76,0l12-9.06,19.7-14.91,40.06-30,.1-.08A48.56,48.56,0,0,0,282.83,170.73Z"/></g></svg>

After

Width:  |  Height:  |  Size: 530 B

View File

@ -13,7 +13,7 @@ h2 { border-bottom: solid black 1px; }
# SPKI CAT: SPKI S-Expressions with Canonical Atom Tags
Tony Garnock-Jones <tonyg@leastfixedpoint.com>
Christopher Lemmer Webber <cwebber@dustycloud.org>
Christine Lemmer Webber <cwebber@dustycloud.org>
May 2018
Version 0.0.1

21
implementations/README.md Normal file
View File

@ -0,0 +1,21 @@
# Preserves Implementations
Here you may find:
- [dhall](dhall/), functions for converting Dhall values to a corresponding
subset of Preserves.
- [javascript](javascript/), an implementation in TypeScript,
compiling to JavaScript, for node.js and the Browser.
- [python](python/), an implementation for Python 2.x and 3.x.
- [racket](racket/), an implementation for Racket 7.x and newer
(though older Rackets may also work with it).
Other implementations are also available:
- [Preserves for Rust](https://gitlab.com/preserves/preserves-rs/), an implementation for Rust
that interoperates with serde.
- [Preserves for Squeak Smalltalk](https://squeaksource.com/Preserves.html)

2
implementations/c/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
m.output.txt
m

View File

@ -0,0 +1,8 @@
m: main.c preserves.h
gcc -Wall -Wextra -Werror -g3 -o $@ main.c
go: m
cat ../../tests/samples.bin | ./m | tee m.output.txt
clean:
rm -f m

96
implementations/c/main.c Normal file
View File

@ -0,0 +1,96 @@
#include <stdlib.h>
#include <stdio.h>
#include <stdint.h>
#include <stdbool.h>
#include <sys/time.h>
#define PRESERVES_IMPLEMENTATION
#include "preserves.h"
static double now() {
struct timeval tv;
if (gettimeofday(&tv, NULL) < 0) {
perror("gettimeofday");
}
return (double) tv.tv_sec + ((double) tv.tv_usec) / 1000000.0;
}
int main(__attribute__ ((unused)) int argc,
__attribute__ ((unused)) char const * const argv[])
{
preserves_bytes_t input = preserves_create_bytes();
bool silent = getenv("SILENT") != NULL;
double start = now();
{
preserves_bytes_t chunk = preserves_create_bytes();
if (preserves_resize_bytes(&chunk, 131072) == -1) {
perror("allocating chunk");
return EXIT_FAILURE;
}
while (true) {
size_t count = fread(chunk.ptr, 1, chunk.len, stdin);
if (count == 0) {
if (ferror(stdin)) {
perror("reading");
return EXIT_FAILURE;
}
break;
}
if (preserves_extend_bytes(&input, preserves_bytes_subsequence(&chunk, 0, count)) == -1) {
perror("appending");
return EXIT_FAILURE;
}
}
preserves_free_bytes(&chunk);
}
double mid = now();
{
preserves_reader_t reader = preserves_create_reader();
preserves_reader_result_t result = preserves_read_binary(&reader, &input, 1);
more_input:
if (result.index == NULL) {
perror("parsing");
return EXIT_FAILURE;
}
if (!silent) {
printf("Size of index: %lu bytes; %lu entries\n",
reader.index_pos * sizeof(preserves_index_entry_t),
reader.index_pos);
}
if (!silent) {
for (preserves_index_entry_t *i = result.index; i != result.end_marker; i++) {
preserves_dump_index_entry(stdout, &reader.input, i, true);
}
preserves_dump_index_entry(stdout, &reader.input, result.end_marker, true);
}
if (result.end_marker->data._err == PRESERVES_END_MORE_INPUT_REMAINING) {
if (!silent) {
printf("\n");
}
reader.index_pos = 0;
result = preserves_read_binary_continue(&reader, 1);
goto more_input;
}
preserves_free_reader(&reader);
}
double end = now();
printf("stage 1: %g s\n", mid - start);
printf("stage 2: %g s\n", end - mid);
printf("total: %g s\n", end - start);
preserves_free_bytes(&input);
return EXIT_SUCCESS;
}

File diff suppressed because it is too large Load Diff

5
implementations/cpp/.gitignore vendored Normal file
View File

@ -0,0 +1,5 @@
m.output.txt
m
.vscode
*.o
/googletest.a

View File

@ -0,0 +1,28 @@
CXX=g++ -std=c++14 -Wall -Wextra -Werror -g -O0 -I googletest
HEADERS=$(wildcard preserves*.hpp)
test: all
./m
all: m
m: main.cpp $(HEADERS) googletest.a
$(CXX) -o $@ main.cpp googletest.a
googletest.a: googletest/src/gtest-all.o googletest/src/gtest_main.o
ar r $@ $^
ranlib $@
%.o: %.cc
$(CXX) -c $< -o $@
%.o: %.cpp
$(CXX) -c $< -o $@
clean:
rm -f m
rm -f *.o
veryclean: clean
rm -f googletest.a
rm -f googletest/src/*.o

View File

@ -0,0 +1,37 @@
Include files and library source code from the GoogleTest library,
`googletest-1.13.0`, <https://github.com/google/googletest>.
The GoogleTest library is licensed as follows (from the
[LICENSE](https://github.com/google/googletest/blob/main/LICENSE) file
in the repository):
```
Copyright 2008, Google Inc.
All rights reserved.
Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are
met:
* Redistributions of source code must retain the above copyright
notice, this list of conditions and the following disclaimer.
* Redistributions in binary form must reproduce the above
copyright notice, this list of conditions and the following disclaimer
in the documentation and/or other materials provided with the
distribution.
* Neither the name of Google Inc. nor the names of its
contributors may be used to endorse or promote products derived from
this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
```

View File

@ -0,0 +1,237 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This file implements the AssertionResult type.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_
#include <memory>
#include <ostream>
#include <string>
#include <type_traits>
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-port.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing {
// A class for indicating whether an assertion was successful. When
// the assertion wasn't successful, the AssertionResult object
// remembers a non-empty message that describes how it failed.
//
// To create an instance of this class, use one of the factory functions
// (AssertionSuccess() and AssertionFailure()).
//
// This class is useful for two purposes:
// 1. Defining predicate functions to be used with Boolean test assertions
// EXPECT_TRUE/EXPECT_FALSE and their ASSERT_ counterparts
// 2. Defining predicate-format functions to be
// used with predicate assertions (ASSERT_PRED_FORMAT*, etc).
//
// For example, if you define IsEven predicate:
//
// testing::AssertionResult IsEven(int n) {
// if ((n % 2) == 0)
// return testing::AssertionSuccess();
// else
// return testing::AssertionFailure() << n << " is odd";
// }
//
// Then the failed expectation EXPECT_TRUE(IsEven(Fib(5)))
// will print the message
//
// Value of: IsEven(Fib(5))
// Actual: false (5 is odd)
// Expected: true
//
// instead of a more opaque
//
// Value of: IsEven(Fib(5))
// Actual: false
// Expected: true
//
// in case IsEven is a simple Boolean predicate.
//
// If you expect your predicate to be reused and want to support informative
// messages in EXPECT_FALSE and ASSERT_FALSE (negative assertions show up
// about half as often as positive ones in our tests), supply messages for
// both success and failure cases:
//
// testing::AssertionResult IsEven(int n) {
// if ((n % 2) == 0)
// return testing::AssertionSuccess() << n << " is even";
// else
// return testing::AssertionFailure() << n << " is odd";
// }
//
// Then a statement EXPECT_FALSE(IsEven(Fib(6))) will print
//
// Value of: IsEven(Fib(6))
// Actual: true (8 is even)
// Expected: false
//
// NB: Predicates that support negative Boolean assertions have reduced
// performance in positive ones so be careful not to use them in tests
// that have lots (tens of thousands) of positive Boolean assertions.
//
// To use this class with EXPECT_PRED_FORMAT assertions such as:
//
// // Verifies that Foo() returns an even number.
// EXPECT_PRED_FORMAT1(IsEven, Foo());
//
// you need to define:
//
// testing::AssertionResult IsEven(const char* expr, int n) {
// if ((n % 2) == 0)
// return testing::AssertionSuccess();
// else
// return testing::AssertionFailure()
// << "Expected: " << expr << " is even\n Actual: it's " << n;
// }
//
// If Foo() returns 5, you will see the following message:
//
// Expected: Foo() is even
// Actual: it's 5
//
class GTEST_API_ AssertionResult {
public:
// Copy constructor.
// Used in EXPECT_TRUE/FALSE(assertion_result).
AssertionResult(const AssertionResult& other);
// C4800 is a level 3 warning in Visual Studio 2015 and earlier.
// This warning is not emitted in Visual Studio 2017.
// This warning is off by default starting in Visual Studio 2019 but can be
// enabled with command-line options.
#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4800 /* forcing value to bool */)
#endif
// Used in the EXPECT_TRUE/FALSE(bool_expression).
//
// T must be contextually convertible to bool.
//
// The second parameter prevents this overload from being considered if
// the argument is implicitly convertible to AssertionResult. In that case
// we want AssertionResult's copy constructor to be used.
template <typename T>
explicit AssertionResult(
const T& success,
typename std::enable_if<
!std::is_convertible<T, AssertionResult>::value>::type*
/*enabler*/
= nullptr)
: success_(success) {}
#if defined(_MSC_VER) && (_MSC_VER < 1910 || _MSC_VER >= 1920)
GTEST_DISABLE_MSC_WARNINGS_POP_()
#endif
// Assignment operator.
AssertionResult& operator=(AssertionResult other) {
swap(other);
return *this;
}
// Returns true if and only if the assertion succeeded.
operator bool() const { return success_; } // NOLINT
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
AssertionResult operator!() const;
// Returns the text streamed into this AssertionResult. Test assertions
// use it when they fail (i.e., the predicate's outcome doesn't match the
// assertion's expectation). When nothing has been streamed into the
// object, returns an empty string.
const char* message() const {
return message_.get() != nullptr ? message_->c_str() : "";
}
// Deprecated; please use message() instead.
const char* failure_message() const { return message(); }
// Streams a custom failure message into this object.
template <typename T>
AssertionResult& operator<<(const T& value) {
AppendMessage(Message() << value);
return *this;
}
// Allows streaming basic output manipulators such as endl or flush into
// this object.
AssertionResult& operator<<(
::std::ostream& (*basic_manipulator)(::std::ostream& stream)) {
AppendMessage(Message() << basic_manipulator);
return *this;
}
private:
// Appends the contents of message to message_.
void AppendMessage(const Message& a_message) {
if (message_.get() == nullptr) message_.reset(new ::std::string);
message_->append(a_message.GetString().c_str());
}
// Swap the contents of this AssertionResult with other.
void swap(AssertionResult& other);
// Stores result of the assertion predicate.
bool success_;
// Stores the message describing the condition in case the expectation
// construct is not satisfied with the predicate's outcome.
// Referenced via a pointer to avoid taking too much stack frame space
// with test assertions.
std::unique_ptr< ::std::string> message_;
};
// Makes a successful assertion result.
GTEST_API_ AssertionResult AssertionSuccess();
// Makes a failed assertion result.
GTEST_API_ AssertionResult AssertionFailure();
// Makes a failed assertion result with the given failure message.
// Deprecated; use AssertionFailure() << msg.
GTEST_API_ AssertionResult AssertionFailure(const Message& msg);
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_ASSERTION_RESULT_H_

View File

@ -0,0 +1,345 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the public API for death tests. It is
// #included by gtest.h so a user doesn't need to include this
// directly.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_
#include "gtest/internal/gtest-death-test-internal.h"
// This flag controls the style of death tests. Valid values are "threadsafe",
// meaning that the death test child process will re-execute the test binary
// from the start, running only a single death test, or "fast",
// meaning that the child process will execute the test logic immediately
// after forking.
GTEST_DECLARE_string_(death_test_style);
namespace testing {
#if GTEST_HAS_DEATH_TEST
namespace internal {
// Returns a Boolean value indicating whether the caller is currently
// executing in the context of the death test child process. Tools such as
// Valgrind heap checkers may need this to modify their behavior in death
// tests. IMPORTANT: This is an internal utility. Using it may break the
// implementation of death tests. User code MUST NOT use it.
GTEST_API_ bool InDeathTestChild();
} // namespace internal
// The following macros are useful for writing death tests.
// Here's what happens when an ASSERT_DEATH* or EXPECT_DEATH* is
// executed:
//
// 1. It generates a warning if there is more than one active
// thread. This is because it's safe to fork() or clone() only
// when there is a single thread.
//
// 2. The parent process clone()s a sub-process and runs the death
// test in it; the sub-process exits with code 0 at the end of the
// death test, if it hasn't exited already.
//
// 3. The parent process waits for the sub-process to terminate.
//
// 4. The parent process checks the exit code and error message of
// the sub-process.
//
// Examples:
//
// ASSERT_DEATH(server.SendMessage(56, "Hello"), "Invalid port number");
// for (int i = 0; i < 5; i++) {
// EXPECT_DEATH(server.ProcessRequest(i),
// "Invalid request .* in ProcessRequest()")
// << "Failed to die on request " << i;
// }
//
// ASSERT_EXIT(server.ExitNow(), ::testing::ExitedWithCode(0), "Exiting");
//
// bool KilledBySIGHUP(int exit_code) {
// return WIFSIGNALED(exit_code) && WTERMSIG(exit_code) == SIGHUP;
// }
//
// ASSERT_EXIT(client.HangUpServer(), KilledBySIGHUP, "Hanging up!");
//
// The final parameter to each of these macros is a matcher applied to any data
// the sub-process wrote to stderr. For compatibility with existing tests, a
// bare string is interpreted as a regular expression matcher.
//
// On the regular expressions used in death tests:
//
// On POSIX-compliant systems (*nix), we use the <regex.h> library,
// which uses the POSIX extended regex syntax.
//
// On other platforms (e.g. Windows or Mac), we only support a simple regex
// syntax implemented as part of Google Test. This limited
// implementation should be enough most of the time when writing
// death tests; though it lacks many features you can find in PCRE
// or POSIX extended regex syntax. For example, we don't support
// union ("x|y"), grouping ("(xy)"), brackets ("[xy]"), and
// repetition count ("x{5,7}"), among others.
//
// Below is the syntax that we do support. We chose it to be a
// subset of both PCRE and POSIX extended regex, so it's easy to
// learn wherever you come from. In the following: 'A' denotes a
// literal character, period (.), or a single \\ escape sequence;
// 'x' and 'y' denote regular expressions; 'm' and 'n' are for
// natural numbers.
//
// c matches any literal character c
// \\d matches any decimal digit
// \\D matches any character that's not a decimal digit
// \\f matches \f
// \\n matches \n
// \\r matches \r
// \\s matches any ASCII whitespace, including \n
// \\S matches any character that's not a whitespace
// \\t matches \t
// \\v matches \v
// \\w matches any letter, _, or decimal digit
// \\W matches any character that \\w doesn't match
// \\c matches any literal character c, which must be a punctuation
// . matches any single character except \n
// A? matches 0 or 1 occurrences of A
// A* matches 0 or many occurrences of A
// A+ matches 1 or many occurrences of A
// ^ matches the beginning of a string (not that of each line)
// $ matches the end of a string (not that of each line)
// xy matches x followed by y
//
// If you accidentally use PCRE or POSIX extended regex features
// not implemented by us, you will get a run-time failure. In that
// case, please try to rewrite your regular expression within the
// above syntax.
//
// This implementation is *not* meant to be as highly tuned or robust
// as a compiled regex library, but should perform well enough for a
// death test, which already incurs significant overhead by launching
// a child process.
//
// Known caveats:
//
// A "threadsafe" style death test obtains the path to the test
// program from argv[0] and re-executes it in the sub-process. For
// simplicity, the current implementation doesn't search the PATH
// when launching the sub-process. This means that the user must
// invoke the test program via a path that contains at least one
// path separator (e.g. path/to/foo_test and
// /absolute/path/to/bar_test are fine, but foo_test is not). This
// is rarely a problem as people usually don't put the test binary
// directory in PATH.
//
// Asserts that a given `statement` causes the program to exit, with an
// integer exit status that satisfies `predicate`, and emitting error output
// that matches `matcher`.
#define ASSERT_EXIT(statement, predicate, matcher) \
GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_FATAL_FAILURE_)
// Like `ASSERT_EXIT`, but continues on to successive tests in the
// test suite, if any:
#define EXPECT_EXIT(statement, predicate, matcher) \
GTEST_DEATH_TEST_(statement, predicate, matcher, GTEST_NONFATAL_FAILURE_)
// Asserts that a given `statement` causes the program to exit, either by
// explicitly exiting with a nonzero exit code or being killed by a
// signal, and emitting error output that matches `matcher`.
#define ASSERT_DEATH(statement, matcher) \
ASSERT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
// Like `ASSERT_DEATH`, but continues on to successive tests in the
// test suite, if any:
#define EXPECT_DEATH(statement, matcher) \
EXPECT_EXIT(statement, ::testing::internal::ExitedUnsuccessfully, matcher)
// Two predicate classes that can be used in {ASSERT,EXPECT}_EXIT*:
// Tests that an exit code describes a normal exit with a given exit code.
class GTEST_API_ ExitedWithCode {
public:
explicit ExitedWithCode(int exit_code);
ExitedWithCode(const ExitedWithCode&) = default;
void operator=(const ExitedWithCode& other) = delete;
bool operator()(int exit_status) const;
private:
const int exit_code_;
};
#if !GTEST_OS_WINDOWS && !GTEST_OS_FUCHSIA
// Tests that an exit code describes an exit due to termination by a
// given signal.
class GTEST_API_ KilledBySignal {
public:
explicit KilledBySignal(int signum);
bool operator()(int exit_status) const;
private:
const int signum_;
};
#endif // !GTEST_OS_WINDOWS
// EXPECT_DEBUG_DEATH asserts that the given statements die in debug mode.
// The death testing framework causes this to have interesting semantics,
// since the sideeffects of the call are only visible in opt mode, and not
// in debug mode.
//
// In practice, this can be used to test functions that utilize the
// LOG(DFATAL) macro using the following style:
//
// int DieInDebugOr12(int* sideeffect) {
// if (sideeffect) {
// *sideeffect = 12;
// }
// LOG(DFATAL) << "death";
// return 12;
// }
//
// TEST(TestSuite, TestDieOr12WorksInDgbAndOpt) {
// int sideeffect = 0;
// // Only asserts in dbg.
// EXPECT_DEBUG_DEATH(DieInDebugOr12(&sideeffect), "death");
//
// #ifdef NDEBUG
// // opt-mode has sideeffect visible.
// EXPECT_EQ(12, sideeffect);
// #else
// // dbg-mode no visible sideeffect.
// EXPECT_EQ(0, sideeffect);
// #endif
// }
//
// This will assert that DieInDebugReturn12InOpt() crashes in debug
// mode, usually due to a DCHECK or LOG(DFATAL), but returns the
// appropriate fallback value (12 in this case) in opt mode. If you
// need to test that a function has appropriate side-effects in opt
// mode, include assertions against the side-effects. A general
// pattern for this is:
//
// EXPECT_DEBUG_DEATH({
// // Side-effects here will have an effect after this statement in
// // opt mode, but none in debug mode.
// EXPECT_EQ(12, DieInDebugOr12(&sideeffect));
// }, "death");
//
#ifdef NDEBUG
#define EXPECT_DEBUG_DEATH(statement, regex) \
GTEST_EXECUTE_STATEMENT_(statement, regex)
#define ASSERT_DEBUG_DEATH(statement, regex) \
GTEST_EXECUTE_STATEMENT_(statement, regex)
#else
#define EXPECT_DEBUG_DEATH(statement, regex) EXPECT_DEATH(statement, regex)
#define ASSERT_DEBUG_DEATH(statement, regex) ASSERT_DEATH(statement, regex)
#endif // NDEBUG for EXPECT_DEBUG_DEATH
#endif // GTEST_HAS_DEATH_TEST
// This macro is used for implementing macros such as
// EXPECT_DEATH_IF_SUPPORTED and ASSERT_DEATH_IF_SUPPORTED on systems where
// death tests are not supported. Those macros must compile on such systems
// if and only if EXPECT_DEATH and ASSERT_DEATH compile with the same parameters
// on systems that support death tests. This allows one to write such a macro on
// a system that does not support death tests and be sure that it will compile
// on a death-test supporting system. It is exposed publicly so that systems
// that have death-tests with stricter requirements than GTEST_HAS_DEATH_TEST
// can write their own equivalent of EXPECT_DEATH_IF_SUPPORTED and
// ASSERT_DEATH_IF_SUPPORTED.
//
// Parameters:
// statement - A statement that a macro such as EXPECT_DEATH would test
// for program termination. This macro has to make sure this
// statement is compiled but not executed, to ensure that
// EXPECT_DEATH_IF_SUPPORTED compiles with a certain
// parameter if and only if EXPECT_DEATH compiles with it.
// regex - A regex that a macro such as EXPECT_DEATH would use to test
// the output of statement. This parameter has to be
// compiled but not evaluated by this macro, to ensure that
// this macro only accepts expressions that a macro such as
// EXPECT_DEATH would accept.
// terminator - Must be an empty statement for EXPECT_DEATH_IF_SUPPORTED
// and a return statement for ASSERT_DEATH_IF_SUPPORTED.
// This ensures that ASSERT_DEATH_IF_SUPPORTED will not
// compile inside functions where ASSERT_DEATH doesn't
// compile.
//
// The branch that has an always false condition is used to ensure that
// statement and regex are compiled (and thus syntactically correct) but
// never executed. The unreachable code macro protects the terminator
// statement from generating an 'unreachable code' warning in case
// statement unconditionally returns or throws. The Message constructor at
// the end allows the syntax of streaming additional messages into the
// macro, for compilational compatibility with EXPECT_DEATH/ASSERT_DEATH.
#define GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, terminator) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
GTEST_LOG_(WARNING) << "Death tests are not supported on this platform.\n" \
<< "Statement '" #statement "' cannot be verified."; \
} else if (::testing::internal::AlwaysFalse()) { \
::testing::internal::RE::PartialMatch(".*", (regex)); \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
terminator; \
} else \
::testing::Message()
// EXPECT_DEATH_IF_SUPPORTED(statement, regex) and
// ASSERT_DEATH_IF_SUPPORTED(statement, regex) expand to real death tests if
// death tests are supported; otherwise they just issue a warning. This is
// useful when you are combining death test assertions with normal test
// assertions in one test.
#if GTEST_HAS_DEATH_TEST
#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
EXPECT_DEATH(statement, regex)
#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
ASSERT_DEATH(statement, regex)
#else
#define EXPECT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, )
#define ASSERT_DEATH_IF_SUPPORTED(statement, regex) \
GTEST_UNSUPPORTED_DEATH_TEST(statement, regex, return)
#endif
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_DEATH_TEST_H_

View File

@ -0,0 +1,956 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This file implements just enough of the matcher interface to allow
// EXPECT_DEATH and friends to accept a matcher argument.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_
#include <atomic>
#include <memory>
#include <ostream>
#include <string>
#include <type_traits>
#include "gtest/gtest-printers.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
// MSVC warning C5046 is new as of VS2017 version 15.8.
#if defined(_MSC_VER) && _MSC_VER >= 1915
#define GTEST_MAYBE_5046_ 5046
#else
#define GTEST_MAYBE_5046_
#endif
GTEST_DISABLE_MSC_WARNINGS_PUSH_(
4251 GTEST_MAYBE_5046_ /* class A needs to have dll-interface to be used by
clients of class B */
/* Symbol involving type with internal linkage not defined */)
namespace testing {
// To implement a matcher Foo for type T, define:
// 1. a class FooMatcherMatcher that implements the matcher interface:
// using is_gtest_matcher = void;
// bool MatchAndExplain(const T&, std::ostream*);
// (MatchResultListener* can also be used instead of std::ostream*)
// void DescribeTo(std::ostream*);
// void DescribeNegationTo(std::ostream*);
//
// 2. a factory function that creates a Matcher<T> object from a
// FooMatcherMatcher.
class MatchResultListener {
public:
// Creates a listener object with the given underlying ostream. The
// listener does not own the ostream, and does not dereference it
// in the constructor or destructor.
explicit MatchResultListener(::std::ostream* os) : stream_(os) {}
virtual ~MatchResultListener() = 0; // Makes this class abstract.
// Streams x to the underlying ostream; does nothing if the ostream
// is NULL.
template <typename T>
MatchResultListener& operator<<(const T& x) {
if (stream_ != nullptr) *stream_ << x;
return *this;
}
// Returns the underlying ostream.
::std::ostream* stream() { return stream_; }
// Returns true if and only if the listener is interested in an explanation
// of the match result. A matcher's MatchAndExplain() method can use
// this information to avoid generating the explanation when no one
// intends to hear it.
bool IsInterested() const { return stream_ != nullptr; }
private:
::std::ostream* const stream_;
MatchResultListener(const MatchResultListener&) = delete;
MatchResultListener& operator=(const MatchResultListener&) = delete;
};
inline MatchResultListener::~MatchResultListener() {}
// An instance of a subclass of this knows how to describe itself as a
// matcher.
class GTEST_API_ MatcherDescriberInterface {
public:
virtual ~MatcherDescriberInterface() {}
// Describes this matcher to an ostream. The function should print
// a verb phrase that describes the property a value matching this
// matcher should have. The subject of the verb phrase is the value
// being matched. For example, the DescribeTo() method of the Gt(7)
// matcher prints "is greater than 7".
virtual void DescribeTo(::std::ostream* os) const = 0;
// Describes the negation of this matcher to an ostream. For
// example, if the description of this matcher is "is greater than
// 7", the negated description could be "is not greater than 7".
// You are not required to override this when implementing
// MatcherInterface, but it is highly advised so that your matcher
// can produce good error messages.
virtual void DescribeNegationTo(::std::ostream* os) const {
*os << "not (";
DescribeTo(os);
*os << ")";
}
};
// The implementation of a matcher.
template <typename T>
class MatcherInterface : public MatcherDescriberInterface {
public:
// Returns true if and only if the matcher matches x; also explains the
// match result to 'listener' if necessary (see the next paragraph), in
// the form of a non-restrictive relative clause ("which ...",
// "whose ...", etc) that describes x. For example, the
// MatchAndExplain() method of the Pointee(...) matcher should
// generate an explanation like "which points to ...".
//
// Implementations of MatchAndExplain() should add an explanation of
// the match result *if and only if* they can provide additional
// information that's not already present (or not obvious) in the
// print-out of x and the matcher's description. Whether the match
// succeeds is not a factor in deciding whether an explanation is
// needed, as sometimes the caller needs to print a failure message
// when the match succeeds (e.g. when the matcher is used inside
// Not()).
//
// For example, a "has at least 10 elements" matcher should explain
// what the actual element count is, regardless of the match result,
// as it is useful information to the reader; on the other hand, an
// "is empty" matcher probably only needs to explain what the actual
// size is when the match fails, as it's redundant to say that the
// size is 0 when the value is already known to be empty.
//
// You should override this method when defining a new matcher.
//
// It's the responsibility of the caller (Google Test) to guarantee
// that 'listener' is not NULL. This helps to simplify a matcher's
// implementation when it doesn't care about the performance, as it
// can talk to 'listener' without checking its validity first.
// However, in order to implement dummy listeners efficiently,
// listener->stream() may be NULL.
virtual bool MatchAndExplain(T x, MatchResultListener* listener) const = 0;
// Inherits these methods from MatcherDescriberInterface:
// virtual void DescribeTo(::std::ostream* os) const = 0;
// virtual void DescribeNegationTo(::std::ostream* os) const;
};
namespace internal {
struct AnyEq {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a == b;
}
};
struct AnyNe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a != b;
}
};
struct AnyLt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a < b;
}
};
struct AnyGt {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a > b;
}
};
struct AnyLe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a <= b;
}
};
struct AnyGe {
template <typename A, typename B>
bool operator()(const A& a, const B& b) const {
return a >= b;
}
};
// A match result listener that ignores the explanation.
class DummyMatchResultListener : public MatchResultListener {
public:
DummyMatchResultListener() : MatchResultListener(nullptr) {}
private:
DummyMatchResultListener(const DummyMatchResultListener&) = delete;
DummyMatchResultListener& operator=(const DummyMatchResultListener&) = delete;
};
// A match result listener that forwards the explanation to a given
// ostream. The difference between this and MatchResultListener is
// that the former is concrete.
class StreamMatchResultListener : public MatchResultListener {
public:
explicit StreamMatchResultListener(::std::ostream* os)
: MatchResultListener(os) {}
private:
StreamMatchResultListener(const StreamMatchResultListener&) = delete;
StreamMatchResultListener& operator=(const StreamMatchResultListener&) =
delete;
};
struct SharedPayloadBase {
std::atomic<int> ref{1};
void Ref() { ref.fetch_add(1, std::memory_order_relaxed); }
bool Unref() { return ref.fetch_sub(1, std::memory_order_acq_rel) == 1; }
};
template <typename T>
struct SharedPayload : SharedPayloadBase {
explicit SharedPayload(const T& v) : value(v) {}
explicit SharedPayload(T&& v) : value(std::move(v)) {}
static void Destroy(SharedPayloadBase* shared) {
delete static_cast<SharedPayload*>(shared);
}
T value;
};
// An internal class for implementing Matcher<T>, which will derive
// from it. We put functionalities common to all Matcher<T>
// specializations here to avoid code duplication.
template <typename T>
class MatcherBase : private MatcherDescriberInterface {
public:
// Returns true if and only if the matcher matches x; also explains the
// match result to 'listener'.
bool MatchAndExplain(const T& x, MatchResultListener* listener) const {
GTEST_CHECK_(vtable_ != nullptr);
return vtable_->match_and_explain(*this, x, listener);
}
// Returns true if and only if this matcher matches x.
bool Matches(const T& x) const {
DummyMatchResultListener dummy;
return MatchAndExplain(x, &dummy);
}
// Describes this matcher to an ostream.
void DescribeTo(::std::ostream* os) const final {
GTEST_CHECK_(vtable_ != nullptr);
vtable_->describe(*this, os, false);
}
// Describes the negation of this matcher to an ostream.
void DescribeNegationTo(::std::ostream* os) const final {
GTEST_CHECK_(vtable_ != nullptr);
vtable_->describe(*this, os, true);
}
// Explains why x matches, or doesn't match, the matcher.
void ExplainMatchResultTo(const T& x, ::std::ostream* os) const {
StreamMatchResultListener listener(os);
MatchAndExplain(x, &listener);
}
// Returns the describer for this matcher object; retains ownership
// of the describer, which is only guaranteed to be alive when
// this matcher object is alive.
const MatcherDescriberInterface* GetDescriber() const {
if (vtable_ == nullptr) return nullptr;
return vtable_->get_describer(*this);
}
protected:
MatcherBase() : vtable_(nullptr), buffer_() {}
// Constructs a matcher from its implementation.
template <typename U>
explicit MatcherBase(const MatcherInterface<U>* impl)
: vtable_(nullptr), buffer_() {
Init(impl);
}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
MatcherBase(M&& m) : vtable_(nullptr), buffer_() { // NOLINT
Init(std::forward<M>(m));
}
MatcherBase(const MatcherBase& other)
: vtable_(other.vtable_), buffer_(other.buffer_) {
if (IsShared()) buffer_.shared->Ref();
}
MatcherBase& operator=(const MatcherBase& other) {
if (this == &other) return *this;
Destroy();
vtable_ = other.vtable_;
buffer_ = other.buffer_;
if (IsShared()) buffer_.shared->Ref();
return *this;
}
MatcherBase(MatcherBase&& other)
: vtable_(other.vtable_), buffer_(other.buffer_) {
other.vtable_ = nullptr;
}
MatcherBase& operator=(MatcherBase&& other) {
if (this == &other) return *this;
Destroy();
vtable_ = other.vtable_;
buffer_ = other.buffer_;
other.vtable_ = nullptr;
return *this;
}
~MatcherBase() override { Destroy(); }
private:
struct VTable {
bool (*match_and_explain)(const MatcherBase&, const T&,
MatchResultListener*);
void (*describe)(const MatcherBase&, std::ostream*, bool negation);
// Returns the captured object if it implements the interface, otherwise
// returns the MatcherBase itself.
const MatcherDescriberInterface* (*get_describer)(const MatcherBase&);
// Called on shared instances when the reference count reaches 0.
void (*shared_destroy)(SharedPayloadBase*);
};
bool IsShared() const {
return vtable_ != nullptr && vtable_->shared_destroy != nullptr;
}
// If the implementation uses a listener, call that.
template <typename P>
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
MatchResultListener* listener)
-> decltype(P::Get(m).MatchAndExplain(value, listener->stream())) {
return P::Get(m).MatchAndExplain(value, listener->stream());
}
template <typename P>
static auto MatchAndExplainImpl(const MatcherBase& m, const T& value,
MatchResultListener* listener)
-> decltype(P::Get(m).MatchAndExplain(value, listener)) {
return P::Get(m).MatchAndExplain(value, listener);
}
template <typename P>
static void DescribeImpl(const MatcherBase& m, std::ostream* os,
bool negation) {
if (negation) {
P::Get(m).DescribeNegationTo(os);
} else {
P::Get(m).DescribeTo(os);
}
}
template <typename P>
static const MatcherDescriberInterface* GetDescriberImpl(
const MatcherBase& m) {
// If the impl is a MatcherDescriberInterface, then return it.
// Otherwise use MatcherBase itself.
// This allows us to implement the GetDescriber() function without support
// from the impl, but some users really want to get their impl back when
// they call GetDescriber().
// We use std::get on a tuple as a workaround of not having `if constexpr`.
return std::get<(
std::is_convertible<decltype(&P::Get(m)),
const MatcherDescriberInterface*>::value
? 1
: 0)>(std::make_tuple(&m, &P::Get(m)));
}
template <typename P>
const VTable* GetVTable() {
static constexpr VTable kVTable = {&MatchAndExplainImpl<P>,
&DescribeImpl<P>, &GetDescriberImpl<P>,
P::shared_destroy};
return &kVTable;
}
union Buffer {
// Add some types to give Buffer some common alignment/size use cases.
void* ptr;
double d;
int64_t i;
// And add one for the out-of-line cases.
SharedPayloadBase* shared;
};
void Destroy() {
if (IsShared() && buffer_.shared->Unref()) {
vtable_->shared_destroy(buffer_.shared);
}
}
template <typename M>
static constexpr bool IsInlined() {
return sizeof(M) <= sizeof(Buffer) && alignof(M) <= alignof(Buffer) &&
std::is_trivially_copy_constructible<M>::value &&
std::is_trivially_destructible<M>::value;
}
template <typename M, bool = MatcherBase::IsInlined<M>()>
struct ValuePolicy {
static const M& Get(const MatcherBase& m) {
// When inlined along with Init, need to be explicit to avoid violating
// strict aliasing rules.
const M* ptr =
static_cast<const M*>(static_cast<const void*>(&m.buffer_));
return *ptr;
}
static void Init(MatcherBase& m, M impl) {
::new (static_cast<void*>(&m.buffer_)) M(impl);
}
static constexpr auto shared_destroy = nullptr;
};
template <typename M>
struct ValuePolicy<M, false> {
using Shared = SharedPayload<M>;
static const M& Get(const MatcherBase& m) {
return static_cast<Shared*>(m.buffer_.shared)->value;
}
template <typename Arg>
static void Init(MatcherBase& m, Arg&& arg) {
m.buffer_.shared = new Shared(std::forward<Arg>(arg));
}
static constexpr auto shared_destroy = &Shared::Destroy;
};
template <typename U, bool B>
struct ValuePolicy<const MatcherInterface<U>*, B> {
using M = const MatcherInterface<U>;
using Shared = SharedPayload<std::unique_ptr<M>>;
static const M& Get(const MatcherBase& m) {
return *static_cast<Shared*>(m.buffer_.shared)->value;
}
static void Init(MatcherBase& m, M* impl) {
m.buffer_.shared = new Shared(std::unique_ptr<M>(impl));
}
static constexpr auto shared_destroy = &Shared::Destroy;
};
template <typename M>
void Init(M&& m) {
using MM = typename std::decay<M>::type;
using Policy = ValuePolicy<MM>;
vtable_ = GetVTable<Policy>();
Policy::Init(*this, std::forward<M>(m));
}
const VTable* vtable_;
Buffer buffer_;
};
} // namespace internal
// A Matcher<T> is a copyable and IMMUTABLE (except by assignment)
// object that can check whether a value of type T matches. The
// implementation of Matcher<T> is just a std::shared_ptr to const
// MatcherInterface<T>. Don't inherit from Matcher!
template <typename T>
class Matcher : public internal::MatcherBase<T> {
public:
// Constructs a null matcher. Needed for storing Matcher objects in STL
// containers. A default-constructed matcher is not yet initialized. You
// cannot use it until a valid value has been assigned to it.
explicit Matcher() {} // NOLINT
// Constructs a matcher from its implementation.
explicit Matcher(const MatcherInterface<const T&>* impl)
: internal::MatcherBase<T>(impl) {}
template <typename U>
explicit Matcher(
const MatcherInterface<U>* impl,
typename std::enable_if<!std::is_same<U, const U&>::value>::type* =
nullptr)
: internal::MatcherBase<T>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) : internal::MatcherBase<T>(std::forward<M>(m)) {} // NOLINT
// Implicit constructor here allows people to write
// EXPECT_CALL(foo, Bar(5)) instead of EXPECT_CALL(foo, Bar(Eq(5))) sometimes
Matcher(T value); // NOLINT
};
// The following two specializations allow the user to write str
// instead of Eq(str) and "foo" instead of Eq("foo") when a std::string
// matcher is expected.
template <>
class GTEST_API_ Matcher<const std::string&>
: public internal::MatcherBase<const std::string&> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const std::string&>* impl)
: internal::MatcherBase<const std::string&>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<const std::string&>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
};
template <>
class GTEST_API_ Matcher<std::string>
: public internal::MatcherBase<std::string> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const std::string&>* impl)
: internal::MatcherBase<std::string>(impl) {}
explicit Matcher(const MatcherInterface<std::string>* impl)
: internal::MatcherBase<std::string>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<std::string>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
};
#if GTEST_INTERNAL_HAS_STRING_VIEW
// The following two specializations allow the user to write str
// instead of Eq(str) and "foo" instead of Eq("foo") when a absl::string_view
// matcher is expected.
template <>
class GTEST_API_ Matcher<const internal::StringView&>
: public internal::MatcherBase<const internal::StringView&> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
: internal::MatcherBase<const internal::StringView&>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<const internal::StringView&>(std::forward<M>(m)) {
}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
// Allows the user to pass absl::string_views or std::string_views directly.
Matcher(internal::StringView s); // NOLINT
};
template <>
class GTEST_API_ Matcher<internal::StringView>
: public internal::MatcherBase<internal::StringView> {
public:
Matcher() {}
explicit Matcher(const MatcherInterface<const internal::StringView&>* impl)
: internal::MatcherBase<internal::StringView>(impl) {}
explicit Matcher(const MatcherInterface<internal::StringView>* impl)
: internal::MatcherBase<internal::StringView>(impl) {}
template <typename M, typename = typename std::remove_reference<
M>::type::is_gtest_matcher>
Matcher(M&& m) // NOLINT
: internal::MatcherBase<internal::StringView>(std::forward<M>(m)) {}
// Allows the user to write str instead of Eq(str) sometimes, where
// str is a std::string object.
Matcher(const std::string& s); // NOLINT
// Allows the user to write "foo" instead of Eq("foo") sometimes.
Matcher(const char* s); // NOLINT
// Allows the user to pass absl::string_views or std::string_views directly.
Matcher(internal::StringView s); // NOLINT
};
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
// Prints a matcher in a human-readable format.
template <typename T>
std::ostream& operator<<(std::ostream& os, const Matcher<T>& matcher) {
matcher.DescribeTo(&os);
return os;
}
// The PolymorphicMatcher class template makes it easy to implement a
// polymorphic matcher (i.e. a matcher that can match values of more
// than one type, e.g. Eq(n) and NotNull()).
//
// To define a polymorphic matcher, a user should provide an Impl
// class that has a DescribeTo() method and a DescribeNegationTo()
// method, and define a member function (or member function template)
//
// bool MatchAndExplain(const Value& value,
// MatchResultListener* listener) const;
//
// See the definition of NotNull() for a complete example.
template <class Impl>
class PolymorphicMatcher {
public:
explicit PolymorphicMatcher(const Impl& an_impl) : impl_(an_impl) {}
// Returns a mutable reference to the underlying matcher
// implementation object.
Impl& mutable_impl() { return impl_; }
// Returns an immutable reference to the underlying matcher
// implementation object.
const Impl& impl() const { return impl_; }
template <typename T>
operator Matcher<T>() const {
return Matcher<T>(new MonomorphicImpl<const T&>(impl_));
}
private:
template <typename T>
class MonomorphicImpl : public MatcherInterface<T> {
public:
explicit MonomorphicImpl(const Impl& impl) : impl_(impl) {}
void DescribeTo(::std::ostream* os) const override { impl_.DescribeTo(os); }
void DescribeNegationTo(::std::ostream* os) const override {
impl_.DescribeNegationTo(os);
}
bool MatchAndExplain(T x, MatchResultListener* listener) const override {
return impl_.MatchAndExplain(x, listener);
}
private:
const Impl impl_;
};
Impl impl_;
};
// Creates a matcher from its implementation.
// DEPRECATED: Especially in the generic code, prefer:
// Matcher<T>(new MyMatcherImpl<const T&>(...));
//
// MakeMatcher may create a Matcher that accepts its argument by value, which
// leads to unnecessary copies & lack of support for non-copyable types.
template <typename T>
inline Matcher<T> MakeMatcher(const MatcherInterface<T>* impl) {
return Matcher<T>(impl);
}
// Creates a polymorphic matcher from its implementation. This is
// easier to use than the PolymorphicMatcher<Impl> constructor as it
// doesn't require you to explicitly write the template argument, e.g.
//
// MakePolymorphicMatcher(foo);
// vs
// PolymorphicMatcher<TypeOfFoo>(foo);
template <class Impl>
inline PolymorphicMatcher<Impl> MakePolymorphicMatcher(const Impl& impl) {
return PolymorphicMatcher<Impl>(impl);
}
namespace internal {
// Implements a matcher that compares a given value with a
// pre-supplied value using one of the ==, <=, <, etc, operators. The
// two values being compared don't have to have the same type.
//
// The matcher defined here is polymorphic (for example, Eq(5) can be
// used to match an int, a short, a double, etc). Therefore we use
// a template type conversion operator in the implementation.
//
// The following template definition assumes that the Rhs parameter is
// a "bare" type (i.e. neither 'const T' nor 'T&').
template <typename D, typename Rhs, typename Op>
class ComparisonBase {
public:
explicit ComparisonBase(const Rhs& rhs) : rhs_(rhs) {}
using is_gtest_matcher = void;
template <typename Lhs>
bool MatchAndExplain(const Lhs& lhs, std::ostream*) const {
return Op()(lhs, Unwrap(rhs_));
}
void DescribeTo(std::ostream* os) const {
*os << D::Desc() << " ";
UniversalPrint(Unwrap(rhs_), os);
}
void DescribeNegationTo(std::ostream* os) const {
*os << D::NegatedDesc() << " ";
UniversalPrint(Unwrap(rhs_), os);
}
private:
template <typename T>
static const T& Unwrap(const T& v) {
return v;
}
template <typename T>
static const T& Unwrap(std::reference_wrapper<T> v) {
return v;
}
Rhs rhs_;
};
template <typename Rhs>
class EqMatcher : public ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq> {
public:
explicit EqMatcher(const Rhs& rhs)
: ComparisonBase<EqMatcher<Rhs>, Rhs, AnyEq>(rhs) {}
static const char* Desc() { return "is equal to"; }
static const char* NegatedDesc() { return "isn't equal to"; }
};
template <typename Rhs>
class NeMatcher : public ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe> {
public:
explicit NeMatcher(const Rhs& rhs)
: ComparisonBase<NeMatcher<Rhs>, Rhs, AnyNe>(rhs) {}
static const char* Desc() { return "isn't equal to"; }
static const char* NegatedDesc() { return "is equal to"; }
};
template <typename Rhs>
class LtMatcher : public ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt> {
public:
explicit LtMatcher(const Rhs& rhs)
: ComparisonBase<LtMatcher<Rhs>, Rhs, AnyLt>(rhs) {}
static const char* Desc() { return "is <"; }
static const char* NegatedDesc() { return "isn't <"; }
};
template <typename Rhs>
class GtMatcher : public ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt> {
public:
explicit GtMatcher(const Rhs& rhs)
: ComparisonBase<GtMatcher<Rhs>, Rhs, AnyGt>(rhs) {}
static const char* Desc() { return "is >"; }
static const char* NegatedDesc() { return "isn't >"; }
};
template <typename Rhs>
class LeMatcher : public ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe> {
public:
explicit LeMatcher(const Rhs& rhs)
: ComparisonBase<LeMatcher<Rhs>, Rhs, AnyLe>(rhs) {}
static const char* Desc() { return "is <="; }
static const char* NegatedDesc() { return "isn't <="; }
};
template <typename Rhs>
class GeMatcher : public ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe> {
public:
explicit GeMatcher(const Rhs& rhs)
: ComparisonBase<GeMatcher<Rhs>, Rhs, AnyGe>(rhs) {}
static const char* Desc() { return "is >="; }
static const char* NegatedDesc() { return "isn't >="; }
};
template <typename T, typename = typename std::enable_if<
std::is_constructible<std::string, T>::value>::type>
using StringLike = T;
// Implements polymorphic matchers MatchesRegex(regex) and
// ContainsRegex(regex), which can be used as a Matcher<T> as long as
// T can be converted to a string.
class MatchesRegexMatcher {
public:
MatchesRegexMatcher(const RE* regex, bool full_match)
: regex_(regex), full_match_(full_match) {}
#if GTEST_INTERNAL_HAS_STRING_VIEW
bool MatchAndExplain(const internal::StringView& s,
MatchResultListener* listener) const {
return MatchAndExplain(std::string(s), listener);
}
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
// Accepts pointer types, particularly:
// const char*
// char*
// const wchar_t*
// wchar_t*
template <typename CharType>
bool MatchAndExplain(CharType* s, MatchResultListener* listener) const {
return s != nullptr && MatchAndExplain(std::string(s), listener);
}
// Matches anything that can convert to std::string.
//
// This is a template, not just a plain function with const std::string&,
// because absl::string_view has some interfering non-explicit constructors.
template <class MatcheeStringType>
bool MatchAndExplain(const MatcheeStringType& s,
MatchResultListener* /* listener */) const {
const std::string s2(s);
return full_match_ ? RE::FullMatch(s2, *regex_)
: RE::PartialMatch(s2, *regex_);
}
void DescribeTo(::std::ostream* os) const {
*os << (full_match_ ? "matches" : "contains") << " regular expression ";
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
}
void DescribeNegationTo(::std::ostream* os) const {
*os << "doesn't " << (full_match_ ? "match" : "contain")
<< " regular expression ";
UniversalPrinter<std::string>::Print(regex_->pattern(), os);
}
private:
const std::shared_ptr<const RE> regex_;
const bool full_match_;
};
} // namespace internal
// Matches a string that fully matches regular expression 'regex'.
// The matcher takes ownership of 'regex'.
inline PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
const internal::RE* regex) {
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, true));
}
template <typename T = std::string>
PolymorphicMatcher<internal::MatchesRegexMatcher> MatchesRegex(
const internal::StringLike<T>& regex) {
return MatchesRegex(new internal::RE(std::string(regex)));
}
// Matches a string that contains regular expression 'regex'.
// The matcher takes ownership of 'regex'.
inline PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
const internal::RE* regex) {
return MakePolymorphicMatcher(internal::MatchesRegexMatcher(regex, false));
}
template <typename T = std::string>
PolymorphicMatcher<internal::MatchesRegexMatcher> ContainsRegex(
const internal::StringLike<T>& regex) {
return ContainsRegex(new internal::RE(std::string(regex)));
}
// Creates a polymorphic matcher that matches anything equal to x.
// Note: if the parameter of Eq() were declared as const T&, Eq("foo")
// wouldn't compile.
template <typename T>
inline internal::EqMatcher<T> Eq(T x) {
return internal::EqMatcher<T>(x);
}
// Constructs a Matcher<T> from a 'value' of type T. The constructed
// matcher matches any value that's equal to 'value'.
template <typename T>
Matcher<T>::Matcher(T value) {
*this = Eq(value);
}
// Creates a monomorphic matcher that matches anything with type Lhs
// and equal to rhs. A user may need to use this instead of Eq(...)
// in order to resolve an overloading ambiguity.
//
// TypedEq<T>(x) is just a convenient short-hand for Matcher<T>(Eq(x))
// or Matcher<T>(x), but more readable than the latter.
//
// We could define similar monomorphic matchers for other comparison
// operations (e.g. TypedLt, TypedGe, and etc), but decided not to do
// it yet as those are used much less than Eq() in practice. A user
// can always write Matcher<T>(Lt(5)) to be explicit about the type,
// for example.
template <typename Lhs, typename Rhs>
inline Matcher<Lhs> TypedEq(const Rhs& rhs) {
return Eq(rhs);
}
// Creates a polymorphic matcher that matches anything >= x.
template <typename Rhs>
inline internal::GeMatcher<Rhs> Ge(Rhs x) {
return internal::GeMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything > x.
template <typename Rhs>
inline internal::GtMatcher<Rhs> Gt(Rhs x) {
return internal::GtMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything <= x.
template <typename Rhs>
inline internal::LeMatcher<Rhs> Le(Rhs x) {
return internal::LeMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything < x.
template <typename Rhs>
inline internal::LtMatcher<Rhs> Lt(Rhs x) {
return internal::LtMatcher<Rhs>(x);
}
// Creates a polymorphic matcher that matches anything != x.
template <typename Rhs>
inline internal::NeMatcher<Rhs> Ne(Rhs x) {
return internal::NeMatcher<Rhs>(x);
}
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251 5046
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MATCHERS_H_

View File

@ -0,0 +1,220 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the Message class.
//
// IMPORTANT NOTE: Due to limitation of the C++ language, we have to
// leave some internal implementation details in this header file.
// They are clearly marked by comments like this:
//
// // INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
//
// Such code is NOT meant to be used by a user directly, and is subject
// to CHANGE WITHOUT NOTICE. Therefore DO NOT DEPEND ON IT in a user
// program!
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_
#include <limits>
#include <memory>
#include <ostream>
#include <sstream>
#include <string>
#include "gtest/internal/gtest-port.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
// Ensures that there is at least one operator<< in the global namespace.
// See Message& operator<<(...) below for why.
void operator<<(const testing::internal::Secret&, int);
namespace testing {
// The Message class works like an ostream repeater.
//
// Typical usage:
//
// 1. You stream a bunch of values to a Message object.
// It will remember the text in a stringstream.
// 2. Then you stream the Message object to an ostream.
// This causes the text in the Message to be streamed
// to the ostream.
//
// For example;
//
// testing::Message foo;
// foo << 1 << " != " << 2;
// std::cout << foo;
//
// will print "1 != 2".
//
// Message is not intended to be inherited from. In particular, its
// destructor is not virtual.
//
// Note that stringstream behaves differently in gcc and in MSVC. You
// can stream a NULL char pointer to it in the former, but not in the
// latter (it causes an access violation if you do). The Message
// class hides this difference by treating a NULL char pointer as
// "(null)".
class GTEST_API_ Message {
private:
// The type of basic IO manipulators (endl, ends, and flush) for
// narrow streams.
typedef std::ostream& (*BasicNarrowIoManip)(std::ostream&);
public:
// Constructs an empty Message.
Message();
// Copy constructor.
Message(const Message& msg) : ss_(new ::std::stringstream) { // NOLINT
*ss_ << msg.GetString();
}
// Constructs a Message from a C-string.
explicit Message(const char* str) : ss_(new ::std::stringstream) {
*ss_ << str;
}
// Streams a non-pointer value to this object.
template <typename T>
inline Message& operator<<(const T& val) {
// Some libraries overload << for STL containers. These
// overloads are defined in the global namespace instead of ::std.
//
// C++'s symbol lookup rule (i.e. Koenig lookup) says that these
// overloads are visible in either the std namespace or the global
// namespace, but not other namespaces, including the testing
// namespace which Google Test's Message class is in.
//
// To allow STL containers (and other types that has a << operator
// defined in the global namespace) to be used in Google Test
// assertions, testing::Message must access the custom << operator
// from the global namespace. With this using declaration,
// overloads of << defined in the global namespace and those
// visible via Koenig lookup are both exposed in this function.
using ::operator<<;
*ss_ << val;
return *this;
}
// Streams a pointer value to this object.
//
// This function is an overload of the previous one. When you
// stream a pointer to a Message, this definition will be used as it
// is more specialized. (The C++ Standard, section
// [temp.func.order].) If you stream a non-pointer, then the
// previous definition will be used.
//
// The reason for this overload is that streaming a NULL pointer to
// ostream is undefined behavior. Depending on the compiler, you
// may get "0", "(nil)", "(null)", or an access violation. To
// ensure consistent result across compilers, we always treat NULL
// as "(null)".
template <typename T>
inline Message& operator<<(T* const& pointer) { // NOLINT
if (pointer == nullptr) {
*ss_ << "(null)";
} else {
*ss_ << pointer;
}
return *this;
}
// Since the basic IO manipulators are overloaded for both narrow
// and wide streams, we have to provide this specialized definition
// of operator <<, even though its body is the same as the
// templatized version above. Without this definition, streaming
// endl or other basic IO manipulators to Message will confuse the
// compiler.
Message& operator<<(BasicNarrowIoManip val) {
*ss_ << val;
return *this;
}
// Instead of 1/0, we want to see true/false for bool values.
Message& operator<<(bool b) { return *this << (b ? "true" : "false"); }
// These two overloads allow streaming a wide C string to a Message
// using the UTF-8 encoding.
Message& operator<<(const wchar_t* wide_c_str);
Message& operator<<(wchar_t* wide_c_str);
#if GTEST_HAS_STD_WSTRING
// Converts the given wide string to a narrow string using the UTF-8
// encoding, and streams the result to this Message object.
Message& operator<<(const ::std::wstring& wstr);
#endif // GTEST_HAS_STD_WSTRING
// Gets the text streamed to this object so far as an std::string.
// Each '\0' character in the buffer is replaced with "\\0".
//
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
std::string GetString() const;
private:
// We'll hold the text streamed to this object here.
const std::unique_ptr< ::std::stringstream> ss_;
// We declare (but don't implement) this to prevent the compiler
// from implementing the assignment operator.
void operator=(const Message&);
};
// Streams a Message to an ostream.
inline std::ostream& operator<<(std::ostream& os, const Message& sb) {
return os << sb.GetString();
}
namespace internal {
// Converts a streamable value to an std::string. A NULL pointer is
// converted to "(null)". When the input value is a ::string,
// ::std::string, ::wstring, or ::std::wstring object, each NUL
// character in it is replaced with "\\0".
template <typename T>
std::string StreamableToString(const T& streamable) {
return (Message() << streamable).GetString();
}
} // namespace internal
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_MESSAGE_H_

View File

@ -0,0 +1,545 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Macros and functions for implementing parameterized tests
// in Google C++ Testing and Mocking Framework (Google Test)
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_
// Value-parameterized tests allow you to test your code with different
// parameters without writing multiple copies of the same test.
//
// Here is how you use value-parameterized tests:
#if 0
// To write value-parameterized tests, first you should define a fixture
// class. It is usually derived from testing::TestWithParam<T> (see below for
// another inheritance scheme that's sometimes useful in more complicated
// class hierarchies), where the type of your parameter values.
// TestWithParam<T> is itself derived from testing::Test. T can be any
// copyable type. If it's a raw pointer, you are responsible for managing the
// lifespan of the pointed values.
class FooTest : public ::testing::TestWithParam<const char*> {
// You can implement all the usual class fixture members here.
};
// Then, use the TEST_P macro to define as many parameterized tests
// for this fixture as you want. The _P suffix is for "parameterized"
// or "pattern", whichever you prefer to think.
TEST_P(FooTest, DoesBlah) {
// Inside a test, access the test parameter with the GetParam() method
// of the TestWithParam<T> class:
EXPECT_TRUE(foo.Blah(GetParam()));
...
}
TEST_P(FooTest, HasBlahBlah) {
...
}
// Finally, you can use INSTANTIATE_TEST_SUITE_P to instantiate the test
// case with any set of parameters you want. Google Test defines a number
// of functions for generating test parameters. They return what we call
// (surprise!) parameter generators. Here is a summary of them, which
// are all in the testing namespace:
//
//
// Range(begin, end [, step]) - Yields values {begin, begin+step,
// begin+step+step, ...}. The values do not
// include end. step defaults to 1.
// Values(v1, v2, ..., vN) - Yields values {v1, v2, ..., vN}.
// ValuesIn(container) - Yields values from a C-style array, an STL
// ValuesIn(begin,end) container, or an iterator range [begin, end).
// Bool() - Yields sequence {false, true}.
// Combine(g1, g2, ..., gN) - Yields all combinations (the Cartesian product
// for the math savvy) of the values generated
// by the N generators.
//
// For more details, see comments at the definitions of these functions below
// in this file.
//
// The following statement will instantiate tests from the FooTest test suite
// each with parameter values "meeny", "miny", and "moe".
INSTANTIATE_TEST_SUITE_P(InstantiationName,
FooTest,
Values("meeny", "miny", "moe"));
// To distinguish different instances of the pattern, (yes, you
// can instantiate it more than once) the first argument to the
// INSTANTIATE_TEST_SUITE_P macro is a prefix that will be added to the
// actual test suite name. Remember to pick unique prefixes for different
// instantiations. The tests from the instantiation above will have
// these names:
//
// * InstantiationName/FooTest.DoesBlah/0 for "meeny"
// * InstantiationName/FooTest.DoesBlah/1 for "miny"
// * InstantiationName/FooTest.DoesBlah/2 for "moe"
// * InstantiationName/FooTest.HasBlahBlah/0 for "meeny"
// * InstantiationName/FooTest.HasBlahBlah/1 for "miny"
// * InstantiationName/FooTest.HasBlahBlah/2 for "moe"
//
// You can use these names in --gtest_filter.
//
// This statement will instantiate all tests from FooTest again, each
// with parameter values "cat" and "dog":
const char* pets[] = {"cat", "dog"};
INSTANTIATE_TEST_SUITE_P(AnotherInstantiationName, FooTest, ValuesIn(pets));
// The tests from the instantiation above will have these names:
//
// * AnotherInstantiationName/FooTest.DoesBlah/0 for "cat"
// * AnotherInstantiationName/FooTest.DoesBlah/1 for "dog"
// * AnotherInstantiationName/FooTest.HasBlahBlah/0 for "cat"
// * AnotherInstantiationName/FooTest.HasBlahBlah/1 for "dog"
//
// Please note that INSTANTIATE_TEST_SUITE_P will instantiate all tests
// in the given test suite, whether their definitions come before or
// AFTER the INSTANTIATE_TEST_SUITE_P statement.
//
// Please also note that generator expressions (including parameters to the
// generators) are evaluated in InitGoogleTest(), after main() has started.
// This allows the user on one hand, to adjust generator parameters in order
// to dynamically determine a set of tests to run and on the other hand,
// give the user a chance to inspect the generated tests with Google Test
// reflection API before RUN_ALL_TESTS() is executed.
//
// You can see samples/sample7_unittest.cc and samples/sample8_unittest.cc
// for more examples.
//
// In the future, we plan to publish the API for defining new parameter
// generators. But for now this interface remains part of the internal
// implementation and is subject to change.
//
//
// A parameterized test fixture must be derived from testing::Test and from
// testing::WithParamInterface<T>, where T is the type of the parameter
// values. Inheriting from TestWithParam<T> satisfies that requirement because
// TestWithParam<T> inherits from both Test and WithParamInterface. In more
// complicated hierarchies, however, it is occasionally useful to inherit
// separately from Test and WithParamInterface. For example:
class BaseTest : public ::testing::Test {
// You can inherit all the usual members for a non-parameterized test
// fixture here.
};
class DerivedTest : public BaseTest, public ::testing::WithParamInterface<int> {
// The usual test fixture members go here too.
};
TEST_F(BaseTest, HasFoo) {
// This is an ordinary non-parameterized test.
}
TEST_P(DerivedTest, DoesBlah) {
// GetParam works just the same here as if you inherit from TestWithParam.
EXPECT_TRUE(foo.Blah(GetParam()));
}
#endif // 0
#include <iterator>
#include <utility>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-param-util.h"
#include "gtest/internal/gtest-port.h"
namespace testing {
// Functions producing parameter generators.
//
// Google Test uses these generators to produce parameters for value-
// parameterized tests. When a parameterized test suite is instantiated
// with a particular generator, Google Test creates and runs tests
// for each element in the sequence produced by the generator.
//
// In the following sample, tests from test suite FooTest are instantiated
// each three times with parameter values 3, 5, and 8:
//
// class FooTest : public TestWithParam<int> { ... };
//
// TEST_P(FooTest, TestThis) {
// }
// TEST_P(FooTest, TestThat) {
// }
// INSTANTIATE_TEST_SUITE_P(TestSequence, FooTest, Values(3, 5, 8));
//
// Range() returns generators providing sequences of values in a range.
//
// Synopsis:
// Range(start, end)
// - returns a generator producing a sequence of values {start, start+1,
// start+2, ..., }.
// Range(start, end, step)
// - returns a generator producing a sequence of values {start, start+step,
// start+step+step, ..., }.
// Notes:
// * The generated sequences never include end. For example, Range(1, 5)
// returns a generator producing a sequence {1, 2, 3, 4}. Range(1, 9, 2)
// returns a generator producing {1, 3, 5, 7}.
// * start and end must have the same type. That type may be any integral or
// floating-point type or a user defined type satisfying these conditions:
// * It must be assignable (have operator=() defined).
// * It must have operator+() (operator+(int-compatible type) for
// two-operand version).
// * It must have operator<() defined.
// Elements in the resulting sequences will also have that type.
// * Condition start < end must be satisfied in order for resulting sequences
// to contain any elements.
//
template <typename T, typename IncrementT>
internal::ParamGenerator<T> Range(T start, T end, IncrementT step) {
return internal::ParamGenerator<T>(
new internal::RangeGenerator<T, IncrementT>(start, end, step));
}
template <typename T>
internal::ParamGenerator<T> Range(T start, T end) {
return Range(start, end, 1);
}
// ValuesIn() function allows generation of tests with parameters coming from
// a container.
//
// Synopsis:
// ValuesIn(const T (&array)[N])
// - returns a generator producing sequences with elements from
// a C-style array.
// ValuesIn(const Container& container)
// - returns a generator producing sequences with elements from
// an STL-style container.
// ValuesIn(Iterator begin, Iterator end)
// - returns a generator producing sequences with elements from
// a range [begin, end) defined by a pair of STL-style iterators. These
// iterators can also be plain C pointers.
//
// Please note that ValuesIn copies the values from the containers
// passed in and keeps them to generate tests in RUN_ALL_TESTS().
//
// Examples:
//
// This instantiates tests from test suite StringTest
// each with C-string values of "foo", "bar", and "baz":
//
// const char* strings[] = {"foo", "bar", "baz"};
// INSTANTIATE_TEST_SUITE_P(StringSequence, StringTest, ValuesIn(strings));
//
// This instantiates tests from test suite StlStringTest
// each with STL strings with values "a" and "b":
//
// ::std::vector< ::std::string> GetParameterStrings() {
// ::std::vector< ::std::string> v;
// v.push_back("a");
// v.push_back("b");
// return v;
// }
//
// INSTANTIATE_TEST_SUITE_P(CharSequence,
// StlStringTest,
// ValuesIn(GetParameterStrings()));
//
//
// This will also instantiate tests from CharTest
// each with parameter values 'a' and 'b':
//
// ::std::list<char> GetParameterChars() {
// ::std::list<char> list;
// list.push_back('a');
// list.push_back('b');
// return list;
// }
// ::std::list<char> l = GetParameterChars();
// INSTANTIATE_TEST_SUITE_P(CharSequence2,
// CharTest,
// ValuesIn(l.begin(), l.end()));
//
template <typename ForwardIterator>
internal::ParamGenerator<
typename std::iterator_traits<ForwardIterator>::value_type>
ValuesIn(ForwardIterator begin, ForwardIterator end) {
typedef typename std::iterator_traits<ForwardIterator>::value_type ParamType;
return internal::ParamGenerator<ParamType>(
new internal::ValuesInIteratorRangeGenerator<ParamType>(begin, end));
}
template <typename T, size_t N>
internal::ParamGenerator<T> ValuesIn(const T (&array)[N]) {
return ValuesIn(array, array + N);
}
template <class Container>
internal::ParamGenerator<typename Container::value_type> ValuesIn(
const Container& container) {
return ValuesIn(container.begin(), container.end());
}
// Values() allows generating tests from explicitly specified list of
// parameters.
//
// Synopsis:
// Values(T v1, T v2, ..., T vN)
// - returns a generator producing sequences with elements v1, v2, ..., vN.
//
// For example, this instantiates tests from test suite BarTest each
// with values "one", "two", and "three":
//
// INSTANTIATE_TEST_SUITE_P(NumSequence,
// BarTest,
// Values("one", "two", "three"));
//
// This instantiates tests from test suite BazTest each with values 1, 2, 3.5.
// The exact type of values will depend on the type of parameter in BazTest.
//
// INSTANTIATE_TEST_SUITE_P(FloatingNumbers, BazTest, Values(1, 2, 3.5));
//
//
template <typename... T>
internal::ValueArray<T...> Values(T... v) {
return internal::ValueArray<T...>(std::move(v)...);
}
// Bool() allows generating tests with parameters in a set of (false, true).
//
// Synopsis:
// Bool()
// - returns a generator producing sequences with elements {false, true}.
//
// It is useful when testing code that depends on Boolean flags. Combinations
// of multiple flags can be tested when several Bool()'s are combined using
// Combine() function.
//
// In the following example all tests in the test suite FlagDependentTest
// will be instantiated twice with parameters false and true.
//
// class FlagDependentTest : public testing::TestWithParam<bool> {
// virtual void SetUp() {
// external_flag = GetParam();
// }
// }
// INSTANTIATE_TEST_SUITE_P(BoolSequence, FlagDependentTest, Bool());
//
inline internal::ParamGenerator<bool> Bool() { return Values(false, true); }
// Combine() allows the user to combine two or more sequences to produce
// values of a Cartesian product of those sequences' elements.
//
// Synopsis:
// Combine(gen1, gen2, ..., genN)
// - returns a generator producing sequences with elements coming from
// the Cartesian product of elements from the sequences generated by
// gen1, gen2, ..., genN. The sequence elements will have a type of
// std::tuple<T1, T2, ..., TN> where T1, T2, ..., TN are the types
// of elements from sequences produces by gen1, gen2, ..., genN.
//
// Example:
//
// This will instantiate tests in test suite AnimalTest each one with
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
// tuple("dog", BLACK), and tuple("dog", WHITE):
//
// enum Color { BLACK, GRAY, WHITE };
// class AnimalTest
// : public testing::TestWithParam<std::tuple<const char*, Color> > {...};
//
// TEST_P(AnimalTest, AnimalLooksNice) {...}
//
// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
// Combine(Values("cat", "dog"),
// Values(BLACK, WHITE)));
//
// This will instantiate tests in FlagDependentTest with all variations of two
// Boolean flags:
//
// class FlagDependentTest
// : public testing::TestWithParam<std::tuple<bool, bool> > {
// virtual void SetUp() {
// // Assigns external_flag_1 and external_flag_2 values from the tuple.
// std::tie(external_flag_1, external_flag_2) = GetParam();
// }
// };
//
// TEST_P(FlagDependentTest, TestFeature1) {
// // Test your code using external_flag_1 and external_flag_2 here.
// }
// INSTANTIATE_TEST_SUITE_P(TwoBoolSequence, FlagDependentTest,
// Combine(Bool(), Bool()));
//
template <typename... Generator>
internal::CartesianProductHolder<Generator...> Combine(const Generator&... g) {
return internal::CartesianProductHolder<Generator...>(g...);
}
// ConvertGenerator() wraps a parameter generator in order to cast each produced
// value through a known type before supplying it to the test suite
//
// Synopsis:
// ConvertGenerator<T>(gen)
// - returns a generator producing the same elements as generated by gen, but
// each element is static_cast to type T before being returned
//
// It is useful when using the Combine() function to get the generated
// parameters in a custom type instead of std::tuple
//
// Example:
//
// This will instantiate tests in test suite AnimalTest each one with
// the parameter values tuple("cat", BLACK), tuple("cat", WHITE),
// tuple("dog", BLACK), and tuple("dog", WHITE):
//
// enum Color { BLACK, GRAY, WHITE };
// struct ParamType {
// using TupleT = std::tuple<const char*, Color>;
// std::string animal;
// Color color;
// ParamType(TupleT t) : animal(std::get<0>(t)), color(std::get<1>(t)) {}
// };
// class AnimalTest
// : public testing::TestWithParam<ParamType> {...};
//
// TEST_P(AnimalTest, AnimalLooksNice) {...}
//
// INSTANTIATE_TEST_SUITE_P(AnimalVariations, AnimalTest,
// ConvertGenerator<ParamType::TupleT>(
// Combine(Values("cat", "dog"),
// Values(BLACK, WHITE))));
//
template <typename T>
internal::ParamConverterGenerator<T> ConvertGenerator(
internal::ParamGenerator<T> gen) {
return internal::ParamConverterGenerator<T>(gen);
}
#define TEST_P(test_suite_name, test_name) \
class GTEST_TEST_CLASS_NAME_(test_suite_name, test_name) \
: public test_suite_name, private ::testing::internal::GTestNonCopyable {\
public: \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)() {} \
void TestBody() override; \
\
private: \
static int AddToRegistry() { \
::testing::UnitTest::GetInstance() \
->parameterized_test_registry() \
.GetTestSuitePatternHolder<test_suite_name>( \
GTEST_STRINGIFY_(test_suite_name), \
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
->AddTestPattern( \
GTEST_STRINGIFY_(test_suite_name), GTEST_STRINGIFY_(test_name), \
new ::testing::internal::TestMetaFactory<GTEST_TEST_CLASS_NAME_( \
test_suite_name, test_name)>(), \
::testing::internal::CodeLocation(__FILE__, __LINE__)); \
return 0; \
} \
static int gtest_registering_dummy_ GTEST_ATTRIBUTE_UNUSED_; \
}; \
int GTEST_TEST_CLASS_NAME_(test_suite_name, \
test_name)::gtest_registering_dummy_ = \
GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::AddToRegistry(); \
void GTEST_TEST_CLASS_NAME_(test_suite_name, test_name)::TestBody()
// The last argument to INSTANTIATE_TEST_SUITE_P allows the user to specify
// generator and an optional function or functor that generates custom test name
// suffixes based on the test parameters. Such a function or functor should
// accept one argument of type testing::TestParamInfo<class ParamType>, and
// return std::string.
//
// testing::PrintToStringParamName is a builtin test suffix generator that
// returns the value of testing::PrintToString(GetParam()).
//
// Note: test names must be non-empty, unique, and may only contain ASCII
// alphanumeric characters or underscore. Because PrintToString adds quotes
// to std::string and C strings, it won't work for these types.
#define GTEST_EXPAND_(arg) arg
#define GTEST_GET_FIRST_(first, ...) first
#define GTEST_GET_SECOND_(first, second, ...) second
#define INSTANTIATE_TEST_SUITE_P(prefix, test_suite_name, ...) \
static ::testing::internal::ParamGenerator<test_suite_name::ParamType> \
gtest_##prefix##test_suite_name##_EvalGenerator_() { \
return GTEST_EXPAND_(GTEST_GET_FIRST_(__VA_ARGS__, DUMMY_PARAM_)); \
} \
static ::std::string gtest_##prefix##test_suite_name##_EvalGenerateName_( \
const ::testing::TestParamInfo<test_suite_name::ParamType>& info) { \
if (::testing::internal::AlwaysFalse()) { \
::testing::internal::TestNotEmpty(GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))); \
auto t = std::make_tuple(__VA_ARGS__); \
static_assert(std::tuple_size<decltype(t)>::value <= 2, \
"Too Many Args!"); \
} \
return ((GTEST_EXPAND_(GTEST_GET_SECOND_( \
__VA_ARGS__, \
::testing::internal::DefaultParamName<test_suite_name::ParamType>, \
DUMMY_PARAM_))))(info); \
} \
static int gtest_##prefix##test_suite_name##_dummy_ \
GTEST_ATTRIBUTE_UNUSED_ = \
::testing::UnitTest::GetInstance() \
->parameterized_test_registry() \
.GetTestSuitePatternHolder<test_suite_name>( \
GTEST_STRINGIFY_(test_suite_name), \
::testing::internal::CodeLocation(__FILE__, __LINE__)) \
->AddTestSuiteInstantiation( \
GTEST_STRINGIFY_(prefix), \
&gtest_##prefix##test_suite_name##_EvalGenerator_, \
&gtest_##prefix##test_suite_name##_EvalGenerateName_, \
__FILE__, __LINE__)
// Allow Marking a Parameterized test class as not needing to be instantiated.
#define GTEST_ALLOW_UNINSTANTIATED_PARAMETERIZED_TEST(T) \
namespace gtest_do_not_use_outside_namespace_scope {} \
static const ::testing::internal::MarkAsIgnored gtest_allow_ignore_##T( \
GTEST_STRINGIFY_(T))
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TEST_CASE_P \
static_assert(::testing::internal::InstantiateTestCase_P_IsDeprecated(), \
""); \
INSTANTIATE_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PARAM_TEST_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,250 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Utilities for testing Google Test itself and code that uses Google Test
// (e.g. frameworks built on top of Google Test).
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_
#include <string>
#include "gtest/gtest.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing {
// This helper class can be used to mock out Google Test failure reporting
// so that we can test Google Test or code that builds on Google Test.
//
// An object of this class appends a TestPartResult object to the
// TestPartResultArray object given in the constructor whenever a Google Test
// failure is reported. It can either intercept only failures that are
// generated in the same thread that created this object or it can intercept
// all generated failures. The scope of this mock object can be controlled with
// the second argument to the two arguments constructor.
class GTEST_API_ ScopedFakeTestPartResultReporter
: public TestPartResultReporterInterface {
public:
// The two possible mocking modes of this object.
enum InterceptMode {
INTERCEPT_ONLY_CURRENT_THREAD, // Intercepts only thread local failures.
INTERCEPT_ALL_THREADS // Intercepts all failures.
};
// The c'tor sets this object as the test part result reporter used
// by Google Test. The 'result' parameter specifies where to report the
// results. This reporter will only catch failures generated in the current
// thread. DEPRECATED
explicit ScopedFakeTestPartResultReporter(TestPartResultArray* result);
// Same as above, but you can choose the interception scope of this object.
ScopedFakeTestPartResultReporter(InterceptMode intercept_mode,
TestPartResultArray* result);
// The d'tor restores the previous test part result reporter.
~ScopedFakeTestPartResultReporter() override;
// Appends the TestPartResult object to the TestPartResultArray
// received in the constructor.
//
// This method is from the TestPartResultReporterInterface
// interface.
void ReportTestPartResult(const TestPartResult& result) override;
private:
void Init();
const InterceptMode intercept_mode_;
TestPartResultReporterInterface* old_reporter_;
TestPartResultArray* const result_;
ScopedFakeTestPartResultReporter(const ScopedFakeTestPartResultReporter&) =
delete;
ScopedFakeTestPartResultReporter& operator=(
const ScopedFakeTestPartResultReporter&) = delete;
};
namespace internal {
// A helper class for implementing EXPECT_FATAL_FAILURE() and
// EXPECT_NONFATAL_FAILURE(). Its destructor verifies that the given
// TestPartResultArray contains exactly one failure that has the given
// type and contains the given substring. If that's not the case, a
// non-fatal failure will be generated.
class GTEST_API_ SingleFailureChecker {
public:
// The constructor remembers the arguments.
SingleFailureChecker(const TestPartResultArray* results,
TestPartResult::Type type, const std::string& substr);
~SingleFailureChecker();
private:
const TestPartResultArray* const results_;
const TestPartResult::Type type_;
const std::string substr_;
SingleFailureChecker(const SingleFailureChecker&) = delete;
SingleFailureChecker& operator=(const SingleFailureChecker&) = delete;
};
} // namespace internal
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
// A set of macros for testing Google Test assertions or code that's expected
// to generate Google Test fatal failures (e.g. a failure from an ASSERT_EQ, but
// not a non-fatal failure, as from EXPECT_EQ). It verifies that the given
// statement will cause exactly one fatal Google Test failure with 'substr'
// being part of the failure message.
//
// There are two different versions of this macro. EXPECT_FATAL_FAILURE only
// affects and considers failures generated in the current thread and
// EXPECT_FATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
//
// The verification of the assertion is done correctly even when the statement
// throws an exception or aborts the current function.
//
// Known restrictions:
// - 'statement' cannot reference local non-static variables or
// non-static members of the current object.
// - 'statement' cannot return a value.
// - You cannot stream a failure message to this macro.
//
// Note that even though the implementations of the following two
// macros are much alike, we cannot refactor them to use a common
// helper macro, due to some peculiarity in how the preprocessor
// works. The AcceptsMacroThatExpandsToUnprotectedComma test in
// gtest_unittest.cc will fail to compile if we do that.
#define EXPECT_FATAL_FAILURE(statement, substr) \
do { \
class GTestExpectFatalFailureHelper { \
public: \
static void Execute() { statement; } \
}; \
::testing::TestPartResultArray gtest_failures; \
::testing::internal::SingleFailureChecker gtest_checker( \
&gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \
{ \
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
::testing::ScopedFakeTestPartResultReporter:: \
INTERCEPT_ONLY_CURRENT_THREAD, \
&gtest_failures); \
GTestExpectFatalFailureHelper::Execute(); \
} \
} while (::testing::internal::AlwaysFalse())
#define EXPECT_FATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
do { \
class GTestExpectFatalFailureHelper { \
public: \
static void Execute() { statement; } \
}; \
::testing::TestPartResultArray gtest_failures; \
::testing::internal::SingleFailureChecker gtest_checker( \
&gtest_failures, ::testing::TestPartResult::kFatalFailure, (substr)); \
{ \
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
&gtest_failures); \
GTestExpectFatalFailureHelper::Execute(); \
} \
} while (::testing::internal::AlwaysFalse())
// A macro for testing Google Test assertions or code that's expected to
// generate Google Test non-fatal failures (e.g. a failure from an EXPECT_EQ,
// but not from an ASSERT_EQ). It asserts that the given statement will cause
// exactly one non-fatal Google Test failure with 'substr' being part of the
// failure message.
//
// There are two different versions of this macro. EXPECT_NONFATAL_FAILURE only
// affects and considers failures generated in the current thread and
// EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS does the same but for all threads.
//
// 'statement' is allowed to reference local variables and members of
// the current object.
//
// The verification of the assertion is done correctly even when the statement
// throws an exception or aborts the current function.
//
// Known restrictions:
// - You cannot stream a failure message to this macro.
//
// Note that even though the implementations of the following two
// macros are much alike, we cannot refactor them to use a common
// helper macro, due to some peculiarity in how the preprocessor
// works. If we do that, the code won't compile when the user gives
// EXPECT_NONFATAL_FAILURE() a statement that contains a macro that
// expands to code containing an unprotected comma. The
// AcceptsMacroThatExpandsToUnprotectedComma test in gtest_unittest.cc
// catches that.
//
// For the same reason, we have to write
// if (::testing::internal::AlwaysTrue()) { statement; }
// instead of
// GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
// to avoid an MSVC warning on unreachable code.
#define EXPECT_NONFATAL_FAILURE(statement, substr) \
do { \
::testing::TestPartResultArray gtest_failures; \
::testing::internal::SingleFailureChecker gtest_checker( \
&gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
(substr)); \
{ \
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
::testing::ScopedFakeTestPartResultReporter:: \
INTERCEPT_ONLY_CURRENT_THREAD, \
&gtest_failures); \
if (::testing::internal::AlwaysTrue()) { \
statement; \
} \
} \
} while (::testing::internal::AlwaysFalse())
#define EXPECT_NONFATAL_FAILURE_ON_ALL_THREADS(statement, substr) \
do { \
::testing::TestPartResultArray gtest_failures; \
::testing::internal::SingleFailureChecker gtest_checker( \
&gtest_failures, ::testing::TestPartResult::kNonFatalFailure, \
(substr)); \
{ \
::testing::ScopedFakeTestPartResultReporter gtest_reporter( \
::testing::ScopedFakeTestPartResultReporter::INTERCEPT_ALL_THREADS, \
&gtest_failures); \
if (::testing::internal::AlwaysTrue()) { \
statement; \
} \
} \
} while (::testing::internal::AlwaysFalse())
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_SPI_H_

View File

@ -0,0 +1,192 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_
#include <iosfwd>
#include <ostream>
#include <string>
#include <vector>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-string.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
namespace testing {
// A copyable object representing the result of a test part (i.e. an
// assertion or an explicit FAIL(), ADD_FAILURE(), or SUCCESS()).
//
// Don't inherit from TestPartResult as its destructor is not virtual.
class GTEST_API_ TestPartResult {
public:
// The possible outcomes of a test part (i.e. an assertion or an
// explicit SUCCEED(), FAIL(), or ADD_FAILURE()).
enum Type {
kSuccess, // Succeeded.
kNonFatalFailure, // Failed but the test can continue.
kFatalFailure, // Failed and the test should be terminated.
kSkip // Skipped.
};
// C'tor. TestPartResult does NOT have a default constructor.
// Always use this constructor (with parameters) to create a
// TestPartResult object.
TestPartResult(Type a_type, const char* a_file_name, int a_line_number,
const char* a_message)
: type_(a_type),
file_name_(a_file_name == nullptr ? "" : a_file_name),
line_number_(a_line_number),
summary_(ExtractSummary(a_message)),
message_(a_message) {}
// Gets the outcome of the test part.
Type type() const { return type_; }
// Gets the name of the source file where the test part took place, or
// NULL if it's unknown.
const char* file_name() const {
return file_name_.empty() ? nullptr : file_name_.c_str();
}
// Gets the line in the source file where the test part took place,
// or -1 if it's unknown.
int line_number() const { return line_number_; }
// Gets the summary of the failure message.
const char* summary() const { return summary_.c_str(); }
// Gets the message associated with the test part.
const char* message() const { return message_.c_str(); }
// Returns true if and only if the test part was skipped.
bool skipped() const { return type_ == kSkip; }
// Returns true if and only if the test part passed.
bool passed() const { return type_ == kSuccess; }
// Returns true if and only if the test part non-fatally failed.
bool nonfatally_failed() const { return type_ == kNonFatalFailure; }
// Returns true if and only if the test part fatally failed.
bool fatally_failed() const { return type_ == kFatalFailure; }
// Returns true if and only if the test part failed.
bool failed() const { return fatally_failed() || nonfatally_failed(); }
private:
Type type_;
// Gets the summary of the failure message by omitting the stack
// trace in it.
static std::string ExtractSummary(const char* message);
// The name of the source file where the test part took place, or
// "" if the source file is unknown.
std::string file_name_;
// The line in the source file where the test part took place, or -1
// if the line number is unknown.
int line_number_;
std::string summary_; // The test failure summary.
std::string message_; // The test failure message.
};
// Prints a TestPartResult object.
std::ostream& operator<<(std::ostream& os, const TestPartResult& result);
// An array of TestPartResult objects.
//
// Don't inherit from TestPartResultArray as its destructor is not
// virtual.
class GTEST_API_ TestPartResultArray {
public:
TestPartResultArray() {}
// Appends the given TestPartResult to the array.
void Append(const TestPartResult& result);
// Returns the TestPartResult at the given index (0-based).
const TestPartResult& GetTestPartResult(int index) const;
// Returns the number of TestPartResult objects in the array.
int size() const;
private:
std::vector<TestPartResult> array_;
TestPartResultArray(const TestPartResultArray&) = delete;
TestPartResultArray& operator=(const TestPartResultArray&) = delete;
};
// This interface knows how to report a test part result.
class GTEST_API_ TestPartResultReporterInterface {
public:
virtual ~TestPartResultReporterInterface() {}
virtual void ReportTestPartResult(const TestPartResult& result) = 0;
};
namespace internal {
// This helper class is used by {ASSERT|EXPECT}_NO_FATAL_FAILURE to check if a
// statement generates new fatal failures. To do so it registers itself as the
// current test part result reporter. Besides checking if fatal failures were
// reported, it only delegates the reporting to the former result reporter.
// The original result reporter is restored in the destructor.
// INTERNAL IMPLEMENTATION - DO NOT USE IN A USER PROGRAM.
class GTEST_API_ HasNewFatalFailureHelper
: public TestPartResultReporterInterface {
public:
HasNewFatalFailureHelper();
~HasNewFatalFailureHelper() override;
void ReportTestPartResult(const TestPartResult& result) override;
bool has_new_fatal_failure() const { return has_new_fatal_failure_; }
private:
bool has_new_fatal_failure_;
TestPartResultReporterInterface* original_reporter_;
HasNewFatalFailureHelper(const HasNewFatalFailureHelper&) = delete;
HasNewFatalFailureHelper& operator=(const HasNewFatalFailureHelper&) = delete;
};
} // namespace internal
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TEST_PART_H_

View File

@ -0,0 +1,331 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_
// This header implements typed tests and type-parameterized tests.
// Typed (aka type-driven) tests repeat the same test for types in a
// list. You must know which types you want to test with when writing
// typed tests. Here's how you do it:
#if 0
// First, define a fixture class template. It should be parameterized
// by a type. Remember to derive it from testing::Test.
template <typename T>
class FooTest : public testing::Test {
public:
...
typedef std::list<T> List;
static T shared_;
T value_;
};
// Next, associate a list of types with the test suite, which will be
// repeated for each type in the list. The typedef is necessary for
// the macro to parse correctly.
typedef testing::Types<char, int, unsigned int> MyTypes;
TYPED_TEST_SUITE(FooTest, MyTypes);
// If the type list contains only one type, you can write that type
// directly without Types<...>:
// TYPED_TEST_SUITE(FooTest, int);
// Then, use TYPED_TEST() instead of TEST_F() to define as many typed
// tests for this test suite as you want.
TYPED_TEST(FooTest, DoesBlah) {
// Inside a test, refer to the special name TypeParam to get the type
// parameter. Since we are inside a derived class template, C++ requires
// us to visit the members of FooTest via 'this'.
TypeParam n = this->value_;
// To visit static members of the fixture, add the TestFixture::
// prefix.
n += TestFixture::shared_;
// To refer to typedefs in the fixture, add the "typename
// TestFixture::" prefix.
typename TestFixture::List values;
values.push_back(n);
...
}
TYPED_TEST(FooTest, HasPropertyA) { ... }
// TYPED_TEST_SUITE takes an optional third argument which allows to specify a
// class that generates custom test name suffixes based on the type. This should
// be a class which has a static template function GetName(int index) returning
// a string for each type. The provided integer index equals the index of the
// type in the provided type list. In many cases the index can be ignored.
//
// For example:
// class MyTypeNames {
// public:
// template <typename T>
// static std::string GetName(int) {
// if (std::is_same<T, char>()) return "char";
// if (std::is_same<T, int>()) return "int";
// if (std::is_same<T, unsigned int>()) return "unsignedInt";
// }
// };
// TYPED_TEST_SUITE(FooTest, MyTypes, MyTypeNames);
#endif // 0
// Type-parameterized tests are abstract test patterns parameterized
// by a type. Compared with typed tests, type-parameterized tests
// allow you to define the test pattern without knowing what the type
// parameters are. The defined pattern can be instantiated with
// different types any number of times, in any number of translation
// units.
//
// If you are designing an interface or concept, you can define a
// suite of type-parameterized tests to verify properties that any
// valid implementation of the interface/concept should have. Then,
// each implementation can easily instantiate the test suite to verify
// that it conforms to the requirements, without having to write
// similar tests repeatedly. Here's an example:
#if 0
// First, define a fixture class template. It should be parameterized
// by a type. Remember to derive it from testing::Test.
template <typename T>
class FooTest : public testing::Test {
...
};
// Next, declare that you will define a type-parameterized test suite
// (the _P suffix is for "parameterized" or "pattern", whichever you
// prefer):
TYPED_TEST_SUITE_P(FooTest);
// Then, use TYPED_TEST_P() to define as many type-parameterized tests
// for this type-parameterized test suite as you want.
TYPED_TEST_P(FooTest, DoesBlah) {
// Inside a test, refer to TypeParam to get the type parameter.
TypeParam n = 0;
...
}
TYPED_TEST_P(FooTest, HasPropertyA) { ... }
// Now the tricky part: you need to register all test patterns before
// you can instantiate them. The first argument of the macro is the
// test suite name; the rest are the names of the tests in this test
// case.
REGISTER_TYPED_TEST_SUITE_P(FooTest,
DoesBlah, HasPropertyA);
// Finally, you are free to instantiate the pattern with the types you
// want. If you put the above code in a header file, you can #include
// it in multiple C++ source files and instantiate it multiple times.
//
// To distinguish different instances of the pattern, the first
// argument to the INSTANTIATE_* macro is a prefix that will be added
// to the actual test suite name. Remember to pick unique prefixes for
// different instances.
typedef testing::Types<char, int, unsigned int> MyTypes;
INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes);
// If the type list contains only one type, you can write that type
// directly without Types<...>:
// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, int);
//
// Similar to the optional argument of TYPED_TEST_SUITE above,
// INSTANTIATE_TEST_SUITE_P takes an optional fourth argument which allows to
// generate custom names.
// INSTANTIATE_TYPED_TEST_SUITE_P(My, FooTest, MyTypes, MyTypeNames);
#endif // 0
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-type-util.h"
// Implements typed tests.
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the name of the typedef for the type parameters of the
// given test suite.
#define GTEST_TYPE_PARAMS_(TestSuiteName) gtest_type_params_##TestSuiteName##_
// Expands to the name of the typedef for the NameGenerator, responsible for
// creating the suffixes of the name.
#define GTEST_NAME_GENERATOR_(TestSuiteName) \
gtest_type_params_##TestSuiteName##_NameGenerator
#define TYPED_TEST_SUITE(CaseName, Types, ...) \
typedef ::testing::internal::GenerateTypeList<Types>::type \
GTEST_TYPE_PARAMS_(CaseName); \
typedef ::testing::internal::NameGeneratorSelector<__VA_ARGS__>::type \
GTEST_NAME_GENERATOR_(CaseName)
#define TYPED_TEST(CaseName, TestName) \
static_assert(sizeof(GTEST_STRINGIFY_(TestName)) > 1, \
"test-name must not be empty"); \
template <typename gtest_TypeParam_> \
class GTEST_TEST_CLASS_NAME_(CaseName, TestName) \
: public CaseName<gtest_TypeParam_> { \
private: \
typedef CaseName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##CaseName##_##TestName##_registered_ \
GTEST_ATTRIBUTE_UNUSED_ = ::testing::internal::TypeParameterizedTest< \
CaseName, \
::testing::internal::TemplateSel<GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)>, \
GTEST_TYPE_PARAMS_( \
CaseName)>::Register("", \
::testing::internal::CodeLocation( \
__FILE__, __LINE__), \
GTEST_STRINGIFY_(CaseName), \
GTEST_STRINGIFY_(TestName), 0, \
::testing::internal::GenerateNames< \
GTEST_NAME_GENERATOR_(CaseName), \
GTEST_TYPE_PARAMS_(CaseName)>()); \
template <typename gtest_TypeParam_> \
void GTEST_TEST_CLASS_NAME_(CaseName, \
TestName)<gtest_TypeParam_>::TestBody()
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_CASE \
static_assert(::testing::internal::TypedTestCaseIsDeprecated(), ""); \
TYPED_TEST_SUITE
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
// Implements type-parameterized tests.
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the namespace name that the type-parameterized tests for
// the given type-parameterized test suite are defined in. The exact
// name of the namespace is subject to change without notice.
#define GTEST_SUITE_NAMESPACE_(TestSuiteName) gtest_suite_##TestSuiteName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE.
//
// Expands to the name of the variable used to remember the names of
// the defined tests in the given test suite.
#define GTEST_TYPED_TEST_SUITE_P_STATE_(TestSuiteName) \
gtest_typed_test_suite_p_state_##TestSuiteName##_
// INTERNAL IMPLEMENTATION - DO NOT USE IN USER CODE DIRECTLY.
//
// Expands to the name of the variable used to remember the names of
// the registered tests in the given test suite.
#define GTEST_REGISTERED_TEST_NAMES_(TestSuiteName) \
gtest_registered_test_names_##TestSuiteName##_
// The variables defined in the type-parameterized test macros are
// static as typically these macros are used in a .h file that can be
// #included in multiple translation units linked together.
#define TYPED_TEST_SUITE_P(SuiteName) \
static ::testing::internal::TypedTestSuitePState \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName)
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_CASE_P \
static_assert(::testing::internal::TypedTestCase_P_IsDeprecated(), ""); \
TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define TYPED_TEST_P(SuiteName, TestName) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
template <typename gtest_TypeParam_> \
class TestName : public SuiteName<gtest_TypeParam_> { \
private: \
typedef SuiteName<gtest_TypeParam_> TestFixture; \
typedef gtest_TypeParam_ TypeParam; \
void TestBody() override; \
}; \
static bool gtest_##TestName##_defined_ GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).AddTestName( \
__FILE__, __LINE__, GTEST_STRINGIFY_(SuiteName), \
GTEST_STRINGIFY_(TestName)); \
} \
template <typename gtest_TypeParam_> \
void GTEST_SUITE_NAMESPACE_( \
SuiteName)::TestName<gtest_TypeParam_>::TestBody()
// Note: this won't work correctly if the trailing arguments are macros.
#define REGISTER_TYPED_TEST_SUITE_P(SuiteName, ...) \
namespace GTEST_SUITE_NAMESPACE_(SuiteName) { \
typedef ::testing::internal::Templates<__VA_ARGS__> gtest_AllTests_; \
} \
static const char* const GTEST_REGISTERED_TEST_NAMES_( \
SuiteName) GTEST_ATTRIBUTE_UNUSED_ = \
GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName).VerifyRegisteredTestNames( \
GTEST_STRINGIFY_(SuiteName), __FILE__, __LINE__, #__VA_ARGS__)
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define REGISTER_TYPED_TEST_CASE_P \
static_assert(::testing::internal::RegisterTypedTestCase_P_IsDeprecated(), \
""); \
REGISTER_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TYPED_TEST_SUITE_P(Prefix, SuiteName, Types, ...) \
static_assert(sizeof(GTEST_STRINGIFY_(Prefix)) > 1, \
"test-suit-prefix must not be empty"); \
static bool gtest_##Prefix##_##SuiteName GTEST_ATTRIBUTE_UNUSED_ = \
::testing::internal::TypeParameterizedTestSuite< \
SuiteName, GTEST_SUITE_NAMESPACE_(SuiteName)::gtest_AllTests_, \
::testing::internal::GenerateTypeList<Types>::type>:: \
Register(GTEST_STRINGIFY_(Prefix), \
::testing::internal::CodeLocation(__FILE__, __LINE__), \
&GTEST_TYPED_TEST_SUITE_P_STATE_(SuiteName), \
GTEST_STRINGIFY_(SuiteName), \
GTEST_REGISTERED_TEST_NAMES_(SuiteName), \
::testing::internal::GenerateNames< \
::testing::internal::NameGeneratorSelector< \
__VA_ARGS__>::type, \
::testing::internal::GenerateTypeList<Types>::type>())
// Legacy API is deprecated but still available
#ifndef GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#define INSTANTIATE_TYPED_TEST_CASE_P \
static_assert( \
::testing::internal::InstantiateTypedTestCase_P_IsDeprecated(), ""); \
INSTANTIATE_TYPED_TEST_SUITE_P
#endif // GTEST_REMOVE_LEGACY_TEST_CASEAPI_
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_TYPED_TEST_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,279 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Implements a family of generic predicate assertion macros.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_
#include "gtest/gtest-assertion-result.h"
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
namespace testing {
// This header implements a family of generic predicate assertion
// macros:
//
// ASSERT_PRED_FORMAT1(pred_format, v1)
// ASSERT_PRED_FORMAT2(pred_format, v1, v2)
// ...
//
// where pred_format is a function or functor that takes n (in the
// case of ASSERT_PRED_FORMATn) values and their source expression
// text, and returns a testing::AssertionResult. See the definition
// of ASSERT_EQ in gtest.h for an example.
//
// If you don't care about formatting, you can use the more
// restrictive version:
//
// ASSERT_PRED1(pred, v1)
// ASSERT_PRED2(pred, v1, v2)
// ...
//
// where pred is an n-ary function or functor that returns bool,
// and the values v1, v2, ..., must support the << operator for
// streaming to std::ostream.
//
// We also define the EXPECT_* variations.
//
// For now we only support predicates whose arity is at most 5.
// Please email googletestframework@googlegroups.com if you need
// support for higher arities.
// GTEST_ASSERT_ is the basic statement to which all of the assertions
// in this file reduce. Don't use this in your code.
#define GTEST_ASSERT_(expression, on_failure) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (const ::testing::AssertionResult gtest_ar = (expression)) \
; \
else \
on_failure(gtest_ar.failure_message())
// Helper function for implementing {EXPECT|ASSERT}_PRED1. Don't use
// this in your code.
template <typename Pred, typename T1>
AssertionResult AssertPred1Helper(const char* pred_text, const char* e1,
Pred pred, const T1& v1) {
if (pred(v1)) return AssertionSuccess();
return AssertionFailure()
<< pred_text << "(" << e1 << ") evaluates to false, where"
<< "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT1.
// Don't use this in your code.
#define GTEST_PRED_FORMAT1_(pred_format, v1, on_failure) \
GTEST_ASSERT_(pred_format(#v1, v1), on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED1. Don't use
// this in your code.
#define GTEST_PRED1_(pred, v1, on_failure) \
GTEST_ASSERT_(::testing::AssertPred1Helper(#pred, #v1, pred, v1), on_failure)
// Unary predicate assertion macros.
#define EXPECT_PRED_FORMAT1(pred_format, v1) \
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT1(pred_format, v1) \
GTEST_PRED_FORMAT1_(pred_format, v1, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED1(pred, v1) GTEST_PRED1_(pred, v1, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED2. Don't use
// this in your code.
template <typename Pred, typename T1, typename T2>
AssertionResult AssertPred2Helper(const char* pred_text, const char* e1,
const char* e2, Pred pred, const T1& v1,
const T2& v2) {
if (pred(v1, v2)) return AssertionSuccess();
return AssertionFailure()
<< pred_text << "(" << e1 << ", " << e2
<< ") evaluates to false, where"
<< "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e2 << " evaluates to " << ::testing::PrintToString(v2);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT2.
// Don't use this in your code.
#define GTEST_PRED_FORMAT2_(pred_format, v1, v2, on_failure) \
GTEST_ASSERT_(pred_format(#v1, #v2, v1, v2), on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED2. Don't use
// this in your code.
#define GTEST_PRED2_(pred, v1, v2, on_failure) \
GTEST_ASSERT_(::testing::AssertPred2Helper(#pred, #v1, #v2, pred, v1, v2), \
on_failure)
// Binary predicate assertion macros.
#define EXPECT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED2(pred, v1, v2) \
GTEST_PRED2_(pred, v1, v2, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT2(pred_format, v1, v2) \
GTEST_PRED_FORMAT2_(pred_format, v1, v2, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED2(pred, v1, v2) \
GTEST_PRED2_(pred, v1, v2, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED3. Don't use
// this in your code.
template <typename Pred, typename T1, typename T2, typename T3>
AssertionResult AssertPred3Helper(const char* pred_text, const char* e1,
const char* e2, const char* e3, Pred pred,
const T1& v1, const T2& v2, const T3& v3) {
if (pred(v1, v2, v3)) return AssertionSuccess();
return AssertionFailure()
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3
<< ") evaluates to false, where"
<< "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< e3 << " evaluates to " << ::testing::PrintToString(v3);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT3.
// Don't use this in your code.
#define GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, on_failure) \
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, v1, v2, v3), on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED3. Don't use
// this in your code.
#define GTEST_PRED3_(pred, v1, v2, v3, on_failure) \
GTEST_ASSERT_( \
::testing::AssertPred3Helper(#pred, #v1, #v2, #v3, pred, v1, v2, v3), \
on_failure)
// Ternary predicate assertion macros.
#define EXPECT_PRED_FORMAT3(pred_format, v1, v2, v3) \
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED3(pred, v1, v2, v3) \
GTEST_PRED3_(pred, v1, v2, v3, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT3(pred_format, v1, v2, v3) \
GTEST_PRED_FORMAT3_(pred_format, v1, v2, v3, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED3(pred, v1, v2, v3) \
GTEST_PRED3_(pred, v1, v2, v3, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED4. Don't use
// this in your code.
template <typename Pred, typename T1, typename T2, typename T3, typename T4>
AssertionResult AssertPred4Helper(const char* pred_text, const char* e1,
const char* e2, const char* e3,
const char* e4, Pred pred, const T1& v1,
const T2& v2, const T3& v3, const T4& v4) {
if (pred(v1, v2, v3, v4)) return AssertionSuccess();
return AssertionFailure()
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
<< ") evaluates to false, where"
<< "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
<< e4 << " evaluates to " << ::testing::PrintToString(v4);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT4.
// Don't use this in your code.
#define GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, on_failure) \
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, v1, v2, v3, v4), on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED4. Don't use
// this in your code.
#define GTEST_PRED4_(pred, v1, v2, v3, v4, on_failure) \
GTEST_ASSERT_(::testing::AssertPred4Helper(#pred, #v1, #v2, #v3, #v4, pred, \
v1, v2, v3, v4), \
on_failure)
// 4-ary predicate assertion macros.
#define EXPECT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED4(pred, v1, v2, v3, v4) \
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT4(pred_format, v1, v2, v3, v4) \
GTEST_PRED_FORMAT4_(pred_format, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED4(pred, v1, v2, v3, v4) \
GTEST_PRED4_(pred, v1, v2, v3, v4, GTEST_FATAL_FAILURE_)
// Helper function for implementing {EXPECT|ASSERT}_PRED5. Don't use
// this in your code.
template <typename Pred, typename T1, typename T2, typename T3, typename T4,
typename T5>
AssertionResult AssertPred5Helper(const char* pred_text, const char* e1,
const char* e2, const char* e3,
const char* e4, const char* e5, Pred pred,
const T1& v1, const T2& v2, const T3& v3,
const T4& v4, const T5& v5) {
if (pred(v1, v2, v3, v4, v5)) return AssertionSuccess();
return AssertionFailure()
<< pred_text << "(" << e1 << ", " << e2 << ", " << e3 << ", " << e4
<< ", " << e5 << ") evaluates to false, where"
<< "\n"
<< e1 << " evaluates to " << ::testing::PrintToString(v1) << "\n"
<< e2 << " evaluates to " << ::testing::PrintToString(v2) << "\n"
<< e3 << " evaluates to " << ::testing::PrintToString(v3) << "\n"
<< e4 << " evaluates to " << ::testing::PrintToString(v4) << "\n"
<< e5 << " evaluates to " << ::testing::PrintToString(v5);
}
// Internal macro for implementing {EXPECT|ASSERT}_PRED_FORMAT5.
// Don't use this in your code.
#define GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, on_failure) \
GTEST_ASSERT_(pred_format(#v1, #v2, #v3, #v4, #v5, v1, v2, v3, v4, v5), \
on_failure)
// Internal macro for implementing {EXPECT|ASSERT}_PRED5. Don't use
// this in your code.
#define GTEST_PRED5_(pred, v1, v2, v3, v4, v5, on_failure) \
GTEST_ASSERT_(::testing::AssertPred5Helper(#pred, #v1, #v2, #v3, #v4, #v5, \
pred, v1, v2, v3, v4, v5), \
on_failure)
// 5-ary predicate assertion macros.
#define EXPECT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
#define EXPECT_PRED5(pred, v1, v2, v3, v4, v5) \
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_NONFATAL_FAILURE_)
#define ASSERT_PRED_FORMAT5(pred_format, v1, v2, v3, v4, v5) \
GTEST_PRED_FORMAT5_(pred_format, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
#define ASSERT_PRED5(pred, v1, v2, v3, v4, v5) \
GTEST_PRED5_(pred, v1, v2, v3, v4, v5, GTEST_FATAL_FAILURE_)
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PRED_IMPL_H_

View File

@ -0,0 +1,60 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Google C++ Testing and Mocking Framework definitions useful in production
// code.
#ifndef GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
#define GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_
// When you need to test the private or protected members of a class,
// use the FRIEND_TEST macro to declare your tests as friends of the
// class. For example:
//
// class MyClass {
// private:
// void PrivateMethod();
// FRIEND_TEST(MyClassTest, PrivateMethodWorks);
// };
//
// class MyClassTest : public testing::Test {
// // ...
// };
//
// TEST_F(MyClassTest, PrivateMethodWorks) {
// // Can call MyClass::PrivateMethod() here.
// }
//
// Note: The test class must be in the same namespace as the class being tested.
// For example, putting MyClassTest in an anonymous namespace will not work.
#define FRIEND_TEST(test_case_name, test_name) \
friend class test_case_name##_##test_name##_Test
#endif // GOOGLETEST_INCLUDE_GTEST_GTEST_PROD_H_

View File

@ -0,0 +1,44 @@
# Customization Points
The custom directory is an injection point for custom user configurations.
## Header `gtest.h`
### The following macros can be defined:
* `GTEST_OS_STACK_TRACE_GETTER_` - The name of an implementation of
`OsStackTraceGetterInterface`.
* `GTEST_CUSTOM_TEMPDIR_FUNCTION_` - An override for `testing::TempDir()`. See
`testing::TempDir` for semantics and signature.
## Header `gtest-port.h`
The following macros can be defined:
### Logging:
* `GTEST_LOG_(severity)`
* `GTEST_CHECK_(condition)`
* Functions `LogToStderr()` and `FlushInfoLog()` have to be provided too.
### Threading:
* `GTEST_HAS_NOTIFICATION_` - Enabled if Notification is already provided.
* `GTEST_HAS_MUTEX_AND_THREAD_LOCAL_` - Enabled if `Mutex` and `ThreadLocal`
are already provided. Must also provide `GTEST_DECLARE_STATIC_MUTEX_(mutex)`
and `GTEST_DEFINE_STATIC_MUTEX_(mutex)`
* `GTEST_EXCLUSIVE_LOCK_REQUIRED_(locks)`
* `GTEST_LOCK_EXCLUDED_(locks)`
### Underlying library support features
* `GTEST_HAS_CXXABI_H_`
### Exporting API symbols:
* `GTEST_API_` - Specifier for exported symbols.
## Header `gtest-printers.h`
* See documentation at `gtest/gtest-printers.h` for details on how to define a
custom printer.

View File

@ -0,0 +1,37 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PORT_H_

View File

@ -0,0 +1,42 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// This file provides an injection point for custom printers in a local
// installation of gTest.
// It will be included from gtest-printers.h and the overrides in this file
// will be visible to everyone.
//
// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_PRINTERS_H_

View File

@ -0,0 +1,37 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Injection point for custom user configurations. See README for details
//
// ** Custom implementation starts here **
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_CUSTOM_GTEST_H_

View File

@ -0,0 +1,307 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines internal utilities needed for implementing
// death tests. They are subject to change without notice.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_
#include <stdio.h>
#include <memory>
#include <string>
#include "gtest/gtest-matchers.h"
#include "gtest/internal/gtest-internal.h"
GTEST_DECLARE_string_(internal_run_death_test);
namespace testing {
namespace internal {
// Names of the flags (needed for parsing Google Test flags).
const char kDeathTestStyleFlag[] = "death_test_style";
const char kDeathTestUseFork[] = "death_test_use_fork";
const char kInternalRunDeathTestFlag[] = "internal_run_death_test";
#if GTEST_HAS_DEATH_TEST
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
// DeathTest is a class that hides much of the complexity of the
// GTEST_DEATH_TEST_ macro. It is abstract; its static Create method
// returns a concrete class that depends on the prevailing death test
// style, as defined by the --gtest_death_test_style and/or
// --gtest_internal_run_death_test flags.
// In describing the results of death tests, these terms are used with
// the corresponding definitions:
//
// exit status: The integer exit information in the format specified
// by wait(2)
// exit code: The integer code passed to exit(3), _exit(2), or
// returned from main()
class GTEST_API_ DeathTest {
public:
// Create returns false if there was an error determining the
// appropriate action to take for the current death test; for example,
// if the gtest_death_test_style flag is set to an invalid value.
// The LastMessage method will return a more detailed message in that
// case. Otherwise, the DeathTest pointer pointed to by the "test"
// argument is set. If the death test should be skipped, the pointer
// is set to NULL; otherwise, it is set to the address of a new concrete
// DeathTest object that controls the execution of the current test.
static bool Create(const char* statement, Matcher<const std::string&> matcher,
const char* file, int line, DeathTest** test);
DeathTest();
virtual ~DeathTest() {}
// A helper class that aborts a death test when it's deleted.
class ReturnSentinel {
public:
explicit ReturnSentinel(DeathTest* test) : test_(test) {}
~ReturnSentinel() { test_->Abort(TEST_ENCOUNTERED_RETURN_STATEMENT); }
private:
DeathTest* const test_;
ReturnSentinel(const ReturnSentinel&) = delete;
ReturnSentinel& operator=(const ReturnSentinel&) = delete;
};
// An enumeration of possible roles that may be taken when a death
// test is encountered. EXECUTE means that the death test logic should
// be executed immediately. OVERSEE means that the program should prepare
// the appropriate environment for a child process to execute the death
// test, then wait for it to complete.
enum TestRole { OVERSEE_TEST, EXECUTE_TEST };
// An enumeration of the three reasons that a test might be aborted.
enum AbortReason {
TEST_ENCOUNTERED_RETURN_STATEMENT,
TEST_THREW_EXCEPTION,
TEST_DID_NOT_DIE
};
// Assumes one of the above roles.
virtual TestRole AssumeRole() = 0;
// Waits for the death test to finish and returns its status.
virtual int Wait() = 0;
// Returns true if the death test passed; that is, the test process
// exited during the test, its exit status matches a user-supplied
// predicate, and its stderr output matches a user-supplied regular
// expression.
// The user-supplied predicate may be a macro expression rather
// than a function pointer or functor, or else Wait and Passed could
// be combined.
virtual bool Passed(bool exit_status_ok) = 0;
// Signals that the death test did not die as expected.
virtual void Abort(AbortReason reason) = 0;
// Returns a human-readable outcome message regarding the outcome of
// the last death test.
static const char* LastMessage();
static void set_last_death_test_message(const std::string& message);
private:
// A string containing a description of the outcome of the last death test.
static std::string last_death_test_message_;
DeathTest(const DeathTest&) = delete;
DeathTest& operator=(const DeathTest&) = delete;
};
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
// Factory interface for death tests. May be mocked out for testing.
class DeathTestFactory {
public:
virtual ~DeathTestFactory() {}
virtual bool Create(const char* statement,
Matcher<const std::string&> matcher, const char* file,
int line, DeathTest** test) = 0;
};
// A concrete DeathTestFactory implementation for normal use.
class DefaultDeathTestFactory : public DeathTestFactory {
public:
bool Create(const char* statement, Matcher<const std::string&> matcher,
const char* file, int line, DeathTest** test) override;
};
// Returns true if exit_status describes a process that was terminated
// by a signal, or exited normally with a nonzero exit code.
GTEST_API_ bool ExitedUnsuccessfully(int exit_status);
// A string passed to EXPECT_DEATH (etc.) is caught by one of these overloads
// and interpreted as a regex (rather than an Eq matcher) for legacy
// compatibility.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
::testing::internal::RE regex) {
return ContainsRegex(regex.pattern());
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(const char* regex) {
return ContainsRegex(regex);
}
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
const ::std::string& regex) {
return ContainsRegex(regex);
}
// If a Matcher<const ::std::string&> is passed to EXPECT_DEATH (etc.), it's
// used directly.
inline Matcher<const ::std::string&> MakeDeathTestMatcher(
Matcher<const ::std::string&> matcher) {
return matcher;
}
// Traps C++ exceptions escaping statement and reports them as test
// failures. Note that trapping SEH exceptions is not implemented here.
#if GTEST_HAS_EXCEPTIONS
#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
try { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} catch (const ::std::exception& gtest_exception) { \
fprintf( \
stderr, \
"\n%s: Caught std::exception-derived exception escaping the " \
"death test statement. Exception message: %s\n", \
::testing::internal::FormatFileLocation(__FILE__, __LINE__).c_str(), \
gtest_exception.what()); \
fflush(stderr); \
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
} catch (...) { \
death_test->Abort(::testing::internal::DeathTest::TEST_THREW_EXCEPTION); \
}
#else
#define GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, death_test) \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement)
#endif
// This macro is for implementing ASSERT_DEATH*, EXPECT_DEATH*,
// ASSERT_EXIT*, and EXPECT_EXIT*.
#define GTEST_DEATH_TEST_(statement, predicate, regex_or_matcher, fail) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
::testing::internal::DeathTest* gtest_dt; \
if (!::testing::internal::DeathTest::Create( \
#statement, \
::testing::internal::MakeDeathTestMatcher(regex_or_matcher), \
__FILE__, __LINE__, &gtest_dt)) { \
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
} \
if (gtest_dt != nullptr) { \
std::unique_ptr< ::testing::internal::DeathTest> gtest_dt_ptr(gtest_dt); \
switch (gtest_dt->AssumeRole()) { \
case ::testing::internal::DeathTest::OVERSEE_TEST: \
if (!gtest_dt->Passed(predicate(gtest_dt->Wait()))) { \
goto GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__); \
} \
break; \
case ::testing::internal::DeathTest::EXECUTE_TEST: { \
::testing::internal::DeathTest::ReturnSentinel gtest_sentinel( \
gtest_dt); \
GTEST_EXECUTE_DEATH_TEST_STATEMENT_(statement, gtest_dt); \
gtest_dt->Abort(::testing::internal::DeathTest::TEST_DID_NOT_DIE); \
break; \
} \
} \
} \
} else \
GTEST_CONCAT_TOKEN_(gtest_label_, __LINE__) \
: fail(::testing::internal::DeathTest::LastMessage())
// The symbol "fail" here expands to something into which a message
// can be streamed.
// This macro is for implementing ASSERT/EXPECT_DEBUG_DEATH when compiled in
// NDEBUG mode. In this case we need the statements to be executed and the macro
// must accept a streamed message even though the message is never printed.
// The regex object is not evaluated, but it is used to prevent "unused"
// warnings and to avoid an expression that doesn't compile in debug mode.
#define GTEST_EXECUTE_STATEMENT_(statement, regex_or_matcher) \
GTEST_AMBIGUOUS_ELSE_BLOCKER_ \
if (::testing::internal::AlwaysTrue()) { \
GTEST_SUPPRESS_UNREACHABLE_CODE_WARNING_BELOW_(statement); \
} else if (!::testing::internal::AlwaysTrue()) { \
::testing::internal::MakeDeathTestMatcher(regex_or_matcher); \
} else \
::testing::Message()
// A class representing the parsed contents of the
// --gtest_internal_run_death_test flag, as it existed when
// RUN_ALL_TESTS was called.
class InternalRunDeathTestFlag {
public:
InternalRunDeathTestFlag(const std::string& a_file, int a_line, int an_index,
int a_write_fd)
: file_(a_file), line_(a_line), index_(an_index), write_fd_(a_write_fd) {}
~InternalRunDeathTestFlag() {
if (write_fd_ >= 0) posix::Close(write_fd_);
}
const std::string& file() const { return file_; }
int line() const { return line_; }
int index() const { return index_; }
int write_fd() const { return write_fd_; }
private:
std::string file_;
int line_;
int index_;
int write_fd_;
InternalRunDeathTestFlag(const InternalRunDeathTestFlag&) = delete;
InternalRunDeathTestFlag& operator=(const InternalRunDeathTestFlag&) = delete;
};
// Returns a newly created InternalRunDeathTestFlag object with fields
// initialized from the GTEST_FLAG(internal_run_death_test) flag if
// the flag is specified; otherwise returns NULL.
InternalRunDeathTestFlag* ParseInternalRunDeathTestFlag();
#endif // GTEST_HAS_DEATH_TEST
} // namespace internal
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_DEATH_TEST_INTERNAL_H_

View File

@ -0,0 +1,227 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Google Test filepath utilities
//
// This header file declares classes and functions used internally by
// Google Test. They are subject to change without notice.
//
// This file is #included in gtest/internal/gtest-internal.h.
// Do not include this header file separately!
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_
#include <string>
#include "gtest/internal/gtest-port.h"
#include "gtest/internal/gtest-string.h"
GTEST_DISABLE_MSC_WARNINGS_PUSH_(4251 \
/* class A needs to have dll-interface to be used by clients of class B */)
#if GTEST_HAS_FILE_SYSTEM
namespace testing {
namespace internal {
// FilePath - a class for file and directory pathname manipulation which
// handles platform-specific conventions (like the pathname separator).
// Used for helper functions for naming files in a directory for xml output.
// Except for Set methods, all methods are const or static, which provides an
// "immutable value object" -- useful for peace of mind.
// A FilePath with a value ending in a path separator ("like/this/") represents
// a directory, otherwise it is assumed to represent a file. In either case,
// it may or may not represent an actual file or directory in the file system.
// Names are NOT checked for syntax correctness -- no checking for illegal
// characters, malformed paths, etc.
class GTEST_API_ FilePath {
public:
FilePath() : pathname_("") {}
FilePath(const FilePath& rhs) : pathname_(rhs.pathname_) {}
explicit FilePath(const std::string& pathname) : pathname_(pathname) {
Normalize();
}
FilePath& operator=(const FilePath& rhs) {
Set(rhs);
return *this;
}
void Set(const FilePath& rhs) { pathname_ = rhs.pathname_; }
const std::string& string() const { return pathname_; }
const char* c_str() const { return pathname_.c_str(); }
// Returns the current working directory, or "" if unsuccessful.
static FilePath GetCurrentDir();
// Given directory = "dir", base_name = "test", number = 0,
// extension = "xml", returns "dir/test.xml". If number is greater
// than zero (e.g., 12), returns "dir/test_12.xml".
// On Windows platform, uses \ as the separator rather than /.
static FilePath MakeFileName(const FilePath& directory,
const FilePath& base_name, int number,
const char* extension);
// Given directory = "dir", relative_path = "test.xml",
// returns "dir/test.xml".
// On Windows, uses \ as the separator rather than /.
static FilePath ConcatPaths(const FilePath& directory,
const FilePath& relative_path);
// Returns a pathname for a file that does not currently exist. The pathname
// will be directory/base_name.extension or
// directory/base_name_<number>.extension if directory/base_name.extension
// already exists. The number will be incremented until a pathname is found
// that does not already exist.
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
// There could be a race condition if two or more processes are calling this
// function at the same time -- they could both pick the same filename.
static FilePath GenerateUniqueFileName(const FilePath& directory,
const FilePath& base_name,
const char* extension);
// Returns true if and only if the path is "".
bool IsEmpty() const { return pathname_.empty(); }
// If input name has a trailing separator character, removes it and returns
// the name, otherwise return the name string unmodified.
// On Windows platform, uses \ as the separator, other platforms use /.
FilePath RemoveTrailingPathSeparator() const;
// Returns a copy of the FilePath with the directory part removed.
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
// returns an empty FilePath ("").
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath RemoveDirectoryName() const;
// RemoveFileName returns the directory path with the filename removed.
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath RemoveFileName() const;
// Returns a copy of the FilePath with the case-insensitive extension removed.
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
// FilePath("dir/file"). If a case-insensitive extension is not
// found, returns a copy of the original FilePath.
FilePath RemoveExtension(const char* extension) const;
// Creates directories so that path exists. Returns true if successful or if
// the directories already exist; returns false if unable to create
// directories for any reason. Will also return false if the FilePath does
// not represent a directory (that is, it doesn't end with a path separator).
bool CreateDirectoriesRecursively() const;
// Create the directory so that path exists. Returns true if successful or
// if the directory already exists; returns false if unable to create the
// directory for any reason, including if the parent directory does not
// exist. Not named "CreateDirectory" because that's a macro on Windows.
bool CreateFolder() const;
// Returns true if FilePath describes something in the file-system,
// either a file, directory, or whatever, and that something exists.
bool FileOrDirectoryExists() const;
// Returns true if pathname describes a directory in the file-system
// that exists.
bool DirectoryExists() const;
// Returns true if FilePath ends with a path separator, which indicates that
// it is intended to represent a directory. Returns false otherwise.
// This does NOT check that a directory (or file) actually exists.
bool IsDirectory() const;
// Returns true if pathname describes a root directory. (Windows has one
// root directory per disk drive.)
bool IsRootDirectory() const;
// Returns true if pathname describes an absolute path.
bool IsAbsolutePath() const;
private:
// Replaces multiple consecutive separators with a single separator.
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
// redundancies that might be in a pathname involving "." or "..".
//
// A pathname with multiple consecutive separators may occur either through
// user error or as a result of some scripts or APIs that generate a pathname
// with a trailing separator. On other platforms the same API or script
// may NOT generate a pathname with a trailing "/". Then elsewhere that
// pathname may have another "/" and pathname components added to it,
// without checking for the separator already being there.
// The script language and operating system may allow paths like "foo//bar"
// but some of the functions in FilePath will not handle that correctly. In
// particular, RemoveTrailingPathSeparator() only removes one separator, and
// it is called in CreateDirectoriesRecursively() assuming that it will change
// a pathname from directory syntax (trailing separator) to filename syntax.
//
// On Windows this method also replaces the alternate path separator '/' with
// the primary path separator '\\', so that for example "bar\\/\\foo" becomes
// "bar\\foo".
void Normalize();
// Returns a pointer to the last occurrence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found.
const char* FindLastPathSeparator() const;
// Returns the length of the path root, including the directory separator at
// the end of the prefix. Returns zero by definition if the path is relative.
// Examples:
// - [Windows] "..\Sibling" => 0
// - [Windows] "\Windows" => 1
// - [Windows] "C:/Windows\Notepad.exe" => 3
// - [Windows] "\\Host\Share\C$/Windows" => 13
// - [UNIX] "/bin" => 1
size_t CalculateRootLength() const;
std::string pathname_;
}; // class FilePath
} // namespace internal
} // namespace testing
GTEST_DISABLE_MSC_WARNINGS_POP_() // 4251
#endif // GTEST_HAS_FILE_SYSTEM
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_FILEPATH_H_

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,118 @@
// Copyright 2015, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file defines the GTEST_OS_* macro.
// It is separate from gtest-port.h so that custom/gtest-port.h can include it.
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_
// Determines the platform on which Google Test is compiled.
#ifdef __CYGWIN__
#define GTEST_OS_CYGWIN 1
#elif defined(__MINGW__) || defined(__MINGW32__) || defined(__MINGW64__)
#define GTEST_OS_WINDOWS_MINGW 1
#define GTEST_OS_WINDOWS 1
#elif defined _WIN32
#define GTEST_OS_WINDOWS 1
#ifdef _WIN32_WCE
#define GTEST_OS_WINDOWS_MOBILE 1
#elif defined(WINAPI_FAMILY)
#include <winapifamily.h>
#if WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_DESKTOP)
#define GTEST_OS_WINDOWS_DESKTOP 1
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_PHONE_APP)
#define GTEST_OS_WINDOWS_PHONE 1
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_APP)
#define GTEST_OS_WINDOWS_RT 1
#elif WINAPI_FAMILY_PARTITION(WINAPI_PARTITION_TV_TITLE)
#define GTEST_OS_WINDOWS_PHONE 1
#define GTEST_OS_WINDOWS_TV_TITLE 1
#else
// WINAPI_FAMILY defined but no known partition matched.
// Default to desktop.
#define GTEST_OS_WINDOWS_DESKTOP 1
#endif
#else
#define GTEST_OS_WINDOWS_DESKTOP 1
#endif // _WIN32_WCE
#elif defined __OS2__
#define GTEST_OS_OS2 1
#elif defined __APPLE__
#define GTEST_OS_MAC 1
#include <TargetConditionals.h>
#if TARGET_OS_IPHONE
#define GTEST_OS_IOS 1
#endif
#elif defined __DragonFly__
#define GTEST_OS_DRAGONFLY 1
#elif defined __FreeBSD__
#define GTEST_OS_FREEBSD 1
#elif defined __Fuchsia__
#define GTEST_OS_FUCHSIA 1
#elif defined(__GNU__)
#define GTEST_OS_GNU_HURD 1
#elif defined(__GLIBC__) && defined(__FreeBSD_kernel__)
#define GTEST_OS_GNU_KFREEBSD 1
#elif defined __linux__
#define GTEST_OS_LINUX 1
#if defined __ANDROID__
#define GTEST_OS_LINUX_ANDROID 1
#endif
#elif defined __MVS__
#define GTEST_OS_ZOS 1
#elif defined(__sun) && defined(__SVR4)
#define GTEST_OS_SOLARIS 1
#elif defined(_AIX)
#define GTEST_OS_AIX 1
#elif defined(__hpux)
#define GTEST_OS_HPUX 1
#elif defined __native_client__
#define GTEST_OS_NACL 1
#elif defined __NetBSD__
#define GTEST_OS_NETBSD 1
#elif defined __OpenBSD__
#define GTEST_OS_OPENBSD 1
#elif defined __QNX__
#define GTEST_OS_QNX 1
#elif defined(__HAIKU__)
#define GTEST_OS_HAIKU 1
#elif defined ESP8266
#define GTEST_OS_ESP8266 1
#elif defined ESP32
#define GTEST_OS_ESP32 1
#elif defined(__XTENSA__)
#define GTEST_OS_XTENSA 1
#elif defined(__hexagon__)
#define GTEST_OS_QURT 1
#endif // __CYGWIN__
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_PORT_ARCH_H_

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,178 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This header file declares the String class and functions used internally by
// Google Test. They are subject to change without notice. They should not used
// by code external to Google Test.
//
// This header file is #included by gtest-internal.h.
// It should not be #included by other files.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_
#ifdef __BORLANDC__
// string.h is not guaranteed to provide strcpy on C++ Builder.
#include <mem.h>
#endif
#include <string.h>
#include <cstdint>
#include <sstream>
#include <string>
#include "gtest/internal/gtest-port.h"
namespace testing {
namespace internal {
// String - an abstract class holding static string utilities.
class GTEST_API_ String {
public:
// Static utility methods
// Clones a 0-terminated C string, allocating memory using new. The
// caller is responsible for deleting the return value using
// delete[]. Returns the cloned string, or NULL if the input is
// NULL.
//
// This is different from strdup() in string.h, which allocates
// memory using malloc().
static const char* CloneCString(const char* c_str);
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE does not have the 'ANSI' versions of Win32 APIs. To be
// able to pass strings to Win32 APIs on CE we need to convert them
// to 'Unicode', UTF-16.
// Creates a UTF-16 wide string from the given ANSI string, allocating
// memory using new. The caller is responsible for deleting the return
// value using delete[]. Returns the wide string, or NULL if the
// input is NULL.
//
// The wide string is created using the ANSI codepage (CP_ACP) to
// match the behaviour of the ANSI versions of Win32 calls and the
// C runtime.
static LPCWSTR AnsiToUtf16(const char* c_str);
// Creates an ANSI string from the given wide string, allocating
// memory using new. The caller is responsible for deleting the return
// value using delete[]. Returns the ANSI string, or NULL if the
// input is NULL.
//
// The returned string is created using the ANSI codepage (CP_ACP) to
// match the behaviour of the ANSI versions of Win32 calls and the
// C runtime.
static const char* Utf16ToAnsi(LPCWSTR utf16_str);
#endif
// Compares two C strings. Returns true if and only if they have the same
// content.
//
// Unlike strcmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string,
// including the empty string.
static bool CStringEquals(const char* lhs, const char* rhs);
// Converts a wide C string to a String using the UTF-8 encoding.
// NULL will be converted to "(null)". If an error occurred during
// the conversion, "(failed to convert from wide string)" is
// returned.
static std::string ShowWideCString(const wchar_t* wide_c_str);
// Compares two wide C strings. Returns true if and only if they have the
// same content.
//
// Unlike wcscmp(), this function can handle NULL argument(s). A
// NULL C string is considered different to any non-NULL C string,
// including the empty string.
static bool WideCStringEquals(const wchar_t* lhs, const wchar_t* rhs);
// Compares two C strings, ignoring case. Returns true if and only if
// they have the same content.
//
// Unlike strcasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL C string,
// including the empty string.
static bool CaseInsensitiveCStringEquals(const char* lhs, const char* rhs);
// Compares two wide C strings, ignoring case. Returns true if and only if
// they have the same content.
//
// Unlike wcscasecmp(), this function can handle NULL argument(s).
// A NULL C string is considered different to any non-NULL wide C string,
// including the empty string.
// NB: The implementations on different platforms slightly differ.
// On windows, this method uses _wcsicmp which compares according to LC_CTYPE
// environment variable. On GNU platform this method uses wcscasecmp
// which compares according to LC_CTYPE category of the current locale.
// On MacOS X, it uses towlower, which also uses LC_CTYPE category of the
// current locale.
static bool CaseInsensitiveWideCStringEquals(const wchar_t* lhs,
const wchar_t* rhs);
// Returns true if and only if the given string ends with the given suffix,
// ignoring case. Any string is considered to end with an empty suffix.
static bool EndsWithCaseInsensitive(const std::string& str,
const std::string& suffix);
// Formats an int value as "%02d".
static std::string FormatIntWidth2(int value); // "%02d" for width == 2
// Formats an int value to given width with leading zeros.
static std::string FormatIntWidthN(int value, int width);
// Formats an int value as "%X".
static std::string FormatHexInt(int value);
// Formats an int value as "%X".
static std::string FormatHexUInt32(uint32_t value);
// Formats a byte as "%02X".
static std::string FormatByte(unsigned char value);
private:
String(); // Not meant to be instantiated.
}; // class String
// Gets the content of the stringstream's buffer as an std::string. Each '\0'
// character in the buffer is replaced with "\\0".
GTEST_API_ std::string StringStreamToString(::std::stringstream* stream);
} // namespace internal
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_STRING_H_

View File

@ -0,0 +1,190 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Type utilities needed for implementing typed and type-parameterized
// tests.
// IWYU pragma: private, include "gtest/gtest.h"
// IWYU pragma: friend gtest/.*
// IWYU pragma: friend gmock/.*
#ifndef GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
#define GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_
#include <string>
#include <type_traits>
#include <typeinfo>
#include "gtest/internal/gtest-port.h"
// #ifdef __GNUC__ is too general here. It is possible to use gcc without using
// libstdc++ (which is where cxxabi.h comes from).
#if GTEST_HAS_CXXABI_H_
#include <cxxabi.h>
#elif defined(__HP_aCC)
#include <acxx_demangle.h>
#endif // GTEST_HASH_CXXABI_H_
namespace testing {
namespace internal {
// Canonicalizes a given name with respect to the Standard C++ Library.
// This handles removing the inline namespace within `std` that is
// used by various standard libraries (e.g., `std::__1`). Names outside
// of namespace std are returned unmodified.
inline std::string CanonicalizeForStdLibVersioning(std::string s) {
static const char prefix[] = "std::__";
if (s.compare(0, strlen(prefix), prefix) == 0) {
std::string::size_type end = s.find("::", strlen(prefix));
if (end != s.npos) {
// Erase everything between the initial `std` and the second `::`.
s.erase(strlen("std"), end - strlen("std"));
}
}
return s;
}
#if GTEST_HAS_RTTI
// GetTypeName(const std::type_info&) returns a human-readable name of type T.
inline std::string GetTypeName(const std::type_info& type) {
const char* const name = type.name();
#if GTEST_HAS_CXXABI_H_ || defined(__HP_aCC)
int status = 0;
// gcc's implementation of typeid(T).name() mangles the type name,
// so we have to demangle it.
#if GTEST_HAS_CXXABI_H_
using abi::__cxa_demangle;
#endif // GTEST_HAS_CXXABI_H_
char* const readable_name = __cxa_demangle(name, nullptr, nullptr, &status);
const std::string name_str(status == 0 ? readable_name : name);
free(readable_name);
return CanonicalizeForStdLibVersioning(name_str);
#else
return name;
#endif // GTEST_HAS_CXXABI_H_ || __HP_aCC
}
#endif // GTEST_HAS_RTTI
// GetTypeName<T>() returns a human-readable name of type T if and only if
// RTTI is enabled, otherwise it returns a dummy type name.
// NB: This function is also used in Google Mock, so don't move it inside of
// the typed-test-only section below.
template <typename T>
std::string GetTypeName() {
#if GTEST_HAS_RTTI
return GetTypeName(typeid(T));
#else
return "<type>";
#endif // GTEST_HAS_RTTI
}
// A unique type indicating an empty node
struct None {};
#define GTEST_TEMPLATE_ \
template <typename T> \
class
// The template "selector" struct TemplateSel<Tmpl> is used to
// represent Tmpl, which must be a class template with one type
// parameter, as a type. TemplateSel<Tmpl>::Bind<T>::type is defined
// as the type Tmpl<T>. This allows us to actually instantiate the
// template "selected" by TemplateSel<Tmpl>.
//
// This trick is necessary for simulating typedef for class templates,
// which C++ doesn't support directly.
template <GTEST_TEMPLATE_ Tmpl>
struct TemplateSel {
template <typename T>
struct Bind {
typedef Tmpl<T> type;
};
};
#define GTEST_BIND_(TmplSel, T) TmplSel::template Bind<T>::type
template <GTEST_TEMPLATE_ Head_, GTEST_TEMPLATE_... Tail_>
struct Templates {
using Head = TemplateSel<Head_>;
using Tail = Templates<Tail_...>;
};
template <GTEST_TEMPLATE_ Head_>
struct Templates<Head_> {
using Head = TemplateSel<Head_>;
using Tail = None;
};
// Tuple-like type lists
template <typename Head_, typename... Tail_>
struct Types {
using Head = Head_;
using Tail = Types<Tail_...>;
};
template <typename Head_>
struct Types<Head_> {
using Head = Head_;
using Tail = None;
};
// Helper metafunctions to tell apart a single type from types
// generated by ::testing::Types
template <typename... Ts>
struct ProxyTypeList {
using type = Types<Ts...>;
};
template <typename>
struct is_proxy_type_list : std::false_type {};
template <typename... Ts>
struct is_proxy_type_list<ProxyTypeList<Ts...>> : std::true_type {};
// Generator which conditionally creates type lists.
// It recognizes if a requested type list should be created
// and prevents creating a new type list nested within another one.
template <typename T>
struct GenerateTypeList {
private:
using proxy = typename std::conditional<is_proxy_type_list<T>::value, T,
ProxyTypeList<T>>::type;
public:
using type = typename proxy::type;
};
} // namespace internal
template <typename... Ts>
using Types = internal::ProxyTypeList<Ts...>;
} // namespace testing
#endif // GOOGLETEST_INCLUDE_GTEST_INTERNAL_GTEST_TYPE_UTIL_H_

View File

@ -0,0 +1,49 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// Google C++ Testing and Mocking Framework (Google Test)
//
// Sometimes it's desirable to build Google Test by compiling a single file.
// This file serves this purpose.
// This line ensures that gtest.h can be compiled on its own, even
// when it's fused.
#include "gtest/gtest.h"
// The following lines pull in the real gtest *.cc files.
#include "src/gtest-assertion-result.cc"
#include "src/gtest-death-test.cc"
#include "src/gtest-filepath.cc"
#include "src/gtest-matchers.cc"
#include "src/gtest-port.cc"
#include "src/gtest-printers.cc"
#include "src/gtest-test-part.cc"
#include "src/gtest-typed-test.cc"
#include "src/gtest.cc"

View File

@ -0,0 +1,77 @@
// Copyright 2005, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This file defines the AssertionResult type.
#include "gtest/gtest-assertion-result.h"
#include <string>
#include <utility>
#include "gtest/gtest-message.h"
namespace testing {
// AssertionResult constructors.
// Used in EXPECT_TRUE/FALSE(assertion_result).
AssertionResult::AssertionResult(const AssertionResult& other)
: success_(other.success_),
message_(other.message_.get() != nullptr
? new ::std::string(*other.message_)
: static_cast< ::std::string*>(nullptr)) {}
// Swaps two AssertionResults.
void AssertionResult::swap(AssertionResult& other) {
using std::swap;
swap(success_, other.success_);
swap(message_, other.message_);
}
// Returns the assertion's negation. Used with EXPECT/ASSERT_FALSE.
AssertionResult AssertionResult::operator!() const {
AssertionResult negation(!success_);
if (message_.get() != nullptr) negation << *message_;
return negation;
}
// Makes a successful assertion result.
AssertionResult AssertionSuccess() { return AssertionResult(true); }
// Makes a failed assertion result.
AssertionResult AssertionFailure() { return AssertionResult(false); }
// Makes a failed assertion result with the given failure message.
// Deprecated; use AssertionFailure() << message.
AssertionResult AssertionFailure(const Message& message) {
return AssertionFailure() << message;
}
} // namespace testing

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,410 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gtest/internal/gtest-filepath.h"
#include <stdlib.h>
#include "gtest/gtest-message.h"
#include "gtest/internal/gtest-port.h"
#if GTEST_OS_WINDOWS_MOBILE
#include <windows.h>
#elif GTEST_OS_WINDOWS
#include <direct.h>
#include <io.h>
#else
#include <limits.h>
#include <climits> // Some Linux distributions define PATH_MAX here.
#endif // GTEST_OS_WINDOWS_MOBILE
#include "gtest/internal/gtest-string.h"
#if GTEST_OS_WINDOWS
#define GTEST_PATH_MAX_ _MAX_PATH
#elif defined(PATH_MAX)
#define GTEST_PATH_MAX_ PATH_MAX
#elif defined(_XOPEN_PATH_MAX)
#define GTEST_PATH_MAX_ _XOPEN_PATH_MAX
#else
#define GTEST_PATH_MAX_ _POSIX_PATH_MAX
#endif // GTEST_OS_WINDOWS
#if GTEST_HAS_FILE_SYSTEM
namespace testing {
namespace internal {
#if GTEST_OS_WINDOWS
// On Windows, '\\' is the standard path separator, but many tools and the
// Windows API also accept '/' as an alternate path separator. Unless otherwise
// noted, a file path can contain either kind of path separators, or a mixture
// of them.
const char kPathSeparator = '\\';
const char kAlternatePathSeparator = '/';
const char kAlternatePathSeparatorString[] = "/";
#if GTEST_OS_WINDOWS_MOBILE
// Windows CE doesn't have a current directory. You should not use
// the current directory in tests on Windows CE, but this at least
// provides a reasonable fallback.
const char kCurrentDirectoryString[] = "\\";
// Windows CE doesn't define INVALID_FILE_ATTRIBUTES
const DWORD kInvalidFileAttributes = 0xffffffff;
#else
const char kCurrentDirectoryString[] = ".\\";
#endif // GTEST_OS_WINDOWS_MOBILE
#else
const char kPathSeparator = '/';
const char kCurrentDirectoryString[] = "./";
#endif // GTEST_OS_WINDOWS
// Returns whether the given character is a valid path separator.
static bool IsPathSeparator(char c) {
#if GTEST_HAS_ALT_PATH_SEP_
return (c == kPathSeparator) || (c == kAlternatePathSeparator);
#else
return c == kPathSeparator;
#endif
}
// Returns the current working directory, or "" if unsuccessful.
FilePath FilePath::GetCurrentDir() {
#if GTEST_OS_WINDOWS_MOBILE || GTEST_OS_WINDOWS_PHONE || \
GTEST_OS_WINDOWS_RT || GTEST_OS_ESP8266 || GTEST_OS_ESP32 || \
GTEST_OS_XTENSA || GTEST_OS_QURT
// These platforms do not have a current directory, so we just return
// something reasonable.
return FilePath(kCurrentDirectoryString);
#elif GTEST_OS_WINDOWS
char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
return FilePath(_getcwd(cwd, sizeof(cwd)) == nullptr ? "" : cwd);
#else
char cwd[GTEST_PATH_MAX_ + 1] = {'\0'};
char* result = getcwd(cwd, sizeof(cwd));
#if GTEST_OS_NACL
// getcwd will likely fail in NaCl due to the sandbox, so return something
// reasonable. The user may have provided a shim implementation for getcwd,
// however, so fallback only when failure is detected.
return FilePath(result == nullptr ? kCurrentDirectoryString : cwd);
#endif // GTEST_OS_NACL
return FilePath(result == nullptr ? "" : cwd);
#endif // GTEST_OS_WINDOWS_MOBILE
}
// Returns a copy of the FilePath with the case-insensitive extension removed.
// Example: FilePath("dir/file.exe").RemoveExtension("EXE") returns
// FilePath("dir/file"). If a case-insensitive extension is not
// found, returns a copy of the original FilePath.
FilePath FilePath::RemoveExtension(const char* extension) const {
const std::string dot_extension = std::string(".") + extension;
if (String::EndsWithCaseInsensitive(pathname_, dot_extension)) {
return FilePath(
pathname_.substr(0, pathname_.length() - dot_extension.length()));
}
return *this;
}
// Returns a pointer to the last occurrence of a valid path separator in
// the FilePath. On Windows, for example, both '/' and '\' are valid path
// separators. Returns NULL if no path separator was found.
const char* FilePath::FindLastPathSeparator() const {
const char* const last_sep = strrchr(c_str(), kPathSeparator);
#if GTEST_HAS_ALT_PATH_SEP_
const char* const last_alt_sep = strrchr(c_str(), kAlternatePathSeparator);
// Comparing two pointers of which only one is NULL is undefined.
if (last_alt_sep != nullptr &&
(last_sep == nullptr || last_alt_sep > last_sep)) {
return last_alt_sep;
}
#endif
return last_sep;
}
size_t FilePath::CalculateRootLength() const {
const auto &path = pathname_;
auto s = path.begin();
auto end = path.end();
#if GTEST_OS_WINDOWS
if (end - s >= 2 && s[1] == ':' &&
(end - s == 2 || IsPathSeparator(s[2])) &&
(('A' <= s[0] && s[0] <= 'Z') || ('a' <= s[0] && s[0] <= 'z'))) {
// A typical absolute path like "C:\Windows" or "D:"
s += 2;
if (s != end) {
++s;
}
} else if (end - s >= 3 && IsPathSeparator(*s) && IsPathSeparator(*(s + 1))
&& !IsPathSeparator(*(s + 2))) {
// Move past the "\\" prefix in a UNC path like "\\Server\Share\Folder"
s += 2;
// Skip 2 components and their following separators ("Server\" and "Share\")
for (int i = 0; i < 2; ++i) {
while (s != end) {
bool stop = IsPathSeparator(*s);
++s;
if (stop) {
break;
}
}
}
} else if (s != end && IsPathSeparator(*s)) {
// A drive-rooted path like "\Windows"
++s;
}
#else
if (s != end && IsPathSeparator(*s)) {
++s;
}
#endif
return static_cast<size_t>(s - path.begin());
}
// Returns a copy of the FilePath with the directory part removed.
// Example: FilePath("path/to/file").RemoveDirectoryName() returns
// FilePath("file"). If there is no directory part ("just_a_file"), it returns
// the FilePath unmodified. If there is no file part ("just_a_dir/") it
// returns an empty FilePath ("").
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveDirectoryName() const {
const char* const last_sep = FindLastPathSeparator();
return last_sep ? FilePath(last_sep + 1) : *this;
}
// RemoveFileName returns the directory path with the filename removed.
// Example: FilePath("path/to/file").RemoveFileName() returns "path/to/".
// If the FilePath is "a_file" or "/a_file", RemoveFileName returns
// FilePath("./") or, on Windows, FilePath(".\\"). If the filepath does
// not have a file, like "just/a/dir/", it returns the FilePath unmodified.
// On Windows platform, '\' is the path separator, otherwise it is '/'.
FilePath FilePath::RemoveFileName() const {
const char* const last_sep = FindLastPathSeparator();
std::string dir;
if (last_sep) {
dir = std::string(c_str(), static_cast<size_t>(last_sep + 1 - c_str()));
} else {
dir = kCurrentDirectoryString;
}
return FilePath(dir);
}
// Helper functions for naming files in a directory for xml output.
// Given directory = "dir", base_name = "test", number = 0,
// extension = "xml", returns "dir/test.xml". If number is greater
// than zero (e.g., 12), returns "dir/test_12.xml".
// On Windows platform, uses \ as the separator rather than /.
FilePath FilePath::MakeFileName(const FilePath& directory,
const FilePath& base_name, int number,
const char* extension) {
std::string file;
if (number == 0) {
file = base_name.string() + "." + extension;
} else {
file =
base_name.string() + "_" + StreamableToString(number) + "." + extension;
}
return ConcatPaths(directory, FilePath(file));
}
// Given directory = "dir", relative_path = "test.xml", returns "dir/test.xml".
// On Windows, uses \ as the separator rather than /.
FilePath FilePath::ConcatPaths(const FilePath& directory,
const FilePath& relative_path) {
if (directory.IsEmpty()) return relative_path;
const FilePath dir(directory.RemoveTrailingPathSeparator());
return FilePath(dir.string() + kPathSeparator + relative_path.string());
}
// Returns true if pathname describes something findable in the file-system,
// either a file, directory, or whatever.
bool FilePath::FileOrDirectoryExists() const {
#if GTEST_OS_WINDOWS_MOBILE
LPCWSTR unicode = String::AnsiToUtf16(pathname_.c_str());
const DWORD attributes = GetFileAttributes(unicode);
delete[] unicode;
return attributes != kInvalidFileAttributes;
#else
posix::StatStruct file_stat{};
return posix::Stat(pathname_.c_str(), &file_stat) == 0;
#endif // GTEST_OS_WINDOWS_MOBILE
}
// Returns true if pathname describes a directory in the file-system
// that exists.
bool FilePath::DirectoryExists() const {
bool result = false;
#if GTEST_OS_WINDOWS
// Don't strip off trailing separator if path is a root directory on
// Windows (like "C:\\").
const FilePath& path(IsRootDirectory() ? *this
: RemoveTrailingPathSeparator());
#else
const FilePath& path(*this);
#endif
#if GTEST_OS_WINDOWS_MOBILE
LPCWSTR unicode = String::AnsiToUtf16(path.c_str());
const DWORD attributes = GetFileAttributes(unicode);
delete[] unicode;
if ((attributes != kInvalidFileAttributes) &&
(attributes & FILE_ATTRIBUTE_DIRECTORY)) {
result = true;
}
#else
posix::StatStruct file_stat{};
result =
posix::Stat(path.c_str(), &file_stat) == 0 && posix::IsDir(file_stat);
#endif // GTEST_OS_WINDOWS_MOBILE
return result;
}
// Returns true if pathname describes a root directory. (Windows has one
// root directory per disk drive. UNC share roots are also included.)
bool FilePath::IsRootDirectory() const {
size_t root_length = CalculateRootLength();
return root_length > 0 && root_length == pathname_.size() &&
IsPathSeparator(pathname_[root_length - 1]);
}
// Returns true if pathname describes an absolute path.
bool FilePath::IsAbsolutePath() const {
return CalculateRootLength() > 0;
}
// Returns a pathname for a file that does not currently exist. The pathname
// will be directory/base_name.extension or
// directory/base_name_<number>.extension if directory/base_name.extension
// already exists. The number will be incremented until a pathname is found
// that does not already exist.
// Examples: 'dir/foo_test.xml' or 'dir/foo_test_1.xml'.
// There could be a race condition if two or more processes are calling this
// function at the same time -- they could both pick the same filename.
FilePath FilePath::GenerateUniqueFileName(const FilePath& directory,
const FilePath& base_name,
const char* extension) {
FilePath full_pathname;
int number = 0;
do {
full_pathname.Set(MakeFileName(directory, base_name, number++, extension));
} while (full_pathname.FileOrDirectoryExists());
return full_pathname;
}
// Returns true if FilePath ends with a path separator, which indicates that
// it is intended to represent a directory. Returns false otherwise.
// This does NOT check that a directory (or file) actually exists.
bool FilePath::IsDirectory() const {
return !pathname_.empty() &&
IsPathSeparator(pathname_.c_str()[pathname_.length() - 1]);
}
// Create directories so that path exists. Returns true if successful or if
// the directories already exist; returns false if unable to create directories
// for any reason.
bool FilePath::CreateDirectoriesRecursively() const {
if (!this->IsDirectory()) {
return false;
}
if (pathname_.length() == 0 || this->DirectoryExists()) {
return true;
}
const FilePath parent(this->RemoveTrailingPathSeparator().RemoveFileName());
return parent.CreateDirectoriesRecursively() && this->CreateFolder();
}
// Create the directory so that path exists. Returns true if successful or
// if the directory already exists; returns false if unable to create the
// directory for any reason, including if the parent directory does not
// exist. Not named "CreateDirectory" because that's a macro on Windows.
bool FilePath::CreateFolder() const {
#if GTEST_OS_WINDOWS_MOBILE
FilePath removed_sep(this->RemoveTrailingPathSeparator());
LPCWSTR unicode = String::AnsiToUtf16(removed_sep.c_str());
int result = CreateDirectory(unicode, nullptr) ? 0 : -1;
delete[] unicode;
#elif GTEST_OS_WINDOWS
int result = _mkdir(pathname_.c_str());
#elif GTEST_OS_ESP8266 || GTEST_OS_XTENSA || GTEST_OS_QURT
// do nothing
int result = 0;
#else
int result = mkdir(pathname_.c_str(), 0777);
#endif // GTEST_OS_WINDOWS_MOBILE
if (result == -1) {
return this->DirectoryExists(); // An error is OK if the directory exists.
}
return true; // No error.
}
// If input name has a trailing separator character, remove it and return the
// name, otherwise return the name string unmodified.
// On Windows platform, uses \ as the separator, other platforms use /.
FilePath FilePath::RemoveTrailingPathSeparator() const {
return IsDirectory() ? FilePath(pathname_.substr(0, pathname_.length() - 1))
: *this;
}
// Removes any redundant separators that might be in the pathname.
// For example, "bar///foo" becomes "bar/foo". Does not eliminate other
// redundancies that might be in a pathname involving "." or "..".
// Note that "\\Host\Share" does not contain a redundancy on Windows!
void FilePath::Normalize() {
auto out = pathname_.begin();
auto i = pathname_.cbegin();
#if GTEST_OS_WINDOWS
// UNC paths are treated specially
if (pathname_.end() - i >= 3 && IsPathSeparator(*i) &&
IsPathSeparator(*(i + 1)) && !IsPathSeparator(*(i + 2))) {
*(out++) = kPathSeparator;
*(out++) = kPathSeparator;
}
#endif
while (i != pathname_.end()) {
const char character = *i;
if (!IsPathSeparator(character)) {
*(out++) = character;
} else if (out == pathname_.begin() || *std::prev(out) != kPathSeparator) {
*(out++) = kPathSeparator;
}
++i;
}
pathname_.erase(out, pathname_.end());
}
} // namespace internal
} // namespace testing
#endif // GTEST_HAS_FILE_SYSTEM

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,98 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// The Google C++ Testing and Mocking Framework (Google Test)
//
// This file implements just enough of the matcher interface to allow
// EXPECT_DEATH and friends to accept a matcher argument.
#include "gtest/gtest-matchers.h"
#include <string>
#include "gtest/internal/gtest-internal.h"
#include "gtest/internal/gtest-port.h"
namespace testing {
// Constructs a matcher that matches a const std::string& whose value is
// equal to s.
Matcher<const std::string&>::Matcher(const std::string& s) { *this = Eq(s); }
// Constructs a matcher that matches a const std::string& whose value is
// equal to s.
Matcher<const std::string&>::Matcher(const char* s) {
*this = Eq(std::string(s));
}
// Constructs a matcher that matches a std::string whose value is equal to
// s.
Matcher<std::string>::Matcher(const std::string& s) { *this = Eq(s); }
// Constructs a matcher that matches a std::string whose value is equal to
// s.
Matcher<std::string>::Matcher(const char* s) { *this = Eq(std::string(s)); }
#if GTEST_INTERNAL_HAS_STRING_VIEW
// Constructs a matcher that matches a const StringView& whose value is
// equal to s.
Matcher<const internal::StringView&>::Matcher(const std::string& s) {
*this = Eq(s);
}
// Constructs a matcher that matches a const StringView& whose value is
// equal to s.
Matcher<const internal::StringView&>::Matcher(const char* s) {
*this = Eq(std::string(s));
}
// Constructs a matcher that matches a const StringView& whose value is
// equal to s.
Matcher<const internal::StringView&>::Matcher(internal::StringView s) {
*this = Eq(std::string(s));
}
// Constructs a matcher that matches a StringView whose value is equal to
// s.
Matcher<internal::StringView>::Matcher(const std::string& s) { *this = Eq(s); }
// Constructs a matcher that matches a StringView whose value is equal to
// s.
Matcher<internal::StringView>::Matcher(const char* s) {
*this = Eq(std::string(s));
}
// Constructs a matcher that matches a StringView whose value is equal to
// s.
Matcher<internal::StringView>::Matcher(internal::StringView s) {
*this = Eq(std::string(s));
}
#endif // GTEST_INTERNAL_HAS_STRING_VIEW
} // namespace testing

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,553 @@
// Copyright 2007, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
// Google Test - The Google C++ Testing and Mocking Framework
//
// This file implements a universal value printer that can print a
// value of any type T:
//
// void ::testing::internal::UniversalPrinter<T>::Print(value, ostream_ptr);
//
// It uses the << operator when possible, and prints the bytes in the
// object otherwise. A user can override its behavior for a class
// type Foo by defining either operator<<(::std::ostream&, const Foo&)
// or void PrintTo(const Foo&, ::std::ostream*) in the namespace that
// defines Foo.
#include "gtest/gtest-printers.h"
#include <stdio.h>
#include <cctype>
#include <cstdint>
#include <cwchar>
#include <ostream> // NOLINT
#include <string>
#include <type_traits>
#include "gtest/internal/gtest-port.h"
#include "src/gtest-internal-inl.h"
namespace testing {
namespace {
using ::std::ostream;
// Prints a segment of bytes in the given object.
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_
GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_
void PrintByteSegmentInObjectTo(const unsigned char* obj_bytes, size_t start,
size_t count, ostream* os) {
char text[5] = "";
for (size_t i = 0; i != count; i++) {
const size_t j = start + i;
if (i != 0) {
// Organizes the bytes into groups of 2 for easy parsing by
// human.
if ((j % 2) == 0)
*os << ' ';
else
*os << '-';
}
GTEST_SNPRINTF_(text, sizeof(text), "%02X", obj_bytes[j]);
*os << text;
}
}
// Prints the bytes in the given value to the given ostream.
void PrintBytesInObjectToImpl(const unsigned char* obj_bytes, size_t count,
ostream* os) {
// Tells the user how big the object is.
*os << count << "-byte object <";
const size_t kThreshold = 132;
const size_t kChunkSize = 64;
// If the object size is bigger than kThreshold, we'll have to omit
// some details by printing only the first and the last kChunkSize
// bytes.
if (count < kThreshold) {
PrintByteSegmentInObjectTo(obj_bytes, 0, count, os);
} else {
PrintByteSegmentInObjectTo(obj_bytes, 0, kChunkSize, os);
*os << " ... ";
// Rounds up to 2-byte boundary.
const size_t resume_pos = (count - kChunkSize + 1) / 2 * 2;
PrintByteSegmentInObjectTo(obj_bytes, resume_pos, count - resume_pos, os);
}
*os << ">";
}
// Helpers for widening a character to char32_t. Since the standard does not
// specify if char / wchar_t is signed or unsigned, it is important to first
// convert it to the unsigned type of the same width before widening it to
// char32_t.
template <typename CharType>
char32_t ToChar32(CharType in) {
return static_cast<char32_t>(
static_cast<typename std::make_unsigned<CharType>::type>(in));
}
} // namespace
namespace internal {
// Delegates to PrintBytesInObjectToImpl() to print the bytes in the
// given object. The delegation simplifies the implementation, which
// uses the << operator and thus is easier done outside of the
// ::testing::internal namespace, which contains a << operator that
// sometimes conflicts with the one in STL.
void PrintBytesInObjectTo(const unsigned char* obj_bytes, size_t count,
ostream* os) {
PrintBytesInObjectToImpl(obj_bytes, count, os);
}
// Depending on the value of a char (or wchar_t), we print it in one
// of three formats:
// - as is if it's a printable ASCII (e.g. 'a', '2', ' '),
// - as a hexadecimal escape sequence (e.g. '\x7F'), or
// - as a special escape sequence (e.g. '\r', '\n').
enum CharFormat { kAsIs, kHexEscape, kSpecialEscape };
// Returns true if c is a printable ASCII character. We test the
// value of c directly instead of calling isprint(), which is buggy on
// Windows Mobile.
inline bool IsPrintableAscii(char32_t c) { return 0x20 <= c && c <= 0x7E; }
// Prints c (of type char, char8_t, char16_t, char32_t, or wchar_t) as a
// character literal without the quotes, escaping it when necessary; returns how
// c was formatted.
template <typename Char>
static CharFormat PrintAsCharLiteralTo(Char c, ostream* os) {
const char32_t u_c = ToChar32(c);
switch (u_c) {
case L'\0':
*os << "\\0";
break;
case L'\'':
*os << "\\'";
break;
case L'\\':
*os << "\\\\";
break;
case L'\a':
*os << "\\a";
break;
case L'\b':
*os << "\\b";
break;
case L'\f':
*os << "\\f";
break;
case L'\n':
*os << "\\n";
break;
case L'\r':
*os << "\\r";
break;
case L'\t':
*os << "\\t";
break;
case L'\v':
*os << "\\v";
break;
default:
if (IsPrintableAscii(u_c)) {
*os << static_cast<char>(c);
return kAsIs;
} else {
ostream::fmtflags flags = os->flags();
*os << "\\x" << std::hex << std::uppercase << static_cast<int>(u_c);
os->flags(flags);
return kHexEscape;
}
}
return kSpecialEscape;
}
// Prints a char32_t c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsStringLiteralTo(char32_t c, ostream* os) {
switch (c) {
case L'\'':
*os << "'";
return kAsIs;
case L'"':
*os << "\\\"";
return kSpecialEscape;
default:
return PrintAsCharLiteralTo(c, os);
}
}
static const char* GetCharWidthPrefix(char) { return ""; }
static const char* GetCharWidthPrefix(signed char) { return ""; }
static const char* GetCharWidthPrefix(unsigned char) { return ""; }
#ifdef __cpp_char8_t
static const char* GetCharWidthPrefix(char8_t) { return "u8"; }
#endif
static const char* GetCharWidthPrefix(char16_t) { return "u"; }
static const char* GetCharWidthPrefix(char32_t) { return "U"; }
static const char* GetCharWidthPrefix(wchar_t) { return "L"; }
// Prints a char c as if it's part of a string literal, escaping it when
// necessary; returns how c was formatted.
static CharFormat PrintAsStringLiteralTo(char c, ostream* os) {
return PrintAsStringLiteralTo(ToChar32(c), os);
}
#ifdef __cpp_char8_t
static CharFormat PrintAsStringLiteralTo(char8_t c, ostream* os) {
return PrintAsStringLiteralTo(ToChar32(c), os);
}
#endif
static CharFormat PrintAsStringLiteralTo(char16_t c, ostream* os) {
return PrintAsStringLiteralTo(ToChar32(c), os);
}
static CharFormat PrintAsStringLiteralTo(wchar_t c, ostream* os) {
return PrintAsStringLiteralTo(ToChar32(c), os);
}
// Prints a character c (of type char, char8_t, char16_t, char32_t, or wchar_t)
// and its code. '\0' is printed as "'\\0'", other unprintable characters are
// also properly escaped using the standard C++ escape sequence.
template <typename Char>
void PrintCharAndCodeTo(Char c, ostream* os) {
// First, print c as a literal in the most readable form we can find.
*os << GetCharWidthPrefix(c) << "'";
const CharFormat format = PrintAsCharLiteralTo(c, os);
*os << "'";
// To aid user debugging, we also print c's code in decimal, unless
// it's 0 (in which case c was printed as '\\0', making the code
// obvious).
if (c == 0) return;
*os << " (" << static_cast<int>(c);
// For more convenience, we print c's code again in hexadecimal,
// unless c was already printed in the form '\x##' or the code is in
// [1, 9].
if (format == kHexEscape || (1 <= c && c <= 9)) {
// Do nothing.
} else {
*os << ", 0x" << String::FormatHexInt(static_cast<int>(c));
}
*os << ")";
}
void PrintTo(unsigned char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
void PrintTo(signed char c, ::std::ostream* os) { PrintCharAndCodeTo(c, os); }
// Prints a wchar_t as a symbol if it is printable or as its internal
// code otherwise and also as its code. L'\0' is printed as "L'\\0'".
void PrintTo(wchar_t wc, ostream* os) { PrintCharAndCodeTo(wc, os); }
// TODO(dcheng): Consider making this delegate to PrintCharAndCodeTo() as well.
void PrintTo(char32_t c, ::std::ostream* os) {
*os << std::hex << "U+" << std::uppercase << std::setfill('0') << std::setw(4)
<< static_cast<uint32_t>(c);
}
// gcc/clang __{u,}int128_t
#if defined(__SIZEOF_INT128__)
void PrintTo(__uint128_t v, ::std::ostream* os) {
if (v == 0) {
*os << "0";
return;
}
// Buffer large enough for ceil(log10(2^128))==39 and the null terminator
char buf[40];
char* p = buf + sizeof(buf);
// Some configurations have a __uint128_t, but no support for built in
// division. Do manual long division instead.
uint64_t high = static_cast<uint64_t>(v >> 64);
uint64_t low = static_cast<uint64_t>(v);
*--p = 0;
while (high != 0 || low != 0) {
uint64_t high_mod = high % 10;
high = high / 10;
// This is the long division algorithm specialized for a divisor of 10 and
// only two elements.
// Notable values:
// 2^64 / 10 == 1844674407370955161
// 2^64 % 10 == 6
const uint64_t carry = 6 * high_mod + low % 10;
low = low / 10 + high_mod * 1844674407370955161 + carry / 10;
char digit = static_cast<char>(carry % 10);
*--p = static_cast<char>('0' + digit);
}
*os << p;
}
void PrintTo(__int128_t v, ::std::ostream* os) {
__uint128_t uv = static_cast<__uint128_t>(v);
if (v < 0) {
*os << "-";
uv = -uv;
}
PrintTo(uv, os);
}
#endif // __SIZEOF_INT128__
// Prints the given array of characters to the ostream. CharType must be either
// char, char8_t, char16_t, char32_t, or wchar_t.
// The array starts at begin, the length is len, it may include '\0' characters
// and may not be NUL-terminated.
template <typename CharType>
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ static CharFormat
PrintCharsAsStringTo(const CharType* begin, size_t len, ostream* os) {
const char* const quote_prefix = GetCharWidthPrefix(*begin);
*os << quote_prefix << "\"";
bool is_previous_hex = false;
CharFormat print_format = kAsIs;
for (size_t index = 0; index < len; ++index) {
const CharType cur = begin[index];
if (is_previous_hex && IsXDigit(cur)) {
// Previous character is of '\x..' form and this character can be
// interpreted as another hexadecimal digit in its number. Break string to
// disambiguate.
*os << "\" " << quote_prefix << "\"";
}
is_previous_hex = PrintAsStringLiteralTo(cur, os) == kHexEscape;
// Remember if any characters required hex escaping.
if (is_previous_hex) {
print_format = kHexEscape;
}
}
*os << "\"";
return print_format;
}
// Prints a (const) char/wchar_t array of 'len' elements, starting at address
// 'begin'. CharType must be either char or wchar_t.
template <typename CharType>
GTEST_ATTRIBUTE_NO_SANITIZE_MEMORY_ GTEST_ATTRIBUTE_NO_SANITIZE_ADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_HWADDRESS_
GTEST_ATTRIBUTE_NO_SANITIZE_THREAD_ static void
UniversalPrintCharArray(const CharType* begin, size_t len,
ostream* os) {
// The code
// const char kFoo[] = "foo";
// generates an array of 4, not 3, elements, with the last one being '\0'.
//
// Therefore when printing a char array, we don't print the last element if
// it's '\0', such that the output matches the string literal as it's
// written in the source code.
if (len > 0 && begin[len - 1] == '\0') {
PrintCharsAsStringTo(begin, len - 1, os);
return;
}
// If, however, the last element in the array is not '\0', e.g.
// const char kFoo[] = { 'f', 'o', 'o' };
// we must print the entire array. We also print a message to indicate
// that the array is not NUL-terminated.
PrintCharsAsStringTo(begin, len, os);
*os << " (no terminating NUL)";
}
// Prints a (const) char array of 'len' elements, starting at address 'begin'.
void UniversalPrintArray(const char* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
#ifdef __cpp_char8_t
// Prints a (const) char8_t array of 'len' elements, starting at address
// 'begin'.
void UniversalPrintArray(const char8_t* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
#endif
// Prints a (const) char16_t array of 'len' elements, starting at address
// 'begin'.
void UniversalPrintArray(const char16_t* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
// Prints a (const) char32_t array of 'len' elements, starting at address
// 'begin'.
void UniversalPrintArray(const char32_t* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
// Prints a (const) wchar_t array of 'len' elements, starting at address
// 'begin'.
void UniversalPrintArray(const wchar_t* begin, size_t len, ostream* os) {
UniversalPrintCharArray(begin, len, os);
}
namespace {
// Prints a null-terminated C-style string to the ostream.
template <typename Char>
void PrintCStringTo(const Char* s, ostream* os) {
if (s == nullptr) {
*os << "NULL";
} else {
*os << ImplicitCast_<const void*>(s) << " pointing to ";
PrintCharsAsStringTo(s, std::char_traits<Char>::length(s), os);
}
}
} // anonymous namespace
void PrintTo(const char* s, ostream* os) { PrintCStringTo(s, os); }
#ifdef __cpp_char8_t
void PrintTo(const char8_t* s, ostream* os) { PrintCStringTo(s, os); }
#endif
void PrintTo(const char16_t* s, ostream* os) { PrintCStringTo(s, os); }
void PrintTo(const char32_t* s, ostream* os) { PrintCStringTo(s, os); }
// MSVC compiler can be configured to define whar_t as a typedef
// of unsigned short. Defining an overload for const wchar_t* in that case
// would cause pointers to unsigned shorts be printed as wide strings,
// possibly accessing more memory than intended and causing invalid
// memory accesses. MSVC defines _NATIVE_WCHAR_T_DEFINED symbol when
// wchar_t is implemented as a native type.
#if !defined(_MSC_VER) || defined(_NATIVE_WCHAR_T_DEFINED)
// Prints the given wide C string to the ostream.
void PrintTo(const wchar_t* s, ostream* os) { PrintCStringTo(s, os); }
#endif // wchar_t is native
namespace {
bool ContainsUnprintableControlCodes(const char* str, size_t length) {
const unsigned char* s = reinterpret_cast<const unsigned char*>(str);
for (size_t i = 0; i < length; i++) {
unsigned char ch = *s++;
if (std::iscntrl(ch)) {
switch (ch) {
case '\t':
case '\n':
case '\r':
break;
default:
return true;
}
}
}
return false;
}
bool IsUTF8TrailByte(unsigned char t) { return 0x80 <= t && t <= 0xbf; }
bool IsValidUTF8(const char* str, size_t length) {
const unsigned char* s = reinterpret_cast<const unsigned char*>(str);
for (size_t i = 0; i < length;) {
unsigned char lead = s[i++];
if (lead <= 0x7f) {
continue; // single-byte character (ASCII) 0..7F
}
if (lead < 0xc2) {
return false; // trail byte or non-shortest form
} else if (lead <= 0xdf && (i + 1) <= length && IsUTF8TrailByte(s[i])) {
++i; // 2-byte character
} else if (0xe0 <= lead && lead <= 0xef && (i + 2) <= length &&
IsUTF8TrailByte(s[i]) && IsUTF8TrailByte(s[i + 1]) &&
// check for non-shortest form and surrogate
(lead != 0xe0 || s[i] >= 0xa0) &&
(lead != 0xed || s[i] < 0xa0)) {
i += 2; // 3-byte character
} else if (0xf0 <= lead && lead <= 0xf4 && (i + 3) <= length &&
IsUTF8TrailByte(s[i]) && IsUTF8TrailByte(s[i + 1]) &&
IsUTF8TrailByte(s[i + 2]) &&
// check for non-shortest form
(lead != 0xf0 || s[i] >= 0x90) &&
(lead != 0xf4 || s[i] < 0x90)) {
i += 3; // 4-byte character
} else {
return false;
}
}
return true;
}
void ConditionalPrintAsText(const char* str, size_t length, ostream* os) {
if (!ContainsUnprintableControlCodes(str, length) &&
IsValidUTF8(str, length)) {
*os << "\n As Text: \"" << str << "\"";
}
}
} // anonymous namespace
void PrintStringTo(const ::std::string& s, ostream* os) {
if (PrintCharsAsStringTo(s.data(), s.size(), os) == kHexEscape) {
if (GTEST_FLAG_GET(print_utf8)) {
ConditionalPrintAsText(s.data(), s.size(), os);
}
}
}
#ifdef __cpp_char8_t
void PrintU8StringTo(const ::std::u8string& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif
void PrintU16StringTo(const ::std::u16string& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
void PrintU32StringTo(const ::std::u32string& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#if GTEST_HAS_STD_WSTRING
void PrintWideStringTo(const ::std::wstring& s, ostream* os) {
PrintCharsAsStringTo(s.data(), s.size(), os);
}
#endif // GTEST_HAS_STD_WSTRING
} // namespace internal
} // namespace testing

View File

@ -0,0 +1,105 @@
// Copyright 2008, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
//
// The Google C++ Testing and Mocking Framework (Google Test)
#include "gtest/gtest-test-part.h"
#include "gtest/internal/gtest-port.h"
#include "src/gtest-internal-inl.h"
namespace testing {
using internal::GetUnitTestImpl;
// Gets the summary of the failure message by omitting the stack trace
// in it.
std::string TestPartResult::ExtractSummary(const char* message) {
const char* const stack_trace = strstr(message, internal::kStackTraceMarker);
return stack_trace == nullptr ? message : std::string(message, stack_trace);
}
// Prints a TestPartResult object.
std::ostream& operator<<(std::ostream& os, const TestPartResult& result) {
return os << internal::FormatFileLocation(result.file_name(),
result.line_number())
<< " "
<< (result.type() == TestPartResult::kSuccess ? "Success"
: result.type() == TestPartResult::kSkip ? "Skipped"
: result.type() == TestPartResult::kFatalFailure
? "Fatal failure"
: "Non-fatal failure")
<< ":\n"
<< result.message() << std::endl;
}
// Appends a TestPartResult to the array.
void TestPartResultArray::Append(const TestPartResult& result) {
array_.push_back(result);
}
// Returns the TestPartResult at the given index (0-based).
const TestPartResult& TestPartResultArray::GetTestPartResult(int index) const {
if (index < 0 || index >= size()) {
printf("\nInvalid index (%d) into TestPartResultArray.\n", index);
internal::posix::Abort();
}
return array_[static_cast<size_t>(index)];
}
// Returns the number of TestPartResult objects in the array.
int TestPartResultArray::size() const {
return static_cast<int>(array_.size());
}
namespace internal {
HasNewFatalFailureHelper::HasNewFatalFailureHelper()
: has_new_fatal_failure_(false),
original_reporter_(
GetUnitTestImpl()->GetTestPartResultReporterForCurrentThread()) {
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(this);
}
HasNewFatalFailureHelper::~HasNewFatalFailureHelper() {
GetUnitTestImpl()->SetTestPartResultReporterForCurrentThread(
original_reporter_);
}
void HasNewFatalFailureHelper::ReportTestPartResult(
const TestPartResult& result) {
if (result.fatally_failed()) has_new_fatal_failure_ = true;
original_reporter_->ReportTestPartResult(result);
}
} // namespace internal
} // namespace testing

View File

@ -0,0 +1,104 @@
// Copyright 2008 Google Inc.
// All Rights Reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include "gtest/gtest-typed-test.h"
#include "gtest/gtest.h"
namespace testing {
namespace internal {
// Skips to the first non-space char in str. Returns an empty string if str
// contains only whitespace characters.
static const char* SkipSpaces(const char* str) {
while (IsSpace(*str)) str++;
return str;
}
static std::vector<std::string> SplitIntoTestNames(const char* src) {
std::vector<std::string> name_vec;
src = SkipSpaces(src);
for (; src != nullptr; src = SkipComma(src)) {
name_vec.push_back(StripTrailingSpaces(GetPrefixUntilComma(src)));
}
return name_vec;
}
// Verifies that registered_tests match the test names in
// registered_tests_; returns registered_tests if successful, or
// aborts the program otherwise.
const char* TypedTestSuitePState::VerifyRegisteredTestNames(
const char* test_suite_name, const char* file, int line,
const char* registered_tests) {
RegisterTypeParameterizedTestSuite(test_suite_name, CodeLocation(file, line));
typedef RegisteredTestsMap::const_iterator RegisteredTestIter;
registered_ = true;
std::vector<std::string> name_vec = SplitIntoTestNames(registered_tests);
Message errors;
std::set<std::string> tests;
for (std::vector<std::string>::const_iterator name_it = name_vec.begin();
name_it != name_vec.end(); ++name_it) {
const std::string& name = *name_it;
if (tests.count(name) != 0) {
errors << "Test " << name << " is listed more than once.\n";
continue;
}
if (registered_tests_.count(name) != 0) {
tests.insert(name);
} else {
errors << "No test named " << name
<< " can be found in this test suite.\n";
}
}
for (RegisteredTestIter it = registered_tests_.begin();
it != registered_tests_.end(); ++it) {
if (tests.count(it->first) == 0) {
errors << "You forgot to list test " << it->first << ".\n";
}
}
const std::string& errors_str = errors.GetString();
if (errors_str != "") {
fprintf(stderr, "%s %s", FormatFileLocation(file, line).c_str(),
errors_str.c_str());
fflush(stderr);
posix::Abort();
}
return registered_tests;
}
} // namespace internal
} // namespace testing

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,65 @@
// Copyright 2006, Google Inc.
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
// * Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
// * Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
// * Neither the name of Google Inc. nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
#include <cstdio>
#include "gtest/gtest.h"
#if GTEST_OS_ESP8266 || GTEST_OS_ESP32
// Arduino-like platforms: program entry points are setup/loop instead of main.
#if GTEST_OS_ESP8266
extern "C" {
#endif
void setup() { testing::InitGoogleTest(); }
void loop() { RUN_ALL_TESTS(); }
#if GTEST_OS_ESP8266
}
#endif
#elif GTEST_OS_QURT
// QuRT: program entry point is main, but argc/argv are unusable.
GTEST_API_ int main() {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest();
return RUN_ALL_TESTS();
}
#else
// Normal platforms: program entry point is main, argc/argv are initialized.
GTEST_API_ int main(int argc, char **argv) {
printf("Running main() from %s\n", __FILE__);
testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}
#endif

View File

@ -0,0 +1,67 @@
#include "preserves.hpp"
#include <fstream>
#include <sstream>
#include "googletest/gtest/gtest.h"
using namespace std;
using namespace Preserves;
TEST(Value, Basics) {
auto vs = Value<>::from(vector<Value<>>{
Value<>::from(1),
Value<>::from(2.0),
Value<>::from("three"),
});
ASSERT_EQ(3U, vs.size());
ASSERT_EQ(1U, vs[0].to_unsigned());
ASSERT_EQ(1.0, vs[0].to_double());
ASSERT_EQ(2.0, vs[1].to_double());
ASSERT_EQ("three", vs[2].to_string());
ASSERT_EQ(ValueKind::Sequence, vs.value_kind());
ASSERT_EQ(ValueKind::String, vs[2].value_kind());
}
TEST(BinaryReader, Negative257) {
istringstream input("\xB0\x02\xFE\xFF");
auto v = BinaryReader<>(input).next();
ASSERT_TRUE(v);
ASSERT_EQ(v->to_signed(), -257);
}
TEST(BinaryReader, Negative127) {
istringstream input("\xB0\x01\x81");
auto v = BinaryReader<>(input).next();
ASSERT_TRUE(v);
ASSERT_EQ(v->to_signed(), -127);
}
TEST(BinaryWriter, Negative257) {
ostringstream s;
BinaryWriter w(s);
w << -257;
std::string output(s.str());
ASSERT_EQ(output[0], char(BinaryTag::SignedInteger));
ASSERT_EQ(output[1], char(0x02));
ASSERT_EQ(output[2], char(0xFE));
ASSERT_EQ(output[3], char(0xFF));
}
TEST(BinaryWriter, Negative127) {
ostringstream s;
BinaryWriter w(s);
w << -127;
std::string output(s.str());
ASSERT_EQ(output[0], char(BinaryTag::SignedInteger));
ASSERT_EQ(output[1], char(0x01));
ASSERT_EQ(output[2], char(0x81));
}
TEST(BinaryReader, ReadSamples) {
ifstream f("../../tests/samples.bin", ios::binary);
BinaryReader<> r(f, &GenericEmbedded::wrap);
auto v = r.next();
ASSERT_TRUE(v);
// BinaryWriter(cerr) << *v;
}

View File

@ -0,0 +1,7 @@
#pragma once
#include "preserves_value.hpp"
#include "preserves_text.hpp"
#include "preserves_binary_writer.hpp"
#include "preserves_impl.hpp"
#include "preserves_binary_reader.hpp"

View File

@ -0,0 +1,189 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <iostream>
#include <functional>
#include <type_traits>
namespace Preserves {
template <typename T = class GenericEmbedded>
class BinaryReader {
std::istream& i;
std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded;
bool next_chunk(void* p, size_t n) {
i.read(static_cast<char *>(p), n);
return i.good();
}
public:
static boost::optional<uint64_t> varint(std::istream &i) {
uint64_t n = 0;
// Can read max 9 bytes, each with 7 bits of payload, for 9*7 = 63 bits.
for (size_t count = 0; count < 9; count++) {
int b = i.get();
if (i.eof()) return boost::none;
n |= (b & 0x7f) << (count * 7);
if ((b & 0x80) == 0) {
return n;
}
}
return boost::none;
}
BinaryReader(typename std::enable_if<std::is_same<T, GenericEmbedded>::value, std::istream&>::type i) :
BinaryReader(i, &GenericEmbedded::wrap)
{}
BinaryReader(std::istream& i, std::function<std::shared_ptr<T>(Value<>)> decodeEmbedded) :
i(i),
decodeEmbedded(decodeEmbedded)
{}
boost::optional<double> next_double() {
uint8_t buf[8];
if (!next_chunk(buf, sizeof(buf))) return boost::none;
uint32_t n1 = buf[0] << 24 | buf[1] << 16 | buf[2] << 8 | buf[3];
uint32_t n2 = buf[4] << 24 | buf[5] << 16 | buf[6] << 8 | buf[7];
uint64_t n = uint64_t(n1) << 32 | n2;
double d;
memcpy(&d, &n, sizeof(d));
return d;
}
boost::optional<Value<T>> next_machineword(size_t n, bool always_unsigned) {
uint8_t buf[n];
if (!next_chunk(buf, n)) return boost::none;
if ((buf[0] & 0x80) && !always_unsigned) {
int64_t v = -1;
for (size_t j = 0; j < n; j++) v = (v << 8) | buf[j];
return Value<T>::from_int(v);
} else {
uint64_t v = 0;
for (size_t j = 0; j < n; j++) v = (v << 8) | buf[j];
return Value<T>::from_int(v);
}
}
boost::optional<Value<T>> next_bignum(size_t n) {
auto b = std::make_shared<BigNum<T>>(std::vector<uint8_t>());
b->_value().resize(n);
if (!next_chunk(&b->_value()[0], n)) return boost::none;
return Value<T>(b);
}
boost::optional<Value<T>> next() {
bool end_sentinel;
return _next(end_sentinel);
}
boost::optional<Value<T>> _next(bool& end_sentinel) {
end_sentinel = false;
auto tag = BinaryTag(i.get());
// std::cout << "tag " << std::hex << int(tag) << " pos " << i.tellg() - 1 << std::endl;
if (i.eof()) return boost::none;
switch (tag) {
case BinaryTag::False: return Value<T>::from_bool(false);
case BinaryTag::True: return Value<T>::from_bool(true);
case BinaryTag::End: end_sentinel = true; return boost::none;
case BinaryTag::Annotation: {
std::vector<Value<T>> annotations;
while (true) {
auto ann = next();
if (!ann) return boost::none;
annotations.push_back(*ann);
if (BinaryTag(i.peek()) != BinaryTag::Annotation) break;
i.get();
}
auto underlying = next();
if (!underlying) return boost::none;
return Value<T>(new AnnotatedValue<T>(std::move(annotations), *underlying));
}
case BinaryTag::Embedded:
return BinaryReader<>(i).next().map(decodeEmbedded).map(Value<T>::from_embedded);
case BinaryTag::Ieee754: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
switch (len) {
case 8: return next_double().map(Value<T>::from_double);
default: return boost::none;
}
});
case BinaryTag::SignedInteger: return varint(i).flat_map([&](size_t n)-> boost::optional<Value<T>> {
if (n == 0) return Value<T>::from_int(uint64_t(0));
if (n < 9) return next_machineword(n, false);
if (n == 9) {
// We can handle this with uint64_t if it's unsigned and the first byte is 0.
if (i.peek() == 0) {
i.get();
return next_machineword(8, true);
}
}
return next_bignum(n);
});
case BinaryTag::String: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
auto s = std::make_shared<String<T>>(std::string());
s->_value().resize(len);
if (!next_chunk(&s->_value()[0], len)) return boost::none;
return Value<T>(s);
});
case BinaryTag::ByteString: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
auto s = std::make_shared<ByteString<T>>(std::vector<uint8_t>());
s->_value().resize(len);
if (!next_chunk(&s->_value()[0], len)) return boost::none;
return Value<T>(s);
});
case BinaryTag::Symbol: return varint(i).flat_map([&](size_t len)-> boost::optional<Value<T>> {
auto s = std::make_shared<Symbol<T>>(std::string());
s->_value().resize(len);
if (!next_chunk(&s->_value()[0], len)) return boost::none;
return Value<T>(s);
});
case BinaryTag::Record: return next().flat_map([&](auto label)-> boost::optional<Value<T>> {
auto r = std::make_shared<Record<T>>(label);
while (true) {
bool end_rec = false;
auto v = _next(end_rec);
if (end_rec) return Value<T>(r);
if (!v) return boost::none;
r->fields.push_back(*v);
}
});
case BinaryTag::Sequence: {
auto s = std::make_shared<Sequence<T>>();
while (true) {
bool end_rec = false;
auto v = _next(end_rec);
if (end_rec) return Value<T>(s);
if (!v) return boost::none;
s->values.push_back(*v);
}
}
case BinaryTag::Set: {
auto s = std::make_shared<Set<T>>();
while (true) {
bool end_rec = false;
auto v = _next(end_rec);
if (end_rec) return Value<T>(s);
if (!v) return boost::none;
s->values.insert(*v);
}
}
case BinaryTag::Dictionary: {
auto s = std::make_shared<Dictionary<T>>();
while (true) {
bool end_rec = false;
auto k = _next(end_rec);
if (end_rec) return Value<T>(s);
if (!k) return boost::none;
auto v = next();
if (!v) return boost::none;
s->values.emplace(*k, *v);
}
}
default:
return boost::none;
}
}
};
}

View File

@ -0,0 +1,232 @@
#pragma once
#include <cstdint>
#include <cstring>
#include <iostream>
#include <functional>
#include <type_traits>
#include <limits>
namespace Preserves {
enum class BinaryTag {
False = 0x80,
True = 0x81,
End = 0x84,
Annotation = 0x85,
Embedded = 0x86,
Ieee754 = 0x87,
SignedInteger = 0xb0,
String = 0xb1,
ByteString = 0xb2,
Symbol = 0xb3,
Record = 0xb4,
Sequence = 0xb5,
Set = 0xb6,
Dictionary = 0xb7,
};
template <uint64_t wholeTest, uint64_t bitTest>
int _shift_for(uint64_t u) {
int shift = 56;
while (true) {
if (shift == 0) break;
if (((u >> shift) & 0xff) != wholeTest) break;
shift -= 8;
if (((u >> shift) & 0x80) != bitTest) {
shift += 8;
break;
}
}
return shift;
}
class BinaryWriter {
std::ostream& o;
template <typename Collection>
struct _iterable {
Collection const& c;
};
template <typename Collection>
_iterable<Collection> iterable(Collection const& c) { return _iterable<Collection>{c}; }
public:
BinaryWriter(std::ostream& o) : o(o) {}
std::ostream& stream() const { return o; }
BinaryWriter& put(uint8_t b) {
o.put(b);
return *this;
}
BinaryWriter& operator<<(BinaryTag const& t) {
return put(uint8_t(t));
}
BinaryWriter& write(void const* buf, size_t size) {
o.write(reinterpret_cast<char const*>(buf), size);
return *this;
}
BinaryWriter& varint(size_t n) {
while (n >= 0x80) {
put(uint8_t(0x80 | (n & 0x7f)));
n >>= 7;
}
return put(uint8_t(n & 0x7f));
}
BinaryWriter& write(BinaryTag const& tag, void const* buf, size_t size) {
(*this) << tag;
varint(size);
return write(buf, size);
}
BinaryWriter& bignum(std::vector<uint8_t> const& n) {
size_t startOffset = 0;
if (n.size() > 0) {
if (n[0] & 0x80) {
while (true) {
if (startOffset == n.size() - 1) break;
if (n[startOffset] != 0xff) break;
startOffset++;
if ((n[startOffset] & 0x80) != 0x80) {
startOffset--;
break;
}
}
} else {
while (true) {
if (startOffset == n.size() - 1) break;
if (n[startOffset] != 0x00) break;
startOffset++;
if ((n[startOffset] & 0x80) != 0x00) {
startOffset--;
break;
}
}
}
}
size_t count = n.size() - startOffset;
(*this) << BinaryTag::SignedInteger;
varint(count);
return write(&n[startOffset], count);
}
BinaryWriter& string(std::string const& s) {
return write(BinaryTag::String, &s[0], s.size());
}
BinaryWriter& symbol(std::string const& s) {
return write(BinaryTag::Symbol, &s[0], s.size());
}
BinaryWriter& bytes(std::vector<uint8_t> const& v) {
return bytes(v.data(), v.size());
}
BinaryWriter& bytes(void const* buf, size_t size) {
return write(BinaryTag::ByteString, buf, size);
}
template <typename T = class GenericEmbedded>
BinaryWriter& operator<<(Value<T> const& v) {
return v->write(*this);
}
template <typename Iter>
BinaryWriter& writeseq(Iter const& begin, Iter const& end) {
for (Iter i = begin; i != end; ++i) (*this) << (*i);
return *this;
}
template <typename Collection>
BinaryWriter& operator<<(_iterable<Collection> const& c) {
return writeseq(c.c.begin(), c.c.end());
}
BinaryWriter& operator<<(bool b) {
return (*this) << (b ? BinaryTag::True : BinaryTag::False);
}
BinaryWriter& operator<<(double d) {
uint64_t n;
memcpy(&n, &d, sizeof(d));
uint8_t buf[8];
buf[0] = (n >> 56) & 0xff;
buf[1] = (n >> 48) & 0xff;
buf[2] = (n >> 40) & 0xff;
buf[3] = (n >> 32) & 0xff;
buf[4] = (n >> 24) & 0xff;
buf[5] = (n >> 16) & 0xff;
buf[6] = (n >> 8) & 0xff;
buf[7] = (n) & 0xff;
(*this) << BinaryTag::Ieee754;
put(uint8_t(sizeof(buf)));
return write(buf, sizeof(buf));
}
BinaryWriter& _put_medium_int(uint64_t u, int shift, int extra) {
(*this) << BinaryTag::SignedInteger;
put(uint8_t((shift >> 3) + extra + 1));
if (extra) put(0);
while (shift >= 0) {
put(uint8_t((u >> shift) & 0xff));
shift -= 8;
}
return *this;
}
template <typename T>
typename std::enable_if<std::is_integral<T>::value, BinaryWriter&>::type operator<<(T t) {
if (t == 0) {
(*this) << BinaryTag::SignedInteger;
put(0);
return *this;
} else if (std::numeric_limits<T>::is_signed) {
auto i = static_cast<int64_t>(t);
uint64_t u;
memcpy(&u, &i, sizeof(i));
int shift = i < 0 ? _shift_for<0xff, 0x80>(u) : _shift_for<0x00, 0x00>(u);
return _put_medium_int(u, shift, 0);
} else {
auto u = static_cast<uint64_t>(t);
if ((u & 0x8000000000000000) != 0) {
return _put_medium_int(u, 56, 1);
} else {
return _put_medium_int(u, _shift_for<0x00, 0x00>(u), 0);
}
}
}
template <typename T>
BinaryWriter& record(Value<T> const& label, std::vector<Value<T>> const& fields) {
return (*this) << BinaryTag::Record << label << iterable(fields) << BinaryTag::End;
}
template <typename T>
BinaryWriter& sequence(std::vector<Value<T>> const& vs) {
return (*this) << BinaryTag::Sequence << iterable(vs) << BinaryTag::End;
}
template <typename T>
BinaryWriter& set(std::set<Value<T>> const& vs) {
return (*this) << BinaryTag::Set << iterable(vs) << BinaryTag::End;
}
template <typename T>
BinaryWriter& dictionary(std::map<Value<T>, Value<T>> const& vs) {
(*this) << BinaryTag::Dictionary;
for (auto& i : vs) { (*this) << i.first << i.second; }
return (*this) << BinaryTag::End;
}
template <typename T>
BinaryWriter& annotated(Value<T> const& underlying, std::vector<Value<T>> const& vs) {
for (auto& v : vs) { (*this) << BinaryTag::Annotation << v; }
return (*this) << underlying;
}
};
}

View File

@ -0,0 +1,367 @@
#pragma once
#include <memory>
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <limits>
#include <boost/optional.hpp>
namespace Preserves {
template <typename T, typename Atom, ValueKind kind>
class Atomic: public ValueImpl<T> {
protected:
Atom value;
public:
Atomic(Atom const& value) : value(value) {}
Atom& _value() { return value; }
Atom const& _value() const { return value; }
ValueKind value_kind() const { return kind; }
};
#define PRESERVES_ATOMIC_VALUE_CLASS(Name, a_t, r_t, VK, getter, extra) \
template <typename T = class GenericEmbedded> \
class Name: public Atomic<T, a_t, VK> { \
public: \
Name(a_t const& value) : Atomic<T, a_t, VK>(value) {} \
boost::optional<r_t> getter() const override { return this->value; } \
extra \
}
PRESERVES_ATOMIC_VALUE_CLASS(Boolean, bool, bool, ValueKind::Boolean, as_bool,
BinaryWriter& write(BinaryWriter& w) const override {
return w << this->_value();
});
PRESERVES_ATOMIC_VALUE_CLASS(Double, double, double, ValueKind::Double, as_double,
BinaryWriter& write(BinaryWriter& w) const override {
return w << this->_value();
});
PRESERVES_ATOMIC_VALUE_CLASS(Uint64, uint64_t, uint64_t, ValueKind::SignedInteger, as_unsigned,
BinaryWriter& write(BinaryWriter& w) const override {
return w << this->_value();
}
boost::optional<int64_t> as_signed() const override {
if (this->value <= uint64_t(std::numeric_limits<int64_t>::max())) {
return this->value;
} else {
return boost::none;
}
}
boost::optional<double> as_double() const override {
if (uint64_t(double(this->value)) == this->value) {
return double(this->value);
} else {
return boost::none;
}
});
PRESERVES_ATOMIC_VALUE_CLASS(Int64, int64_t, int64_t, ValueKind::SignedInteger, as_signed,
BinaryWriter& write(BinaryWriter& w) const override {
return w << this->_value();
}
boost::optional<uint64_t> as_unsigned() const override {
if (this->value >= 0) {
return this->value;
} else {
return boost::none;
}
}
boost::optional<double> as_double() const override {
if (int64_t(double(this->value)) == this->value) {
return double(this->value);
} else {
return boost::none;
}
});
PRESERVES_ATOMIC_VALUE_CLASS(BigNum, std::vector<uint8_t>, std::vector<uint8_t> const&, ValueKind::SignedInteger, as_bignum,
BinaryWriter& write(BinaryWriter& w) const override {
return w.bignum(this->_value());
});
PRESERVES_ATOMIC_VALUE_CLASS(String, std::string, std::string const&, ValueKind::String, as_string,
BinaryWriter& write(BinaryWriter& w) const override {
return w.string(this->_value());
});
PRESERVES_ATOMIC_VALUE_CLASS(ByteString, std::vector<uint8_t>, std::vector<uint8_t> const&, ValueKind::ByteString, as_bytes,
BinaryWriter& write(BinaryWriter& w) const override {
return w.bytes(this->_value());
});
PRESERVES_ATOMIC_VALUE_CLASS(Symbol, std::string, std::string const&, ValueKind::Symbol, as_symbol,
BinaryWriter& write(BinaryWriter& w) const override {
return w.symbol(this->_value());
});
template <typename T>
class Record: public ValueImpl<T> {
public:
Value<T> labelValue;
std::vector<Value<T>> fields;
Record(Value<T> const& label) : labelValue(label), fields() {}
Record(Value<T> const& label, std::vector<Value<T>> const& fields) : labelValue(label), fields(fields) {}
ValueKind value_kind() const { return ValueKind::Record; }
boost::optional<Value<T>> label() const override { return labelValue; }
size_t size() const { return fields.size(); }
boost::optional<Value<T>> get(size_t index) const {
if (index < size()) {
return fields[index];
} else {
return boost::none;
}
}
bool add(Value<T> const& value) override {
fields.push_back(value);
return true;
}
bool set(size_t index, Value<T> const& value) override {
if (index < size()) {
fields[index] = value;
}
return false;
}
bool operator<(Record<T> const& other) const {
if (labelValue < other.labelValue) return true;
if (other.labelValue < labelValue) return false;
return fields < other.fields;
}
BinaryWriter& write(BinaryWriter& w) const override {
return w.record(labelValue, fields);
}
};
template <typename T = class GenericEmbedded>
class Sequence: public ValueImpl<T> {
public:
std::vector<Value<T>> values;
Sequence() : values() {}
Sequence(std::vector<Value<T>> const& values) : values(values) {}
ValueKind value_kind() const { return ValueKind::Sequence; }
boost::optional<std::vector<Value<T>> const&> as_sequence() const override {
return values;
}
size_t size() const override { return values.size(); }
boost::optional<Value<T>> get(size_t index) const override {
if (index < size()) {
return values[index];
} else {
return boost::none;
}
}
bool add(Value<T> const& value) override {
values.push_back(value);
return true;
}
bool set(size_t index, Value<T> const& value) override {
if (index < size()) {
values[index] = value;
}
return false;
}
BinaryWriter& write(BinaryWriter& w) const override {
return w.sequence(values);
}
};
template <typename T = class GenericEmbedded>
class Set: public ValueImpl<T> {
public:
std::set<Value<T>> values;
Set() : values() {}
Set(std::set<Value<T>> const& values) : values(values) {}
ValueKind value_kind() const { return ValueKind::Set; }
boost::optional<std::set<Value<T>> const&> as_set() const override {
return values;
}
size_t size() const override { return values.size(); }
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
bool add(Value<T> const& value) override {
return values.insert(value).second;
}
bool erase(Value<T> const& value) override {
return values.erase(value) > 0;
}
BinaryWriter& write(BinaryWriter& w) const override {
return w.set(values);
}
};
template <typename T = class GenericEmbedded>
class Dictionary: public ValueImpl<T> {
public:
std::map<Value<T>, Value<T>> values;
Dictionary() : values() {}
Dictionary(std::map<Value<T>, Value<T>> const& values) : values(values) {}
ValueKind value_kind() const { return ValueKind::Dictionary; }
boost::optional<std::map<Value<T>, Value<T>> const&> as_dictionary() const override {
return values;
}
size_t size() const override { return values.size(); }
bool contains(Value<T> const& key) const override { return values.count(key) > 0; }
boost::optional<Value<T>> get(Value<T> const& key) const override {
auto i = values.find(key);
if (i == values.end()) return boost::none;
return i->second;
}
bool set(Value<T> const& key, Value<T> const& value) override {
return values.emplace(key, value).second;
}
bool erase(Value<T> const& key) override {
return values.erase(key) > 0;
}
BinaryWriter& write(BinaryWriter& w) const override {
return w.dictionary(values);
}
};
template <typename T = class GenericEmbedded>
class Embedded: public ValueImpl<T> {
public:
std::shared_ptr<T> value;
Embedded(std::shared_ptr<T> const& value) : value(value) {}
ValueKind value_kind() const { return ValueKind::Embedded; }
boost::optional<std::shared_ptr<T>> as_embedded() const override {
return value;
}
BinaryWriter& write(BinaryWriter& w) const override {
w << BinaryTag::Embedded;
return value->write(w);
}
};
class GenericEmbedded: public Value<GenericEmbedded> {
public:
GenericEmbedded(std::shared_ptr<ValueImpl<GenericEmbedded>> p) :
Value(p)
{}
static std::shared_ptr<GenericEmbedded> wrap(Value<> v) {
return std::make_shared<GenericEmbedded>(v._impl());
}
BinaryWriter& write(BinaryWriter& w) const {
return (*this)->write(w);
}
};
template <typename T>
class AnnotatedValue: public ValueImpl<T> {
std::vector<Value<T>> anns;
Value<T> underlying;
friend class ValueImpl<T>;
protected:
Value<T> internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const &ann) {
if (self.use_count() == 1) {
anns.push_back(ann);
return self;
} else {
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(anns), underlying);
a->anns.push_back(ann);
return Value<T>(a);
}
}
public:
AnnotatedValue(std::vector<Value<T>> &&anns, Value<T> const& v) : anns(anns), underlying(v) {}
ValueKind value_kind() const override { return underlying.value_kind(); }
bool is_mutable() const override { return underlying.is_mutable(); }
boost::optional<bool> as_bool() const override { return underlying.as_bool(); }
boost::optional<double> as_double() const override { return underlying.as_double(); }
boost::optional<uint64_t> as_unsigned() const override { return underlying.as_unsigned(); }
boost::optional<int64_t> as_signed() const override { return underlying.as_signed(); }
boost::optional<std::vector<uint8_t> const&> as_bignum() const override { return underlying.as_bignum(); }
boost::optional<std::string const&> as_string() const override { return underlying.as_string(); }
boost::optional<std::vector<uint8_t> const&> as_bytes() const override { return underlying.as_bytes(); }
boost::optional<std::string const&> as_symbol() const override { return underlying.as_symbol(); }
boost::optional<Record<T> const&> as_record() const override { return underlying.as_record(); }
boost::optional<std::vector<Value<T>> const&> as_sequence() const override { return underlying.as_sequence(); }
boost::optional<std::set<Value<T>> const&> as_set() const override { return underlying.as_set(); }
boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const override { return underlying.as_dictionary(); }
boost::optional<std::shared_ptr<T>> as_embedded() const override { return underlying.as_embedded(); }
boost::optional<Value<T>> label() const override { return underlying.label(); }
size_t size() const override { return underlying.size(); }
bool contains(Value<T> const& key) const override { return underlying.contains(key); }
boost::optional<Value<T>> get(Value<T> const& key) const override { return underlying.get(key); }
boost::optional<Value<T>> get(size_t index) const override { return underlying.get(index); }
bool add(Value<T> const& item) override {
return underlying->add(item);
}
bool set(Value<T> const& key, Value<T> const& value) override {
return underlying->set(key, value);
}
bool set(size_t index, Value<T> const& value) override {
return underlying->set(index, value);
}
bool erase(Value<T> const& key) override {
return underlying->erase(key);
}
boost::optional<std::vector<Value<T>> const&> annotations() const override {
return anns;
}
BinaryWriter& write(BinaryWriter &w) const override {
return w.annotated(underlying, anns);
}
};
template <typename T>
Value<T> Value<T>::annotate(Value<T> const& ann) const {
return p->internal_annotate(p, ann);
}
template <typename T>
Value<T> ValueImpl<T>::internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const &ann) {
auto a = std::make_shared<AnnotatedValue<T>>(std::vector<Value<T>>(), Value<T>(self));
a->anns.push_back(ann);
return Value<T>(a);
}
template <typename T>
Value<T> Value<T>::from_bool(bool b)
{
return Value<T>(new Boolean<T>(b));
}
template <typename T>
Value<T> Value<T>::from_double(double d)
{
return Value<T>(new Double<T>(d));
}
template <typename T>
Value<T> Value<T>::from_int(uint64_t i)
{
return Value<T>(new Uint64<T>(i));
}
template <typename T>
Value<T> Value<T>::from_int(int64_t i) {
return Value<T>(new Int64<T>(i));
}
template <typename T>
Value<T> Value<T>::from_string(std::string const& s) {
return Value<T>(new String<T>(s));
}
template <typename T>
Value<T> Value<T>::sequence(std::vector<Value<T>> const& values) {
return Value<T>(new Sequence<T>(values));
}
template <typename T>
Value<T> Value<T>::from_embedded(std::shared_ptr<T> const& v) {
return Value<T>(new Embedded<T>(v));
}
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <iostream>
#include "preserves.hpp"
namespace Preserves {
template <typename T>
class TextReader {
public:
TextReader(std::istream& i);
};
}

View File

@ -0,0 +1,297 @@
#pragma once
#include <memory>
#include <cstdint>
#include <string>
#include <vector>
#include <map>
#include <set>
#include <limits>
#include <boost/optional.hpp>
namespace Preserves {
enum class ValueKind {
Boolean,
Double,
SignedInteger,
String,
ByteString,
Symbol,
Record,
Sequence,
Set,
Dictionary,
Embedded,
};
template <typename T = class GenericEmbedded> class Record;
template <typename T = class GenericEmbedded> class ValueImpl;
template <typename T = class GenericEmbedded>
class Value {
std::shared_ptr<ValueImpl<T>> p;
public:
Value(std::shared_ptr<ValueImpl<T>> const& p) : p(p) {}
Value(ValueImpl<T> *p) : p(p) {}
std::shared_ptr<ValueImpl<T>> _impl() const { return p; }
static Value from_bool(bool b);
static Value from_double(double d);
static Value from_int(uint64_t i);
static Value from_int(int64_t i);
static Value from_string(std::string const& s);
static Value from_bytes(std::vector<uint8_t> const& v);
static Value from_bytes(std::vector<char> const& v);
static Value from_bytes(void *p, size_t len);
static Value from_symbol(std::string const& s);
static Value record(Record<T> const& r);
static Value record(Value const& label, std::vector<Value> const& fields);
static Value sequence(std::vector<Value> const& items);
static Value set(std::set<Value> const& items);
static Value dictionary(std::map<Value, Value> const& entries);
static Value from_embedded(std::shared_ptr<T> const& p);
static Value from_unsigned(uint64_t i) { return from_int(i); }
static Value from_signed(int64_t i) { return from_int(i); }
static Value from_bignum(std::vector<uint8_t> const& v);
static Value from_number(uint64_t i) { return from_int(i); }
static Value from_number(int64_t i) { return from_int(i); }
static Value from_number(double d) { return from_double(d); }
static Value from(bool b) { return from_bool(b); }
static Value from(double d) { return from_double(d); }
static Value from(uint64_t i) { return from_int(i); }
static Value from(unsigned i) { return from_int(uint64_t(i)); }
static Value from(int64_t i) { return from_int(i); }
static Value from(signed i) { return from_int(int64_t(i)); }
static Value from(std::string const& s) { return from_string(s); }
static Value from(char const* s) { return from_string(s); }
static Value from(std::vector<uint8_t> const& v) { return from_bytes(v); }
static Value from(std::vector<char> const& v) { return from_bytes(v); }
static Value from(void *p, size_t len) { return from_bytes(p, len); }
static Value from(Record<T> const& r) { return record(r); }
static Value from(Value const& label, std::vector<Value> const& fields) { return record(label, fields); }
static Value from(std::vector<Value> const& items) { return sequence(items); }
static Value from(std::set<Value> const& items) { return set(items); }
static Value from(std::map<Value, Value> const& entries) { return dictionary(entries); }
ValueImpl<T>& operator*() const { return *p; }
ValueImpl<T>* operator->() const { return p.get(); }
ValueKind value_kind() const;
bool is_mutable() const;
bool is_bool() const { return value_kind() == ValueKind::Boolean; }
bool is_double() const { return value_kind() == ValueKind::Double; }
bool is_int() const { return value_kind() == ValueKind::SignedInteger; }
bool is_string() const { return value_kind() == ValueKind::String; }
bool is_bytes() const { return value_kind() == ValueKind::ByteString; }
bool is_symbol() const { return value_kind() == ValueKind::Symbol; }
bool is_record() const { return value_kind() == ValueKind::Record; }
bool is_sequence() const { return value_kind() == ValueKind::Sequence; }
bool is_set() const { return value_kind() == ValueKind::Set; }
bool is_dictionary() const { return value_kind() == ValueKind::Dictionary; }
boost::optional<bool> as_bool() const;
bool to_bool() const { return as_bool().value(); }
boost::optional<double> as_double() const;
double to_double() const { return as_double().value(); }
boost::optional<uint64_t> as_unsigned() const;
uint64_t to_unsigned() const { return as_unsigned().value(); }
boost::optional<int64_t> as_signed() const;
int64_t to_signed() const { return as_signed().value(); }
boost::optional<std::vector<uint8_t> const&> as_bignum() const;
std::vector<uint8_t> const& to_bignum() const { return as_bignum().value(); }
boost::optional<std::string const&> as_string() const;
std::string const& to_string() const { return as_string().value(); }
boost::optional<std::vector<uint8_t> const&> as_bytes() const;
std::vector<uint8_t> const& to_bytes() const { return as_bytes().value(); }
boost::optional<std::string const&> as_symbol() const;
std::string const& to_symbol() const { return as_symbol().value(); }
boost::optional<Record<T> const&> as_record() const;
Record<T> const& to_record() const { return as_record().value(); };
boost::optional<std::vector<Value> const&> as_sequence() const;
std::vector<Value> const& to_sequence() const { return as_sequence().value(); }
boost::optional<std::set<Value> const&> as_set() const;
std::set<Value> const& to_set() const { return as_set().value(); }
boost::optional<std::map<Value,Value> const&> as_dictionary() const;
std::map<Value,Value> const& to_dictionary() const { return as_dictionary().value(); }
boost::optional<std::shared_ptr<T>> as_embedded() const;
std::shared_ptr<T> to_embedded() const { return as_embedded().value(); }
Value annotate(Value const& ann) const;
boost::optional<std::vector<Value> const&> annotations() const;
boost::optional<Value> label() const;
size_t size() const;
bool contains(Value const& key) const;
boost::optional<Value> get(Value const& key) const;
Value operator[](Value const& key) const { return get(key).value(); }
boost::optional<Value> get(size_t index) const;
Value operator[](size_t index) const { return get(index).value(); }
};
class BinaryWriter; // forward declaration; see preserves_binary_writer.hpp
template <typename T>
class ValueImpl {
protected:
virtual Value<T> internal_annotate(std::shared_ptr<ValueImpl<T>> const& self, Value<T> const& ann);
friend class Value<T>;
public:
virtual ~ValueImpl() {}
virtual ValueKind value_kind() const = 0;
virtual bool is_mutable() const { return false; }
virtual boost::optional<bool> as_bool() const { return boost::none; }
virtual boost::optional<double> as_double() const { return boost::none; }
virtual boost::optional<uint64_t> as_unsigned() const { return boost::none; }
virtual boost::optional<int64_t> as_signed() const { return boost::none; }
virtual boost::optional<std::vector<uint8_t> const&> as_bignum() const { return boost::none; }
virtual boost::optional<std::string const&> as_string() const { return boost::none; }
virtual boost::optional<std::vector<uint8_t> const&> as_bytes() const { return boost::none; }
virtual boost::optional<std::string const&> as_symbol() const { return boost::none; }
virtual boost::optional<Record<T> const&> as_record() const { return boost::none; }
virtual boost::optional<std::vector<Value<T>> const&> as_sequence() const { return boost::none; }
virtual boost::optional<std::set<Value<T>> const&> as_set() const { return boost::none; }
virtual boost::optional<std::map<Value<T>,Value<T>> const&> as_dictionary() const { return boost::none; }
virtual boost::optional<std::shared_ptr<T>> as_embedded() const { return boost::none; }
virtual boost::optional<Value<T>> label() const { return boost::none; }
virtual size_t size() const { return 0; }
virtual bool contains(Value<T> const& /* key */) const { return false; }
virtual boost::optional<Value<T>> get(Value<T> const& /* key */) const { return boost::none; }
virtual boost::optional<Value<T>> get(size_t /* index */) const { return boost::none; }
virtual bool add(Value<T> const& /* item */) {
throw std::runtime_error("Cannot add item to Preserves value");
}
virtual bool set(Value<T> const& /* key */, Value<T> const& /* value */) {
throw std::runtime_error("Cannot set item by key in Preserves value");
}
virtual bool set(size_t /* index */, Value<T> const& /* value */) {
throw std::runtime_error("Cannot set item by index in Preserves value");
}
virtual bool erase(Value<T> const& /* key */) {
throw std::runtime_error("Cannot erase item in Preserves value");
}
virtual boost::optional<std::vector<Value<T>> const&> annotations() const { return boost::none; }
virtual BinaryWriter& write(BinaryWriter& w) const = 0;
};
template <typename T> ValueKind Value<T>::value_kind() const { return p->value_kind(); }
template <typename T> bool Value<T>::is_mutable() const { return p->is_mutable(); }
#define PRESERVES_DELEGATE_CAST(t, name) \
template <typename T> boost::optional<t> Value<T>::name() const { return p->name(); }
PRESERVES_DELEGATE_CAST(bool, as_bool);
PRESERVES_DELEGATE_CAST(double, as_double);
PRESERVES_DELEGATE_CAST(uint64_t, as_unsigned);
PRESERVES_DELEGATE_CAST(int64_t, as_signed);
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bignum);
PRESERVES_DELEGATE_CAST(std::string const&, as_string);
PRESERVES_DELEGATE_CAST(std::vector<uint8_t> const&, as_bytes);
PRESERVES_DELEGATE_CAST(std::string const&, as_symbol);
PRESERVES_DELEGATE_CAST(Record<T> const&, as_record);
PRESERVES_DELEGATE_CAST(std::vector<Value<T>> const&, as_sequence);
PRESERVES_DELEGATE_CAST(std::set<Value<T>> const&, as_set);
#define COMMA ,
PRESERVES_DELEGATE_CAST(std::map<Value<T> COMMA Value<T>> const&, as_dictionary);
#undef COMMA
PRESERVES_DELEGATE_CAST(std::shared_ptr<T>, as_embedded);
#undef PRESERVES_DELEGATE_CAST
template <typename T> boost::optional<Value<T>> Value<T>::label() const { return p->label(); }
template <typename T> bool Value<T>::contains(Value const& key) const { return p->contains(key); }
template <typename T> boost::optional<Value<T>> Value<T>::get(Value<T> const& key) const { return p->get(key); }
template <typename T> boost::optional<Value<T>> Value<T>::get(size_t index) const { return p->get(index); }
template <typename T> size_t Value<T>::size() const { return p->size(); }
inline bool bignum_lt(std::vector<uint8_t> const& a, std::vector<uint8_t> const& b) {
bool aNegative = (a.size() > 0) && (a[0] & 0x80);
bool bNegative = (b.size() > 0) && (b[0] & 0x80);
if (aNegative != bNegative) return aNegative;
if (aNegative) {
if (a.size() > b.size()) return true;
if (a.size() < b.size()) return false;
return a < b;
} else {
if (a.size() > b.size()) return false;
if (a.size() < b.size()) return true;
return a < b;
}
}
template <typename T>
bool operator<(Value<T> const& a, Value<T> const &b) {
auto aKind = a.value_kind();
auto bKind = b.value_kind();
if (aKind < bKind) return true;
if (bKind < aKind) return false;
switch (aKind) {
case ValueKind::Boolean: return a.to_bool() < b.to_bool();
case ValueKind::Double: return a.to_double() < b.to_double();
case ValueKind::SignedInteger: {
if (auto av = a.as_signed()) {
if (auto bv = b.as_signed()) {
return *av < *bv;
} else {
return true;
}
} else {
if (auto bv = b.as_signed()) {
return false;
} else {
if (auto av = a.as_unsigned()) {
if (auto bv = b.as_unsigned()) {
return *av < *bv;
} else {
return true;
}
} else {
if (auto bv = b.as_unsigned()) {
return false;
} else {
return bignum_lt(a.to_bignum(), b.to_bignum());
}
}
}
}
}
case ValueKind::String: return a.to_string() < b.to_string();
case ValueKind::ByteString: return a.to_bytes() < b.to_bytes();
case ValueKind::Symbol: return a.to_symbol() < b.to_symbol();
case ValueKind::Record: return a.to_record() < b.to_record();
case ValueKind::Sequence: return a.to_sequence() < b.to_sequence();
case ValueKind::Set: return a.to_set() < b.to_set();
case ValueKind::Dictionary: return a.to_dictionary() < b.to_dictionary();
case ValueKind::Embedded: return *a.to_embedded() < *b.to_embedded();
default: throw std::runtime_error("Invalid ValueKind");
}
}
}

View File

@ -0,0 +1,3 @@
env:DHALL_PRELUDE
? https://prelude.dhall-lang.org/v20.2.0/package.dhall
sha256:a6036bc38d883450598d1de7c98ead113196fe2db02e9733855668b18096f07b

View File

@ -0,0 +1,48 @@
# Dhall
Not a true implementation of Preserves, but functions for translating Dhall
values to Preserves and rendering them.
For example, to generate configuration for a Syndicate server listener:
```dhall
let Prelude = ./Prelude.dhall
let Preserves = ./package.dhall
let Tcp/Type = { address : Text, port : Natural }
let RelayListener/Type = { transport : Tcp/Type }
let RequireService/Type = { relayListener : RelayListener/Type }
let Tcp/toPreserves =
λ(tcp : Tcp/Type) →
Preserves.record
(Preserves.symbol "tcp")
[ Preserves.string tcp.address
, Preserves.integer (Prelude.Natural.toInteger tcp.port)
]
let RelayListener/toPreserves =
λ(relayListener : RelayListener/Type) →
Preserves.record
(Preserves.symbol "relay-listener")
[ Tcp.toPreserves relayListener.transport ]
let RequireService/toPreserves =
λ(requireService : RequireService/Type) →
Preserves.record
(Preserves.symbol "require-service")
[ RelayListener.toPreserves requireService.relayListener ]
let example = { relayListener.transport = { address = "127.0.0.1", port = 1 } }
let rendering = Preserves.render (RequireService.toPreserves example)
let check =
assert
: rendering ≡ "<require-service <relay-listener <tcp \"127.0.0.1\" 1>>>"
in rendering
```

View File

@ -0,0 +1,10 @@
{-|
Dhall encoding of an arbitrary Preserves value
-}
let Preserves/function = ./function.dhall
let Preserves/Type
: Type
= ∀(Preserves : Type) → ∀(value : Preserves/function Preserves) → Preserves
in Preserves/Type

View File

@ -0,0 +1,15 @@
{-|
Create a Preserves boolean map from a `Bool` value
-}
let Preserves/Type = ./Type.dhall
let Preserves/function = ./function.dhall
let bool
: Bool → Preserves/Type
= λ(x : Bool) →
λ(Preserves : Type) →
λ(value : Preserves/function Preserves) →
value.boolean x
in bool

Some files were not shown because too many files have changed in this diff Show More