Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Backport v1 syntax changes #1034

Merged
merged 6 commits into from
Jan 30, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
9 changes: 7 additions & 2 deletions src/Scope.ts
Original file line number Diff line number Diff line change
Expand Up @@ -878,7 +878,9 @@ export class Scope {
// check if this custom type is in our class map
const returnTypeName = func.returnType.name;
const currentNamespaceName = func.findAncestor<NamespaceStatement>(isNamespaceStatement)?.getName(ParseMode.BrighterScript);
if (!this.hasClass(returnTypeName, currentNamespaceName) && !this.hasInterface(returnTypeName) && !this.hasEnum(returnTypeName)) {
// check for built in types
const isBuiltInType = util.isBuiltInType(returnTypeName);
if (!isBuiltInType && !this.hasClass(returnTypeName, currentNamespaceName) && !this.hasInterface(returnTypeName) && !this.hasEnum(returnTypeName)) {
this.diagnostics.push({
...DiagnosticMessages.invalidFunctionReturnType(returnTypeName),
range: func.returnTypeToken.range,
Expand All @@ -891,7 +893,10 @@ export class Scope {
if (isCustomType(param.type) && param.typeToken) {
const paramTypeName = param.type.name;
const currentNamespaceName = func.findAncestor<NamespaceStatement>(isNamespaceStatement)?.getName(ParseMode.BrighterScript);
if (!this.hasClass(paramTypeName, currentNamespaceName) && !this.hasInterface(paramTypeName) && !this.hasEnum(paramTypeName)) {
// check for built in types
const isBuiltInType = util.isBuiltInType(paramTypeName);

if (!isBuiltInType && !this.hasClass(paramTypeName, currentNamespaceName) && !this.hasInterface(paramTypeName) && !this.hasEnum(paramTypeName)) {
this.diagnostics.push({
...DiagnosticMessages.functionParameterTypeIsInvalid(param.name.text, paramTypeName),
range: param.typeToken.range,
Expand Down
5 changes: 4 additions & 1 deletion src/astUtils/reflection.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import type { Body, AssignmentStatement, Block, ExpressionStatement, CommentStatement, ExitForStatement, ExitWhileStatement, FunctionStatement, IfStatement, IncrementStatement, PrintStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassFieldStatement, ClassMethodStatement, ClassStatement, InterfaceFieldStatement, InterfaceMethodStatement, InterfaceStatement, EnumStatement, EnumMemberStatement, TryCatchStatement, CatchStatement, ThrowStatement, MethodStatement, FieldStatement, ConstStatement, ContinueStatement } from '../parser/Statement';
import type { LiteralExpression, BinaryExpression, CallExpression, FunctionExpression, NamespacedVariableNameExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression, FunctionParameterExpression, AAMemberExpression } from '../parser/Expression';
import type { LiteralExpression, BinaryExpression, CallExpression, FunctionExpression, NamespacedVariableNameExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression, FunctionParameterExpression, AAMemberExpression, TypeCastExpression } from '../parser/Expression';
import type { BrsFile } from '../files/BrsFile';
import type { XmlFile } from '../files/XmlFile';
import type { BscFile, File, TypedefProvider } from '../interfaces';
Expand Down Expand Up @@ -260,6 +260,9 @@ export function isAnnotationExpression(element: AstNode | undefined): element is
export function isTypedefProvider(element: any): element is TypedefProvider {
return 'getTypedef' in element;
}
export function isTypeCastExpression(element: any): element is TypeCastExpression {
return element?.constructor.name === 'TypeCastExpression';
}

// BscType reflection
export function isStringType(value: any): value is StringType {
Expand Down
3 changes: 2 additions & 1 deletion src/bscPlugin/validation/BrsFileValidator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ import type { LiteralExpression } from '../../parser/Expression';
import { ParseMode } from '../../parser/Parser';
import type { ContinueStatement, EnumMemberStatement, EnumStatement, ForEachStatement, ForStatement, ImportStatement, LibraryStatement, WhileStatement } from '../../parser/Statement';
import { DynamicType } from '../../types/DynamicType';
import { InterfaceType } from '../../types/InterfaceType';
import util from '../../util';
import type { Range } from 'vscode-languageserver';

Expand Down Expand Up @@ -120,10 +121,10 @@ export class BrsFileValidator {
},
InterfaceStatement: (node) => {
this.validateDeclarationLocations(node, 'interface', () => util.createBoundingRange(node.tokens.interface, node.tokens.name));
node.parent?.getSymbolTable()?.addSymbol(node.tokens.name.text, node.tokens.name.range, new InterfaceType(new Map()));
},
ConstStatement: (node) => {
this.validateDeclarationLocations(node, 'const', () => util.createBoundingRange(node.tokens.const, node.tokens.name));

node.parent.getSymbolTable().addSymbol(node.tokens.name.text, node.tokens.name.range, DynamicType.instance);
},
CatchStatement: (node) => {
Expand Down
218 changes: 218 additions & 0 deletions src/files/BrsFile.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3636,4 +3636,222 @@ describe('BrsFile', () => {
range: util.createRange(4, 12, 4, 24)
}]);
});


describe('backporting v1 syntax changes', () => {

it('transpiles typed arrays to dynamic', () => {
testTranspile(`
sub main(param1 as string[], param2 as SomeType[])
end sub
`, `
sub main(param1 as dynamic, param2 as dynamic)
end sub
`);
});

it('transpiles typed arrays in return types to dynamic', () => {
testTranspile(`
function main() as integer[]
return []
end function
`, `
function main() as dynamic
return []
end function
`);
});

it('transpiles typed arrays in return types to dynamic', () => {
testTranspile(`
function main() as integer[]
return []
end function
`, `
function main() as dynamic
return []
end function
`);
});

it('transpiles multi-dimension typed arrays to dynamic', () => {
testTranspile(`
sub main(param1 as float[][][])
end sub
`, `
sub main(param1 as dynamic)
end sub
`);
});

it('removes typecasts in transpiled code', () => {
testTranspile(`
sub main(myNode, myString)
print (myNode as roSGNode).id
print (myNode as roSGNode).getParent().id
myNode2 = myNode as roSgNode
print (myString as string).len()
print (myString as string).right(3)
myString2 = myString as string
end sub
`, `
sub main(myNode, myString)
print myNode.id
print myNode.getParent().id
myNode2 = myNode
print myString.len()
print myString.right(3)
myString2 = myString
end sub
`);
});

it('allows and removes multiple typecasts in transpiled code', () => {
testTranspile(`
sub main(myNode)
print ((myNode as roSGNode as roSGNodeLabel).text as string as ifStringOps).len()
end sub
`, `
sub main(myNode)
print myNode.text.len()
end sub
`);
});

it('allows built in objects as type names', () => {
testTranspile(`
sub main(x as roSGNode, y as roSGNodeEvent, z as ifArray)
end sub
`, `
sub main(x as object, y as object, z as object)
end sub
`);
});

it('allows component names as types names', () => {
testTranspile(`
sub main(x as roSGNodeGroup, y as roSGNodeRowList, z as roSGNodeCustomComponent)
end sub
`, `
sub main(x as object, y as object, z as object)
end sub
`);
});

it('allows union types for primitives', () => {
testTranspile(`
sub main(x as string or float, y as object or float or string)
end sub
`, `
sub main(x as dynamic, y as dynamic)
end sub
`);
});

it('allows union types for classes, interfaces', () => {
testTranspile(`
interface IFaceA
name as string
data as integer
end interface

interface IFaceB
name as string
value as float
end interface

sub main(x as IFaceA or IFaceB)
end sub
`, `
sub main(x as dynamic)
end sub
`);
});

it('allows union types for classes, interfaces', () => {
testTranspile(`
namespace alpha.beta
interface IFaceA
name as string
data as integer
end interface

interface IFaceB
name as string
value as float
end interface
end namespace

sub main(x as alpha.beta.IFaceA or alpha.beta.IFaceB)
end sub
`, `
sub main(x as dynamic)
end sub
`);
});

it('allows union types of arrays', () => {
testTranspile(`
namespace alpha.beta
interface IFaceA
name as string
data as integer
end interface

interface IFaceB
name as string
value as float
end interface
end namespace

sub main(x as alpha.beta.IFaceA[][] or alpha.beta.IFaceB[] or ifStringOps)
end sub
`, `
sub main(x as dynamic)
end sub
`);
});

it('allows built-in types for return values', () => {
testTranspile(`
function makeLabel(text as string) as roSGNodeLabel
label = createObject("roSGNode", "Label")
label.text = text
end function
`, `
function makeLabel(text as string) as object
label = createObject("roSGNode", "Label")
label.text = text
end function
`);
});

it('allows extends on interfaces', () => {
testTranspile(`
interface MyBase
url as string
end interface

interface MyExtends extends MyBase
method as string
end interface
`, `
`);
});

it('allows extends on classes', () => {
program.setFile<BrsFile>('source/main.bs', `
class MyBase
url as string
end class

class MyExtends extends MyBase
method as string
end class
`);
program.validate();
expectZeroDiagnostics(program);
});

});
});
32 changes: 31 additions & 1 deletion src/parser/Expression.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import * as fileUrl from 'file-url';
import type { WalkOptions, WalkVisitor } from '../astUtils/visitors';
import { createVisitor, WalkMode } from '../astUtils/visitors';
import { walk, InternalWalkMode, walkArray } from '../astUtils/visitors';
import { isAALiteralExpression, isArrayLiteralExpression, isCallExpression, isCallfuncExpression, isCommentStatement, isDottedGetExpression, isEscapedCharCodeLiteralExpression, isFunctionExpression, isFunctionStatement, isIntegerType, isLiteralBoolean, isLiteralExpression, isLiteralNumber, isLiteralString, isLongIntegerType, isMethodStatement, isNamespaceStatement, isStringType, isUnaryExpression, isVariableExpression } from '../astUtils/reflection';
import { isAALiteralExpression, isArrayLiteralExpression, isCallExpression, isCallfuncExpression, isCommentStatement, isDottedGetExpression, isEscapedCharCodeLiteralExpression, isFunctionExpression, isFunctionStatement, isIntegerType, isLiteralBoolean, isLiteralExpression, isLiteralNumber, isLiteralString, isLongIntegerType, isMethodStatement, isNamespaceStatement, isStringType, isTypeCastExpression, isUnaryExpression, isVariableExpression } from '../astUtils/reflection';
import type { TranspileResult, TypedefProvider } from '../interfaces';
import { VoidType } from '../types/VoidType';
import { DynamicType } from '../types/DynamicType';
Expand Down Expand Up @@ -554,6 +554,9 @@ export class GroupingExpression extends Expression {
public readonly range: Range;

transpile(state: BrsTranspileState) {
if (isTypeCastExpression(this.expression)) {
return this.expression.transpile(state);
}
return [
state.transpileToken(this.tokens.left),
...this.expression.transpile(state),
Expand Down Expand Up @@ -1543,6 +1546,33 @@ export class RegexLiteralExpression extends Expression {
}
}


export class TypeCastExpression extends Expression {
constructor(
public obj: Expression,
public asToken: Token,
public typeToken: Token
) {
super();
this.range = util.createBoundingRange(
this.obj,
this.asToken,
this.typeToken
);
}

public range: Range;

public transpile(state: BrsTranspileState): TranspileResult {
return this.obj.transpile(state);
}
public walk(visitor: WalkVisitor, options: WalkOptions) {
if (options.walkMode & InternalWalkMode.walkExpressions) {
walk(this, 'obj', visitor, options);
}
}
}

// eslint-disable-next-line @typescript-eslint/consistent-indexed-object-style
type ExpressionValue = string | number | boolean | Expression | ExpressionValue[] | { [key: string]: ExpressionValue };

Expand Down
Loading
Loading