commit bfa7c9fc781ec4e269357a24a22fe71b01be7bf9
parent 3fd8e229286dd332dbe8ec1ef64673b19cd75311
Author: Jan Pobrislo <ccx@te2000.cz>
Date: Tue, 29 Apr 2025 00:40:42 +0000
Rename specification file, use it as module, WIP miniroon specification
Diffstat:
| M | doc/Makefile | | | 6 | +++--- |
| A | doc/miniroon_spec.html | | | 186 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| A | doc/miniroon_spec.pl | | | 175 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
| D | doc/spec.html | | | 118 | ------------------------------------------------------------------------------- |
| D | doc/spec.pl | | | 107 | ------------------------------------------------------------------------------- |
| M | doc/tests.pl | | | 126 | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- |
6 files changed, 489 insertions(+), 229 deletions(-)
diff --git a/doc/Makefile b/doc/Makefile
@@ -1,8 +1,8 @@
-all: spec.html test
+all: miniroon_spec.html test
.PHONY: all
-spec.html: spec.pl spec2html.awk
- awk -f spec2html.awk spec.pl >'$@.new'
+miniroon_spec.html: miniroon_spec.pl spec2html.awk
+ awk -f spec2html.awk miniroon_spec.pl >'$@.new'
mv '$@.new' '$@'
test:
diff --git a/doc/miniroon_spec.html b/doc/miniroon_spec.html
@@ -0,0 +1,186 @@
+<!DOCTYPE html>
+<html><head><style type="text/css">
+ table.code_doc { border-collapse: collapse; border: 0px; }
+ table.code_doc code { white-space: pre; }
+ 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>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>6</td><td><code> , netstring_of/2</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). => 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:number_codes(ground,_)).</code></td></tr>
+<tr><td>26</td><td><code>%delay:mode(system:number_codes(_,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:length(_,ground)).</code></td></tr>
+<tr><td>29</td><td><code>%delay:mode(system:length(list,_)).</code></td></tr>
+<tr><td>30</td><td><code></code></td><td rowspan="3"></td></tr>
+<tr><td>31</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(list, _, _)).</code></td></tr>
+<tr><td>32</td><td><code>delay:mode(miniroon_spec:netstring_prefix_digits(_, 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_encoding(ground, _)).</code></td></tr>
+<tr><td>35</td><td><code>delay:mode(miniroon_spec:netstring_encoding(_, 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:miniroon_v0_action(ground, _)).</code></td></tr>
+<tr><td>38</td><td><code>delay:mode(miniroon_spec:miniroon_v0_action(_, list)).</code></td></tr>
+<tr><td>39</td><td><code></code></td><td rowspan="3"></td></tr>
+<tr><td>40</td><td><code>:- op(950, xfx, will_be).</code></td></tr>
+<tr><td>41</td><td><code>:- op(800, fx, ~).</code></td></tr>
+<tr><td>42</td><td><code></code></td><td rowspan="2"></td></tr>
+<tr><td>43</td><td><code>%%% Basic definitions</code></td></tr>
+<tr><td>44</td><td><code></code></td><td rowspan="7"></td></tr>
+<tr><td>45</td><td><code>% swapped arguments append for use in DCGs</code></td></tr>
+<tr><td>46</td><td><code>% when called with instanced list as first argument it unifies it's content with DCG.</code></td></tr>
+<tr><td>47</td><td><code>dcgappend([], L, L) :- !.</code></td></tr>
+<tr><td>48</td><td><code>dcgappend([H|T], [H|R], L) :-</code></td></tr>
+<tr><td>49</td><td><code> dcgappend(T, R, L).</code></td></tr>
+<tr><td>50</td><td><code>%dcgappend(List, L0, L1) :- append(List, L1, L0).</code></td></tr>
+<tr><td>51</td><td><code></code></td><td rowspan="2"></td></tr>
+<tr><td>52</td><td><code>~(Goal) :- delay(Goal).</code></td></tr>
+<tr><td>53</td><td><code></code></td><td rowspan="4"></td></tr>
+<tr><td>54</td><td><code>% apply check to value when bound</code></td></tr>
+<tr><td>55</td><td><code>will_be(Value, Goal) :-</code></td></tr>
+<tr><td>56</td><td><code> freeze(Value, assertion(call(Goal, Value))).</code></td></tr>
+<tr><td>57</td><td><code></code></td><td rowspan="2"></td></tr>
+<tr><td>58</td><td><code>byte(Value) :- must_be(between(0, 255), Value).</code></td></tr>
+<tr><td>59</td><td><code></code></td><td rowspan="2"></td></tr>
+<tr><td>60</td><td><code>nonnegative_integer(Value) :- must_be(nonneg, Value).</code></td></tr>
+<tr><td>61</td><td><code></code></td><td rowspan="5"></td></tr>
+<tr><td>62</td><td><code>sequence_of_bytes([]).</code></td></tr>
+<tr><td>63</td><td><code>sequence_of_bytes([First|Rest]) :-</code></td></tr>
+<tr><td>64</td><td><code> First will_be byte,</code></td></tr>
+<tr><td>65</td><td><code> Rest will_be sequence_of_bytes.</code></td></tr>
+<tr><td>66</td><td><code></code></td><td rowspan="1"></td></tr>
+<tr><td>67</td><td><code>netstring_encoding(PayloadBytes) --> </code></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr>
+<tr><td>68</td><td><code> netstring_prefix_for_payload(PayloadBytes), </code></td><td rowspan="1">It consists of prefix,</td></tr>
+<tr><td>69</td><td><code> PayloadBytes, </code></td><td rowspan="1">payload,</td></tr>
+<tr><td>70</td><td><code> `,`. </code></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr>
+<tr><td>71</td><td><code>netstring_encoding(netstring(PayloadBytes), Bytes) :-</code></td></tr>
+<tr><td>72</td><td><code> assertion(ground(PayloadBytes); ground(Bytes)),</code></td></tr>
+<tr><td>73</td><td><code> Bytes will_be sequence_of_bytes,</code></td></tr>
+<tr><td>74</td><td><code> phrase(netstring_encoding(PayloadBytes), Bytes).</code></td></tr>
+<tr><td>75</td><td><code></code></td><td rowspan="1"></td></tr>
+<tr><td>76</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>77</td><td><code></code></td></tr>
+<tr><td>78</td><td><code></code></td></tr>
+<tr><td>79</td><td><code></code></td></tr>
+<tr><td>80</td><td><code>netstring_prefix_for_payload(PayloadBytes, A, B) :-</code></td></tr>
+<tr><td>81</td><td><code> ~ netstring_prefix_digits(Prefix, A, B),</code></td></tr>
+<tr><td>82</td><td><code> ~ number_codes(PayloadLength, Prefix),</code></td></tr>
+<tr><td>83</td><td><code> ~ length(PayloadBytes, PayloadLength).</code></td></tr>
+<tr><td>84</td><td><code></code></td><td rowspan="7"></td></tr>
+<tr><td>85</td><td><code>netstring_prefix_digits(`0`) --> `0:`, !.</code></td></tr>
+<tr><td>86</td><td><code>netstring_prefix_digits([C|Cs]) --></code></td></tr>
+<tr><td>87</td><td><code> nonzero_digit(C),</code></td></tr>
+<tr><td>88</td><td><code> !,</code></td></tr>
+<tr><td>89</td><td><code> digits(Cs),</code></td></tr>
+<tr><td>90</td><td><code> `:`.</code></td></tr>
+<tr><td>91</td><td><code></code></td><td rowspan="5"></td></tr>
+<tr><td>92</td><td><code>nonzero_digit(Code) --></code></td></tr>
+<tr><td>93</td><td><code> [Code],</code></td></tr>
+<tr><td>94</td><td><code> { assertion(ground(Code)) },</code></td></tr>
+<tr><td>95</td><td><code> { member(Code, `123456789`) }.</code></td></tr>
+<tr><td>96</td><td><code></code></td><td rowspan="6"></td></tr>
+<tr><td>97</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
+<tr><td>98</td><td><code> ground(Goal),</code></td></tr>
+<tr><td>99</td><td><code> !,</code></td></tr>
+<tr><td>100</td><td><code> call(Goal, PayloadBytes),</code></td></tr>
+<tr><td>101</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes).</code></td></tr>
+<tr><td>102</td><td><code></code></td><td rowspan="6"></td></tr>
+<tr><td>103</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
+<tr><td>104</td><td><code> ground(Bytes),</code></td></tr>
+<tr><td>105</td><td><code> !,</code></td></tr>
+<tr><td>106</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes),</code></td></tr>
+<tr><td>107</td><td><code> call(Goal, PayloadBytes).</code></td></tr>
+<tr><td>108</td><td><code></code></td><td rowspan="9"></td></tr>
+<tr><td>109</td><td><code>phrase_length_agg(List, Terminator, CurrentDepth, Length) :-</code></td></tr>
+<tr><td>110</td><td><code> ( List == Terminator</code></td></tr>
+<tr><td>111</td><td><code> -> CurrentDepth = Length</code></td></tr>
+<tr><td>112</td><td><code> ; NextDepth is CurrentDepth + 1,</code></td></tr>
+<tr><td>113</td><td><code> assertion(compound(List)),</code></td></tr>
+<tr><td>114</td><td><code> List = [_|NextList],</code></td></tr>
+<tr><td>115</td><td><code> phrase_length_agg(NextList, Terminator, NextDepth, Length)</code></td></tr>
+<tr><td>116</td><td><code> ).</code></td></tr>
+<tr><td>117</td><td><code></code></td><td rowspan="5"></td></tr>
+<tr><td>118</td><td><code>phrase_length(DCGBody, List, Rest, Length) :-</code></td></tr>
+<tr><td>119</td><td><code> phrase(DCGBody, List, Terminator),</code></td></tr>
+<tr><td>120</td><td><code> phrase_length_agg(List, Terminator, 0, Length),</code></td></tr>
+<tr><td>121</td><td><code> Terminator = Rest.</code></td></tr>
+<tr><td>122</td><td><code></code></td><td rowspan="7"></td></tr>
+<tr><td>123</td><td><code>nsdcg(DCGBody, L0, Rest) :-</code></td></tr>
+<tr><td>124</td><td><code> var(L0),</code></td></tr>
+<tr><td>125</td><td><code> !,</code></td></tr>
+<tr><td>126</td><td><code> phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),</code></td></tr>
+<tr><td>127</td><td><code> number_codes(PayloadLength, PrefixDigits),</code></td></tr>
+<tr><td>128</td><td><code> netstring_prefix_digits(PrefixDigits, L0, Payload).</code></td></tr>
+<tr><td>129</td><td><code></code></td><td rowspan="6"></td></tr>
+<tr><td>130</td><td><code>nsdcg(DCGBody, L0, Rest) :-</code></td></tr>
+<tr><td>131</td><td><code> assertion(nonvar(L0)),</code></td></tr>
+<tr><td>132</td><td><code> netstring_prefix_digits(PrefixDigits, L0, Payload),</code></td></tr>
+<tr><td>133</td><td><code> number_codes(PayloadLength, PrefixDigits),</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></code></td><td rowspan="1"></td></tr>
+<tr><td>136</td><td><code></code></td><td rowspan="6"></td></tr>
+<tr><td>137</td><td><code>% Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature.</code></td></tr>
+<tr><td>138</td><td><code>miniroon_content(miniroon_v0(Identifier, Action, Caveats, Signature)) --></code></td></tr>
+<tr><td>139</td><td><code> nsdcg(miniroon_header(header_v0(Identifier, Action))),</code></td></tr>
+<tr><td>140</td><td><code> nsdcg(miniroon_v0_caveat_list(Caveats)),</code></td></tr>
+<tr><td>141</td><td><code> netstring_encoding(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(nsdcg(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)) --></code></td></tr>
+<tr><td>151</td><td><code> netstring_encoding(`capv0`),</code></td></tr>
+<tr><td>152</td><td><code> netstring_encoding(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_encoding(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)) --></code></td></tr>
+<tr><td>157</td><td><code> netstring_encoding(`env-is`),</code></td></tr>
+<tr><td>158</td><td><code> netstring_encoding(VarName),</code></td></tr>
+<tr><td>159</td><td><code> netstring_encoding(Value).</code></td></tr>
+<tr><td>160</td><td><code>miniroon_v0_caveat(caveat_v0_env_absent(VarName)) --></code></td></tr>
+<tr><td>161</td><td><code> netstring_encoding(`env-absent`),</code></td></tr>
+<tr><td>162</td><td><code> netstring_encoding(VarName).</code></td></tr>
+<tr><td>163</td><td><code>miniroon_v0_caveat(caveat_v0_env_regmatch(VarName, Pattern)) --></code></td></tr>
+<tr><td>164</td><td><code> netstring_encoding(`env-re`),</code></td></tr>
+<tr><td>165</td><td><code> netstring_encoding(VarName),</code></td></tr>
+<tr><td>166</td><td><code> netstring_encoding(Pattern).</code></td></tr>
+<tr><td>167</td><td><code>miniroon_v0_caveat(caveat_v0_env_fnmatch(VarName, Pattern)) --></code></td></tr>
+<tr><td>168</td><td><code> netstring_encoding(`env-glob`),</code></td></tr>
+<tr><td>169</td><td><code> netstring_encoding(VarName),</code></td></tr>
+<tr><td>170</td><td><code> netstring_encoding(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([]) --> {true}.</code></td></tr>
+<tr><td>173</td><td><code>miniroon_v0_caveat_list([Caveat|Rest]) --></code></td></tr>
+<tr><td>174</td><td><code> 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
@@ -0,0 +1,175 @@
+:- module(miniroon_spec,
+ [ dcgappend/3
+ , netstring_prefix_for_payload/3
+ , netstring_encoding/2
+ , netstring_encoding/3
+ , netstring_of/2
+ , phrase_length/4
+ , nsdcg/3
+ , miniroon_encoding/2
+ , miniroon_header/3
+ , miniroon_v0_caveat_list/3
+ ]).
+:- use_module(library(error)).
+:- use_module(library(dcg/basics)).
+
+% pack_install(delay). => https://storage.googleapis.com/packs.ndrix.com/delay/delay-0.3.3.zip
+% Also at: https://github.com/mndrix/delay
+:- use_module(library(delay), [delay/1]).
+
+:- multifile delay:mode/1.
+
+delay:mode(lists:append(ground,_)).
+delay:mode(lists:append(_,ground)).
+
+%delay:mode(system:number_codes(ground,_)).
+%delay:mode(system:number_codes(_,ground)).
+
+%delay:mode(system:length(_,ground)).
+%delay:mode(system:length(list,_)).
+
+delay:mode(miniroon_spec:netstring_prefix_digits(list, _, _)).
+delay:mode(miniroon_spec:netstring_prefix_digits(_, list, _)).
+
+delay:mode(miniroon_spec:netstring_encoding(ground, _)).
+delay:mode(miniroon_spec:netstring_encoding(_, list)).
+
+delay:mode(miniroon_spec:miniroon_v0_action(ground, _)).
+delay:mode(miniroon_spec:miniroon_v0_action(_, list)).
+
+:- op(950, xfx, will_be).
+:- op(800, fx, ~).
+
+%%% Basic definitions
+
+% swapped arguments append for use in DCGs
+% when called with instanced list as first argument it unifies it's content with DCG.
+dcgappend([], L, L) :- !.
+dcgappend([H|T], [H|R], L) :-
+ dcgappend(T, R, L).
+%dcgappend(List, L0, L1) :- append(List, L1, L0).
+
+~(Goal) :- delay(Goal).
+
+% apply check to value when bound
+will_be(Value, Goal) :-
+ freeze(Value, assertion(call(Goal, Value))).
+
+byte(Value) :- must_be(between(0, 255), Value).
+
+nonnegative_integer(Value) :- must_be(nonneg, Value).
+
+sequence_of_bytes([]).
+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_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).
+
+%> 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`.
+netstring_prefix_for_payload(PayloadBytes, A, B) :-
+ ~ netstring_prefix_digits(Prefix, A, B),
+ ~ number_codes(PayloadLength, Prefix),
+ ~ length(PayloadBytes, PayloadLength).
+
+netstring_prefix_digits(`0`) --> `0:`, !.
+netstring_prefix_digits([C|Cs]) -->
+ nonzero_digit(C),
+ !,
+ digits(Cs),
+ `:`.
+
+nonzero_digit(Code) -->
+ [Code],
+ { assertion(ground(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),
+ call(Goal, PayloadBytes).
+
+phrase_length_agg(List, Terminator, CurrentDepth, Length) :-
+ ( List == Terminator
+ -> CurrentDepth = Length
+ ; NextDepth is CurrentDepth + 1,
+ assertion(compound(List)),
+ List = [_|NextList],
+ phrase_length_agg(NextList, Terminator, NextDepth, Length)
+ ).
+
+phrase_length(DCGBody, List, Rest, Length) :-
+ phrase(DCGBody, List, Terminator),
+ phrase_length_agg(List, Terminator, 0, Length),
+ Terminator = Rest.
+
+nsdcg(DCGBody, L0, Rest) :-
+ var(L0),
+ !,
+ phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),
+ number_codes(PayloadLength, PrefixDigits),
+ netstring_prefix_digits(PrefixDigits, L0, Payload).
+
+nsdcg(DCGBody, L0, Rest) :-
+ assertion(nonvar(L0)),
+ netstring_prefix_digits(PrefixDigits, L0, Payload),
+ number_codes(PayloadLength, PrefixDigits),
+ phrase_length(DCGBody,Payload,[44|Rest],PayloadLength).
+
+
+% 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).
+
+miniroon_encoding(Miniroon, Bytes) :-
+ phrase(nsdcg(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),
+ { ~ miniroon_v0_action(Action, ActionBytes) },
+ netstring_encoding(ActionBytes).
+
+miniroon_v0_caveat(caveat_v0_env_is(VarName, Value)) -->
+ netstring_encoding(`env-is`),
+ netstring_encoding(VarName),
+ netstring_encoding(Value).
+miniroon_v0_caveat(caveat_v0_env_absent(VarName)) -->
+ netstring_encoding(`env-absent`),
+ netstring_encoding(VarName).
+miniroon_v0_caveat(caveat_v0_env_regmatch(VarName, Pattern)) -->
+ netstring_encoding(`env-re`),
+ netstring_encoding(VarName),
+ netstring_encoding(Pattern).
+miniroon_v0_caveat(caveat_v0_env_fnmatch(VarName, Pattern)) -->
+ netstring_encoding(`env-glob`),
+ netstring_encoding(VarName),
+ netstring_encoding(Pattern).
+
+miniroon_v0_caveat_list([]) --> {true}.
+miniroon_v0_caveat_list([Caveat|Rest]) -->
+ miniroon_v0_caveat(Caveat),
+ miniroon_v0_caveat_list(Rest).
diff --git a/doc/spec.html b/doc/spec.html
@@ -1,118 +0,0 @@
-<!DOCTYPE html>
-<html><head><style type="text/css">
- table.code_doc { border-collapse: collapse; border: 0px; }
- table.code_doc code { white-space: pre; }
- 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>:- use_module(library(error)).</code></td><td rowspan="2"></td></tr>
-<tr><td>2</td><td><code>:- use_module(library(dcg/basics)).</code></td></tr>
-<tr><td>3</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>4</td><td><code>:- op(950, xfx, will_be).</code></td></tr>
-<tr><td>5</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>6</td><td><code>%%% Basic definitions</code></td></tr>
-<tr><td>7</td><td><code></code></td><td rowspan="7"></td></tr>
-<tr><td>8</td><td><code>% swapped arguments append for use in DCGs</code></td></tr>
-<tr><td>9</td><td><code>% when called with instanced list as first argument it unifies it's content with DCG.</code></td></tr>
-<tr><td>10</td><td><code>dcgappend([], L, L) :- !.</code></td></tr>
-<tr><td>11</td><td><code>dcgappend([H|T], [H|R], L) :-</code></td></tr>
-<tr><td>12</td><td><code> dcgappend(T, R, L).</code></td></tr>
-<tr><td>13</td><td><code>%dcgappend(List, L0, L1) :- append(List, L1, L0).</code></td></tr>
-<tr><td>14</td><td><code></code></td><td rowspan="4"></td></tr>
-<tr><td>15</td><td><code>% apply check to value when bound</code></td></tr>
-<tr><td>16</td><td><code>will_be(Value, Goal) :-</code></td></tr>
-<tr><td>17</td><td><code> freeze(Value, assertion(call(Goal, Value))).</code></td></tr>
-<tr><td>18</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>19</td><td><code>byte(Value) :- must_be(between(0, 255), Value).</code></td></tr>
-<tr><td>20</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>21</td><td><code>nonnegative_integer(Value) :- must_be(nonneg, Value).</code></td></tr>
-<tr><td>22</td><td><code></code></td><td rowspan="5"></td></tr>
-<tr><td>23</td><td><code>sequence_of_bytes([]).</code></td></tr>
-<tr><td>24</td><td><code>sequence_of_bytes([First|Rest]) :-</code></td></tr>
-<tr><td>25</td><td><code> First will_be byte,</code></td></tr>
-<tr><td>26</td><td><code> Rest will_be sequence_of_bytes.</code></td></tr>
-<tr><td>27</td><td><code></code></td><td rowspan="1"></td></tr>
-<tr><td>28</td><td><code>netstring_encoding(PayloadBytes) --> </code></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr>
-<tr><td>29</td><td><code> netstring_prefix_for_payload(PayloadBytes), </code></td><td rowspan="1">It consists of prefix,</td></tr>
-<tr><td>30</td><td><code> PayloadBytes, </code></td><td rowspan="1">payload,</td></tr>
-<tr><td>31</td><td><code> `,`. </code></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr>
-<tr><td>32</td><td><code>netstring_encoding(netstring(PayloadBytes), Bytes) :-</code></td></tr>
-<tr><td>33</td><td><code> assertion(ground(PayloadBytes); ground(Bytes)),</code></td></tr>
-<tr><td>34</td><td><code> Bytes will_be sequence_of_bytes,</code></td></tr>
-<tr><td>35</td><td><code> phrase(netstring_encoding(PayloadBytes), Bytes).</code></td></tr>
-<tr><td>36</td><td><code></code></td><td rowspan="1"></td></tr>
-<tr><td>37</td><td><code></code></td><td rowspan="19">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>38</td><td><code></code></td></tr>
-<tr><td>39</td><td><code></code></td></tr>
-<tr><td>40</td><td><code></code></td></tr>
-<tr><td>41</td><td><code>netstring_prefix_for_payload(PayloadBytes, A, B) :-</code></td></tr>
-<tr><td>42</td><td><code> ( var(PayloadBytes)</code></td></tr>
-<tr><td>43</td><td><code> -> netstring_prefix_codes(Prefix, A, B),</code></td></tr>
-<tr><td>44</td><td><code> number_codes(PayloadLength, Prefix),</code></td></tr>
-<tr><td>45</td><td><code> length(PayloadBytes, PayloadLength)</code></td></tr>
-<tr><td>46</td><td><code> ; length(PayloadBytes, PayloadLength),</code></td></tr>
-<tr><td>47</td><td><code> number_codes(PayloadLength, Prefix),</code></td></tr>
-<tr><td>48</td><td><code> netstring_prefix_codes(Prefix, A, B)</code></td></tr>
-<tr><td>49</td><td><code> ).</code></td></tr>
-<tr><td>50</td><td><code>netstring_prefix_codes(`0`) --> `0:`, !.</code></td></tr>
-<tr><td>51</td><td><code>netstring_prefix_codes([C|Cs]) --></code></td></tr>
-<tr><td>52</td><td><code> nonzero_digit(C),</code></td></tr>
-<tr><td>53</td><td><code> !,</code></td></tr>
-<tr><td>54</td><td><code> digits(Cs),</code></td></tr>
-<tr><td>55</td><td><code> `:`.</code></td></tr>
-<tr><td>56</td><td><code></code></td><td rowspan="5"></td></tr>
-<tr><td>57</td><td><code>nonzero_digit(Code) --></code></td></tr>
-<tr><td>58</td><td><code> [Code],</code></td></tr>
-<tr><td>59</td><td><code> { assertion(ground(Code)) },</code></td></tr>
-<tr><td>60</td><td><code> { member(Code, `123456789`) }.</code></td></tr>
-<tr><td>61</td><td><code></code></td><td rowspan="6"></td></tr>
-<tr><td>62</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
-<tr><td>63</td><td><code> ground(Goal),</code></td></tr>
-<tr><td>64</td><td><code> !,</code></td></tr>
-<tr><td>65</td><td><code> call(Goal, PayloadBytes),</code></td></tr>
-<tr><td>66</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes).</code></td></tr>
-<tr><td>67</td><td><code></code></td><td rowspan="6"></td></tr>
-<tr><td>68</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
-<tr><td>69</td><td><code> ground(Bytes),</code></td></tr>
-<tr><td>70</td><td><code> !,</code></td></tr>
-<tr><td>71</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes),</code></td></tr>
-<tr><td>72</td><td><code> call(Goal, PayloadBytes).</code></td></tr>
-<tr><td>73</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>74</td><td><code>% Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature.</code></td></tr>
-<tr><td>75</td><td><code></code></td><td rowspan="3"></td></tr>
-<tr><td>76</td><td><code>%miniroon_encoding(header_capv0(Identifier, Action), Caveats, Signature, Bytes) :-</code></td></tr>
-<tr><td>77</td><td><code>% assertion(false). % TODO</code></td></tr>
-<tr><td>78</td><td><code></code></td><td rowspan="9"></td></tr>
-<tr><td>79</td><td><code>phrase_length_agg(List, Terminator, CurrentDepth, Length) :-</code></td></tr>
-<tr><td>80</td><td><code> ( List == Terminator</code></td></tr>
-<tr><td>81</td><td><code> -> CurrentDepth = Length</code></td></tr>
-<tr><td>82</td><td><code> ; NextDepth is CurrentDepth + 1,</code></td></tr>
-<tr><td>83</td><td><code> assertion(compound(List)),</code></td></tr>
-<tr><td>84</td><td><code> List = [_|NextList],</code></td></tr>
-<tr><td>85</td><td><code> phrase_length_agg(NextList, Terminator, NextDepth, Length)</code></td></tr>
-<tr><td>86</td><td><code> ).</code></td></tr>
-<tr><td>87</td><td><code></code></td><td rowspan="5"></td></tr>
-<tr><td>88</td><td><code>phrase_length(DCGBody, List, Rest, Length) :-</code></td></tr>
-<tr><td>89</td><td><code> phrase(DCGBody, List, Terminator),</code></td></tr>
-<tr><td>90</td><td><code> phrase_length_agg(List, Terminator, 0, Length),</code></td></tr>
-<tr><td>91</td><td><code> Terminator = Rest.</code></td></tr>
-<tr><td>92</td><td><code></code></td><td rowspan="7"></td></tr>
-<tr><td>93</td><td><code>nsdcg(DCGBody, L0, Rest) :-</code></td></tr>
-<tr><td>94</td><td><code> var(L0),</code></td></tr>
-<tr><td>95</td><td><code> !,</code></td></tr>
-<tr><td>96</td><td><code> phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),</code></td></tr>
-<tr><td>97</td><td><code> number_codes(PayloadLength, PrefixDigits),</code></td></tr>
-<tr><td>98</td><td><code> netstring_prefix_codes(PrefixDigits, L0, Payload).</code></td></tr>
-<tr><td>99</td><td><code></code></td><td rowspan="6"></td></tr>
-<tr><td>100</td><td><code>nsdcg(DCGBody, L0, Rest) :-</code></td></tr>
-<tr><td>101</td><td><code> assertion(nonvar(L0)),</code></td></tr>
-<tr><td>102</td><td><code> netstring_prefix_codes(PrefixDigits, L0, Payload),</code></td></tr>
-<tr><td>103</td><td><code> number_codes(PayloadLength, PrefixDigits),</code></td></tr>
-<tr><td>104</td><td><code> phrase_length(DCGBody,Payload,[44|Rest],PayloadLength).</code></td></tr>
-<tr><td>105</td><td><code></code></td><td rowspan="3"></td></tr>
-<tr><td>106</td><td><code>% :- use_module(library(pldoc/doc_files)).</code></td></tr>
-<tr><td>107</td><td><code>% doc_save('spec.pl', [format(html), doc_root(.)]).</code></td></tr>
-</table></body></html>
diff --git a/doc/spec.pl b/doc/spec.pl
@@ -1,107 +0,0 @@
-:- use_module(library(error)).
-:- use_module(library(dcg/basics)).
-
-:- op(950, xfx, will_be).
-
-%%% Basic definitions
-
-% swapped arguments append for use in DCGs
-% when called with instanced list as first argument it unifies it's content with DCG.
-dcgappend([], L, L) :- !.
-dcgappend([H|T], [H|R], L) :-
- dcgappend(T, R, L).
-%dcgappend(List, L0, L1) :- append(List, L1, L0).
-
-% apply check to value when bound
-will_be(Value, Goal) :-
- freeze(Value, assertion(call(Goal, Value))).
-
-byte(Value) :- must_be(between(0, 255), Value).
-
-nonnegative_integer(Value) :- must_be(nonneg, Value).
-
-sequence_of_bytes([]).
-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_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).
-
-%> 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`.
-netstring_prefix_for_payload(PayloadBytes, A, B) :-
- ( var(PayloadBytes)
- -> netstring_prefix_codes(Prefix, A, B),
- number_codes(PayloadLength, Prefix),
- length(PayloadBytes, PayloadLength)
- ; length(PayloadBytes, PayloadLength),
- number_codes(PayloadLength, Prefix),
- netstring_prefix_codes(Prefix, A, B)
- ).
-netstring_prefix_codes(`0`) --> `0:`, !.
-netstring_prefix_codes([C|Cs]) -->
- nonzero_digit(C),
- !,
- digits(Cs),
- `:`.
-
-nonzero_digit(Code) -->
- [Code],
- { assertion(ground(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),
- call(Goal, PayloadBytes).
-
-% Miniroon is a netstring whose payload is concatenation of three parts: header, caveat list and signature.
-
-%miniroon_encoding(header_capv0(Identifier, Action), Caveats, Signature, Bytes) :-
-% assertion(false). % TODO
-
-phrase_length_agg(List, Terminator, CurrentDepth, Length) :-
- ( List == Terminator
- -> CurrentDepth = Length
- ; NextDepth is CurrentDepth + 1,
- assertion(compound(List)),
- List = [_|NextList],
- phrase_length_agg(NextList, Terminator, NextDepth, Length)
- ).
-
-phrase_length(DCGBody, List, Rest, Length) :-
- phrase(DCGBody, List, Terminator),
- phrase_length_agg(List, Terminator, 0, Length),
- Terminator = Rest.
-
-nsdcg(DCGBody, L0, Rest) :-
- var(L0),
- !,
- phrase_length(DCGBody,Payload,[44|Rest],PayloadLength),
- number_codes(PayloadLength, PrefixDigits),
- netstring_prefix_codes(PrefixDigits, L0, Payload).
-
-nsdcg(DCGBody, L0, Rest) :-
- assertion(nonvar(L0)),
- netstring_prefix_codes(PrefixDigits, L0, Payload),
- number_codes(PayloadLength, PrefixDigits),
- phrase_length(DCGBody,Payload,[44|Rest],PayloadLength).
-
-% :- use_module(library(pldoc/doc_files)).
-% doc_save('spec.pl', [format(html), doc_root(.)]).
diff --git a/doc/tests.pl b/doc/tests.pl
@@ -1,8 +1,30 @@
:- use_module(library(error)).
:- use_module(library(yall)).
+:- use_module(miniroon_spec).
+
+test_one_result(Template, Goal, Output) :-
+ findall(Template, Goal, Results),
+ assertion(length(Results, 1)),
+ Results = [Output].
+
+test_result_matches(Template, Goal, Expected) :-
+ test_one_result(Template, Goal, Output),
+ assertion(Output =@= Expected).
+
+test_encode(Goal, ExpectedBytes) :-
+ test_one_result(Bytes, call(Goal, Bytes), OutputBytes),
+ string_bytes(Output, OutputBytes, octet),
+ string_bytes(Expected, ExpectedBytes, octet),
+ assertion(Output =@= Expected).
+
+test_encode_dcg(Goal, ExpectedBytes) :-
+ test_one_result(Bytes, phrase(Goal, Bytes), OutputBytes),
+ string_bytes(Output, OutputBytes, octet),
+ string_bytes(Expected, ExpectedBytes, octet),
+ assertion(Output =@= Expected).
+
:- begin_tests(spec_netstring).
-:- [spec].
test(prefix_encode_empty) :-
PayloadBytes = ``,
@@ -106,3 +128,105 @@ test(decode_nsdcg_single) :-
assertion(Length =@= 5).
:- end_tests(spec_netstring).
+
+:- begin_tests(spec_miniroon).
+
+test(header_v0_decode) :-
+ ExpectedHeader = header_v0(`example1`, invoke),
+ Bytes = `28:5:capv0,8:example1,6:invoke,,`,
+ test_result_matches(
+ Header,
+ phrase(nsdcg(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(caveat_list_v0_decode_empty) :-
+ ExpectedCaveats = [],
+ Bytes = `0:,`,
+ findall(Caveats, phrase(nsdcg(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(caveat_list_v0_decode) :-
+ ExpectedCaveats = [
+ caveat_v0_env_is(`var1`, `hello`),
+ caveat_v0_env_absent(`var2`),
+ caveat_v0_env_glob(`var3`, `_*`),
+ caveat_v0_env_is(`var3`, `_hello`)
+ ],
+ 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,,,`,
+ phrase(nsdcg(miniroon_v0_caveat_list(Caveats)), Bytes),
+ assertion(Caveats =@= ExpectedCaveats).
+
+test(caveat_list_v0_encode) :-
+ Caveats = [
+ caveat_v0_env_is(`var1`, `hello`),
+ caveat_v0_env_absent(`var2`),
+ caveat_v0_env_glob(`var3`, `_*`),
+ 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(decode_example1) :-
+ ExpectedSignature = [
+ 0xda, 0xde, 0xbd, 0xd0, 0xf3, 0x39, 0x14, 0x7c,
+ 0xbe, 0x48, 0xaa, 0x0c, 0x5d, 0x1f, 0xa4, 0xff,
+ 0x87, 0x58, 0x65, 0x79, 0xff, 0x89, 0x28, 0xb5,
+ 0xda, 0xb3, 0x4e, 0x7c, 0x5d, 0xfa, 0x4a, 0x89
+ ],
+ ExpectedCaveats = [
+ caveat_v0_env_is(`var1`, `hello`),
+ caveat_v0_env_absent(`var2`),
+ caveat_v0_env_glob(`var3`, `_*`),
+ caveat_v0_env_is(`var3`, `_hello`)
+ ],
+ flatten([
+ `182:`,
+ `28:5:capv0,8:example1,6:invoke,,`,
+ `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,,,`,
+ `32:`,
+ ExpectedSignature,
+ `,,`
+ ], Bytes),
+ ExpectedMiniroon = miniroon_v0(`example1`, invoke, ExpectedCaveats, ExpectedSignature),
+ assertion(ground(Bytes)),
+ assertion(ground(ExpectedMiniroon)),
+ test_result_matches(Miniroon, miniroon_encoding(Miniroon, Bytes), ExpectedMiniroon).
+
+test(encode_example1) :-
+ Signature = [
+ 0xda, 0xde, 0xbd, 0xd0, 0xf3, 0x39, 0x14, 0x7c,
+ 0xbe, 0x48, 0xaa, 0x0c, 0x5d, 0x1f, 0xa4, 0xff,
+ 0x87, 0x58, 0x65, 0x79, 0xff, 0x89, 0x28, 0xb5,
+ 0xda, 0xb3, 0x4e, 0x7c, 0x5d, 0xfa, 0x4a, 0x89
+ ],
+ Caveats = [
+ caveat_v0_env_is(`var1`, `hello`),
+ caveat_v0_env_absent(`var2`),
+ caveat_v0_env_glob(`var3`, `_*`),
+ caveat_v0_env_is(`var3`, `_hello`)
+ ],
+ flatten([
+ `182:`,
+ `28:5:capv0,8:example1,6:invoke,,`,
+ `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,,,`,
+ `32:`,
+ Signature,
+ `,,`
+ ], ExpectedBytes),
+ Miniroon = miniroon_v0(`example1`, invoke, Caveats, Signature),
+ assertion(ground(Miniroon)),
+ test_encode(miniroon_encoding(Miniroon), ExpectedBytes).
+
+:- end_tests(spec_miniroon).