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 <[email protected]>
>>> ---
>>> 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 <[email protected]>
>>
>> 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
_______________________________________________
Libguestfs mailing list
[email protected]
https://listman.redhat.com/mailman/listinfo/libguestfs