diff options
102 files changed, 342 insertions, 122 deletions
diff --git a/.hypothesis/examples/17015ad1b47afd98/09a8a14f5f6826f2 b/.hypothesis/examples/17015ad1b47afd98/09a8a14f5f6826f2 Binary files differnew file mode 100644 index 0000000..85074f4 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/09a8a14f5f6826f2 diff --git a/.hypothesis/examples/17015ad1b47afd98/0e88086df0517527 b/.hypothesis/examples/17015ad1b47afd98/0e88086df0517527 deleted file mode 100644 index 2bcc35b..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/0e88086df0517527 +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/.hypothesis/examples/17015ad1b47afd98/19bb68f9d92692f9 b/.hypothesis/examples/17015ad1b47afd98/19bb68f9d92692f9 Binary files differnew file mode 100644 index 0000000..05d7933 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/19bb68f9d92692f9 diff --git a/.hypothesis/examples/17015ad1b47afd98/3ee6ff39f2ac6dea b/.hypothesis/examples/17015ad1b47afd98/3ee6ff39f2ac6dea Binary files differdeleted file mode 100644 index 8da7f4b..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/3ee6ff39f2ac6dea +++ /dev/null diff --git a/.hypothesis/examples/17015ad1b47afd98/49e6f38c5cc772ec b/.hypothesis/examples/17015ad1b47afd98/49e6f38c5cc772ec Binary files differdeleted file mode 100644 index 5c100e8..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/49e6f38c5cc772ec +++ /dev/null diff --git a/.hypothesis/examples/17015ad1b47afd98/56980d5ed7b87686 b/.hypothesis/examples/17015ad1b47afd98/56980d5ed7b87686 Binary files differnew file mode 100644 index 0000000..7361f5f --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/56980d5ed7b87686 diff --git a/.hypothesis/examples/17015ad1b47afd98/5b55ec37559bb110 b/.hypothesis/examples/17015ad1b47afd98/5b55ec37559bb110 deleted file mode 100644 index 5e2ed41..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/5b55ec37559bb110 +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/.hypothesis/examples/17015ad1b47afd98/61b71c2df2d87966 b/.hypothesis/examples/17015ad1b47afd98/61b71c2df2d87966 Binary files differnew file mode 100644 index 0000000..d03d367 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/61b71c2df2d87966 diff --git a/.hypothesis/examples/17015ad1b47afd98/69b43aa9ca8c9717 b/.hypothesis/examples/17015ad1b47afd98/69b43aa9ca8c9717 Binary files differnew file mode 100644 index 0000000..9398068 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/69b43aa9ca8c9717 diff --git a/.hypothesis/examples/17015ad1b47afd98/745fc588673e7088 b/.hypothesis/examples/17015ad1b47afd98/745fc588673e7088 Binary files differnew file mode 100644 index 0000000..4a6a131 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/745fc588673e7088 diff --git a/.hypothesis/examples/17015ad1b47afd98/79086a9d57f9948d b/.hypothesis/examples/17015ad1b47afd98/79086a9d57f9948d Binary files differnew file mode 100644 index 0000000..460c58e --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/79086a9d57f9948d diff --git a/.hypothesis/examples/17015ad1b47afd98/8065c2327096a7a3 b/.hypothesis/examples/17015ad1b47afd98/8065c2327096a7a3 Binary files differdeleted file mode 100644 index fad8af1..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/8065c2327096a7a3 +++ /dev/null diff --git a/.hypothesis/examples/17015ad1b47afd98/8221fd2283c91e1f b/.hypothesis/examples/17015ad1b47afd98/8221fd2283c91e1f Binary files differnew file mode 100644 index 0000000..7401b2e --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/8221fd2283c91e1f diff --git a/.hypothesis/examples/17015ad1b47afd98/8611cbd7e46390b8 b/.hypothesis/examples/17015ad1b47afd98/8611cbd7e46390b8 Binary files differnew file mode 100644 index 0000000..f906aa7 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/8611cbd7e46390b8 diff --git a/.hypothesis/examples/17015ad1b47afd98/8b092171a745ad44 b/.hypothesis/examples/17015ad1b47afd98/8b092171a745ad44 new file mode 100644 index 0000000..6368d9d --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/8b092171a745ad44 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/17015ad1b47afd98/8ee2d9884de49d9a b/.hypothesis/examples/17015ad1b47afd98/8ee2d9884de49d9a Binary files differnew file mode 100644 index 0000000..87376ae --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/8ee2d9884de49d9a diff --git a/.hypothesis/examples/17015ad1b47afd98/ae775858a7e56781 b/.hypothesis/examples/17015ad1b47afd98/ae775858a7e56781 Binary files differdeleted file mode 100644 index ec47c8b..0000000 --- a/.hypothesis/examples/17015ad1b47afd98/ae775858a7e56781 +++ /dev/null diff --git a/.hypothesis/examples/17015ad1b47afd98/b0d0244d1f73df81 b/.hypothesis/examples/17015ad1b47afd98/b0d0244d1f73df81 Binary files differnew file mode 100644 index 0000000..ec53a8a --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/b0d0244d1f73df81 diff --git a/.hypothesis/examples/17015ad1b47afd98/c92920944247d80c b/.hypothesis/examples/17015ad1b47afd98/c92920944247d80c new file mode 100644 index 0000000..03afaa5 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/c92920944247d80c @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/17015ad1b47afd98/ddaf0ed54dfc227c b/.hypothesis/examples/17015ad1b47afd98/ddaf0ed54dfc227c Binary files differnew file mode 100644 index 0000000..a786e12 --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/ddaf0ed54dfc227c diff --git a/.hypothesis/examples/17015ad1b47afd98/df031fe309e7c5bf b/.hypothesis/examples/17015ad1b47afd98/df031fe309e7c5bf new file mode 100644 index 0000000..6e595fc --- /dev/null +++ b/.hypothesis/examples/17015ad1b47afd98/df031fe309e7c5bf @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/641567e7f1117698/005f5867178a5b25 b/.hypothesis/examples/641567e7f1117698/005f5867178a5b25 new file mode 100644 index 0000000..99d1269 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/005f5867178a5b25 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/641567e7f1117698/0478b7c7ead9c10d b/.hypothesis/examples/641567e7f1117698/0478b7c7ead9c10d Binary files differnew file mode 100644 index 0000000..893e09d --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/0478b7c7ead9c10d diff --git a/.hypothesis/examples/641567e7f1117698/1cd68f1893d28b93 b/.hypothesis/examples/641567e7f1117698/1cd68f1893d28b93 new file mode 100644 index 0000000..1e4c7a3 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/1cd68f1893d28b93 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/641567e7f1117698/1d14332589e1f360 b/.hypothesis/examples/641567e7f1117698/1d14332589e1f360 Binary files differnew file mode 100644 index 0000000..4b05fc7 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/1d14332589e1f360 diff --git a/.hypothesis/examples/641567e7f1117698/1e7cf357b0153eda b/.hypothesis/examples/641567e7f1117698/1e7cf357b0153eda Binary files differdeleted file mode 100644 index da412f0..0000000 --- a/.hypothesis/examples/641567e7f1117698/1e7cf357b0153eda +++ /dev/null diff --git a/.hypothesis/examples/641567e7f1117698/203107b3bbad5816 b/.hypothesis/examples/641567e7f1117698/203107b3bbad5816 Binary files differnew file mode 100644 index 0000000..406c80a --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/203107b3bbad5816 diff --git a/.hypothesis/examples/641567e7f1117698/2bd9f6ed10af66c0 b/.hypothesis/examples/641567e7f1117698/2bd9f6ed10af66c0 Binary files differnew file mode 100644 index 0000000..938aced --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/2bd9f6ed10af66c0 diff --git a/.hypothesis/examples/641567e7f1117698/2f69395da78f14d8 b/.hypothesis/examples/641567e7f1117698/2f69395da78f14d8 Binary files differnew file mode 100644 index 0000000..77a7ec0 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/2f69395da78f14d8 diff --git a/.hypothesis/examples/641567e7f1117698/3923acd66c0b7e18 b/.hypothesis/examples/641567e7f1117698/3923acd66c0b7e18 deleted file mode 100644 index 70f3b85..0000000 --- a/.hypothesis/examples/641567e7f1117698/3923acd66c0b7e18 +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/.hypothesis/examples/641567e7f1117698/3c6c5c8e00e8affe b/.hypothesis/examples/641567e7f1117698/3c6c5c8e00e8affe Binary files differnew file mode 100644 index 0000000..4943902 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/3c6c5c8e00e8affe diff --git a/.hypothesis/examples/641567e7f1117698/45f01200098f83fe b/.hypothesis/examples/641567e7f1117698/45f01200098f83fe Binary files differnew file mode 100644 index 0000000..f38d7c8 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/45f01200098f83fe diff --git a/.hypothesis/examples/641567e7f1117698/5be455f23fea5189 b/.hypothesis/examples/641567e7f1117698/5be455f23fea5189 Binary files differnew file mode 100644 index 0000000..f4afa45 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/5be455f23fea5189 diff --git a/.hypothesis/examples/641567e7f1117698/5e762c293b8926ea b/.hypothesis/examples/641567e7f1117698/5e762c293b8926ea Binary files differdeleted file mode 100644 index cdf9180..0000000 --- a/.hypothesis/examples/641567e7f1117698/5e762c293b8926ea +++ /dev/null diff --git a/.hypothesis/examples/641567e7f1117698/5ec5c4954a59b6a4 b/.hypothesis/examples/641567e7f1117698/5ec5c4954a59b6a4 Binary files differdeleted file mode 100644 index e915073..0000000 --- a/.hypothesis/examples/641567e7f1117698/5ec5c4954a59b6a4 +++ /dev/null diff --git a/.hypothesis/examples/641567e7f1117698/63399f455589d4ba b/.hypothesis/examples/641567e7f1117698/63399f455589d4ba Binary files differnew file mode 100644 index 0000000..ab41705 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/63399f455589d4ba diff --git a/.hypothesis/examples/641567e7f1117698/6d688fd550d32db0 b/.hypothesis/examples/641567e7f1117698/6d688fd550d32db0 Binary files differnew file mode 100644 index 0000000..9e6d12c --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/6d688fd550d32db0 diff --git a/.hypothesis/examples/641567e7f1117698/7f8faf9da637e86f b/.hypothesis/examples/641567e7f1117698/7f8faf9da637e86f Binary files differnew file mode 100644 index 0000000..474cc5c --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/7f8faf9da637e86f diff --git a/.hypothesis/examples/641567e7f1117698/881b6ce80b5bd1fc b/.hypothesis/examples/641567e7f1117698/881b6ce80b5bd1fc Binary files differnew file mode 100644 index 0000000..c8c74ca --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/881b6ce80b5bd1fc diff --git a/.hypothesis/examples/641567e7f1117698/a6039ffc453749e1 b/.hypothesis/examples/641567e7f1117698/a6039ffc453749e1 Binary files differnew file mode 100644 index 0000000..9878f1a --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/a6039ffc453749e1 diff --git a/.hypothesis/examples/641567e7f1117698/b19b7b820dddb3c6 b/.hypothesis/examples/641567e7f1117698/b19b7b820dddb3c6 Binary files differnew file mode 100644 index 0000000..ecb949e --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/b19b7b820dddb3c6 diff --git a/.hypothesis/examples/641567e7f1117698/b52e65a9e0e8f9db b/.hypothesis/examples/641567e7f1117698/b52e65a9e0e8f9db Binary files differnew file mode 100644 index 0000000..cfd86fa --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/b52e65a9e0e8f9db diff --git a/.hypothesis/examples/641567e7f1117698/b6420ce3b52b1ab7 b/.hypothesis/examples/641567e7f1117698/b6420ce3b52b1ab7 Binary files differdeleted file mode 100644 index 798716b..0000000 --- a/.hypothesis/examples/641567e7f1117698/b6420ce3b52b1ab7 +++ /dev/null diff --git a/.hypothesis/examples/641567e7f1117698/c17293c1c7f91651 b/.hypothesis/examples/641567e7f1117698/c17293c1c7f91651 Binary files differnew file mode 100644 index 0000000..29ce808 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/c17293c1c7f91651 diff --git a/.hypothesis/examples/641567e7f1117698/cfb16a37ef90423b b/.hypothesis/examples/641567e7f1117698/cfb16a37ef90423b Binary files differnew file mode 100644 index 0000000..6402578 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/cfb16a37ef90423b diff --git a/.hypothesis/examples/641567e7f1117698/d1f7b282fca409b2 b/.hypothesis/examples/641567e7f1117698/d1f7b282fca409b2 Binary files differdeleted file mode 100644 index 5d072ed..0000000 --- a/.hypothesis/examples/641567e7f1117698/d1f7b282fca409b2 +++ /dev/null diff --git a/.hypothesis/examples/641567e7f1117698/ea786d015fba3bee b/.hypothesis/examples/641567e7f1117698/ea786d015fba3bee Binary files differnew file mode 100644 index 0000000..23dcd5a --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/ea786d015fba3bee diff --git a/.hypothesis/examples/641567e7f1117698/f41cb651e160d454 b/.hypothesis/examples/641567e7f1117698/f41cb651e160d454 Binary files differnew file mode 100644 index 0000000..4f7bfb6 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/f41cb651e160d454 diff --git a/.hypothesis/examples/641567e7f1117698/f7b6a134061a22a3 b/.hypothesis/examples/641567e7f1117698/f7b6a134061a22a3 Binary files differnew file mode 100644 index 0000000..f4de7f5 --- /dev/null +++ b/.hypothesis/examples/641567e7f1117698/f7b6a134061a22a3 diff --git a/.hypothesis/examples/c5004b86438ec8a9/a1a8c54704cd3ce5 b/.hypothesis/examples/c5004b86438ec8a9/a1a8c54704cd3ce5 Binary files differnew file mode 100644 index 0000000..ee13fd2 --- /dev/null +++ b/.hypothesis/examples/c5004b86438ec8a9/a1a8c54704cd3ce5 diff --git a/.hypothesis/examples/c72254ca7f2d3ae9/18c072a6c0de03a3 b/.hypothesis/examples/c72254ca7f2d3ae9/18c072a6c0de03a3 Binary files differnew file mode 100644 index 0000000..9d4e4f1 --- /dev/null +++ b/.hypothesis/examples/c72254ca7f2d3ae9/18c072a6c0de03a3 diff --git a/.hypothesis/examples/c72254ca7f2d3ae9/76c7ab7f4af319c7 b/.hypothesis/examples/c72254ca7f2d3ae9/76c7ab7f4af319c7 Binary files differnew file mode 100644 index 0000000..c28f678 --- /dev/null +++ b/.hypothesis/examples/c72254ca7f2d3ae9/76c7ab7f4af319c7 diff --git a/.hypothesis/examples/dc26b29be33957c5/3ac7fd17438f7df3 b/.hypothesis/examples/dc26b29be33957c5/3ac7fd17438f7df3 Binary files differdeleted file mode 100644 index 0419200..0000000 --- a/.hypothesis/examples/dc26b29be33957c5/3ac7fd17438f7df3 +++ /dev/null diff --git a/.hypothesis/examples/dc26b29be33957c5/4b5882e6feb52553 b/.hypothesis/examples/dc26b29be33957c5/4b5882e6feb52553 Binary files differnew file mode 100644 index 0000000..5c95675 --- /dev/null +++ b/.hypothesis/examples/dc26b29be33957c5/4b5882e6feb52553 diff --git a/.hypothesis/examples/dc26b29be33957c5/5b55ec37559bb110 b/.hypothesis/examples/dc26b29be33957c5/5b55ec37559bb110 deleted file mode 100644 index 5e2ed41..0000000 --- a/.hypothesis/examples/dc26b29be33957c5/5b55ec37559bb110 +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/.hypothesis/examples/dc26b29be33957c5/c512123626a98914 b/.hypothesis/examples/dc26b29be33957c5/c512123626a98914 deleted file mode 100644 index 9388380..0000000 --- a/.hypothesis/examples/dc26b29be33957c5/c512123626a98914 +++ /dev/null @@ -1 +0,0 @@ -
\ No newline at end of file diff --git a/.hypothesis/examples/dc26b29be33957c5/d6459ab29c7b9a9f b/.hypothesis/examples/dc26b29be33957c5/d6459ab29c7b9a9f Binary files differdeleted file mode 100644 index 86906d2..0000000 --- a/.hypothesis/examples/dc26b29be33957c5/d6459ab29c7b9a9f +++ /dev/null diff --git a/.hypothesis/examples/dc26b29be33957c5/ee3ad785c37b610d b/.hypothesis/examples/dc26b29be33957c5/ee3ad785c37b610d Binary files differnew file mode 100644 index 0000000..81d5bc2 --- /dev/null +++ b/.hypothesis/examples/dc26b29be33957c5/ee3ad785c37b610d diff --git a/.hypothesis/examples/ec453dcb33aa8b66/00d874a28a65de9b b/.hypothesis/examples/ec453dcb33aa8b66/00d874a28a65de9b Binary files differnew file mode 100644 index 0000000..97fbfa6 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/00d874a28a65de9b diff --git a/.hypothesis/examples/ec453dcb33aa8b66/0c69f7daba8b9b86 b/.hypothesis/examples/ec453dcb33aa8b66/0c69f7daba8b9b86 Binary files differnew file mode 100644 index 0000000..9034b55 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/0c69f7daba8b9b86 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/0f42e404bef2b1a3 b/.hypothesis/examples/ec453dcb33aa8b66/0f42e404bef2b1a3 Binary files differnew file mode 100644 index 0000000..1471346 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/0f42e404bef2b1a3 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/16bceb1530a91002 b/.hypothesis/examples/ec453dcb33aa8b66/16bceb1530a91002 Binary files differdeleted file mode 100644 index 2e7651b..0000000 --- a/.hypothesis/examples/ec453dcb33aa8b66/16bceb1530a91002 +++ /dev/null diff --git a/.hypothesis/examples/ec453dcb33aa8b66/207b79c0dd1b6fba b/.hypothesis/examples/ec453dcb33aa8b66/207b79c0dd1b6fba Binary files differnew file mode 100644 index 0000000..6345bd4 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/207b79c0dd1b6fba diff --git a/.hypothesis/examples/ec453dcb33aa8b66/218d87f650043d4d b/.hypothesis/examples/ec453dcb33aa8b66/218d87f650043d4d Binary files differnew file mode 100644 index 0000000..cfd6a40 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/218d87f650043d4d diff --git a/.hypothesis/examples/ec453dcb33aa8b66/275df7aaf0e8504b b/.hypothesis/examples/ec453dcb33aa8b66/275df7aaf0e8504b Binary files differnew file mode 100644 index 0000000..235b250 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/275df7aaf0e8504b diff --git a/.hypothesis/examples/ec453dcb33aa8b66/28a055f3278de950 b/.hypothesis/examples/ec453dcb33aa8b66/28a055f3278de950 Binary files differnew file mode 100644 index 0000000..bfce649 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/28a055f3278de950 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/29a465ca925a55c2 b/.hypothesis/examples/ec453dcb33aa8b66/29a465ca925a55c2 Binary files differnew file mode 100644 index 0000000..1a9248f --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/29a465ca925a55c2 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/36d7ba28eb961f14 b/.hypothesis/examples/ec453dcb33aa8b66/36d7ba28eb961f14 Binary files differnew file mode 100644 index 0000000..bd1fa9b --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/36d7ba28eb961f14 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/3b15208ee23b0387 b/.hypothesis/examples/ec453dcb33aa8b66/3b15208ee23b0387 Binary files differnew file mode 100644 index 0000000..0bbbf2f --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/3b15208ee23b0387 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/3be6ee08630f7415 b/.hypothesis/examples/ec453dcb33aa8b66/3be6ee08630f7415 Binary files differnew file mode 100644 index 0000000..da37579 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/3be6ee08630f7415 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/41d22663c8456cf1 b/.hypothesis/examples/ec453dcb33aa8b66/41d22663c8456cf1 Binary files differnew file mode 100644 index 0000000..8ed04c1 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/41d22663c8456cf1 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/4566df4571cae443 b/.hypothesis/examples/ec453dcb33aa8b66/4566df4571cae443 Binary files differnew file mode 100644 index 0000000..1e11405 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/4566df4571cae443 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/51ab005a386f8742 b/.hypothesis/examples/ec453dcb33aa8b66/51ab005a386f8742 Binary files differnew file mode 100644 index 0000000..6af7a69 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/51ab005a386f8742 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/52f15fc15e039804 b/.hypothesis/examples/ec453dcb33aa8b66/52f15fc15e039804 Binary files differnew file mode 100644 index 0000000..11037b4 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/52f15fc15e039804 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/56622e534f8c66db b/.hypothesis/examples/ec453dcb33aa8b66/56622e534f8c66db Binary files differnew file mode 100644 index 0000000..97c0daf --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/56622e534f8c66db diff --git a/.hypothesis/examples/ec453dcb33aa8b66/5bcf6972d8812615 b/.hypothesis/examples/ec453dcb33aa8b66/5bcf6972d8812615 Binary files differdeleted file mode 100644 index f677871..0000000 --- a/.hypothesis/examples/ec453dcb33aa8b66/5bcf6972d8812615 +++ /dev/null diff --git a/.hypothesis/examples/ec453dcb33aa8b66/5e1b75e52e4472a7 b/.hypothesis/examples/ec453dcb33aa8b66/5e1b75e52e4472a7 Binary files differnew file mode 100644 index 0000000..47ff157 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/5e1b75e52e4472a7 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/619a0adc5abca17e b/.hypothesis/examples/ec453dcb33aa8b66/619a0adc5abca17e Binary files differdeleted file mode 100644 index 3436347..0000000 --- a/.hypothesis/examples/ec453dcb33aa8b66/619a0adc5abca17e +++ /dev/null diff --git a/.hypothesis/examples/ec453dcb33aa8b66/b2d7ebd146f76d3d b/.hypothesis/examples/ec453dcb33aa8b66/b2d7ebd146f76d3d Binary files differnew file mode 100644 index 0000000..5ed6ebb --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/b2d7ebd146f76d3d diff --git a/.hypothesis/examples/ec453dcb33aa8b66/b691cacbafa2b728 b/.hypothesis/examples/ec453dcb33aa8b66/b691cacbafa2b728 Binary files differnew file mode 100644 index 0000000..2502421 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/b691cacbafa2b728 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/bdff285d783ed32f b/.hypothesis/examples/ec453dcb33aa8b66/bdff285d783ed32f Binary files differnew file mode 100644 index 0000000..0c77431 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/bdff285d783ed32f diff --git a/.hypothesis/examples/ec453dcb33aa8b66/f01e2bfd10c9b16e b/.hypothesis/examples/ec453dcb33aa8b66/f01e2bfd10c9b16e Binary files differnew file mode 100644 index 0000000..9fb6e87 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/f01e2bfd10c9b16e diff --git a/.hypothesis/examples/ec453dcb33aa8b66/f41ac3052b176061 b/.hypothesis/examples/ec453dcb33aa8b66/f41ac3052b176061 Binary files differnew file mode 100644 index 0000000..683ea2e --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/f41ac3052b176061 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/f635e57ce2b75454 b/.hypothesis/examples/ec453dcb33aa8b66/f635e57ce2b75454 Binary files differnew file mode 100644 index 0000000..d3ef683 --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/f635e57ce2b75454 diff --git a/.hypothesis/examples/ec453dcb33aa8b66/faf1cd4bdf2d5926 b/.hypothesis/examples/ec453dcb33aa8b66/faf1cd4bdf2d5926 Binary files differnew file mode 100644 index 0000000..135723e --- /dev/null +++ b/.hypothesis/examples/ec453dcb33aa8b66/faf1cd4bdf2d5926 diff --git a/.hypothesis/examples/ef6354fc2348e0ad/2d0134ed3b9de132 b/.hypothesis/examples/ef6354fc2348e0ad/2d0134ed3b9de132 new file mode 100644 index 0000000..f8fa5a2 --- /dev/null +++ b/.hypothesis/examples/ef6354fc2348e0ad/2d0134ed3b9de132 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/ef6354fc2348e0ad/54d00d52177f7dc5 b/.hypothesis/examples/ef6354fc2348e0ad/54d00d52177f7dc5 Binary files differdeleted file mode 100644 index 1fa7e76..0000000 --- a/.hypothesis/examples/ef6354fc2348e0ad/54d00d52177f7dc5 +++ /dev/null diff --git a/.hypothesis/examples/ef6354fc2348e0ad/5d1be7e9dda1ee88 b/.hypothesis/examples/ef6354fc2348e0ad/5d1be7e9dda1ee88 new file mode 100644 index 0000000..303e398 --- /dev/null +++ b/.hypothesis/examples/ef6354fc2348e0ad/5d1be7e9dda1ee88 @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/ef6354fc2348e0ad/a42c6cf1de3abfde b/.hypothesis/examples/ef6354fc2348e0ad/a42c6cf1de3abfde new file mode 100644 index 0000000..45a8ca0 --- /dev/null +++ b/.hypothesis/examples/ef6354fc2348e0ad/a42c6cf1de3abfde @@ -0,0 +1 @@ +
\ No newline at end of file diff --git a/.hypothesis/examples/ef6354fc2348e0ad/e7faf0b097a6f469 b/.hypothesis/examples/ef6354fc2348e0ad/e7faf0b097a6f469 Binary files differnew file mode 100644 index 0000000..cf2a103 --- /dev/null +++ b/.hypothesis/examples/ef6354fc2348e0ad/e7faf0b097a6f469 diff --git a/actinide/__init__.py b/actinide/__init__.py index 0f09299..b6ace49 100644 --- a/actinide/__init__.py +++ b/actinide/__init__.py @@ -5,6 +5,7 @@ class BaseSession(object): def __init__(self): self.symbols = symbol_table.SymbolTable() self.environment = evaluator.Environment() + self.macros = evaluator.Environment() self.core_builtins() self.standard_library() @@ -14,9 +15,9 @@ class BaseSession(object): return reader.read(port, self.symbols) def eval(self, form): - form = expander.expand(form, self.symbols) + form = expander.expand(form, self.symbols, self.macros) cps = evaluator.eval(form, self.symbols, None) - return evaluator.run(cps, self.environment) + return evaluator.run(cps, self.environment, self.macros) def run(self, port): form = self.read(port) @@ -56,17 +57,10 @@ class BaseSession(object): symb = types.symbol(symb, self.symbols) return symb - def core_builtins(self): - self.bind_module(core) - - def standard_library(self): - pass + def display(self, form): + return types.display(form, self.symbols) -class Session(BaseSession): - def standard_library(self): - @self.bind_fn - def symbol(val): - return types.symbol(val, self.symbols) + def core_builtins(self): @self.bind_fn def read(port): return reader.read(port, self.symbols) @@ -76,6 +70,17 @@ class Session(BaseSession): @self.bind_fn def expand(form): return expander.expand(form, self.symbols) + @self.bind_fn + def symbol(val): + return types.symbol(val, self.symbols) self.bind_module(types) + self.bind_module(core) + self.bind_fn(self.display) + + def standard_library(self): + pass + +class Session(BaseSession): + def standard_library(self): self.bind_module(stdlib) self.bind_module(ports) diff --git a/actinide/evaluator.py b/actinide/evaluator.py index 042f4b0..89878f8 100644 --- a/actinide/evaluator.py +++ b/actinide/evaluator.py @@ -44,9 +44,9 @@ from . import types as t # The result of evaluating a continuation is always a Python tuple. For # expressions, this tuple contains the value(s) produced by the expression. For # forms which do not produce a value, this returns the empty tuple. -def run(continuation, environment, args=()): +def run(continuation, env, macros, args=()): while continuation is not None: - continuation, environment, *args = continuation(environment, *args) + continuation, env, macros, *args = continuation(env, macros, *args) return tuple(args) # ## FLAT CONTINUATIONS @@ -57,13 +57,20 @@ def run(continuation, environment, args=()): # Returns a continuation which yields a single value, verbatim, and chains to a # known target continuation. This implements evaluation for literals. def literal(value, continuation): - return lambda environment: (continuation, environment, value) + return lambda env, macros: (continuation, env, macros, value) # Returns a continuation which looks up a symbol in an environment, yields the # result, and chains to a known target continuation. This implements evaluation # for variable lookups. def symbol(symb, continuation): - return lambda environment: (continuation, environment, environment.find(symb)) + return lambda env, macros: (continuation, env, macros, env.find(symb)) + +# Unquotes the tail of a quoted form, yielding the form as a literal value +# before chaining to the known target continuation. This implements unquoting of +# quoted forms. +def quote(quoted, continuation): + value, = t.flatten(quoted) + return literal(value, continuation) # Returns a continuation which yields a newly-created procedure, and chains to a # known target continuation. This implements evaluation for the tail of a lambda @@ -72,37 +79,43 @@ def symbol(symb, continuation): def lambda_(defn, symbols, continuation): formals = t.flatten(t.head(defn)) body = t.head(t.tail(defn)) - def lambda__(environment): - proc = t.Procedure(body, formals, environment, symbols) - return (continuation, environment, proc) + def lambda__(env, macros): + proc = t.Procedure(body, formals, env, macros, symbols) + return (continuation, env, macros, proc) return lambda__ # Returns a continuation which takes a value and binds that value to a symbol in # a specific environment, then chains to a known target continuation. This # implements evaluation of the `define` special form, once the value is known. def bind(symbol, continuation): - def bind_(environment, value): - environment.define(symbol, value) - return (continuation, environment) + def bind_(env, macros, value): + env.define(symbol, value) + return (continuation, env, macros) return bind_ +def macro_bind(symbol, continuation): + def macro_bind_(env, macros, value): + macros.define(symbol, value) + return (continuation, env, macros) + return macro_bind_ + # Returns a continuation which takes a value and returns one of two known target # continuations based on the value. If the value is true (Python truthy), this # chains to the `on_true` continuation. If the value is not true (Python falsy), # this chains to the `on_false` continuation. In either case, the continuation # not chained to is discarded. def branch(on_true, on_false): - return lambda environment, val: (on_true if val else on_false, environment) + return lambda env, macros, val: (on_true if val else on_false, env, macros) # Returns a continuation which receives values, and appends them to the values # passed to this factory, before chaining to a known target continuation. This # implements intermediate evaluation of list forms, where part of the list is # already known, as well as splicing for forms that yield multiple values. def append(args, continuation): - return lambda environment, *tail: (continuation, environment, *args, *tail) + return lambda env, macros, *tail: (continuation, env, macros, *args, *tail) def begin(continuation): - return lambda environment, *args: (continuation, environment, *(args[-1:] if args else ())) + return lambda env, macros, *args: (continuation, env, macros, *(args[-1:] if args else ())) # Transforms a continuation which should receive function results into a # function call continuation. A function call continuation receives a function @@ -111,20 +124,21 @@ def begin(continuation): # If the function is a procedure, this instead returns a continuation which will # invoke the procedure, then chain to the wrapped continuation. def invoke(continuation): - def invoke_(environment, fn, *args): + def invoke_(env, macros, fn, *args): if isinstance(fn, t.Procedure): - return procedure_call(environment, fn, *args) - return builtin(environment, fn, *args) + return procedure_call(env, macros, fn, *args) + return builtin(env, macros, fn, *args) - def procedure_call(environment, fn, *args): + def procedure_call(env, macros, fn, *args): call_env = fn.invocation_environment(*args) + call_macros = Environment(parent=macros) call_cont = fn.continuation - return_cont = tail_graft(continuation, environment, call_cont) - return (return_cont, call_env) + return_cont = tail_graft(continuation, env, macros, call_cont) + return (return_cont, call_env, call_macros) - def builtin(environment, fn, *args): + def builtin(env, macros, fn, *args): result = fn(*args) - return (continuation, environment, *result) + return (continuation, env, macros, *result) return invoke_ # Continuation transformer. Given a guarded continuation, and a graft @@ -136,17 +150,20 @@ def invoke(continuation): # continuation chains to None, the guard replaces the returned continuation and # environment with the graft continuation and environment. This handles # environment restoration after a function call. -def tail_graft(continuation, environment, guarded): +def tail_graft(continuation, environment, macros, guarded): # Tail call magic: if we're not going to transition to another continuation, # don't bother grafting environment recovery on. if continuation is None: return guarded - def guard(env, *args): - next, env, *args = guarded(env, *args) + def guard(env, macros, *args): + next, env, macros, *args = guarded(env, macros, *args) if next is None: - return (continuation, environment, *args) - return (tail_graft(continuation, environment, next), env, *args) + return (continuation, environment, macros, *args) + return ( + tail_graft(continuation, environment, macros, next), + env, macros, *args, + ) return guard @@ -171,9 +188,13 @@ def eval(value, symbols, continuation): if t.head(value) == symbols['if']: return if_(t.tail(value), symbols, continuation) if t.head(value) == symbols['define']: - return define(t.tail(value), symbols, continuation) + return define(t.tail(value), symbols, continuation, bind) + if t.head(value) == symbols['defmacro']: + return define(t.tail(value), symbols, continuation, macro_bind) if t.head(value) == symbols['lambda']: return lambda_(t.tail(value), symbols, continuation) + if t.head(value) == symbols['quote']: + return quote(t.tail(value), continuation) if t.head(value) == symbols['begin']: return apply(t.tail(value), symbols, begin(continuation)) # Ran out of alternatives, must be a function application @@ -185,7 +206,7 @@ def eval(value, symbols, continuation): # continuation`. The result of this evaluation is chained to a `bind` # continuation, to store the result of evaluation in the target environment. # Finally, the `bind` continuation chains to the target continuation. -def define(value, symbols, continuation): +def define(value, symbols, continuation, bind): symb, expr = t.flatten(value) if not t.symbol_p(symb): @@ -193,7 +214,7 @@ def define(value, symbols, continuation): bind_cont = bind(symb, continuation) eval_cont = eval(expr, symbols, bind_cont) - return lambda environment: (eval_cont, environment) + return lambda env, macros: (eval_cont, env, macros) # Returns a continuation which fully evaluates an `(if cond if-true if-false)` # form, before chaining to a known target continuation. First, the returned @@ -219,9 +240,9 @@ def if_(value, symbols, continuation): # calling `apply` on the tail of the list. def apply(list, symbols, continuation): if t.nil_p(list): - return lambda environment, *args: (continuation, environment, *args) + return lambda env, macros, *args: (continuation, env, macros, *args) tail_cont = apply(t.tail(list), symbols, continuation) - return lambda environment, *args: ( + return lambda env, macros, *args: ( eval(t.head(list), symbols, append(args, tail_cont)), - environment, + env, macros, ) diff --git a/actinide/expander.py b/actinide/expander.py index b8bc56d..a72a64e 100644 --- a/actinide/expander.py +++ b/actinide/expander.py @@ -2,6 +2,10 @@ from .types import * +# Raised if expansion of a form fails. +class ExpansionError(Exception): + pass + # Expand and syntax-check a form. # # This replaces shorthand notations, such as ``(define (a b c) body)``, with @@ -9,26 +13,118 @@ from .types import * # # Because this deals with unevaluated programs, this algorithm can safely # recurse: the input depth simply isn't that large. -def expand(form, symbols): - if nil_p(form) or not list_p(form): +def expand(form, symbols, macros): + if nil_p(form) or not cons_p(form): return form - # huge cheat. Working with python lists is hugely easier for this than - # working with conses, and it's after midnight. Flatten the form, expand it, - # and reconstitute it. This incurs two bonus copies per form: suck it up. - symb, *args = flatten(form) - if symb == symbols['if'] and len(args) == 2: - args.append(None) - elif symb == symbols['define']: - decl, *body = args - if list_p(decl): - decl = flatten(decl) - name, *formals = decl - lambda_ = list(symbols['lambda'], list(*formals), *body) - args = [name, lambda_] - elif symb == symbols['lambda']: - formals, *body = args - if len(body) != 1: - body = [list(symbols['begin'], *body)] - args = [formals, *body] - form = list(expand(symb, symbols), *[expand(subform, symbols) for subform in args]) + if head(form) == symbols['if']: + form = expand_if(form) + elif head(form) == symbols['define']: + form = expand_define(form, symbols) + elif head(form) == symbols['defmacro']: + form = expand_define(form, symbols) + elif head(form) == symbols['lambda']: + form = expand_lambda(form, symbols) + elif head(form) == symbols['quasiquote']: + form = expand_quasiquote(form, symbols) + elif symbol_p(head(form)) and head(form) in macros: + form = expand_macro(form, symbols, macros) + form = expand_subforms(form, symbols, macros) return form + +# Recursively expand subforms in an already-expanded top-level form. +def expand_subforms(list, symbols, macros): + if nil_p(list): + return nil + if not cons_p(list): + return expand_subforms(list, symbols, macros) + head, tail = uncons(list) + return cons( + expand(head, symbols, macros), + expand_subforms(tail, symbols, macros), + ) + +# Expand an `if` form. +# +# (if COND TRUE) +# => (if COND TRUE nil) +def expand_if(form): + head, form = uncons(form) + cond, form = uncons(form) + true, form = uncons(form) + if nil_p(form): + return list(head, cond, true, None) + false, form = uncons(form) + return list(head, cond, true, false) + +# Expand a define or defmacro form. +# +# (define (NAME FORMALS) BODY) +# => (define name (lambda FORMALS BODY)) +def expand_define(form, symbols): + head, form = uncons(form) + symb, form = uncons(form) + if cons_p(symb): + return expand_lambda_define(head, symb, form, symbols) + val, form = uncons(form) + return list(head, symb, val) + +def expand_lambda_define(head, symb, body, symbols): + name, formals = uncons(symb) + return list(head, name, cons(symbols['lambda'], cons(formals, body))) + +# Expands a lambda. +# +# (lambda FORMALS FORM) +# => unchanged +# (lambda FORMALS) +# => (lambda FORMALS (begin)) +# (lambda FORMALS FORM ...FORMS) +# => (lambda FORMALS (begin FORM ...FORMS)) +def expand_lambda(form, symbols): + head, form = uncons(form) + formals, form = uncons(form) + # XXX check formals + if cons_p(form) and not nil_p(tail(form)): + form = list(cons(symbols['begin'], form)) + return cons(head, cons(formals, form)) + +# Expands a quasiquote, recursively, expanding unquotes as needed. +def expand_quasiquote(form, symbols): + head, form = uncons(form) + body, form = uncons(form) + return expand_quasiquoted(body, symbols) + +def expand_quasiquoted(form, symbols): + if nil_p(form): + return form + if not cons_p(form): + return list(symbols['quote'], form) + first, rest = uncons(form) + if first == symbols['unquote']: + next, rest = uncons(rest) + return next + if not nil_p(first) and cons_p(first): + candidate, body = uncons(first) + if candidate == symbols['unquote-splicing']: + next, rest = uncons(body) + return list( + symbols['append'], + next, + expand_quasiquoted(body, symbols), + ) + return list( + symbols['cons'], + expand_quasiquoted(first, symbols), + expand_quasiquoted(rest, symbols), + ) + +# Expands macro definitions, iterating until no further expansion is possible. +def expand_macro(form, symbols, macros): + macro, args = uncons(form) + macro_body = macros[macro] + args = flatten(args) + expansion, = macro_body(*args) + return expand(expansion, symbols, macros) + +def uncons(value): + return head(value), tail(value) diff --git a/actinide/reader.py b/actinide/reader.py index f7703b5..9c21beb 100644 --- a/actinide/reader.py +++ b/actinide/reader.py @@ -32,7 +32,7 @@ def read(port, symbols): raise SyntaxError("Unexpected ')'") if head == '(': return read_list(port, symbols) - return read_atom(head, symbols) + return read_atom(head, port, symbols) # Reads the body of a list from a port. This will read forms, recursively, until # it encounters the terminating ``)`` that closes the current list, or until it @@ -53,7 +53,7 @@ def read_list(port, symbols): read_list_tail(port, symbols), ) return cons( - read_atom(head, symbols), + read_atom(head, port, symbols), read_list_tail(port, symbols), ) @@ -74,7 +74,7 @@ def read_list_tail(port, symbols): if head == '.': return read_cons_head(port, symbols) return cons( - read_atom(head, symbols), + read_atom(head, port, symbols), read_list_tail(port, symbols), ) @@ -92,7 +92,7 @@ def read_cons_head(port, symbols): port, symbols, ) - return read_cons_tail(read_atom(head, symbols), port, symbols) + return read_cons_tail(read_atom(head, port, symbols), port, symbols) # Reads the second form of a dotted pair from the input. This must either be a # terminating ``)``, or another dotted pair. @@ -116,14 +116,28 @@ def read_cons_tail(head, port, symbols): # * ``read_decimal`` # * ``read_symbol`` in the current symbol table (which always succeeds) # +# This also reconstructs quoted forms. +# # The first reader to accept the string determines the type of the result. -def read_atom(atom, symbols): +quotes = { + "'": 'quote', + "`": 'quasiquote', + ",": 'unquote', + ",@": 'unquote-splicing', +} +def read_atom(atom, port, symbols): def read_as_first(val, *funcs): for func in funcs: result = func(val) if result is not None: return result + if atom in quotes: + quoted = read(port, symbols) + if quoted == EOF: + raise SyntaxError("Unexpected end of input") + return list(symbols[quotes[atom]], quoted) + if atom[0] == '"': return read_string(atom) return read_as_first( diff --git a/actinide/tokenizer.py b/actinide/tokenizer.py index 449fc3d..b17bb48 100644 --- a/actinide/tokenizer.py +++ b/actinide/tokenizer.py @@ -44,6 +44,13 @@ class TokenError(Exception): ''' pass +whitespace = " \n\t" +parens = "()" +quotes = "'`," # ,@ is also a quote, but not a single-character quote +comment_delim = ';' +string_delim = '"' +string_escaped = '"\\' + # Read one token from a port. # # This is the top-level driver for the state machine that divides the underlying @@ -73,11 +80,13 @@ def tokenize_any(port): lookahead = peek_next(port) if lookahead == '': return None, tokenize_eof - if lookahead == ';': + if lookahead in comment_delim: return None, tokenize_comment - if lookahead in '()': + if lookahead in parens: return None, tokenize_syntax - if lookahead in ' \t\n': + if lookahead in quotes: + return None, tokenize_quote + if lookahead in whitespace: return None, tokenize_whitespace return None, tokenize_atom @@ -105,6 +114,20 @@ def tokenize_comment(port): return None, tokenize_any return None, tokenize_comment +def tokenize_quote(port): + next = read_next(port) + if next == ',': + return None, tokenize_unquote(next) + return next, tokenize_any + +def tokenize_unquote(state): + def tokenize_unquote_next(port): + next = peek_next(port) + if next == '@': + return state + read_next(port), tokenize_any + return state, tokenize_any + return tokenize_unquote_next + # Consumes one character, returning it as a token, before transitioning back to # the ``tokenize_any`` state. This correctly tokenizes the ``(`` and ``)`` # tokens if they are at the front of the port. @@ -206,9 +229,7 @@ def tokenize_escaped_string_character(state): next = read_next(port) if next == '': raise TokenError('Unclosed string literal') - if next in '\\"': - return None, tokenize_string_character(state + next) - raise TokenError(f"Invalid string escape '\\{next}'") + return None, tokenize_string_character(state + next) return tokenize_escaped_string_character_next # A state factory which terminates a string literal. These states read off the diff --git a/actinide/types.py b/actinide/types.py index b5b87cd..17f7a3f 100644 --- a/actinide/types.py +++ b/actinide/types.py @@ -27,7 +27,6 @@ def nil_p(value): def read_nil(value): return nil -@fn def display_nil(value): return '()' @@ -50,7 +49,6 @@ def read_boolean(value): return false return None -@fn def display_boolean(value): return '#t' if value else '#f' @@ -73,7 +71,6 @@ def read_integer(value): except ValueError: return nil -@fn def display_integer(value): return str(value) @@ -94,7 +91,6 @@ def read_decimal(value): except InvalidOperation: return nil -@fn def display_decimal(value): return str(value) @@ -115,7 +111,6 @@ def read_string(value): value = value.replace('\\\\', '\\') return value -@fn def display_string(value): value = value.replace('\\', '\\\\') value = value.replace('"', '\\"') @@ -148,7 +143,6 @@ def symbol_p(value): def read_symbol(value, symbol_table): return symbol(value, symbol_table) -@fn def display_symbol(value): return str(value) @@ -174,15 +168,14 @@ def head(cons): def tail(cons): return cons.tail -@fn -def display_cons(value): +def display_cons(value, symbols): parts = [] while cons_p(value): - parts.append(display(head(value))) + parts.append(display(head(value), symbols)) value = tail(value) if not nil_p(value): parts.append('.') - parts.append(display(value)) + parts.append(display(value, symbols)) return '(' + ' '.join(parts) + ')' # ### Lists @@ -199,6 +192,23 @@ def list(*elems): def list_p(value): return nil_p(value) or cons_p(value) and list_p(tail(value)) +@fn +def append(list, *lists): + if not lists: + return list + if nil_p(list): + return append(*lists) + value, next = head(list), tail(list) + return cons(value, append(next, *lists)) + +@fn +def len(list): + l = 0 + while not nil_p(list): + l += 1 + list = tail(list) + return l + def flatten(list): r = [] while not nil_p(list): @@ -209,15 +219,17 @@ def flatten(list): # ### Procedures class Procedure(object): - def __init__(self, body, formals, environment, symbols): + def __init__(self, body, formals, environment, macros, symbols): self.body = body self.continuation = e.eval(body, symbols, None) self.formals = formals self.environment = environment + self.macros = macros def __call__(self, *args): call_env = self.invocation_environment(*args) - return e.run(self.continuation, call_env, ()) + call_macros = Environment(parent=self.macros) + return e.run(self.continuation, call_env, call_macros, ()) def invocation_environment(self, *args): return Environment(zip(self.formals, args), self.environment) @@ -226,19 +238,21 @@ class Procedure(object): def procedure_p(value): return callable(value) -@fn -def display_procedure(proc): +def display_procedure(proc, symbols): if isinstance(proc, Procedure): - formals = ' '.join(display(formal) for formal in proc.formals) - body = display(proc.body) + formals = ' '.join(display(formal, symbols) for formal in proc.formals) + body = display(proc.body, symbols) return f'<procedure: (lambda ({formals}) {body})>' return f'<builtin: {proc.__name__}>' # ### General-purpose functions -@fn -def display(value): + +# Bind manually, fixing the symbol table at the bind site +def display(value, symbols): + if quote_p(value, symbols): + return display_quote(value, symbols) if cons_p(value): - return display_cons(value) + return display_cons(value, symbols) if symbol_p(value): return display_symbol(value) if string_p(value): @@ -252,6 +266,25 @@ def display(value): if decimal_p(value): return display_decimal(value) if procedure_p(value): - return display_procedure(value) + return display_procedure(value, symbols) # Give up and use repr to avoid printing `None`. return repr(value) + +def quote_p(value, symbols): + return cons_p(value) and head(value) in [ + symbols[q] + for q in ['quote', 'quasiquote', 'unquote', 'unquote-splicing'] + ] + +def display_quote(value, symbols): + quote, form = flatten(value) + if quote == symbols['quote']: + return "'" + display(form, symbols) + if quote == symbols['quasiquote']: + return "`" + display(form, symbols) + if quote == symbols['unquote']: + return "," + display(form, symbols) + if quote == symbols['unquote-splicing']: + return ",@" + display(form, symbols) + # emergency fallback + return display_cons(value, symbols) diff --git a/bin/actinide-repl b/bin/actinide-repl index c4343b6..9b66891 100755 --- a/bin/actinide-repl +++ b/bin/actinide-repl @@ -17,7 +17,7 @@ def repl(session, port): print() return 0 results = session.eval(form) - print(*(at.display(result) for result in results)) + print(*(session.display(result) for result in results)) except Exception as e: print(e) except KeyboardInterrupt: diff --git a/primer.py b/primer.py new file mode 100644 index 0000000..6954bea --- /dev/null +++ b/primer.py @@ -0,0 +1,10 @@ +import actinide as a +import actinide.types as t +import actinide.expander as x + +s = a.Session() + +def expand(form): + return x.expand(form, s.symbols, s.macros) + +s.macros[s.symbol('twice')] = lambda f: (t.list(f, f),) diff --git a/tests/forms.py b/tests/forms.py index 1a49636..7ea216f 100644 --- a/tests/forms.py +++ b/tests/forms.py @@ -1,6 +1,7 @@ from hypothesis.strategies import integers, decimals as hypo_decimals, booleans, characters, text, tuples, lists as hypo_lists, just, one_of from hypothesis.strategies import deferred, recursive +from actinide import tokenizer as t from actinide.symbol_table import * from actinide.types import * @@ -37,7 +38,7 @@ def strings(): # Generates any character legal in a symbol, which cannot be part of some other # kind of atom. def symbol_characters(): - return characters(blacklist_characters='01234567890#. \t\n();"') + return characters(blacklist_characters='01234567890#' + t.whitespace + t.parens + t.quotes + t.string_delim + t.comment_delim) # Generates symbols guaranteed not to conflict with other kinds of literal. This # is a subset of the legal symbols. @@ -45,6 +46,15 @@ def symbols(): return text(symbol_characters(), min_size=1)\ .map(lambda item: symbol_table[item]) +def quoted_forms(): + return tuples( + one_of( + symbol_table[q] + for q in ['quote', 'quasiquote', 'unquote', 'unquote-splicing'] + ), + deferred(lambda: forms), + ).map(lambda elems: list(*elems)) + # Generates atoms. def atoms(): return one_of( diff --git a/tests/test_evaluator.py b/tests/test_evaluator.py index dbccbce..cb7c11c 100644 --- a/tests/test_evaluator.py +++ b/tests/test_evaluator.py @@ -13,6 +13,7 @@ from .programs import * def test_evaluator(program_result): program, result, bindings = program_result environment = Environment() - assert run(eval(program, symbol_table, None), environment) == result + macros = Environment() + assert run(eval(program, symbol_table, None), environment, macros) == result for symbol, value in bindings: assert environment[symbol] == value diff --git a/tests/test_reader.py b/tests/test_reader.py index 54a1681..b67c05e 100644 --- a/tests/test_reader.py +++ b/tests/test_reader.py @@ -12,7 +12,7 @@ from .forms import * # * Given a form, can the reader recover it from its display? @given(forms()) def test_reader(form): - input = display(form) + input = display(form, symbol_table) port = string_to_input_port(input) assert read(port, symbol_table) == form @@ -21,7 +21,7 @@ def test_reader(form): # without touching the garbage? This is only reliable with lists and conses. @given(lists() | conses(), text()) def test_reader_with_trailing(form, text): - input = display(form) + text + input = display(form, symbol_table) + text port = string_to_input_port(input) assert read(port, symbol_table) == form diff --git a/tests/tokens.py b/tests/tokens.py index 3eb58b8..0e98494 100644 --- a/tests/tokens.py +++ b/tests/tokens.py @@ -1,23 +1,29 @@ from hypothesis.strategies import just, one_of, characters, text, lists, tuples from hypothesis.strategies import composite, recursive +from actinide import tokenizer as t + # Generators for token families -# Generates the `(` token. -def open_parens(): - return just('(') +# Generates the `(` and ')' tokens. +def parens(): + return one_of(just(p) for p in t.parens) -# Generates the ')' token. -def close_parens(): - return just(')') +def quotes(): + return one_of( + just("'"), + just('`'), + just(','), + just(',@'), + ) # Generates characters that are legal, unescaped, inside of a string. def string_bare_characters(): - return characters(blacklist_characters='\\"') + return characters(blacklist_characters=t.string_escaped) # Generates legal string escape sequences. def string_escaped_characters(): - return one_of(just('"'), just('\\')).map(lambda c: '\\' + c) + return one_of(just(c) for c in t.string_escaped).map(lambda c: '\\' + c) # Generates single-character string representations, including escapes. def string_characters(): @@ -34,7 +40,7 @@ def strings(): # Generates characters which are legal within a symbol. def symbol_characters(): - return characters(blacklist_characters=' \t\n();"') + return characters(blacklist_characters=t.whitespace + t.parens + t.quotes + t.string_delim + t.comment_delim) # Generates legal symbols. def symbols(): @@ -42,11 +48,11 @@ def symbols(): # Generates single whitespace characters. def whitespace_characters(): - return one_of(just('\n'), just(' '), just('\t')) + return one_of(just(c) for c in t.whitespace) # Generates a single token. def tokens(): - return one_of(symbols(), strings(), open_parens(), close_parens()) + return one_of(symbols(), strings(), parens(), quotes()) # Generates a string which may not be empty, but which does not contain a token. def nontokens(): @@ -78,16 +84,16 @@ def spaced_tokens(): return tuples(intertokens(), strategy) def unspaced(strategy): return tuples(one_of(just(''), intertokens()), strategy) + def spaced_quotes(): + return spaced(quotes()) def spaced_symbols(): return spaced(symbols()) def spaced_strings(): return unspaced(strings()) - def spaced_open_parens(): - return unspaced(open_parens()) - def spaced_close_parens(): - return unspaced(close_parens()) + def spaced_parens(): + return unspaced(parens()) - return one_of(spaced_symbols(), spaced_strings(), spaced_open_parens(), spaced_close_parens()) + return one_of(spaced_symbols(), spaced_quotes(), spaced_strings(), spaced_parens()) # Generats a list of pairs as per spaced_token(). def spaced_token_sequences(): |
