Skip to content

Commit be7ff9d

Browse files
authored
feat(graindoc)!: Improve docgen for labeled & default arguments (#1776)
1 parent 1a948bb commit be7ff9d

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

58 files changed

+462
-409
lines changed

compiler/graindoc/docblock.re

Lines changed: 58 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ open Grain_utils;
44
open Grain_diagnostics;
55

66
type param = {
7-
param_name: string,
7+
param_id: string,
88
param_type: string,
99
param_msg: string,
1010
};
@@ -77,7 +77,8 @@ exception
7777
attr: string,
7878
});
7979

80-
exception MissingParamType({name: string});
80+
exception MissingLabeledParamType({name: string});
81+
exception MissingUnlabeledParamType({idx: int});
8182
exception MissingReturnType;
8283
exception
8384
InvalidAttribute({
@@ -96,11 +97,19 @@ let () =
9697
attr,
9798
);
9899
Some(msg);
99-
| MissingParamType({name}) =>
100+
| MissingLabeledParamType({name}) =>
100101
let msg =
101102
Printf.sprintf(
102-
"Unable to find a type for %s. Did you specify too many @param attributes?",
103+
"Unable to find a matching function parameter for %s. Make sure a parameter exists with this label or use `@param <param_index> %s` for unlabeled parameters.",
103104
name,
105+
name,
106+
);
107+
Some(msg);
108+
| MissingUnlabeledParamType({idx}) =>
109+
let msg =
110+
Printf.sprintf(
111+
"Unable to find a type for parameter at index %d. Make sure a parameter exists at this index in the parameter list.",
112+
idx,
104113
);
105114
Some(msg);
106115
| MissingReturnType =>
@@ -161,8 +170,8 @@ let output_for_params = params => {
161170
Markdown.table(
162171
~headers=["param", "type", "description"],
163172
List.map(
164-
({param_name, param_type, param_msg}) => {
165-
[Markdown.code(param_name), Markdown.code(param_type), param_msg]
173+
({param_id, param_type, param_msg}) => {
174+
[Markdown.code(param_id), Markdown.code(param_type), param_msg]
166175
},
167176
params,
168177
),
@@ -207,14 +216,25 @@ let output_for_throws = throws => {
207216

208217
let types_for_function = (~ident, vd: Types.value_description) => {
209218
switch (Ctype.repr(vd.val_type).desc) {
210-
| TTyArrow(args, returns, _) => (
211-
Some(List.map(snd, args)),
212-
Some(returns),
213-
)
219+
| TTyArrow(args, returns, _) => (Some(args), Some(returns))
214220
| _ => (None, None)
215221
};
216222
};
217223

224+
let lookup_arg_by_label = (name, args_opt) => {
225+
Option.bind(args_opt, args =>
226+
List.find_opt(
227+
((label: Grain_parsing.Asttypes.argument_label, _)) =>
228+
switch (label) {
229+
| Default(l)
230+
| Labeled(l) => l.txt == name
231+
| _ => false
232+
},
233+
args,
234+
)
235+
);
236+
};
237+
218238
let lookup_type_expr = (~idx, type_exprs) => {
219239
Option.bind(type_exprs, te => List.nth_opt(te, idx));
220240
};
@@ -253,7 +273,7 @@ let for_value_description =
253273
| None => (None, [])
254274
};
255275

256-
let (arg_types, return_type) = types_for_function(~ident, vd);
276+
let (args, return_type) = types_for_function(~ident, vd);
257277

258278
let (deprecations, since, history, params, returns, throws, examples) =
259279
List.fold_left(
@@ -291,19 +311,37 @@ let for_value_description =
291311
throws,
292312
examples,
293313
)
294-
| Param({attr_name: param_name, attr_desc: param_msg}) =>
295-
// TODO: Use label lookups when labeled parameters are introduced
296-
let param_type =
297-
switch (lookup_type_expr(~idx=List.length(params), arg_types)) {
298-
| Some(typ) => Printtyp.string_of_type_sch(typ)
299-
| None => raise(MissingParamType({name: param_name}))
314+
| Param({attr_id: param_id, attr_desc: param_msg}) =>
315+
let (param_id, param_type) =
316+
switch (param_id) {
317+
| PositionalParam(idx) =>
318+
switch (lookup_type_expr(~idx, args)) {
319+
| Some((_, typ)) => (
320+
string_of_int(idx),
321+
Printtyp.string_of_type_sch(typ),
322+
)
323+
| None => raise(MissingUnlabeledParamType({idx: idx}))
324+
}
325+
| LabeledParam(name) =>
326+
switch (lookup_arg_by_label(name, args)) {
327+
| Some((Labeled(_), typ)) => (
328+
name,
329+
Printtyp.string_of_type_sch(typ),
330+
)
331+
// Default parameters have the type Option<a>; extract the type from the Option
332+
| Some((Default(_), {desc: TTyConstr(_, [typ], _)})) => (
333+
"?" ++ name,
334+
Printtyp.string_of_type_sch(typ),
335+
)
336+
| _ => raise(MissingLabeledParamType({name: name}))
337+
}
300338
};
301339

302340
(
303341
deprecations,
304342
since,
305343
history,
306-
[{param_name, param_type, param_msg}, ...params],
344+
[{param_id, param_type, param_msg}, ...params],
307345
returns,
308346
throws,
309347
examples,
@@ -402,7 +440,7 @@ let for_type_declaration =
402440
[{history_version, history_msg}, ...history],
403441
examples,
404442
)
405-
| Param({attr_name: param_name, attr_desc: param_msg}) =>
443+
| Param({attr_id: param_id, attr_desc: param_msg}) =>
406444
raise(InvalidAttribute({name, attr: "param"}))
407445
| Returns({attr_desc: returns_msg}) =>
408446
raise(InvalidAttribute({name, attr: "returns"}))
@@ -529,7 +567,7 @@ and for_signature_items =
529567
[{history_version, history_msg}, ...history],
530568
examples,
531569
)
532-
| Param({attr_name: param_name, attr_desc: param_msg}) =>
570+
| Param({attr_id: param_id, attr_desc: param_msg}) =>
533571
raise(InvalidAttribute({name, attr: "param"}))
534572
| Returns({attr_desc: returns_msg}) =>
535573
raise(InvalidAttribute({name, attr: "returns"}))

compiler/src/diagnostics/comment_attributes.re

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,13 @@
11
exception InvalidAttribute(string);
22
exception MalformedAttribute(string, string);
33

4+
type param_id =
5+
| LabeledParam(string)
6+
| PositionalParam(int);
7+
48
type t =
59
| Param({
6-
attr_name: string,
10+
attr_id: param_id,
711
attr_desc: string,
812
})
913
| Returns({attr_desc: string})

compiler/src/diagnostics/graindoc_lexer.re

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -52,6 +52,9 @@ let newline = [%sedlex.regexp? 0x0A | 0x0C | 0x0D | 0x85 | 0x2028 | 0x2029];
5252
// A colon eats whitespace after it
5353
let colon = [%sedlex.regexp? (':', Star(blank))];
5454

55+
let dec_digit = [%sedlex.regexp? '0' .. '9'];
56+
let dec_int = [%sedlex.regexp? (dec_digit, Star(dec_digit))];
57+
5558
// A asterisk eats whitespace before it and up to 1 after it
5659
let asterisk = [%sedlex.regexp? (Star(blank), '*', Opt(blank))];
5760

@@ -131,6 +134,7 @@ and param = (state, lexbuf) => {
131134
switch%sedlex (lexbuf) {
132135
| blank => param(state, lexbuf)
133136
| ident => IDENT(Sedlexing.Utf8.lexeme(lexbuf))
137+
| dec_int => INT(Sedlexing.Utf8.lexeme(lexbuf))
134138
| colon =>
135139
state.lexer_mode = Default;
136140
COLON;

compiler/src/diagnostics/graindoc_parser.mly

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,9 @@
1+
%{
2+
open Comment_attributes
3+
%}
4+
15
%token PARAM SECTION SINCE HISTORY THROWS RETURNS EXAMPLE DEPRECATED COLON EOL EOF
2-
%token <string> TEXT IDENT SEMVER CONSTRUCTOR
6+
%token <string> TEXT IDENT INT SEMVER CONSTRUCTOR
37

48
%right EOL
59

@@ -26,8 +30,12 @@ description:
2630
attribute_text:
2731
| ioption(eols) multiline_text ioption(eols) %prec EOL { $2 }
2832

33+
param_id:
34+
| IDENT { LabeledParam $1 }
35+
| INT { PositionalParam (int_of_string $1) }
36+
2937
attribute:
30-
| PARAM IDENT COLON attribute_text { Param({ attr_name=$2; attr_desc=$4 }) }
38+
| PARAM param_id COLON attribute_text { Param({ attr_id=$2; attr_desc=$4 }) }
3139
| RETURNS attribute_text { Returns({attr_desc=$2}) }
3240
| EXAMPLE attribute_text { Example({attr_desc=$2}) }
3341
| DEPRECATED attribute_text { Deprecated({attr_desc=$2}) }

compiler/src/typed/typedtree.re

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -40,6 +40,7 @@ type provide_flag =
4040
Asttypes.provide_flag = | NotProvided | Provided | Abstract;
4141
type rec_flag = Asttypes.rec_flag = | Nonrecursive | Recursive;
4242
type mut_flag = Asttypes.mut_flag = | Mutable | Immutable;
43+
[@deriving sexp]
4344
type argument_label =
4445
Asttypes.argument_label =
4546
| Unlabeled | Labeled(loc(string)) | Default(loc(string));

stdlib/array.gr

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -83,7 +83,7 @@ provide let length = array => {
8383
* @since v0.1.0
8484
*/
8585
@unsafe
86-
provide let make: (Number, a) => Array<a> = (length: Number, item: a) => {
86+
provide let make = (length: Number, item: a) => {
8787
from WasmI32 use { (+), (*), (<) }
8888
checkLength(length)
8989
let length = coerceNumberToWasmI32(length)
@@ -116,11 +116,7 @@ provide let make: (Number, a) => Array<a> = (length: Number, item: a) => {
116116
* @since v0.1.0
117117
*/
118118
@unsafe
119-
provide let init: (Number, Number => a) => Array<a> =
120-
(
121-
length: Number,
122-
fn: Number => a,
123-
) => {
119+
provide let init = (length: Number, fn: Number => a) => {
124120
from WasmI32 use { (+), (*), (<) }
125121
checkLength(length)
126122
let length = coerceNumberToWasmI32(length)
@@ -1821,8 +1817,8 @@ provide module Immutable {
18211817
* @history v0.5.4: Originally in `"immutablearray"` module
18221818
*/
18231819

1824-
provide let contains = (value, array) => {
1825-
reduce((acc, x) => acc || x == value, false, array)
1820+
provide let contains = (search, array) => {
1821+
reduce((acc, x) => acc || x == search, false, array)
18261822
}
18271823

18281824
/**

stdlib/array.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -66,7 +66,7 @@ No other changes yet.
6666
</details>
6767

6868
```grain
69-
make : (Number, a) => Array<a>
69+
make : (length: Number, item: a) => Array<a>
7070
```
7171

7272
Creates a new array of the specified length with each element being
@@ -106,7 +106,7 @@ No other changes yet.
106106
</details>
107107

108108
```grain
109-
init : (Number, (Number => a)) => Array<a>
109+
init : (length: Number, fn: (Number => a)) => Array<a>
110110
```
111111

112112
Creates a new array of the specified length where each element is
@@ -1216,7 +1216,7 @@ Parameters:
12161216
|param|type|description|
12171217
|-----|----|-----------|
12181218
|`start`|`Number`|The index of the array where the slice will begin (inclusive)|
1219-
|`end`|`Option<Number>`|The index of the array where the slice will end (exclusive)|
1219+
|`?end`|`Number`|The index of the array where the slice will end (exclusive)|
12201220
|`array`|`Array<a>`|The array to be sliced|
12211221

12221222
Returns:
@@ -2150,7 +2150,7 @@ Returns:
21502150
</details>
21512151

21522152
```grain
2153-
contains : (value: a, array: ImmutableArray<a>) => Bool
2153+
contains : (search: a, array: ImmutableArray<a>) => Bool
21542154
```
21552155

21562156
Checks if the value is an element of the input array.
@@ -2528,7 +2528,7 @@ Parameters:
25282528
|param|type|description|
25292529
|-----|----|-----------|
25302530
|`start`|`Number`|The index of the array where the slice will begin (inclusive)|
2531-
|`end`|`Option<Number>`|The index of the array where the slice will end (exclusive)|
2531+
|`?end`|`Number`|The index of the array where the slice will end (exclusive)|
25322532
|`array`|`ImmutableArray<a>`|The array to be sliced|
25332533

25342534
Returns:

stdlib/bigint.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -37,7 +37,7 @@ No other changes yet.
3737
</details>
3838

3939
```grain
40-
fromNumber : (x: Number) => BigInt
40+
fromNumber : (number: Number) => BigInt
4141
```
4242

4343
Converts a Number to a BigInt.
@@ -62,7 +62,7 @@ No other changes yet.
6262
</details>
6363

6464
```grain
65-
toNumber : (x: BigInt) => Number
65+
toNumber : (num: BigInt) => Number
6666
```
6767

6868
Converts a BigInt to a Number.

stdlib/bytes.gr

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -166,9 +166,9 @@ provide let length = (bytes: Bytes) => {
166166
* @since v0.3.2
167167
*/
168168
@unsafe
169-
provide let copy = (b: Bytes) => {
169+
provide let copy = (bytes: Bytes) => {
170170
from WasmI32 use { (+) }
171-
let src = WasmI32.fromGrain(b)
171+
let src = WasmI32.fromGrain(bytes)
172172
let size = getSize(src)
173173
let dst = allocateBytes(size)
174174
Memory.copy(dst + _VALUE_OFFSET, src + _VALUE_OFFSET, size)

stdlib/bytes.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -188,7 +188,7 @@ No other changes yet.
188188
</details>
189189

190190
```grain
191-
copy : (b: Bytes) => Bytes
191+
copy : (bytes: Bytes) => Bytes
192192
```
193193

194194
Creates a new byte sequence that contains the same bytes as the input byte sequence.

0 commit comments

Comments
 (0)