miniroon

Simplistic macaroon-based authorization for Unix systems
git clone https://ccx.te2000.cz/git/miniroon
Log | Files | Refs | README

commit bbebe1e47af179befc777518d2ec2f8f9111d47c
parent 46a80cd995ef03ac1a20f00c5531dc1d4785ca83
Author: Jan Pobrislo <ccx@te2000.cz>
Date:   Tue, 29 Apr 2025 01:43:41 +0000

Rename some predicates, add netstring_maplist_dcg

Diffstat:
Mdoc/miniroon_spec.html | 341+++++++++++++++++++++++++++++++++++++++----------------------------------------
Mdoc/miniroon_spec.pl | 55+++++++++++++++++++++++++++----------------------------
Mdoc/tests.pl | 28++++++++++++++--------------
3 files changed, 211 insertions(+), 213 deletions(-)

diff --git a/doc/miniroon_spec.html b/doc/miniroon_spec.html @@ -5,183 +5,182 @@ table.code_doc tr { vertical-align: top; } table.code_doc td:hover { background-color: #3331; } </style></head><body><table class="code_doc"> -<tr><td>1</td><td><code>:- module(miniroon_spec,</code></td><td rowspan="14"></td></tr> +<tr><td>1</td><td><code>:- module(miniroon_spec,</code></td><td rowspan="15"></td></tr> <tr><td>2</td><td><code> [ dcgappend/3</code></td></tr> <tr><td>3</td><td><code> , netstring_prefix_for_payload/3</code></td></tr> -<tr><td>4</td><td><code> , netstring_encoding/2</code></td></tr> -<tr><td>5</td><td><code> , netstring_encoding/3</code></td></tr> +<tr><td>4</td><td><code> , netstring_encoding/2 % TODO: remove if unused</code></td></tr> +<tr><td>5</td><td><code> , netstring_bytes/3</code></td></tr> <tr><td>6</td><td><code> , netstring_of/2 % TODO: remove if unused</code></td></tr> -<tr><td>7</td><td><code> , phrase_length/4</code></td></tr> -<tr><td>8</td><td><code> , nsdcg/3</code></td></tr> -<tr><td>9</td><td><code> , miniroon_encoding/2</code></td></tr> -<tr><td>10</td><td><code> , miniroon_header/3</code></td></tr> -<tr><td>11</td><td><code> , miniroon_v0_caveat_list/3</code></td></tr> -<tr><td>12</td><td><code> ]).</code></td></tr> -<tr><td>13</td><td><code>:- use_module(library(error)).</code></td></tr> -<tr><td>14</td><td><code>:- use_module(library(dcg/basics)).</code></td></tr> -<tr><td>15</td><td><code></code></td><td rowspan="4"></td></tr> -<tr><td>16</td><td><code>% pack_install(delay). =&gt; https://storage.googleapis.com/packs.ndrix.com/delay/delay-0.3.3.zip</code></td></tr> -<tr><td>17</td><td><code>% Also at: https://github.com/mndrix/delay</code></td></tr> -<tr><td>18</td><td><code>:- use_module(library(delay), [delay/1]).</code></td></tr> -<tr><td>19</td><td><code></code></td><td rowspan="2"></td></tr> -<tr><td>20</td><td><code>:- multifile delay:mode/1.</code></td></tr> -<tr><td>21</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>22</td><td><code>delay:mode(lists:append(ground,_)).</code></td></tr> -<tr><td>23</td><td><code>delay:mode(lists:append(_,ground)).</code></td></tr> -<tr><td>24</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>25</td><td><code>delay:mode(system:string_bytes(ground,_,ground)).</code></td></tr> -<tr><td>26</td><td><code>delay:mode(system:string_bytes(_,ground,ground)).</code></td></tr> -<tr><td>27</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>28</td><td><code>%delay:mode(system:number_codes(ground,_)).</code></td></tr> -<tr><td>29</td><td><code>%delay:mode(system:number_codes(_,ground)).</code></td></tr> -<tr><td>30</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>31</td><td><code>%delay:mode(system:length(_,ground)).</code></td></tr> -<tr><td>32</td><td><code>%delay:mode(system:length(list,_)).</code></td></tr> -<tr><td>33</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>34</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(list, _, _)).</code></td></tr> -<tr><td>35</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(_, list, _)).</code></td></tr> -<tr><td>36</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>37</td><td><code>delay:mode(miniroon_spec:netstring_encoding(ground, _)).</code></td></tr> -<tr><td>38</td><td><code>delay:mode(miniroon_spec:netstring_encoding(_, list)).</code></td></tr> -<tr><td>39</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>40</td><td><code>delay:mode(miniroon_spec:miniroon_v0_action(ground, _)).</code></td></tr> -<tr><td>41</td><td><code>delay:mode(miniroon_spec:miniroon_v0_action(_, list)).</code></td></tr> -<tr><td>42</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>43</td><td><code>delay:mode(miniroon_spec:phrase_length(nonvar, ground, _, _)).</code></td></tr> -<tr><td>44</td><td><code>delay:mode(miniroon_spec:phrase_length(ground, _, _, _)).</code></td></tr> -<tr><td>45</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>46</td><td><code>:- op(950, xfx, will_be).</code></td></tr> -<tr><td>47</td><td><code>:- op(800, fx, ~).</code></td></tr> -<tr><td>48</td><td><code></code></td><td rowspan="2"></td></tr> -<tr><td>49</td><td><code>%%% Basic definitions</code></td></tr> -<tr><td>50</td><td><code></code></td><td rowspan="7"></td></tr> -<tr><td>51</td><td><code>% swapped arguments append for use in DCGs</code></td></tr> -<tr><td>52</td><td><code>% when called with instanced list as first argument it unifies it's content with DCG.</code></td></tr> -<tr><td>53</td><td><code>dcgappend([], L, L) :- !.</code></td></tr> -<tr><td>54</td><td><code>dcgappend([H|T], [H|R], L) :-</code></td></tr> -<tr><td>55</td><td><code> dcgappend(T, R, L).</code></td></tr> -<tr><td>56</td><td><code>%dcgappend(List, L0, L1) :- append(List, L1, L0).</code></td></tr> -<tr><td>57</td><td><code></code></td><td rowspan="2"></td></tr> -<tr><td>58</td><td><code>~(Goal) :- delay(Goal).</code></td></tr> -<tr><td>59</td><td><code></code></td><td rowspan="4"></td></tr> -<tr><td>60</td><td><code>% apply check to value when bound</code></td></tr> -<tr><td>61</td><td><code>will_be(Value, Goal) :-</code></td></tr> -<tr><td>62</td><td><code> freeze(Value, assertion(call(Goal, Value))).</code></td></tr> -<tr><td>63</td><td><code></code></td><td rowspan="2"></td></tr> -<tr><td>64</td><td><code>byte(Value) :- must_be(between(0, 255), Value).</code></td></tr> -<tr><td>65</td><td><code></code></td><td rowspan="2"></td></tr> -<tr><td>66</td><td><code>nonnegative_integer(Value) :- must_be(nonneg, Value).</code></td></tr> -<tr><td>67</td><td><code></code></td><td rowspan="5"></td></tr> -<tr><td>68</td><td><code>sequence_of_bytes([]).</code></td></tr> -<tr><td>69</td><td><code>sequence_of_bytes([First|Rest]) :-</code></td></tr> -<tr><td>70</td><td><code> First will_be byte,</code></td></tr> -<tr><td>71</td><td><code> Rest will_be sequence_of_bytes.</code></td></tr> -<tr><td>72</td><td><code></code></td><td rowspan="1"></td></tr> -<tr><td>73</td><td><code>netstring_encoding(PayloadBytes) --&gt; </code></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr> -<tr><td>74</td><td><code> netstring_prefix_for_payload(PayloadBytes), </code></td><td rowspan="1">It consists of prefix,</td></tr> -<tr><td>75</td><td><code> PayloadBytes, </code></td><td rowspan="1">payload,</td></tr> -<tr><td>76</td><td><code> `,`. </code></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr> -<tr><td>77</td><td><code>netstring_encoding(netstring(PayloadBytes), Bytes) :-</code></td></tr> -<tr><td>78</td><td><code> assertion(ground(PayloadBytes); ground(Bytes)),</code></td></tr> -<tr><td>79</td><td><code> Bytes will_be sequence_of_bytes,</code></td></tr> -<tr><td>80</td><td><code> phrase(netstring_encoding(PayloadBytes), Bytes).</code></td></tr> -<tr><td>81</td><td><code></code></td><td rowspan="4"></td></tr> -<tr><td>82</td><td><code>netstring_string(PayloadString) --&gt;</code></td></tr> -<tr><td>83</td><td><code> { ~ string_bytes(PayloadString, PayloadBytes, octet) },</code></td></tr> -<tr><td>84</td><td><code> netstring_encoding(PayloadBytes).</code></td></tr> -<tr><td>85</td><td><code></code></td><td rowspan="1"></td></tr> -<tr><td>86</td><td><code></code></td><td rowspan="8">Netstring prefix is the shortest ASCII decimal representation for length of +<tr><td>7</td><td><code> , netstring_maplist_dcg/4</code></td></tr> +<tr><td>8</td><td><code> , phrase_length/4</code></td></tr> +<tr><td>9</td><td><code> , netstring_call_dcg/3</code></td></tr> +<tr><td>10</td><td><code> , miniroon_encoding/2</code></td></tr> +<tr><td>11</td><td><code> , miniroon_header/3</code></td></tr> +<tr><td>12</td><td><code> , miniroon_v0_caveat_list/3</code></td></tr> +<tr><td>13</td><td><code> ]).</code></td></tr> +<tr><td>14</td><td><code>:- use_module(library(error)).</code></td></tr> +<tr><td>15</td><td><code>:- use_module(library(dcg/basics)).</code></td></tr> +<tr><td>16</td><td><code></code></td><td rowspan="4"></td></tr> +<tr><td>17</td><td><code>% pack_install(delay). =&gt; https://storage.googleapis.com/packs.ndrix.com/delay/delay-0.3.3.zip</code></td></tr> +<tr><td>18</td><td><code>% Also at: https://github.com/mndrix/delay</code></td></tr> +<tr><td>19</td><td><code>:- use_module(library(delay), [delay/1]).</code></td></tr> +<tr><td>20</td><td><code></code></td><td rowspan="2"></td></tr> +<tr><td>21</td><td><code>:- multifile delay:mode/1.</code></td></tr> +<tr><td>22</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>23</td><td><code>delay:mode(lists:append(ground,_)).</code></td></tr> +<tr><td>24</td><td><code>delay:mode(lists:append(_,ground)).</code></td></tr> +<tr><td>25</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>26</td><td><code>delay:mode(system:string_bytes(ground,_,ground)).</code></td></tr> +<tr><td>27</td><td><code>delay:mode(system:string_bytes(_,ground,ground)).</code></td></tr> +<tr><td>28</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>29</td><td><code>%delay:mode(system:number_codes(ground,_)).</code></td></tr> +<tr><td>30</td><td><code>%delay:mode(system:number_codes(_,ground)).</code></td></tr> +<tr><td>31</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>32</td><td><code>%delay:mode(system:length(_,ground)).</code></td></tr> +<tr><td>33</td><td><code>%delay:mode(system:length(list,_)).</code></td></tr> +<tr><td>34</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>35</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(list, _, _)).</code></td></tr> +<tr><td>36</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(_, list, _)).</code></td></tr> +<tr><td>37</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>38</td><td><code>delay:mode(miniroon_spec:netstring_encoding(ground, _)).</code></td></tr> +<tr><td>39</td><td><code>delay:mode(miniroon_spec:netstring_encoding(_, list)).</code></td></tr> +<tr><td>40</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>41</td><td><code>delay:mode(miniroon_spec:miniroon_v0_action(ground, _)).</code></td></tr> +<tr><td>42</td><td><code>delay:mode(miniroon_spec:miniroon_v0_action(_, list)).</code></td></tr> +<tr><td>43</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>44</td><td><code>delay:mode(miniroon_spec:phrase_length(nonvar, ground, _, _)).</code></td></tr> +<tr><td>45</td><td><code>delay:mode(miniroon_spec:phrase_length(ground, _, _, _)).</code></td></tr> +<tr><td>46</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>47</td><td><code>:- op(950, xfx, will_be).</code></td></tr> +<tr><td>48</td><td><code>:- op(800, fx, ~).</code></td></tr> +<tr><td>49</td><td><code></code></td><td rowspan="2"></td></tr> +<tr><td>50</td><td><code>%%% Basic definitions</code></td></tr> +<tr><td>51</td><td><code></code></td><td rowspan="7"></td></tr> +<tr><td>52</td><td><code>% swapped arguments append for use in DCGs</code></td></tr> +<tr><td>53</td><td><code>% when called with instanced list as first argument it unifies it's content with DCG.</code></td></tr> +<tr><td>54</td><td><code>dcgappend([], L, L) :- !.</code></td></tr> +<tr><td>55</td><td><code>dcgappend([H|T], [H|R], L) :-</code></td></tr> +<tr><td>56</td><td><code> dcgappend(T, R, L).</code></td></tr> +<tr><td>57</td><td><code>%dcgappend(List, L0, L1) :- append(List, L1, L0).</code></td></tr> +<tr><td>58</td><td><code></code></td><td rowspan="2"></td></tr> +<tr><td>59</td><td><code>~(Goal) :- delay(Goal).</code></td></tr> +<tr><td>60</td><td><code></code></td><td rowspan="4"></td></tr> +<tr><td>61</td><td><code>% apply check to value when bound</code></td></tr> +<tr><td>62</td><td><code>will_be(Value, Goal) :-</code></td></tr> +<tr><td>63</td><td><code> freeze(Value, assertion(call(Goal, Value))).</code></td></tr> +<tr><td>64</td><td><code></code></td><td rowspan="2"></td></tr> +<tr><td>65</td><td><code>byte(Value) :- must_be(between(0, 255), Value).</code></td></tr> +<tr><td>66</td><td><code></code></td><td rowspan="2"></td></tr> +<tr><td>67</td><td><code>nonnegative_integer(Value) :- must_be(nonneg, Value).</code></td></tr> +<tr><td>68</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>69</td><td><code>sequence_of_bytes([]).</code></td></tr> +<tr><td>70</td><td><code>sequence_of_bytes([First|Rest]) :-</code></td></tr> +<tr><td>71</td><td><code> First will_be byte,</code></td></tr> +<tr><td>72</td><td><code> Rest will_be sequence_of_bytes.</code></td></tr> +<tr><td>73</td><td><code></code></td><td rowspan="1"></td></tr> +<tr><td>74</td><td><code>netstring_bytes(PayloadBytes) --&gt; </code></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr> +<tr><td>75</td><td><code> netstring_prefix_for_payload(PayloadBytes), </code></td><td rowspan="1">It consists of prefix,</td></tr> +<tr><td>76</td><td><code> PayloadBytes, </code></td><td rowspan="1">payload,</td></tr> +<tr><td>77</td><td><code> `,`. </code></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr> +<tr><td>78</td><td><code>netstring_encoding(netstring(PayloadBytes), Bytes) :-</code></td></tr> +<tr><td>79</td><td><code> assertion(ground(PayloadBytes); ground(Bytes)),</code></td></tr> +<tr><td>80</td><td><code> Bytes will_be sequence_of_bytes,</code></td></tr> +<tr><td>81</td><td><code> phrase(netstring_bytes(PayloadBytes), Bytes).</code></td></tr> +<tr><td>82</td><td><code></code></td><td rowspan="4"></td></tr> +<tr><td>83</td><td><code>netstring_string(PayloadString) --&gt;</code></td></tr> +<tr><td>84</td><td><code> { ~ string_bytes(PayloadString, PayloadBytes, octet) },</code></td></tr> +<tr><td>85</td><td><code> netstring_bytes(PayloadBytes).</code></td></tr> +<tr><td>86</td><td><code></code></td><td rowspan="1"></td></tr> +<tr><td>87</td><td><code></code></td><td rowspan="8">Netstring prefix is the shortest ASCII decimal representation for length of payload in bytes, followed by ASCII colon `:`. That is number starting with non-zero digit unless payload is empty, in which case it's `0`.</td></tr> -<tr><td>87</td><td><code></code></td></tr> <tr><td>88</td><td><code></code></td></tr> <tr><td>89</td><td><code></code></td></tr> -<tr><td>90</td><td><code>netstring_prefix_for_payload(PayloadBytes, A, B) :-</code></td></tr> -<tr><td>91</td><td><code> ~ netstring_prefix_digits(Prefix, A, B),</code></td></tr> -<tr><td>92</td><td><code> ~ number_codes(PayloadLength, Prefix),</code></td></tr> -<tr><td>93</td><td><code> length(PayloadBytes, PayloadLength).</code></td></tr> -<tr><td>94</td><td><code></code></td><td rowspan="7"></td></tr> -<tr><td>95</td><td><code>netstring_prefix_digits(`0`) --&gt; `0:`, !.</code></td></tr> -<tr><td>96</td><td><code>netstring_prefix_digits([C|Cs]) --&gt;</code></td></tr> -<tr><td>97</td><td><code> nonzero_digit(C),</code></td></tr> -<tr><td>98</td><td><code> !,</code></td></tr> -<tr><td>99</td><td><code> digits(Cs),</code></td></tr> -<tr><td>100</td><td><code> `:`.</code></td></tr> -<tr><td>101</td><td><code></code></td><td rowspan="5"></td></tr> -<tr><td>102</td><td><code>nonzero_digit(Code) --&gt;</code></td></tr> -<tr><td>103</td><td><code> [Code],</code></td></tr> -<tr><td>104</td><td><code> { assertion(ground(Code)) },</code></td></tr> -<tr><td>105</td><td><code> { member(Code, `123456789`) }.</code></td></tr> -<tr><td>106</td><td><code></code></td><td rowspan="6"></td></tr> -<tr><td>107</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr> -<tr><td>108</td><td><code> ground(Goal),</code></td></tr> -<tr><td>109</td><td><code> !,</code></td></tr> -<tr><td>110</td><td><code> call(Goal, PayloadBytes),</code></td></tr> -<tr><td>111</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes).</code></td></tr> -<tr><td>112</td><td><code></code></td><td rowspan="6"></td></tr> -<tr><td>113</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr> -<tr><td>114</td><td><code> ground(Bytes),</code></td></tr> -<tr><td>115</td><td><code> !,</code></td></tr> -<tr><td>116</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes),</code></td></tr> -<tr><td>117</td><td><code> call(Goal, PayloadBytes).</code></td></tr> -<tr><td>118</td><td><code></code></td><td rowspan="9"></td></tr> -<tr><td>119</td><td><code>phrase_length_agg(List, Terminator, CurrentDepth, Length) :-</code></td></tr> -<tr><td>120</td><td><code> ( List == Terminator</code></td></tr> -<tr><td>121</td><td><code> -&gt; CurrentDepth = Length</code></td></tr> -<tr><td>122</td><td><code> ; NextDepth is CurrentDepth + 1,</code></td></tr> -<tr><td>123</td><td><code> assertion(compound(List)),</code></td></tr> -<tr><td>124</td><td><code> List = [_|NextList],</code></td></tr> -<tr><td>125</td><td><code> phrase_length_agg(NextList, Terminator, NextDepth, Length)</code></td></tr> -<tr><td>126</td><td><code> ).</code></td></tr> -<tr><td>127</td><td><code></code></td><td rowspan="5"></td></tr> -<tr><td>128</td><td><code>phrase_length(DCGBody, List, Rest, Length) :-</code></td></tr> -<tr><td>129</td><td><code> phrase(DCGBody, List, Terminator),</code></td></tr> -<tr><td>130</td><td><code> phrase_length_agg(List, Terminator, 0, Length),</code></td></tr> -<tr><td>131</td><td><code> Terminator = Rest.</code></td></tr> -<tr><td>132</td><td><code></code></td><td rowspan="5"></td></tr> -<tr><td>133</td><td><code>nsdcg(DCGBody, L0, Rest) :-</code></td></tr> -<tr><td>134</td><td><code> ~ phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),</code></td></tr> -<tr><td>135</td><td><code> ~ number_codes(PayloadLength, PrefixDigits),</code></td></tr> -<tr><td>136</td><td><code> netstring_prefix_digits(PrefixDigits, L0, Payload).</code></td></tr> -<tr><td>137</td><td><code></code></td><td rowspan="6"></td></tr> -<tr><td>138</td><td><code>% Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature.</code></td></tr> -<tr><td>139</td><td><code>miniroon_content(miniroon_v0(Identifier, Action, Caveats, Signature)) --&gt;</code></td></tr> -<tr><td>140</td><td><code> nsdcg(miniroon_header(header_v0(Identifier, Action))),</code></td></tr> -<tr><td>141</td><td><code> nsdcg(miniroon_v0_caveat_list(Caveats)),</code></td></tr> -<tr><td>142</td><td><code> netstring_encoding(Signature).</code></td></tr> -<tr><td>143</td><td><code></code></td><td rowspan="3"></td></tr> -<tr><td>144</td><td><code>miniroon_encoding(Miniroon, Bytes) :-</code></td></tr> -<tr><td>145</td><td><code> phrase(nsdcg(miniroon_content(Miniroon)), Bytes).</code></td></tr> -<tr><td>146</td><td><code></code></td><td rowspan="4"></td></tr> -<tr><td>147</td><td><code>miniroon_v0_action(revoke, `revoke`).</code></td></tr> -<tr><td>148</td><td><code>miniroon_v0_action(invoke, `invoke`).</code></td></tr> -<tr><td>149</td><td><code>miniroon_v0_action(invoke_once, `invoke-once`).</code></td></tr> -<tr><td>150</td><td><code></code></td><td rowspan="6"></td></tr> -<tr><td>151</td><td><code>miniroon_header(header_v0(Identifier, Action)) --&gt;</code></td></tr> -<tr><td>152</td><td><code> netstring_encoding(`capv0`),</code></td></tr> -<tr><td>153</td><td><code> netstring_encoding(Identifier),</code></td></tr> -<tr><td>154</td><td><code> { ~ miniroon_v0_action(Action, ActionBytes) },</code></td></tr> -<tr><td>155</td><td><code> netstring_encoding(ActionBytes).</code></td></tr> -<tr><td>156</td><td><code></code></td><td rowspan="16"></td></tr> -<tr><td>157</td><td><code>miniroon_v0_caveat(caveat_v0_env_is(VarName, Value)) --&gt;</code></td></tr> -<tr><td>158</td><td><code> netstring_encoding(`env-is`),</code></td></tr> -<tr><td>159</td><td><code> netstring_string(VarName),</code></td></tr> -<tr><td>160</td><td><code> netstring_string(Value).</code></td></tr> -<tr><td>161</td><td><code>miniroon_v0_caveat(caveat_v0_env_absent(VarName)) --&gt;</code></td></tr> -<tr><td>162</td><td><code> netstring_encoding(`env-absent`),</code></td></tr> -<tr><td>163</td><td><code> netstring_string(VarName).</code></td></tr> -<tr><td>164</td><td><code>miniroon_v0_caveat(caveat_v0_env_regmatch(VarName, Pattern)) --&gt;</code></td></tr> -<tr><td>165</td><td><code> netstring_encoding(`env-re`),</code></td></tr> -<tr><td>166</td><td><code> netstring_string(VarName),</code></td></tr> -<tr><td>167</td><td><code> netstring_string(Pattern).</code></td></tr> -<tr><td>168</td><td><code>miniroon_v0_caveat(caveat_v0_env_glob(VarName, Pattern)) --&gt;</code></td></tr> -<tr><td>169</td><td><code> netstring_encoding(`env-glob`),</code></td></tr> -<tr><td>170</td><td><code> netstring_string(VarName),</code></td></tr> -<tr><td>171</td><td><code> netstring_string(Pattern).</code></td></tr> -<tr><td>172</td><td><code></code></td><td rowspan="5"></td></tr> -<tr><td>173</td><td><code>miniroon_v0_caveat_list([]) --&gt; {true}.</code></td></tr> -<tr><td>174</td><td><code>miniroon_v0_caveat_list([Caveat|Rest]) --&gt;</code></td></tr> -<tr><td>175</td><td><code> nsdcg(miniroon_v0_caveat(Caveat)),</code></td></tr> -<tr><td>176</td><td><code> miniroon_v0_caveat_list(Rest).</code></td></tr> +<tr><td>90</td><td><code></code></td></tr> +<tr><td>91</td><td><code>netstring_prefix_for_payload(PayloadBytes, A, B) :-</code></td></tr> +<tr><td>92</td><td><code> ~ netstring_prefix_digits(Prefix, A, B),</code></td></tr> +<tr><td>93</td><td><code> ~ number_codes(PayloadLength, Prefix),</code></td></tr> +<tr><td>94</td><td><code> length(PayloadBytes, PayloadLength).</code></td></tr> +<tr><td>95</td><td><code></code></td><td rowspan="7"></td></tr> +<tr><td>96</td><td><code>netstring_prefix_digits(`0`) --&gt; `0:`, !.</code></td></tr> +<tr><td>97</td><td><code>netstring_prefix_digits([C|Cs]) --&gt;</code></td></tr> +<tr><td>98</td><td><code> nonzero_digit(C),</code></td></tr> +<tr><td>99</td><td><code> !,</code></td></tr> +<tr><td>100</td><td><code> digits(Cs),</code></td></tr> +<tr><td>101</td><td><code> `:`.</code></td></tr> +<tr><td>102</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>103</td><td><code>nonzero_digit(Code) --&gt;</code></td></tr> +<tr><td>104</td><td><code> [Code],</code></td></tr> +<tr><td>105</td><td><code> { assertion(ground(Code)) },</code></td></tr> +<tr><td>106</td><td><code> { member(Code, `123456789`) }.</code></td></tr> +<tr><td>107</td><td><code></code></td><td rowspan="4"></td></tr> +<tr><td>108</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr> +<tr><td>109</td><td><code> ~ netstring_encoding(netstring(PayloadBytes), Bytes),</code></td></tr> +<tr><td>110</td><td><code> call(Goal, PayloadBytes).</code></td></tr> +<tr><td>111</td><td><code></code></td><td rowspan="9"></td></tr> +<tr><td>112</td><td><code>phrase_length_agg(List, Terminator, CurrentDepth, Length) :-</code></td></tr> +<tr><td>113</td><td><code> ( List == Terminator</code></td></tr> +<tr><td>114</td><td><code> -&gt; CurrentDepth = Length</code></td></tr> +<tr><td>115</td><td><code> ; NextDepth is CurrentDepth + 1,</code></td></tr> +<tr><td>116</td><td><code> assertion(compound(List)),</code></td></tr> +<tr><td>117</td><td><code> List = [_|NextList],</code></td></tr> +<tr><td>118</td><td><code> phrase_length_agg(NextList, Terminator, NextDepth, Length)</code></td></tr> +<tr><td>119</td><td><code> ).</code></td></tr> +<tr><td>120</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>121</td><td><code>phrase_length(DCGBody, List, Rest, Length) :-</code></td></tr> +<tr><td>122</td><td><code> phrase(DCGBody, List, Terminator),</code></td></tr> +<tr><td>123</td><td><code> phrase_length_agg(List, Terminator, 0, Length),</code></td></tr> +<tr><td>124</td><td><code> Terminator = Rest.</code></td></tr> +<tr><td>125</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>126</td><td><code>netstring_call_dcg(DCGBody, L0, Rest) :-</code></td></tr> +<tr><td>127</td><td><code> ~ phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),</code></td></tr> +<tr><td>128</td><td><code> ~ number_codes(PayloadLength, PrefixDigits),</code></td></tr> +<tr><td>129</td><td><code> netstring_prefix_digits(PrefixDigits, L0, Payload).</code></td></tr> +<tr><td>130</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>131</td><td><code>netstring_maplist_dcg(_, []) --&gt; {true}.</code></td></tr> +<tr><td>132</td><td><code>netstring_maplist_dcg(DCGBody, [Item|Rest]) --&gt;</code></td></tr> +<tr><td>133</td><td><code> netstring_call_dcg(call(DCGBody, Item)),</code></td></tr> +<tr><td>134</td><td><code> netstring_maplist_dcg(DCGBody, Rest).</code></td></tr> +<tr><td>135</td><td><code></code></td><td rowspan="7"></td></tr> +<tr><td>136</td><td><code>% Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature.</code></td></tr> +<tr><td>137</td><td><code>miniroon_content(miniroon_v0(Identifier, Action, Caveats, Signature)) --&gt;</code></td></tr> +<tr><td>138</td><td><code> netstring_call_dcg(miniroon_header(header_v0(Identifier, Action))),</code></td></tr> +<tr><td>139</td><td><code> %netstring_call_dcg(miniroon_v0_caveat_list(Caveats)),</code></td></tr> +<tr><td>140</td><td><code> netstring_call_dcg(netstring_maplist_dcg(miniroon_v0_caveat, Caveats)),</code></td></tr> +<tr><td>141</td><td><code> netstring_bytes(Signature).</code></td></tr> +<tr><td>142</td><td><code></code></td><td rowspan="3"></td></tr> +<tr><td>143</td><td><code>miniroon_encoding(Miniroon, Bytes) :-</code></td></tr> +<tr><td>144</td><td><code> phrase(netstring_call_dcg(miniroon_content(Miniroon)), Bytes).</code></td></tr> +<tr><td>145</td><td><code></code></td><td rowspan="4"></td></tr> +<tr><td>146</td><td><code>miniroon_v0_action(revoke, `revoke`).</code></td></tr> +<tr><td>147</td><td><code>miniroon_v0_action(invoke, `invoke`).</code></td></tr> +<tr><td>148</td><td><code>miniroon_v0_action(invoke_once, `invoke-once`).</code></td></tr> +<tr><td>149</td><td><code></code></td><td rowspan="6"></td></tr> +<tr><td>150</td><td><code>miniroon_header(header_v0(Identifier, Action)) --&gt;</code></td></tr> +<tr><td>151</td><td><code> netstring_bytes(`capv0`),</code></td></tr> +<tr><td>152</td><td><code> netstring_bytes(Identifier),</code></td></tr> +<tr><td>153</td><td><code> { ~ miniroon_v0_action(Action, ActionBytes) },</code></td></tr> +<tr><td>154</td><td><code> netstring_bytes(ActionBytes).</code></td></tr> +<tr><td>155</td><td><code></code></td><td rowspan="16"></td></tr> +<tr><td>156</td><td><code>miniroon_v0_caveat(caveat_v0_env_is(VarName, Value)) --&gt;</code></td></tr> +<tr><td>157</td><td><code> netstring_bytes(`env-is`),</code></td></tr> +<tr><td>158</td><td><code> netstring_string(VarName),</code></td></tr> +<tr><td>159</td><td><code> netstring_string(Value).</code></td></tr> +<tr><td>160</td><td><code>miniroon_v0_caveat(caveat_v0_env_absent(VarName)) --&gt;</code></td></tr> +<tr><td>161</td><td><code> netstring_bytes(`env-absent`),</code></td></tr> +<tr><td>162</td><td><code> netstring_string(VarName).</code></td></tr> +<tr><td>163</td><td><code>miniroon_v0_caveat(caveat_v0_env_regmatch(VarName, Pattern)) --&gt;</code></td></tr> +<tr><td>164</td><td><code> netstring_bytes(`env-re`),</code></td></tr> +<tr><td>165</td><td><code> netstring_string(VarName),</code></td></tr> +<tr><td>166</td><td><code> netstring_string(Pattern).</code></td></tr> +<tr><td>167</td><td><code>miniroon_v0_caveat(caveat_v0_env_glob(VarName, Pattern)) --&gt;</code></td></tr> +<tr><td>168</td><td><code> netstring_bytes(`env-glob`),</code></td></tr> +<tr><td>169</td><td><code> netstring_string(VarName),</code></td></tr> +<tr><td>170</td><td><code> netstring_string(Pattern).</code></td></tr> +<tr><td>171</td><td><code></code></td><td rowspan="5"></td></tr> +<tr><td>172</td><td><code>miniroon_v0_caveat_list([]) --&gt; {true}.</code></td></tr> +<tr><td>173</td><td><code>miniroon_v0_caveat_list([Caveat|Rest]) --&gt;</code></td></tr> +<tr><td>174</td><td><code> netstring_call_dcg(miniroon_v0_caveat(Caveat)),</code></td></tr> +<tr><td>175</td><td><code> miniroon_v0_caveat_list(Rest).</code></td></tr> </table></body></html> diff --git a/doc/miniroon_spec.pl b/doc/miniroon_spec.pl @@ -1,11 +1,12 @@ :- module(miniroon_spec, [ dcgappend/3 , netstring_prefix_for_payload/3 - , netstring_encoding/2 - , netstring_encoding/3 + , netstring_encoding/2 % TODO: remove if unused + , netstring_bytes/3 , netstring_of/2 % TODO: remove if unused + , netstring_maplist_dcg/4 , phrase_length/4 - , nsdcg/3 + , netstring_call_dcg/3 , miniroon_encoding/2 , miniroon_header/3 , miniroon_v0_caveat_list/3 @@ -70,18 +71,18 @@ sequence_of_bytes([First|Rest]) :- First will_be byte, Rest will_be sequence_of_bytes. -netstring_encoding(PayloadBytes) --> %> Netstring is a sequence of bytes. +netstring_bytes(PayloadBytes) --> %> Netstring is a sequence of bytes. netstring_prefix_for_payload(PayloadBytes), %> It consists of prefix, PayloadBytes, %> payload, `,`. %> and terminator. Terminator is single ASCII comma `,`. netstring_encoding(netstring(PayloadBytes), Bytes) :- assertion(ground(PayloadBytes); ground(Bytes)), Bytes will_be sequence_of_bytes, - phrase(netstring_encoding(PayloadBytes), Bytes). + phrase(netstring_bytes(PayloadBytes), Bytes). netstring_string(PayloadString) --> { ~ string_bytes(PayloadString, PayloadBytes, octet) }, - netstring_encoding(PayloadBytes). + netstring_bytes(PayloadBytes). %> Netstring prefix is the shortest ASCII decimal representation for length of %^ payload in bytes, followed by ASCII colon `:`. @@ -105,15 +106,7 @@ nonzero_digit(Code) --> { member(Code, `123456789`) }. netstring_of(Goal, Bytes) :- - ground(Goal), - !, - call(Goal, PayloadBytes), - netstring_encoding(netstring(PayloadBytes), Bytes). - -netstring_of(Goal, Bytes) :- - ground(Bytes), - !, - netstring_encoding(netstring(PayloadBytes), Bytes), + ~ netstring_encoding(netstring(PayloadBytes), Bytes), call(Goal, PayloadBytes). phrase_length_agg(List, Terminator, CurrentDepth, Length) :- @@ -130,47 +123,53 @@ phrase_length(DCGBody, List, Rest, Length) :- phrase_length_agg(List, Terminator, 0, Length), Terminator = Rest. -nsdcg(DCGBody, L0, Rest) :- +netstring_call_dcg(DCGBody, L0, Rest) :- ~ phrase_length(DCGBody,Payload,[44|Rest],PayloadLength), ~ number_codes(PayloadLength, PrefixDigits), netstring_prefix_digits(PrefixDigits, L0, Payload). +netstring_maplist_dcg(_, []) --> {true}. +netstring_maplist_dcg(DCGBody, [Item|Rest]) --> + netstring_call_dcg(call(DCGBody, Item)), + netstring_maplist_dcg(DCGBody, Rest). + % Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature. miniroon_content(miniroon_v0(Identifier, Action, Caveats, Signature)) --> - nsdcg(miniroon_header(header_v0(Identifier, Action))), - nsdcg(miniroon_v0_caveat_list(Caveats)), - netstring_encoding(Signature). + netstring_call_dcg(miniroon_header(header_v0(Identifier, Action))), + %netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), + netstring_call_dcg(netstring_maplist_dcg(miniroon_v0_caveat, Caveats)), + netstring_bytes(Signature). miniroon_encoding(Miniroon, Bytes) :- - phrase(nsdcg(miniroon_content(Miniroon)), Bytes). + phrase(netstring_call_dcg(miniroon_content(Miniroon)), Bytes). miniroon_v0_action(revoke, `revoke`). miniroon_v0_action(invoke, `invoke`). miniroon_v0_action(invoke_once, `invoke-once`). miniroon_header(header_v0(Identifier, Action)) --> - netstring_encoding(`capv0`), - netstring_encoding(Identifier), + netstring_bytes(`capv0`), + netstring_bytes(Identifier), { ~ miniroon_v0_action(Action, ActionBytes) }, - netstring_encoding(ActionBytes). + netstring_bytes(ActionBytes). miniroon_v0_caveat(caveat_v0_env_is(VarName, Value)) --> - netstring_encoding(`env-is`), + netstring_bytes(`env-is`), netstring_string(VarName), netstring_string(Value). miniroon_v0_caveat(caveat_v0_env_absent(VarName)) --> - netstring_encoding(`env-absent`), + netstring_bytes(`env-absent`), netstring_string(VarName). miniroon_v0_caveat(caveat_v0_env_regmatch(VarName, Pattern)) --> - netstring_encoding(`env-re`), + netstring_bytes(`env-re`), netstring_string(VarName), netstring_string(Pattern). miniroon_v0_caveat(caveat_v0_env_glob(VarName, Pattern)) --> - netstring_encoding(`env-glob`), + netstring_bytes(`env-glob`), netstring_string(VarName), netstring_string(Pattern). miniroon_v0_caveat_list([]) --> {true}. miniroon_v0_caveat_list([Caveat|Rest]) --> - nsdcg(miniroon_v0_caveat(Caveat)), + netstring_call_dcg(miniroon_v0_caveat(Caveat)), miniroon_v0_caveat_list(Rest). diff --git a/doc/tests.pl b/doc/tests.pl @@ -104,26 +104,26 @@ test(dcgappend5) :- assertion(Next =@= `efgh`), assertion(Length1 =@= 4). -test(encode_nsdcg) :- +test(encode_netstring_call_dcg) :- InnerPayload = [0, 255], flatten([`5:2:\0`, 255, `,,`], ExpectedBytes), - phrase_length(nsdcg(nsdcg(dcgappend(InnerPayload))), Bytes, [], Length), + phrase_length(netstring_call_dcg(netstring_call_dcg(dcgappend(InnerPayload))), Bytes, [], Length), assertion(Bytes =@= ExpectedBytes), assertion(Length =@= 8). -test(decode_nsdcg) :- +test(decode_netstring_call_dcg) :- ExpectedPayload = [0, 255], flatten([`5:2:\0`, 255, `,,`], Bytes), InnerPayload = [_,_], - phrase_length(nsdcg(nsdcg(dcgappend(InnerPayload))), Bytes, [], Length), + phrase_length(netstring_call_dcg(netstring_call_dcg(dcgappend(InnerPayload))), Bytes, [], Length), assertion(InnerPayload =@= ExpectedPayload), assertion(Length =@= 8). -test(decode_nsdcg_single) :- +test(decode_netstring_call_dcg_single) :- ExpectedPayload = [0, 255], flatten([`2:\0`, 255, `,`], Bytes), InnerPayload = [_,_], - phrase_length(nsdcg(dcgappend(InnerPayload)), Bytes, [], Length), + phrase_length(netstring_call_dcg(dcgappend(InnerPayload)), Bytes, [], Length), assertion(InnerPayload =@= ExpectedPayload), assertion(Length =@= 5). @@ -136,36 +136,36 @@ test(header_v0_decode) :- Bytes = `28:5:capv0,8:example1,6:invoke,,`, test_result_matches( Header, - phrase(nsdcg(miniroon_header(Header)), Bytes), + phrase(netstring_call_dcg(miniroon_header(Header)), Bytes), ExpectedHeader ). test(header_v0_encode) :- Header = header_v0(`example1`, invoke), ExpectedBytes = `28:5:capv0,8:example1,6:invoke,,`, - test_encode_dcg(nsdcg(miniroon_header(Header)), ExpectedBytes). + test_encode_dcg(netstring_call_dcg(miniroon_header(Header)), ExpectedBytes). test(caveat_list_v0_decode_empty) :- ExpectedCaveats = [], Bytes = `0:,`, - findall(Caveats, phrase(nsdcg(miniroon_v0_caveat_list(Caveats)), Bytes), Results), + findall(Caveats, phrase(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), Bytes), Results), Results = [OutputCaveats], assertion(OutputCaveats =@= ExpectedCaveats). test(caveat_list_v0_encode_empty) :- Caveats = [], ExpectedBytes = `0:,`, - test_encode_dcg(nsdcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). + test_encode_dcg(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). test(caveat_list_v0_encode_one) :- Caveats = [caveat_v0_env_absent("var2")], ExpectedBytes = `25:21:10:env-absent,4:var2,,,`, - test_encode_dcg(nsdcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). + test_encode_dcg(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). test(caveat_list_v0_decode_one) :- ExpectedCaveats = [caveat_v0_env_absent("var2")], Bytes = `25:21:10:env-absent,4:var2,,,`, - findall(Caveats, phrase(nsdcg(miniroon_v0_caveat_list(Caveats)), Bytes), Results), + findall(Caveats, phrase(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), Bytes), Results), Results = [OutputCaveats], assertion(OutputCaveats =@= ExpectedCaveats). @@ -179,7 +179,7 @@ test(caveat_list_v0_decode) :- Bytes = `109:24:6:env-is,4:var1,5:hello,,21:10:env-absent,4:var2,,23:8:env-glob,4:var3,2:_*,,25:6:env-is,4:var3,6:_hello,,,`, test_result_matches( Caveats, - phrase(nsdcg(miniroon_v0_caveat_list(Caveats)), Bytes), + phrase(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), Bytes), ExpectedCaveats ). @@ -191,7 +191,7 @@ test(caveat_list_v0_encode) :- caveat_v0_env_is("var3", "_hello") ], ExpectedBytes = `109:24:6:env-is,4:var1,5:hello,,21:10:env-absent,4:var2,,23:8:env-glob,4:var3,2:_*,,25:6:env-is,4:var3,6:_hello,,,`, - test_encode_dcg(nsdcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). + test_encode_dcg(netstring_call_dcg(miniroon_v0_caveat_list(Caveats)), ExpectedBytes). test(decode_example1) :- ExpectedSignature = [