Skip to content

Commit 48eeddc

Browse files
committed
accept nested type arguments
Fixes C++98-like limitation, pointed out by @wqferr.
1 parent 93a343f commit 48eeddc

File tree

3 files changed

+67
-34
lines changed

3 files changed

+67
-34
lines changed

spec/parser/parser_spec.lua

Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -67,4 +67,33 @@ describe("parser", function()
6767
assert.same("table_item", result.ast[1].exps[1][1].kind)
6868
assert.same("implicit", result.ast[1].exps[1][1].key_parsed)
6969
end)
70+
71+
it("accepts nested type arguments", function ()
72+
local result = tl.process_string([[
73+
local record List<T>
74+
items: {T}
75+
end
76+
77+
local record Box<T>
78+
item: {T}
79+
end
80+
81+
local list_of_boxes: List<Box<string>> = {
82+
items = {}
83+
}
84+
85+
local box: Box<string> = { item = "hello" }
86+
87+
list_of_boxes.items[1] = box
88+
]])
89+
assert.same({}, result.syntax_errors)
90+
assert.same(5, #result.ast)
91+
assert.same("local_type", result.ast[1].kind)
92+
assert.same("local_type", result.ast[2].kind)
93+
assert.same("local_declaration", result.ast[3].kind)
94+
assert.same("local_declaration", result.ast[4].kind)
95+
assert.same("assignment", result.ast[5].kind)
96+
end)
97+
98+
7099
end)

tl.lua

Lines changed: 15 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,7 @@ local function parse_trying_list(ps, i, list, parse_item)
15341534
return i, list
15351535
end
15361536

1537-
local function parse_typearg_type(ps, i)
1537+
local function parse_typearg(ps, i)
15381538
i = verify_kind(ps, i, "identifier")
15391539
return i, a_type({
15401540
y = ps.tokens[i - 2].y,
@@ -1544,20 +1544,22 @@ local function parse_typearg_type(ps, i)
15441544
})
15451545
end
15461546

1547-
local function parse_typearg_list(ps, i)
1547+
local function parse_anglebracket_list(ps, i, parse_item)
15481548
if ps.tokens[i + 1].tk == ">" then
15491549
return fail(ps, i + 1, "type argument list cannot be empty")
15501550
end
15511551
local typ = new_type(ps, i, "tuple")
1552-
return parse_bracket_list(ps, i, typ, "<", ">", "sep", parse_typearg_type)
1553-
end
1552+
i = verify_tk(ps, i, "<")
1553+
i = parse_list(ps, i, typ, { [">"] = true, [">>"] = true }, "sep", parse_item)
1554+
if ps.tokens[i].tk == ">" then
1555+
i = i + 1
1556+
elseif ps.tokens[i].tk == ">>" then
15541557

1555-
local function parse_typeval_list(ps, i)
1556-
if ps.tokens[i + 1].tk == ">" then
1557-
return fail(ps, i + 1, "type argument list cannot be empty")
1558+
ps.tokens[i].tk = ">"
1559+
else
1560+
return fail(ps, i, "syntax error, expected '>'")
15581561
end
1559-
local typ = new_type(ps, i, "tuple")
1560-
return parse_bracket_list(ps, i, typ, "<", ">", "sep", parse_type)
1562+
return i, typ
15611563
end
15621564

15631565
local function parse_return_types(ps, i)
@@ -1568,7 +1570,7 @@ local function parse_function_type(ps, i)
15681570
local typ = new_type(ps, i, "function")
15691571
i = i + 1
15701572
if ps.tokens[i].tk == "<" then
1571-
i, typ.typeargs = parse_typearg_list(ps, i)
1573+
i, typ.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
15721574
end
15731575
if ps.tokens[i].tk == "(" then
15741576
i, typ.args = parse_argument_type_list(ps, i)
@@ -1621,7 +1623,7 @@ local function parse_base_type(ps, i)
16211623
end
16221624

16231625
if ps.tokens[i].tk == "<" then
1624-
i, typ.typevals = parse_typeval_list(ps, i)
1626+
i, typ.typevals = parse_anglebracket_list(ps, i, parse_type)
16251627
end
16261628
return i, typ
16271629
elseif tk == "{" then
@@ -1752,7 +1754,7 @@ end
17521754
local function parse_function_args_rets_body(ps, i, node)
17531755
local istart = i - 1
17541756
if ps.tokens[i].tk == "<" then
1755-
i, node.typeargs = parse_typearg_list(ps, i)
1757+
i, node.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
17561758
end
17571759
i, node.args = parse_argument_list(ps, i)
17581760
i, node.rets = parse_return_types(ps, i)
@@ -2551,7 +2553,7 @@ parse_record_body = function(ps, i, def, node)
25512553
def.fields = {}
25522554
def.field_order = {}
25532555
if ps.tokens[i].tk == "<" then
2554-
i, def.typeargs = parse_typearg_list(ps, i)
2556+
i, def.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
25552557
end
25562558
while not (ps.tokens[i].kind == "$EOF$" or ps.tokens[i].tk == "end") do
25572559
if ps.tokens[i].tk == "userdata" and ps.tokens[i + 1].tk ~= ":" then

tl.tl

Lines changed: 23 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -1534,7 +1534,25 @@ local function parse_trying_list<T>(ps: ParseState, i: integer, list: {T}, parse
15341534
return i, list
15351535
end
15361536

1537-
local function parse_typearg_type(ps: ParseState, i: integer): integer, Type, integer
1537+
local function parse_anglebracket_list<T>(ps: ParseState, i: integer, parse_item: ParseItem<Type>): integer, {Type}
1538+
if ps.tokens[i+1].tk == ">" then
1539+
return fail(ps, i+1, "type argument list cannot be empty")
1540+
end
1541+
local typ = new_type(ps, i, "tuple")
1542+
i = verify_tk(ps, i, "<")
1543+
i = parse_list(ps, i, typ, { [">"] = true, [">>"] = true, }, "sep", parse_item)
1544+
if ps.tokens[i].tk == ">" then
1545+
i = i + 1
1546+
elseif ps.tokens[i].tk == ">>" then
1547+
-- tokenizer hack: consume one bracket from '>>', don't increment i
1548+
ps.tokens[i].tk = ">"
1549+
else
1550+
return fail(ps, i, "syntax error, expected '>'")
1551+
end
1552+
return i, typ
1553+
end
1554+
1555+
local function parse_typearg(ps: ParseState, i: integer): integer, Type, integer
15381556
i = verify_kind(ps, i, "identifier")
15391557
return i, a_type {
15401558
y = ps.tokens[i - 2].y,
@@ -1544,22 +1562,6 @@ local function parse_typearg_type(ps: ParseState, i: integer): integer, Type, in
15441562
}
15451563
end
15461564

1547-
local function parse_typearg_list(ps: ParseState, i: integer): integer, Type
1548-
if ps.tokens[i+1].tk == ">" then
1549-
return fail(ps, i+1, "type argument list cannot be empty")
1550-
end
1551-
local typ = new_type(ps, i, "tuple")
1552-
return parse_bracket_list(ps, i, typ, "<", ">", "sep", parse_typearg_type)
1553-
end
1554-
1555-
local function parse_typeval_list(ps: ParseState, i: integer): integer, Type
1556-
if ps.tokens[i+1].tk == ">" then
1557-
return fail(ps, i+1, "type argument list cannot be empty")
1558-
end
1559-
local typ = new_type(ps, i, "tuple")
1560-
return parse_bracket_list(ps, i, typ, "<", ">", "sep", parse_type)
1561-
end
1562-
15631565
local function parse_return_types(ps: ParseState, i: integer): integer, Type
15641566
return parse_type_list(ps, i, "rets")
15651567
end
@@ -1568,7 +1570,7 @@ local function parse_function_type(ps: ParseState, i: integer): integer, Type
15681570
local typ = new_type(ps, i, "function")
15691571
i = i + 1
15701572
if ps.tokens[i].tk == "<" then
1571-
i, typ.typeargs = parse_typearg_list(ps, i)
1573+
i, typ.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
15721574
end
15731575
if ps.tokens[i].tk == "(" then
15741576
i, typ.args = parse_argument_type_list(ps, i)
@@ -1621,7 +1623,7 @@ local function parse_base_type(ps: ParseState, i: integer): integer, Type, integ
16211623
end
16221624

16231625
if ps.tokens[i].tk == "<" then
1624-
i, typ.typevals = parse_typeval_list(ps, i)
1626+
i, typ.typevals = parse_anglebracket_list(ps, i, parse_type)
16251627
end
16261628
return i, typ
16271629
elseif tk == "{" then
@@ -1752,7 +1754,7 @@ end
17521754
local function parse_function_args_rets_body(ps: ParseState, i: integer, node: Node): integer, Node
17531755
local istart = i - 1
17541756
if ps.tokens[i].tk == "<" then
1755-
i, node.typeargs = parse_typearg_list(ps, i)
1757+
i, node.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
17561758
end
17571759
i, node.args = parse_argument_list(ps, i)
17581760
i, node.rets = parse_return_types(ps, i)
@@ -2551,7 +2553,7 @@ parse_record_body = function(ps: ParseState, i: integer, def: Type, node: Node):
25512553
def.fields = {}
25522554
def.field_order = {}
25532555
if ps.tokens[i].tk == "<" then
2554-
i, def.typeargs = parse_typearg_list(ps, i)
2556+
i, def.typeargs = parse_anglebracket_list(ps, i, parse_typearg)
25552557
end
25562558
while not (ps.tokens[i].kind == "$EOF$" or ps.tokens[i].tk == "end") do
25572559
if ps.tokens[i].tk == "userdata" and ps.tokens[i+1].tk ~= ":" then

0 commit comments

Comments
 (0)