Skip to content

Commit e002c61

Browse files
authored
Backport V1 Typed function type syntax to v0 (#1623)
1 parent 88514ca commit e002c61

3 files changed

Lines changed: 144 additions & 1 deletion

File tree

src/files/BrsFile.spec.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4583,6 +4583,20 @@ describe('BrsFile', () => {
45834583
`);
45844584
});
45854585
});
4586+
4587+
describe('typed functions in type expressions', () => {
4588+
it('transpiles to function', () => {
4589+
testTranspile(`
4590+
function test(func as function(name as string, num as integer) as integer) as integer
4591+
return func("hello", 123)
4592+
end function
4593+
`, `
4594+
function test(func as Function) as integer
4595+
return func("hello", 123)
4596+
end function
4597+
`);
4598+
});
4599+
});
45864600
});
45874601

45884602
it('allows up to 63 function params', () => {

src/parser/Parser.spec.ts

Lines changed: 102 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1785,6 +1785,108 @@ describe('parser', () => {
17851785
expectZeroDiagnostics(diagnostics);
17861786
});
17871787
});
1788+
1789+
describe('typed functions as types', () => {
1790+
it('disallowed in brightscript mode', () => {
1791+
let { diagnostics } = parse(`
1792+
function test(func as function())
1793+
return func()
1794+
end function
1795+
`, ParseMode.BrightScript);
1796+
expectDiagnosticsIncludes(diagnostics, [
1797+
DiagnosticMessages.unexpectedToken(')')
1798+
]);
1799+
});
1800+
1801+
it('can be passed as param types', () => {
1802+
let { diagnostics } = parse(`
1803+
function test(func as function())
1804+
return func()
1805+
end function
1806+
`, ParseMode.BrighterScript);
1807+
expectZeroDiagnostics(diagnostics);
1808+
});
1809+
1810+
it('can have a return type', () => {
1811+
let { diagnostics } = parse(`
1812+
function test(func as sub() as integer) as integer
1813+
return func()
1814+
end function
1815+
`, ParseMode.BrighterScript);
1816+
expectZeroDiagnostics(diagnostics);
1817+
});
1818+
1819+
it('can use sub or function', () => {
1820+
let { diagnostics } = parse(`
1821+
function test(func as sub() as integer) as integer
1822+
return func()
1823+
end function
1824+
1825+
function test2(func as function() as integer) as integer
1826+
return func()
1827+
end function
1828+
`, ParseMode.BrighterScript);
1829+
expectZeroDiagnostics(diagnostics);
1830+
});
1831+
1832+
it('can have primitive parameters', () => {
1833+
let { diagnostics } = parse(`
1834+
function test(func as function(name as string, num as integer) as integer) as integer
1835+
return func("hello", 123)
1836+
end function
1837+
`, ParseMode.BrighterScript);
1838+
expectZeroDiagnostics(diagnostics);
1839+
});
1840+
1841+
it('can have complex parameters', () => {
1842+
let { diagnostics } = parse(`
1843+
interface IFace
1844+
name as string
1845+
end interface
1846+
1847+
function test(func as function(thing as IFace) as integer) as integer
1848+
return func({name: "hello"})
1849+
end function
1850+
`, ParseMode.BrighterScript);
1851+
expectZeroDiagnostics(diagnostics);
1852+
});
1853+
1854+
it('can have compound parameters', () => {
1855+
let { diagnostics } = parse(`
1856+
interface IFace
1857+
name as string
1858+
end interface
1859+
1860+
function test(func as function(arg1 as string or integer, arg2 as IFace) as integer) as integer
1861+
return func("hello", {name: "hello"})
1862+
end function
1863+
`, ParseMode.BrighterScript);
1864+
expectZeroDiagnostics(diagnostics);
1865+
});
1866+
1867+
it('can be used as return types', () => {
1868+
let { diagnostics } = parse(`
1869+
function test() as function() as integer
1870+
return function() as integer
1871+
return 123
1872+
end function
1873+
end function
1874+
`, ParseMode.BrighterScript);
1875+
expectZeroDiagnostics(diagnostics);
1876+
});
1877+
1878+
it('can have a union as return type', () => {
1879+
let { diagnostics } = parse(`
1880+
type foo = function() as integer or string
1881+
function test() as foo
1882+
return function() as integer
1883+
return 123
1884+
end function
1885+
end function
1886+
`, ParseMode.BrighterScript);
1887+
expectZeroDiagnostics(diagnostics);
1888+
});
1889+
});
17881890
});
17891891

17901892
function parse(text: string, mode?: ParseMode) {

src/parser/Parser.ts

Lines changed: 28 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2813,7 +2813,9 @@ export class Parser {
28132813
while (lookForCompounds) {
28142814
lookForCompounds = false;
28152815

2816-
if (this.checkAny(...DeclarableTypes)) {
2816+
const isTypedFunction = this.checkAny(TokenKind.Function, TokenKind.Sub) && this.checkNext(TokenKind.LeftParen);
2817+
2818+
if (this.checkAny(...DeclarableTypes) && !isTypedFunction) {
28172819
// Token is a built in type
28182820
typeToken = this.advance();
28192821
} else if (this.options.mode === ParseMode.BrighterScript) {
@@ -2824,6 +2826,9 @@ export class Parser {
28242826
} else if (this.check(TokenKind.LeftParen)) {
28252827
// could be an inline interface
28262828
typeToken = this.groupedTypeExpression();
2829+
} else if (isTypedFunction) {
2830+
//typed function type
2831+
typeToken = this.typedFunctionType();
28272832
} else {
28282833
// see if we can get a namespaced identifer
28292834
const qualifiedType = this.getNamespacedVariableNameExpression(ignoreDiagnostics);
@@ -2941,6 +2946,28 @@ export class Parser {
29412946
return createToken(TokenKind.Dynamic, null, util.createBoundingRange(leftParen, typeToken, rightParen));
29422947
}
29432948

2949+
private typedFunctionType() {
2950+
const funcOrSub = this.advance();
2951+
const leftParen = this.advance();
2952+
2953+
let params = [] as FunctionParameterExpression[];
2954+
if (!this.check(TokenKind.RightParen)) {
2955+
do {
2956+
params.push(this.functionParameter());
2957+
} while (this.match(TokenKind.Comma));
2958+
}
2959+
const rightParen = this.advance();
2960+
let asToken: Token;
2961+
let returnType: Token;
2962+
if ((this.check(TokenKind.As))) {
2963+
// this is a function type with a return type, e.g. `function(string) as void`
2964+
asToken = this.advance();
2965+
returnType = this.typeToken();
2966+
}
2967+
2968+
return createToken(TokenKind.Function, null, util.createBoundingRange(funcOrSub, leftParen, rightParen, asToken, returnType));
2969+
}
2970+
29442971
private primary(): Expression {
29452972
switch (true) {
29462973
case this.matchAny(

0 commit comments

Comments
 (0)