diff --git a/CHANGELOG.md b/CHANGELOG.md index 4741a12..3b27efd 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,15 @@ # Revision history for Nekomata +## 0.8.0.0 -- Unreleased + +* New builtin: `\lastValue`. +* A new mode `last` is added. It outputs the last possible result. You can switch to this mode by the `-t` flag in the command line or the `\Mode last` command in the REPL. + +### Breaking changes + +* `\oneValue` is renamed to `\firstValue`. +* The built-in function `\normalForm` was removed in version 0.7.0.0. Its short name `¤` is now reused for the new function `\lastValue`. + ## 0.7.0.0 -- 2024-08-10 * Added examples to the documentation of built-in functions and particles. diff --git a/Nekomata.cabal b/Nekomata.cabal index 3c2dc2d..2b10b43 100644 --- a/Nekomata.cabal +++ b/Nekomata.cabal @@ -66,6 +66,7 @@ library Nekomata.CodePage Nekomata.Eval Nekomata.Particle + Nekomata.Result -- Modules included in this library but not exported. other-modules: @@ -80,7 +81,6 @@ library Nekomata.Parser.Data Nekomata.Parser.Program Nekomata.Program - Nekomata.Result -- LANGUAGE extensions used by modules in this package. -- other-extensions: diff --git a/analysis/Ideas.md b/analysis/Ideas.md index 5f4d693..6c3fb17 100644 --- a/analysis/Ideas.md +++ b/analysis/Ideas.md @@ -5,11 +5,9 @@ 此处总结一下 Code Page 中已有但还没有用上的字符: ``` -¤.W`w +.W`w ``` -`¤` 原本是用于 `\normalForm` 函数。但这个函数由于定义不清晰且在现有的答案中没有用到,因此被移除了。这个字符空了出来,可以用于其它的内置函数。 - 如果 CodePage 中的 256 个字符都用完了,可以考虑用像 05AB1E 那样,用 `.` 开头的字符来表示双字节的内置函数。现在的 CodePage 还没有用完,先不考虑这个问题,但 `.` 也先不要用掉。 LiberationMono 字体所支持的字符列举于[此文件](analysis/LiberationMonoGlyphs.txt),以供选取。 @@ -26,10 +24,9 @@ LiberationMono 字体所支持的字符列举于[此文件](analysis/LiberationM * `1` 排到第十四,是最常见的常量。 * `ç`(`\cons0`)能排到第十八。这个完全出乎我的意料,因为很多别的 golfing 语言中根本没有这个操作。它的作用是给列表加一个 0,通常是配合 `\head`、`\last`、`\minimum`、`\maximum` 等函数使用,来处理空列表的特殊情况。需要看一看其它语言是怎么处理空列表的。 * 与 non-deterministic 机制有关的操作,除了 `=`(`\eq`)没有一个排到前二十。排最高的是第二十四的 `a`(`\allValues`)。 -* 在两个字符的组合中,排前六的分别是 `ᶦ{`、`ᵖ{`、`ʷ{`、`ˡ{`、`ᵑ{`、`ᶠ{`。也许可以像 Vyxal 等语言一样,让这些助词自动开启 block,省去一个 `{`。不过另外一些助词通常只修饰单个 built-in,自动开启 block 反而会额外需要一个 `}`。尤其是 `ᵖ`、`ᵑ`、`ᵐ`,两种用法都很常见,不好取舍。详见 [`analysis/particles.txt`](particles.txt),以及下文的[关于助词](#关于助词)。 -* `{$` 在两个字符的组合中排第七,`{:` 排第八。这两个组合没有特别的意义,只是因为 `\dup` 和 `\swap` 是最常用的两个操作,因此它们的组合也很常用。 -* `Jᵐ` 在两个字符的组合中排第八,和 `{:`、`ᵐ{`、`Ťđ` 并列。这完全出乎我的意料。`J` 是将输入 non-deterministic 地拆成若干部分,`ᵐ` 是对后一个函数进行 map,这两个操作的组合可以看作 Haskell 中 `concatMap` 的反操作。可能 `concatMap` 也是一个常用的操作,但由于 `ᵐ` 和 `j`(`\concat`)之间会插入别的操作,所以没有出现在这个统计结果中。可以考虑加上 `\concatMap` 和 `\unconcatMap` 两个助词。目前已加上。 -* `Ťđ` 在两个字符的组合中也排第八,和 `{:`、`Jᵐ`、`ᵐ{` 并列。这个组合是将二维列表转置,然后进行 unpair 操作。看起来确实比较常用。先等更多的解答出现再决定是否增加这个 built-in。 +* 在两个字符的组合中,排前六的分别是 `ᶦ{`、`ᵖ{`、`ʷ{`、`ˡ{`、`ᶠ{`、`ᵑ{`。也许可以像 Vyxal 等语言一样,让这些助词自动开启 block,省去一个 `{`。不过另外一些助词通常只修饰单个 built-in,自动开启 block 反而会额外需要一个 `}`。尤其是 `ᵖ`、`ᵑ`、`ᵐ`,两种用法都很常见,不好取舍。详见 [`analysis/particles.txt`](particles.txt),以及下文的[关于助词](#关于助词)。 +* `{$` 在两个字符的组合中排第六,`{:` 排第八。这两个组合没有特别的意义,只是因为 `\dup` 和 `\swap` 是最常用的两个操作,因此它们的组合也很常用。 +* `Ťđ` 在两个字符的组合中也排九,和 `{:`、`Jᵐ`、`ᵐ{` 并列。这个组合是将二维列表转置,然后进行 unpair 操作。看起来确实比较常用。先等更多的解答出现再决定是否增加这个 built-in。 * `Ňᵖ{` 在三个字符的组合中排第一,也许值得为它增加一个助词。 * 统计结果与其它语言的差异,除了考虑到语言本身的特点之外,还要考虑到语言的使用者的偏好。目前 Nekomata 的使用者基本上只有我自己,解答的也主要是我感兴趣的题目类型,string 相关的题目较少,ascii-art 更是完全没有。 @@ -216,6 +213,6 @@ LiberationMono 字体所支持的字符列举于[此文件](analysis/LiberationM 目前的文档还很不完善。需要增加更多的例子,以及更详细的说明。 -- [ ] 为每个函数和助词增加例子,并把这些例子用作单元测试。目前函数的部分已完成,助词的部分还没有。 +- [ ] 为每个函数和助词增加例子,并把这些例子用作单元测试。目前函数的部分已完成。助词的部分每个只有一个例子,需要给不同的用法都增加例子。由于一些助词的用法可能要大改(见[关于助词](#关于助词)),等改过之后再增加例子。 - [ ] 重写现有的 Tutorial。考虑分成多个文件,每个文件只讲一个主题。 - [ ] REPL 的 `\Info` 命令,可以考虑改成能一次输出多个函数的信息。 diff --git a/analysis/corpus.txt b/analysis/corpus.txt index cfbe2f5..0edf10c 100644 --- a/analysis/corpus.txt +++ b/analysis/corpus.txt @@ -21,7 +21,7 @@ u∕u ŢƵḟ pƆᵖ≤ ᶦP -ᵃp=al +ᵃp= Ƃ£E∫Ɔž≥ pZ‼l Ä$Ç$→/ @@ -49,7 +49,7 @@ rFŗ∑ #Ry RG1Ĉ Ö∫¬∑T -pᵖ½al +pᵖ½ ᵒ+j∕= N;< 2ᶦ{:←*→ @@ -226,6 +226,7 @@ x:→&xï@Ŝ ŢṂaş ᵐ{į→ŋ+}≡H Cᶦ{ᵈsf¡C +ᴶ{ᵉhl= ʷ{ᶜŤĕ≡¿}Ø= ↕ᵃ{2Šᵐɗ"∩<"<60b}-_På :←/∏* diff --git a/analysis/freq_1gram.txt b/analysis/freq_1gram.txt index 0e3d25c..0b9feb2 100644 --- a/analysis/freq_1gram.txt +++ b/analysis/freq_1gram.txt @@ -1,6 +1,6 @@ -{ : 81 +{ : 82 $ : 56 -= : 54 += : 55 : : 50 + : 46 ∑ : 39 @@ -14,8 +14,8 @@ x : 30 - : 26 1 : 25 j : 25 +ᵉ : 24 o : 24 -ᵉ : 23 ç : 23 2 : 23 , : 22 @@ -26,14 +26,14 @@ o : 24 ᵒ : 20 ↔ : 20 đ : 19 -a : 19 ĭ : 18 u : 18 ᵖ : 18 ~ : 18 S : 17 -l : 17 p : 17 +a : 17 +l : 16 ĉ : 16 Ɔ : 15 Z : 15 @@ -78,6 +78,7 @@ c : 10 % : 10 ᶠ : 10 N : 10 +h : 10 O : 10 i : 10 z : 10 @@ -88,7 +89,6 @@ f : 9 ŗ : 9 ᵗ : 9 ≈ : 9 -h : 9 C : 9 P : 8 Ƶ : 8 @@ -104,6 +104,7 @@ H : 8 4 : 8 ¿ : 8 ᵈ : 8 +ᴶ : 7 Ä : 7 / : 7 Ħ : 7 @@ -113,7 +114,6 @@ H : 8 ä : 7 Ř : 7 ¢ : 7 -ᴶ : 6 ᵏ : 6 Ö : 6 ƃ : 6 diff --git a/analysis/freq_2gram.txt b/analysis/freq_2gram.txt index 1c49b8a..21c346d 100644 --- a/analysis/freq_2gram.txt +++ b/analysis/freq_2gram.txt @@ -12,6 +12,7 @@ ∕u : 4 £E : 4 Ɔž : 4 +{ᵉ : 4 1Ĩ : 4 ᵏ{ : 4 ᵒ{ : 4 @@ -33,7 +34,6 @@ pƆ : 3 ž≥ : 3 Z‼ : 3 $ᵑ : 3 -{ᵉ : 3 ?} : 3 }Ø : 3 Ø= : 3 @@ -70,6 +70,7 @@ pN : 3 ᶻL : 3 x→ : 3 +$ : 3 +ᴶ{ : 3 *$ : 3 #← : 3 }a : 3 @@ -98,7 +99,6 @@ qŁ : 2 lo : 2 Ɔᵖ : 2 p= : 2 -al : 2 $Ç : 2 →/ : 2 çƆ : 2 @@ -119,7 +119,6 @@ $Ĭ : 2 rF : 2 G1 : 2 ¬∑ : 2 -½a : 2 +j : 2 xᵐ : 2 ᶜt : 2 @@ -228,6 +227,8 @@ x- : 2 ŋ+ : 2 +} : 2 {ᵈ : 2 +ᵉh : 2 +hl : 2 ≡¿ : 2 2Š : 2 }- : 2 @@ -238,7 +239,6 @@ RS : 2 ɔ$ : 2 jŢ : 2 ↔$ : 2 -ᴶ{ : 2 ž¿ : 2 çṀ : 2 3~ : 2 @@ -317,7 +317,6 @@ d! : 1 ᵖ≤ : 1 ᶦP : 1 ᵃp : 1 -=a : 1 Ƃ£ : 1 E∫ : 1 pZ : 1 @@ -833,6 +832,7 @@ Cᶦ : 1 sf : 1 f¡ : 1 ¡C : 1 +l= : 1 {ᶜ : 1 ᶜŤ : 1 Ťĕ : 1 @@ -921,6 +921,7 @@ I} : 1 }ṁ : 1 Sj : 1 Ţ½ : 1 +½a : 1 ađ : 1 R↔ : 1 $∆ : 1 @@ -995,8 +996,6 @@ Nᵖ : 1 Ťů : 1 ů} : 1 şᵉ : 1 -ᵉh : 1 -hl : 1 l→ : 1 →Ð : 1 ,Ţ : 1 diff --git a/analysis/freq_3gram.txt b/analysis/freq_3gram.txt index 945f5a6..b43def8 100644 --- a/analysis/freq_3gram.txt +++ b/analysis/freq_3gram.txt @@ -38,6 +38,7 @@ tut : 2 ᵒ{ᵋ : 2 ᵖ{: : 2 ŋ+} : 2 +ᵉhl : 2 3~ᵑ : 2 ʸ{ˣ : 2 ∆±ĉ : 2 @@ -96,8 +97,6 @@ d!" : 1 pƆᵖ : 1 Ɔᵖ≤ : 1 ᵃp= : 1 -p=a : 1 -=al : 1 Ƃ£E : 1 £E∫ : 1 E∫Ɔ : 1 @@ -165,8 +164,6 @@ G1Ĉ : 1 ∫¬∑ : 1 ¬∑T : 1 pᵖ½ : 1 -ᵖ½a : 1 -½al : 1 ᵒ+j : 1 +j∕ : 1 j∕= : 1 @@ -697,6 +694,9 @@ Cᶦ{ : 1 ᵈsf : 1 sf¡ : 1 f¡C : 1 +ᴶ{ᵉ : 1 +{ᵉh : 1 +hl= : 1 ʷ{ᶜ : 1 {ᶜŤ : 1 ᶜŤĕ : 1 @@ -903,7 +903,6 @@ Nᵖ{ : 1 ů}a : 1 aşᵉ : 1 şᵉh : 1 -ᵉhl : 1 hl→ : 1 l→Ð : 1 ,Ţ← : 1 diff --git a/analysis/freq_4gram.txt b/analysis/freq_4gram.txt index 4956e0e..080df44 100644 --- a/analysis/freq_4gram.txt +++ b/analysis/freq_4gram.txt @@ -51,8 +51,6 @@ orld : 1 rld! : 1 ld!" : 1 pƆᵖ≤ : 1 -ᵃp=a : 1 -p=al : 1 Ƃ£E∫ : 1 £E∫Ɔ : 1 E∫Ɔž : 1 @@ -104,8 +102,6 @@ rFŗ∑ : 1 RG1Ĉ : 1 Ö∫¬∑ : 1 ∫¬∑T : 1 -pᵖ½a : 1 -ᵖ½al : 1 ᵒ+j∕ : 1 +j∕= : 1 2ᶦ{: : 1 @@ -507,6 +503,9 @@ Cᶦ{ᵈ : 1 {ᵈsf : 1 ᵈsf¡ : 1 sf¡C : 1 +ᴶ{ᵉh : 1 +{ᵉhl : 1 +ᵉhl= : 1 ʷ{ᶜŤ : 1 {ᶜŤĕ : 1 ᶜŤĕ≡ : 1 diff --git a/analysis/freq_5gram.txt b/analysis/freq_5gram.txt index 81f9a1d..c887bc7 100644 --- a/analysis/freq_5gram.txt +++ b/analysis/freq_5gram.txt @@ -32,7 +32,6 @@ o, Wo : 1 World : 1 orld! : 1 rld!" : 1 -ᵃp=al : 1 Ƃ£E∫Ɔ : 1 £E∫Ɔž : 1 E∫Ɔž≥ : 1 @@ -71,7 +70,6 @@ R1cʳ× : 1 {$Zᵉ% : 1 ṀRᶠ¦Ṁ : 1 Ö∫¬∑T : 1 -pᵖ½al : 1 ᵒ+j∕= : 1 2ᶦ{:← : 1 ᶦ{:←* : 1 @@ -365,6 +363,8 @@ Cᶦ{ᵈs : 1 ᶦ{ᵈsf : 1 {ᵈsf¡ : 1 ᵈsf¡C : 1 +ᴶ{ᵉhl : 1 +{ᵉhl= : 1 ʷ{ᶜŤĕ : 1 {ᶜŤĕ≡ : 1 ᶜŤĕ≡¿ : 1 diff --git a/analysis/particles.txt b/analysis/particles.txt index efe627f..d4c1464 100644 --- a/analysis/particles.txt +++ b/analysis/particles.txt @@ -1,6 +1,6 @@ particle : with "{" / total ᵈ : 0 / 8 -ᵉ : 0 / 23 +ᵉ : 0 / 24 ʳ : 0 / 5 ˣ : 0 / 4 ᶾ : 0 / 1 @@ -12,8 +12,8 @@ particle : with "{" / total ᵒ : 4 / 20 ᵗ : 2 / 9 ᵚ : 2 / 8 -ᴶ : 2 / 6 ʲ : 2 / 5 +ᴶ : 3 / 7 ʰ : 1 / 2 ᵑ : 6 / 11 ᵖ : 11 / 18 diff --git a/app/Main.hs b/app/Main.hs index 6c861fd..66d35bd 100644 --- a/app/Main.hs +++ b/app/Main.hs @@ -64,6 +64,12 @@ optMode = <> short '1' <> help "Show only the first value" ) + <|> flag' + LastValue + ( long "last" + <> short 't' + <> help "Show only the last value" + ) <|> flag' CountValues ( long "count" diff --git a/app/Repl.hs b/app/Repl.hs index d66f078..adae3c3 100644 --- a/app/Repl.hs +++ b/app/Repl.hs @@ -42,6 +42,7 @@ parseReplCommand input = ("\\Help" : _) -> ReplHelp ("\\Mode" : "all" : _) -> ReplMode AllValues ("\\Mode" : "first" : _) -> ReplMode FirstValue + ("\\Mode" : "last" : _) -> ReplMode LastValue ("\\Mode" : "count" : _) -> ReplMode CountValues ("\\Mode" : "exists" : _) -> ReplMode CheckExistence ("\\Mode" : _) -> ReplShowMode @@ -60,6 +61,7 @@ replCommandStrings = , "\\Mode" , "\\Mode all" , "\\Mode first" + , "\\Mode last" , "\\Mode count" , "\\Mode exists" , "\\Limit" @@ -80,8 +82,9 @@ helpString = , " \\Help Show this help" , " \\Mode Show current mode (default: all)" , " \\Mode all Show all results" - , " \\Mode first Show first result" - , " \\Mode count Show number of results" + , " \\Mode first Show the first result" + , " \\Mode last Show the last result" + , " \\Mode count Show the number of results" , " \\Mode exists Show whether there are any results" , " \\Limit Set the max number of results to show (default: 16)" , " \\Input Reset the stack with the given input" diff --git a/doc/Builtins.md b/doc/Builtins.md index 4229d33..f814669 100644 --- a/doc/Builtins.md +++ b/doc/Builtins.md @@ -33,7 +33,7 @@ __Examples__: - `1 2?a` → `[1,2]` -### `oneValue` (`¡`, `1 -> 1`) +### `firstValue` (`¡`, `1 -> 1`) Get the first possible value from a non-deterministic object. @@ -43,6 +43,16 @@ __Examples__: - `1 2?¡` → `1` +### `lastValue` (`¤`, `1 -> 1`) + +Get the last possible value from a non-deterministic object. + +Fails if the object has no values. + +__Examples__: + +- `1 2?¤` → `2` + ### `countValues` (`n`, `1 -> 1`) Count the number of values in a non-deterministic object. diff --git a/doc/GettingStarted.md b/doc/GettingStarted.md index 05bdfd7..c15edea 100644 --- a/doc/GettingStarted.md +++ b/doc/GettingStarted.md @@ -100,7 +100,7 @@ If you want to clear the stack without taking input, you can simply type `\Input ## Modes -Nekomata is a non-deterministic programming language. A program can have multiple possible outputs. Nekomata supports four modes for outputting the results of a program: +Nekomata is a non-deterministic programming language. A program can have multiple possible outputs. Nekomata supports five modes for outputting the results of a program: ### `all` @@ -116,7 +116,17 @@ This mode outputs the first result of a program. If the program has no results, When running a program from the command line, you can switch to this mode by passing the `-1` flag. -In the REPL, you can switch to this mode by typing `\Mode first`.s +In the REPL, you can switch to this mode by typing `\Mode first`. + +### `last` + +This mode outputs the last result of a program. If the program has no results, it outputs nothing. + +When running a program from the command line, you can switch to this mode by passing the `-t` flag. + +In the REPL, you can switch to this mode by typing `\Mode last`. + +This mode is added in version 0.8.0.0, so it may not be available in the online interpreter. ### `count` diff --git a/doc/Tutorial.md b/doc/Tutorial.md index 5807e27..3488f62 100644 --- a/doc/Tutorial.md +++ b/doc/Tutorial.md @@ -1,6 +1,6 @@ # Tutorial -Nekomata is still in an early stage of development. The syntax and semantics of the language may change in the future. This tutorial is also incomplete. +Nekomata is still in an early stage of development. The syntax and semantics of the language may change in the future. This tutorial is also incomplete. I'm planning to rewrite it completely when the language is more stable. For installing and using the interpreter, please see [Getting Started](GettingStarted.md). @@ -184,7 +184,7 @@ This looks similar to the previous code. It also pushes two elements onto the st ## Modes -Nekomata is a non-deterministic programming language. A program can have multiple possible outputs. Nekomata supports four modes for outputting the results of a program: +Nekomata is a non-deterministic programming language. A program can have multiple possible outputs. Nekomata supports five modes for outputting the results of a program: ### `all` @@ -200,7 +200,17 @@ This mode outputs the first result of a program. If the program has no results, When running a program from the command line, you can switch to this mode by passing the `-1` flag. -In the REPL, you can switch to this mode by typing `\Mode first`.s +In the REPL, you can switch to this mode by typing `\Mode first`. + +### `last` + +This mode outputs the last result of a program. If the program has no results, it outputs nothing. + +When running a program from the command line, you can switch to this mode by passing the `-t` flag. + +In the REPL, you can switch to this mode by typing `\Mode last`. + +This mode is added in version 0.8.0.0, so it may not be available in the online interpreter. ### `count` diff --git a/src/Nekomata/Builtin.hs b/src/Nekomata/Builtin.hs index 3443291..7122611 100644 --- a/src/Nekomata/Builtin.hs +++ b/src/Nekomata/Builtin.hs @@ -116,12 +116,19 @@ builtins = "Get a list of all possible values for a non-deterministic object." [("1 2?a", all_ ["[1,2]"])] , Builtin - "oneValue" + "firstValue" '¡' - oneValue + firstValue' "Get the first possible value from a non-deterministic object.\n\ \Fails if the object has no values." [("1 2?¡", all_ ["1"])] + , Builtin + "lastValue" + '¤' + lastValue' + "Get the last possible value from a non-deterministic object.\n\ + \Fails if the object has no values." + [("1 2?¤", all_ ["2"])] , Builtin "countValues" 'n' diff --git a/src/Nekomata/Builtin/Basic.hs b/src/Nekomata/Builtin/Basic.hs index 556df47..e92df5c 100644 --- a/src/Nekomata/Builtin/Basic.hs +++ b/src/Nekomata/Builtin/Basic.hs @@ -16,11 +16,16 @@ allValues :: Function allValues = Function (Arity 1 1) $ \_ (x :+ s) -> Cut (\ds -> (ds, toTryData . fromList $ values ds x)) :+ s -oneValue :: Function -oneValue = Function (Arity 1 1) $ +firstValue' :: Function +firstValue' = Function (Arity 1 1) $ \_ (x :+ s) -> Cut (\ds -> maybe (ds, Fail) (second toTryData) $ firstValue ds x) :+ s +lastValue' :: Function +lastValue' = Function (Arity 1 1) $ + \_ (x :+ s) -> + Cut (\ds -> maybe (ds, Fail) (second toTryData) $ lastValue ds x) :+ s + countValues' :: Function countValues' = Function (Arity 1 1) $ \_ (x :+ s) -> Cut (\ds -> (ds, toTryData $ countValues ds x)) :+ s @@ -31,7 +36,7 @@ uniqueValue = Function (Arity 1 1) $ Cut (\ds -> (ds, anyOf' i (nub $ values ds x) >>= toTryData)) :+ s if' :: Function -if' = choice .* oneValue +if' = choice .* firstValue' andThen :: Function andThen = Function (Arity 2 1) $ \_ (x :+ y :+ s) -> (normalForm x >> y) :+ s diff --git a/src/Nekomata/Eval.hs b/src/Nekomata/Eval.hs index 8593c6e..7e25bc3 100644 --- a/src/Nekomata/Eval.hs +++ b/src/Nekomata/Eval.hs @@ -5,7 +5,6 @@ module Nekomata.Eval ( NekomataData, NekomataError, Mode (..), - Result (..), Runtime, allResults, checkResult, @@ -15,6 +14,7 @@ module Nekomata.Eval ( firstResult, getArity, initRuntime, + lastResult, readInput, runFunction, showResult, @@ -86,6 +86,8 @@ data Mode AllValues | -- | Show the first result FirstValue + | -- | Show the last result + LastValue | -- | Count the number of results CountValues | -- | Check if there are any results @@ -104,6 +106,11 @@ allResults = map showData . values initDecisions . fromNekomataData firstResult :: NekomataData -> Maybe String firstResult = fmap showData . values initDecisions . fromNekomataData +-- | Get the last result of a Nekomata evaluation as a string +lastResult :: NekomataData -> Maybe String +lastResult = + fmap showData . getLastAlt . values initDecisions . fromNekomataData + -- | Count the number of results of a Nekomata evaluation countResults :: NekomataData -> Integer countResults = countValues initDecisions . fromNekomataData @@ -117,6 +124,7 @@ toResult :: Mode -> Maybe Int -> NekomataData -> Result toResult AllValues Nothing = All False . allResults toResult AllValues (Just n) = truncate' n . allResults toResult FirstValue _ = First . firstResult +toResult LastValue _ = Last . lastResult toResult CountValues _ = Count . countResults toResult CheckExistence _ = Check . checkResult diff --git a/src/Nekomata/NonDet.hs b/src/Nekomata/NonDet.hs index e09924a..31aed77 100644 --- a/src/Nekomata/NonDet.hs +++ b/src/Nekomata/NonDet.hs @@ -1,3 +1,4 @@ +{-# LANGUAGE GeneralizedNewtypeDeriving #-} {-# LANGUAGE TypeFamilies #-} module Nekomata.NonDet where @@ -47,7 +48,8 @@ instance Monad Try where Cut g >>= f = Cut (second (>>= f) . g) -- | A wrapper for deterministic tryValues -newtype Det a = Det {unDet :: a} deriving (Eq, Ord, Show) +newtype Det a = Det {unDet :: a} + deriving (Eq, Ord, Show) instance Functor Det where fmap f (Det x) = Det (f x) @@ -109,6 +111,17 @@ tryValues ds (Choice i t1 t2) = case getChoice i ds of tryValues _ Fail = empty tryValues ds (Cut g) = let (ds', t) = g ds in tryValues ds' t +{- | A @Maybe@ wrapper whose @Alternative@ instance chooses the rightmost @Just@ +value +-} +newtype LastAlt a = LastAlt {getLastAlt :: Maybe a} + deriving (Eq, Ord, Show, Functor, Applicative, Monad) + +instance Alternative LastAlt where + empty = LastAlt Nothing + LastAlt l <|> LastAlt Nothing = LastAlt l + LastAlt _ <|> r = r + -- | Find all values of a @NonDet@ values :: (NonDet a, Alternative m) => Decisions -> a -> m (Value a) values ds = fmap snd . tryValues ds . toTry @@ -117,6 +130,10 @@ values ds = fmap snd . tryValues ds . toTry firstValue :: (NonDet a) => Decisions -> a -> Maybe (Decisions, Value a) firstValue ds = tryValues ds . toTry +-- | Find the last value and the corresponding decisions of a @NonDet@ +lastValue :: (NonDet a) => Decisions -> a -> Maybe (Decisions, Value a) +lastValue ds = getLastAlt . tryValues ds . toTry + -- | Count all values of a @Try@ countTryValues :: Decisions -> Try a -> Integer countTryValues _ (Val _) = 1 diff --git a/src/Nekomata/Result.hs b/src/Nekomata/Result.hs index 948d7f5..af64fc3 100644 --- a/src/Nekomata/Result.hs +++ b/src/Nekomata/Result.hs @@ -2,6 +2,7 @@ module Nekomata.Result ( Result (..), all_, first_, + last_, nothing_, showResult, truncate', @@ -16,6 +17,8 @@ data Result All Bool [String] | -- | The first result First (Maybe String) + | -- | The last result + Last (Maybe String) | -- | The number of results Count Integer | -- | Whether there are any results @@ -25,6 +28,7 @@ data Result instance Show Result where show (All truncated xs) = unwords xs ++ if truncated then " ..." else "" show (First x) = fromMaybe "" x + show (Last x) = fromMaybe "" x show (Count n) = show n show (Check b) = show b @@ -32,6 +36,7 @@ instance Show Result where showResult :: Result -> [String] showResult (All truncated xs) = xs ++ ["..." | truncated] showResult (First x) = [fromMaybe "" x] +showResult (Last x) = [fromMaybe "" x] showResult (Count n) = [show n] showResult (Check b) = [show b] @@ -54,4 +59,8 @@ first_ = First . Just -- | No results nothing_ :: Result -nothing_ = First Nothing \ No newline at end of file +nothing_ = First Nothing + +-- | The last result +last_ :: String -> Result +last_ = Last . Just diff --git a/test/Eval.hs b/test/Eval.hs index 9b2e992..50c1ae8 100644 --- a/test/Eval.hs +++ b/test/Eval.hs @@ -6,12 +6,14 @@ import Control.Monad import Data.Either (isRight) import Nekomata.Builtin import Nekomata.Eval +import Nekomata.Result (Result (..), all_, first_, last_, nothing_, truncate_) import Test.Hspec shouldMatch :: NekomataData -> Result -> Expectation shouldMatch x (All False xs) = allResults x `shouldBe` xs shouldMatch x (All True xs) = take (length xs) (allResults x) `shouldBe` xs shouldMatch x (First y) = firstResult x `shouldBe` y +shouldMatch x (Last y) = lastResult x `shouldBe` y shouldMatch x (Count n) = countResults x `shouldBe` n shouldMatch x (Check b) = checkResult x `shouldBe` b @@ -43,18 +45,6 @@ specEval code testCases = context code $ do let result = snd $ runFunction f runtime result `shouldMatch` output -all_ :: [String] -> Result -all_ = All False - -truncate_ :: [String] -> Result -truncate_ = All True - -first_ :: String -> Result -first_ = First . Just - -nothing_ :: Result -nothing_ = First Nothing - testEval :: Spec testEval = describe "Solutions to Code Golf Stack Exchange challenges" $ do describe "q69: Golf you a quine for great good!" $ do @@ -272,10 +262,10 @@ testEval = describe "Solutions to Code Golf Stack Exchange challenges" $ do ] describe "q62752: Longest Common Prefix of 2 Strings" $ do specEval - "ᵃp=al" - [ ("\"global\" \"glossary\"", all_ ["glo"]) - , ("\"department\" \"depart\"", all_ ["depart"]) - , ("\"glove\" \"dove\"", all_ ["[]"]) + "ᵃp=" + [ ("\"global\" \"glossary\"", last_ "glo") + , ("\"department\" \"depart\"", last_ "depart") + , ("\"glove\" \"dove\"", last_ "[]") ] describe "q63999: Parenthifiable Binary Numbers" $ do specEval @@ -572,13 +562,13 @@ testEval = describe "Solutions to Code Golf Stack Exchange challenges" $ do , ("[42,14,42,43,41,4080622,171480372]", all_ ["[42,14,42]"]) ] specEval - "pᵖ½al" - [ ("[14,42,2324,97090,4080622,171480372]", all_ ["[14,42,2324,97090,4080622,171480372]"]) - , ("[42,14,42,2324]", all_ ["[42,14,42,2324]"]) - , ("[7,14,42]", all_ ["[]"]) - , ("[]", all_ ["[]"]) - , ("[171480372,13,14,42]", all_ ["[171480372]"]) - , ("[42,14,42,43,41,4080622,171480372]", all_ ["[42,14,42]"]) + "pᵖ½" + [ ("[14,42,2324,97090,4080622,171480372]", last_ "[14,42,2324,97090,4080622,171480372]") + , ("[42,14,42,2324]", last_ "[42,14,42,2324]") + , ("[7,14,42]", last_ "[]") + , ("[]", last_ "[]") + , ("[171480372,13,14,42]", last_ "[171480372]") + , ("[42,14,42,43,41,4080622,171480372]", last_ "[42,14,42]") ] describe "q84673: Is it a sum-free set?" $ do specEval @@ -2452,6 +2442,13 @@ testEval = describe "Solutions to Code Golf Stack Exchange challenges" $ do , ("[1,1,2]", all_ ["1", "2"]) , ("[1,2,7,2,7,2,3,7]", all_ ["1", "2", "3", "7"]) ] + specEval + "ᴶ{ᵉhl=" + [ ("[1,2,3,4,2,5]", last_ "[1,2,5]") + , ("[4,3,6,2,3,8,5,2,8,7]", last_ "[4,3,8,7]") + , ("[1,1,2]", last_ "[1,2]") + , ("[1,2,7,2,7,2,3,7]", last_ "[1,2,3,7]") + ] describe "q256978: Painting with Line Filler" $ do specEval "ʷ{ᶜŤĕ≡¿}Ø="