commit 3fd8e229286dd332dbe8ec1ef64673b19cd75311
parent 376fc0a2ece9f9cd1c181035ed62f256bfeea40c
Author: Jan Pobrislo <ccx@te2000.cz>
Date: Mon, 28 Apr 2025 13:37:13 +0000
DCG-only version of netstring wrapper
Diffstat:
| M | doc/spec.html | | | 164 | +++++++++++++++++++++++++++++++++++++++++++++++++------------------------------ |
| M | doc/spec.pl | | | 38 | ++++++++++++++++++++++++++++++++++++++ |
| M | doc/tests.pl | | | 57 | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
3 files changed, 196 insertions(+), 63 deletions(-)
diff --git a/doc/spec.html b/doc/spec.html
@@ -11,70 +11,108 @@
<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="4"></td></tr>
-<tr><td>8</td><td><code>% apply check to value when bound</code></td></tr>
-<tr><td>9</td><td><code>will_be(Value, Goal) :-</code></td></tr>
-<tr><td>10</td><td><code> freeze(Value, assertion(call(Goal, Value))).</code></td></tr>
-<tr><td>11</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>12</td><td><code>byte(Value) :- must_be(between(0, 255), Value).</code></td></tr>
-<tr><td>13</td><td><code></code></td><td rowspan="2"></td></tr>
-<tr><td>14</td><td><code>nonnegative_integer(Value) :- must_be(nonneg, Value).</code></td></tr>
-<tr><td>15</td><td><code></code></td><td rowspan="5"></td></tr>
-<tr><td>16</td><td><code>sequence_of_bytes([]).</code></td></tr>
-<tr><td>17</td><td><code>sequence_of_bytes([First|Rest]) :-</code></td></tr>
-<tr><td>18</td><td><code> First will_be byte,</code></td></tr>
-<tr><td>19</td><td><code> Rest will_be sequence_of_bytes.</code></td></tr>
-<tr><td>20</td><td><code></code></td><td rowspan="1"></td></tr>
-<tr><td>21</td><td><code>netstring_encoding(PayloadBytes) --> </code></td><td rowspan="1">Netstring is a sequence of bytes.</td></tr>
-<tr><td>22</td><td><code> netstring_prefix_for_payload(PayloadBytes), </code></td><td rowspan="1">It consists of prefix,</td></tr>
-<tr><td>23</td><td><code> PayloadBytes, </code></td><td rowspan="1">payload,</td></tr>
-<tr><td>24</td><td><code> `,`. </code></td><td rowspan="5">and terminator. Terminator is single ASCII comma `,`.</td></tr>
-<tr><td>25</td><td><code>netstring_encoding(netstring(PayloadBytes), Bytes) :-</code></td></tr>
-<tr><td>26</td><td><code> assertion(ground(PayloadBytes); ground(Bytes)),</code></td></tr>
-<tr><td>27</td><td><code> Bytes will_be sequence_of_bytes,</code></td></tr>
-<tr><td>28</td><td><code> phrase(netstring_encoding(PayloadBytes), Bytes).</code></td></tr>
-<tr><td>29</td><td><code></code></td><td rowspan="1"></td></tr>
-<tr><td>30</td><td><code></code></td><td rowspan="19">Netstring prefix is the shortest ASCII decimal representation for length of
+<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>31</td><td><code></code></td></tr>
-<tr><td>32</td><td><code></code></td></tr>
-<tr><td>33</td><td><code></code></td></tr>
-<tr><td>34</td><td><code>netstring_prefix_for_payload(PayloadBytes, A, B) :-</code></td></tr>
-<tr><td>35</td><td><code> ( var(PayloadBytes)</code></td></tr>
-<tr><td>36</td><td><code> -> netstring_prefix_codes(Prefix, A, B),</code></td></tr>
-<tr><td>37</td><td><code> number_codes(PayloadLength, Prefix),</code></td></tr>
-<tr><td>38</td><td><code> length(PayloadBytes, PayloadLength)</code></td></tr>
-<tr><td>39</td><td><code> ; length(PayloadBytes, PayloadLength),</code></td></tr>
-<tr><td>40</td><td><code> number_codes(PayloadLength, Prefix),</code></td></tr>
-<tr><td>41</td><td><code> netstring_prefix_codes(Prefix, A, B)</code></td></tr>
-<tr><td>42</td><td><code> ).</code></td></tr>
-<tr><td>43</td><td><code>netstring_prefix_codes(`0`) --> `0:`, !.</code></td></tr>
-<tr><td>44</td><td><code>netstring_prefix_codes([C|Cs]) --></code></td></tr>
-<tr><td>45</td><td><code> nonzero_digit(C),</code></td></tr>
-<tr><td>46</td><td><code> !,</code></td></tr>
-<tr><td>47</td><td><code> digits(Cs),</code></td></tr>
-<tr><td>48</td><td><code> `:`.</code></td></tr>
-<tr><td>49</td><td><code></code></td><td rowspan="5"></td></tr>
-<tr><td>50</td><td><code>nonzero_digit(Code) --></code></td></tr>
-<tr><td>51</td><td><code> [Code],</code></td></tr>
-<tr><td>52</td><td><code> { assertion(ground(Code)) },</code></td></tr>
-<tr><td>53</td><td><code> { member(Code, `123456789`) }.</code></td></tr>
-<tr><td>54</td><td><code></code></td><td rowspan="6"></td></tr>
-<tr><td>55</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
-<tr><td>56</td><td><code> ground(Goal),</code></td></tr>
-<tr><td>57</td><td><code> !,</code></td></tr>
-<tr><td>58</td><td><code> call(Goal, PayloadBytes),</code></td></tr>
-<tr><td>59</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes).</code></td></tr>
-<tr><td>60</td><td><code></code></td><td rowspan="6"></td></tr>
-<tr><td>61</td><td><code>netstring_of(Goal, Bytes) :-</code></td></tr>
-<tr><td>62</td><td><code> ground(Bytes),</code></td></tr>
-<tr><td>63</td><td><code> !,</code></td></tr>
-<tr><td>64</td><td><code> netstring_encoding(netstring(PayloadBytes), Bytes),</code></td></tr>
-<tr><td>65</td><td><code> call(Goal, PayloadBytes).</code></td></tr>
-<tr><td>66</td><td><code></code></td><td rowspan="1"></td></tr>
-<tr><td>67</td><td><code></code></td><td rowspan="3"></td></tr>
-<tr><td>68</td><td><code>% :- use_module(library(pldoc/doc_files)).</code></td></tr>
-<tr><td>69</td><td><code>% doc_save('spec.pl', [format(html), doc_root(.)]).</code></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
@@ -5,6 +5,13 @@
%%% 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))).
@@ -64,6 +71,37 @@ netstring_of(Goal, 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,3 +1,6 @@
+:- use_module(library(error)).
+:- use_module(library(yall)).
+
:- begin_tests(spec_netstring).
:- [spec].
@@ -48,4 +51,58 @@ test(decode_nested) :-
netstring_of(PayloadGoal, Bytes),
assertion(InnerPayload =@= [0, 255]).
+
+test(dcgappend1) :-
+ phrase_length(``, Bytes, [], Length),
+ assertion(Bytes =@= ``),
+ assertion(Length =@= 0).
+
+test(dcgappend2) :-
+ phrase_length(`abcd`, Bytes, [], Length),
+ assertion(Bytes =@= `abcd`),
+ assertion(Length =@= 4).
+
+test(dcgappend3) :-
+ phrase_length(`abcd`, Bytes, Next, Length1),
+ phrase_length(`efgh`, Next, [], Length2),
+ assertion(Bytes =@= `abcdefgh`),
+ assertion(Length1 =@= 4),
+ assertion(Length2 =@= 4).
+
+test(dcgappend4) :-
+ phrase_length(`efgh`, Next, [], Length2),
+ phrase_length(`abcd`, Bytes, Next, Length1),
+ assertion(Bytes =@= `abcdefgh`),
+ assertion(Length1 =@= 4),
+ assertion(Length2 =@= 4).
+
+test(dcgappend5) :-
+ Bytes = `abcdefgh`,
+ phrase_length(`abcd`, Bytes, Next, Length1),
+ assertion(Next =@= `efgh`),
+ assertion(Length1 =@= 4).
+
+test(encode_nsdcg) :-
+ InnerPayload = [0, 255],
+ flatten([`5:2:\0`, 255, `,,`], ExpectedBytes),
+ phrase_length(nsdcg(nsdcg(dcgappend(InnerPayload))), Bytes, [], Length),
+ assertion(Bytes =@= ExpectedBytes),
+ assertion(Length =@= 8).
+
+test(decode_nsdcg) :-
+ ExpectedPayload = [0, 255],
+ flatten([`5:2:\0`, 255, `,,`], Bytes),
+ InnerPayload = [_,_],
+ phrase_length(nsdcg(nsdcg(dcgappend(InnerPayload))), Bytes, [], Length),
+ assertion(InnerPayload =@= ExpectedPayload),
+ assertion(Length =@= 8).
+
+test(decode_nsdcg_single) :-
+ ExpectedPayload = [0, 255],
+ flatten([`2:\0`, 255, `,`], Bytes),
+ InnerPayload = [_,_],
+ phrase_length(nsdcg(dcgappend(InnerPayload)), Bytes, [], Length),
+ assertion(InnerPayload =@= ExpectedPayload),
+ assertion(Length =@= 5).
+
:- end_tests(spec_netstring).