On 7/27/23 19:27, Eric Blake wrote:
On Thu, Jul 27, 2023 at 01:54:25PM +0200, Laszlo Ersek wrote:
> On 7/26/23 19:50, Eric Blake wrote:
>> Use Go-preferred whitespace: TAB for indent, and no space before
>> opening '(', better use of blank lines. These changes reduce the size
>> of a diff produced by gofmt, although there are still other places
>> where we are not idiomatic in what we generate (mainly in lining up
>> columns of = in const() blocks).
>>
>> Signed-off-by: Eric Blake <eblake(a)redhat.com>
>> ---
>> generator/GoLang.ml | 244 ++++++++++++++++++++++----------------------
>> 1 file changed, 123 insertions(+), 121 deletions(-)
>
> Unfortunately:
>
> - This must have been an incredible amount of work for you.
Some automation involved (emacs macros are nice), but yeah, it's
grunt-work.
>
> - The many \t escape sequences make the generator source code harder to
> read.
Agreed. Maybe a better approach would be figuring out an optional
argument to pr to insist on TAB instead of space indent? Or, as
mentioned elsewhere, not bothering with TAB at all in OCaml but
instead figuring out how to run gofmt itself as part of the generation
pipeline.
>
> - This patch depends on patch#4 (minimally), so if you modify that one
> like I've asked, it will create a conflict for this patch. :(
Yep, I've already encountered a number of conflicts as I shuffle
around patches, trying to pick out which changes are more than just
whitespace (such as the RBool change). Not terribly difficult, but
back in the grunt-work category.
>
> Acked-by: Laszlo Ersek <lersek(a)redhat.com>
>
> Thanks
> Laszlo
>
>>
>> diff --git a/generator/GoLang.ml b/generator/GoLang.ml
>> index 77dacadb..a38aa19f 100644
>> --- a/generator/GoLang.ml
>> +++ b/generator/GoLang.ml
>> @@ -175,6 +175,7 @@ let
>> let print_binding (name, { args; optargs; ret; shortdesc }) =
>> let cname = camel_case name in
>>
>> + pr "\n";
>> (* Tedious method of passing optional arguments in golang. *)
>> if optargs <> [] then (
>> pr "/* Struct carrying optional arguments for %s. */\n" cname;
>> @@ -182,10 +183,10 @@ let
>> List.iter (
>> fun optarg ->
>> let fname = go_name_of_optarg optarg in
>> - pr " /* %s field is ignored unless %sSet == true. */\n"
>> + pr "\t/* %s field is ignored unless %sSet == true. */\n"
>> fname fname;
I tried:
+ pr "\t" ^ "/* %s field is ignored unless %sSet == true.
*/\n"
fname fname;
File "GoLang.ml", line 186, characters 8-15:
186 | pr "\t" ^ "/* %s field is ignored unless %sSet == true.
*/\n"
^^^^^^^
Error: This expression has type unit but an expression was expected of type
string
and:
File "GoLang.ml", line 186, characters 11-71:
186 | pr ("\t" ^ "/* %s field is ignored unless %sSet == true.
*/\n")
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Error: This expression has type string but an expression was expected of type
('weak2 -> 'weak3 -> 'weak4, unit, string, unit) format4 =
('weak2 -> 'weak3 -> 'weak4, unit, string, string, string,
unit)
CamlinternalFormatBasics.format6
Sigh. :/
"pr" is declared like this [generator/utils.mli]:
val pr : ('a, unit, string, unit) format4 -> 'a
The format string would normally be converted to a "format4" type, which
is a polymorphic type, taking one type variable ('a).
It seems that this automatic conversion/construction, from string to
format4, is performed by a mechanism that is similar to
"Stdlib.format_of_string":
https://v2.ocaml.org/api/Stdlib.html#1_Operationsonformatstrings
There are two catches however:
- Stdlib.format_of_string only works with string *literals*, according
to the documentation,
- Stdlib.format_of_string produces a format6, not a format4.
The documentation recommends "Scanf.format_from_string":
https://v2.ocaml.org/api/Scanf.html#VALformat_from_string
so you could *theoretically* do something like
printf (Scanf.format_from_string ("%d " ^ "%d\n")) 1 2
it still doesn't work though, because Scanf.format_from_string also
produces a format6 (like Stdlib.format_of_string does), but printf
expects a plain "format" (similarly to how "pr" expects
"format4", i.e.,
*not* format6).
So this appears unsolvable. "Scanf.format_from_string" produces a
format6, which takes six type variables, but "pr" expects "format4",
which is much less generic:
type ('a, 'b, 'c, 'd) format4 = ('a, 'b, 'c, 'c, 'c,
'd) format6
We'd have to find a way to "narrow" the format6 produced by
"Scanf.format_from_string" into a format4, and I don't think that's
possible (the parameters are not values but types). And I couldn't find
a Printf module function that took a format6.
Ultimately open-coding the \t escape sequences seems the least intrusive...
so those are non-starters. We can get more verbose with:
+ pr "\t";
+ pr "/* %s field is ignored unless %sSet == true. */\n"
fname fname;
but that may not scale as nicely. Go's use of TAB does not specify
what tabstop it prefers;
well that I find unfathomable; how can any coding style mandate TABs for
indentation if it doesn't explain how wide a TAB should be? For example,
that makes "maximum line width" mostly impossible to define.
our .editorconfig suggests using a tabstop of
4 (instead of the usual 8) when reading a .go file for less visual
distraction, and which matches with GoLang.ml currently using 4-space
indentation.
Rich, do you have any ideas on any better approach to take here?
>> - pr " %sSet bool\n" fname;
>> - pr " %s " fname;
>> + pr "\t%sSet bool\n" fname;
>> + pr "\t%s " fname;
I also debated about splitting this patch into two (yes, more
gruntwork): one to address space before '(', and the other to address
leading indentation. Lines like this would be touched by both
patches if I split.
I wouldn't insist on such a split. :)
Laszlo