Skip to content

Commit

Permalink
Merge pull request #199 from rokucommunity/type-enhancements
Browse files Browse the repository at this point in the history
Type enhancements
  • Loading branch information
TwitchBronBron committed Nov 18, 2020
2 parents e920e8f + 914e6ab commit 8d29542
Show file tree
Hide file tree
Showing 75 changed files with 702 additions and 4,241 deletions.
47 changes: 22 additions & 25 deletions src/astUtils/creators.ts
Original file line number Diff line number Diff line change
@@ -1,54 +1,51 @@
import type { Position } from 'vscode-languageserver';
import { Range } from 'vscode-languageserver';
import type { Token } from '../lexer/Token';
import { TokenKind } from '../lexer/TokenKind';
import type { Expression, NamespacedVariableNameExpression } from '../parser/Expression';
import { LiteralExpression, CallExpression, DottedGetExpression, VariableExpression } from '../parser/Expression';
import type { BrsType } from '../brsTypes';
import { BrsString, BrsInvalid, Int32, Float } from '../brsTypes';
import util from '../util';

export function createRange(pos: Position) {
return util.createRange(pos.line, pos.character, pos.line, pos.character);
}
export const interpolatedRange = Range.create(-1, -1, -1, -1);

export function createToken<T extends TokenKind>(kind: T, pos: Position, text?: string, literal?: BrsType): Token & { kind: T } {
export function createToken<T extends TokenKind>(kind: T, text?: string, range = interpolatedRange): Token & { kind: T } {
return {
kind: kind,
text: text || kind.toString(),
isReserved: !text || text === kind.toString(),
range: createRange(pos),
literal: literal,
range: range,
leadingWhitespace: ''
};
}

export function createIdentifier(ident: string, pos: Position, namespaceName?: NamespacedVariableNameExpression): VariableExpression {
return new VariableExpression(createToken(TokenKind.Identifier, pos, ident), namespaceName);
export function createIdentifier(ident: string, range?: Range, namespaceName?: NamespacedVariableNameExpression): VariableExpression {
return new VariableExpression(createToken(TokenKind.Identifier, ident, range), namespaceName);
}
export function createDottedIdentifier(path: string[], pos: Position, namespaceName?: NamespacedVariableNameExpression): DottedGetExpression {
export function createDottedIdentifier(path: string[], range?: Range, namespaceName?: NamespacedVariableNameExpression): DottedGetExpression {
const ident = path.pop();
const obj = path.length > 1 ? createDottedIdentifier(path, pos, namespaceName) : createIdentifier(path[0], pos, namespaceName);
return new DottedGetExpression(obj, createToken(TokenKind.Identifier, pos, ident), createToken(TokenKind.Dot, pos, '.'));
const obj = path.length > 1 ? createDottedIdentifier(path, range, namespaceName) : createIdentifier(path[0], range, namespaceName);
return new DottedGetExpression(obj, createToken(TokenKind.Identifier, ident, range), createToken(TokenKind.Dot, '.', range));
}

export function createStringLiteral(value: string, pos: Position) {
return new LiteralExpression(new BrsString(value), createRange(pos));
export function createStringLiteral(value: string, range?: Range) {
return new LiteralExpression(createToken(TokenKind.StringLiteral, value, range));
}
export function createIntegerLiteral(value: string, range?: Range) {
return new LiteralExpression(createToken(TokenKind.IntegerLiteral, value, range));
}
export function createIntegerLiteral(value: number, pos: Position) {
return new LiteralExpression(new Int32(value), createRange(pos));
export function createFloatLiteral(value: string, range?: Range) {
return new LiteralExpression(createToken(TokenKind.FloatLiteral, value, range));
}
export function createFloatLiteral(value: number, pos: Position) {
return new LiteralExpression(new Float(value), createRange(pos));
export function createInvalidLiteral(value?: string, range?: Range) {
return new LiteralExpression(createToken(TokenKind.Invalid, value, range));
}
export function createInvalidLiteral(pos: Position) {
return new LiteralExpression(new BrsInvalid(), createRange(pos));
export function createBooleanLiteral(value: 'true' | 'false', range?: Range) {
return new LiteralExpression(createToken(value === 'true' ? TokenKind.True : TokenKind.False, value, range));
}

export function createCall(callee: Expression, args?: Expression[], namespaceName?: NamespacedVariableNameExpression) {
return new CallExpression(
callee,
createToken(TokenKind.LeftParen, callee.range.end, '('),
createToken(TokenKind.RightParen, callee.range.end, ')'),
createToken(TokenKind.LeftParen, '('),
createToken(TokenKind.RightParen, ')'),
args || [],
namespaceName
);
Expand Down
38 changes: 15 additions & 23 deletions src/astUtils/reflection.spec.ts
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
/* eslint-disable no-multi-spaces */
import { Position, Range } from 'vscode-languageserver';
import { expect } from 'chai';
import { PrintStatement, Block, Body, AssignmentStatement, CommentStatement, ExitForStatement, ExitWhileStatement, ExpressionStatement, FunctionStatement, IfStatement, IncrementStatement, GotoStatement, LabelStatement, ReturnStatement, EndStatement, StopStatement, ForStatement, ForEachStatement, WhileStatement, DottedSetStatement, IndexedSetStatement, LibraryStatement, NamespaceStatement, ImportStatement, ClassStatement, EmptyStatement } from '../parser/Statement';
import { FunctionExpression, NamespacedVariableNameExpression, BinaryExpression, CallExpression, DottedGetExpression, IndexedGetExpression, GroupingExpression, LiteralExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, XmlAttributeGetExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression } from '../parser/Expression';
import { FunctionExpression, NamespacedVariableNameExpression, BinaryExpression, CallExpression, DottedGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, XmlAttributeGetExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression } from '../parser/Expression';
import type { Token } from '../lexer';
import { TokenKind } from '../lexer';
import { BrsString } from '../brsTypes';
import { isPrintStatement, isIfStatement, isBody, isAssignmentStatement, isBlock, isExpressionStatement, isCommentStatement, isExitForStatement, isExitWhileStatement, isFunctionStatement, isIncrementStatement, isGotoStatement, isLabelStatement, isReturnStatement, isEndStatement, isStopStatement, isForStatement, isForEachStatement, isWhileStatement, isDottedSetStatement, isIndexedSetStatement, isLibraryStatement, isNamespaceStatement, isImportStatement, isExpression, isBinaryExpression, isCallExpression, isFunctionExpression, isNamespacedVariableNameExpression, isDottedGetExpression, isXmlAttributeGetExpression, isIndexedGetExpression, isGroupingExpression, isLiteralExpression, isEscapedCharCodeLiteralExpression, isArrayLiteralExpression, isAALiteralExpression, isUnaryExpression, isVariableExpression, isSourceLiteralExpression, isNewExpression, isCallfuncExpression, isTemplateStringQuasiExpression, isTemplateStringExpression, isTaggedTemplateStringExpression, isBrsFile, isXmlFile, isClassStatement, isStatement, isAnnotationExpression } from './reflection';
import { createRange, createToken, createStringLiteral, createIdentifier } from './creators';
import { createToken, createStringLiteral, createIdentifier, interpolatedRange as range } from './creators';
import { Program } from '../Program';
import { BrsFile } from '../files/BrsFile';
import { XmlFile } from '../files/XmlFile';
Expand All @@ -26,19 +24,17 @@ describe('reflection', () => {
});

describe('Statements', () => {
const pos = Position.create(0, 0);
const range = createRange(pos);
const ident = createToken(TokenKind.Identifier, pos, 'a');
const expr = createStringLiteral('', pos);
const token = createToken(TokenKind.StringLiteral, pos, '');
const ident = createToken(TokenKind.Identifier, 'a', range);
const expr = createStringLiteral('', range);
const token = createToken(TokenKind.StringLiteral, '', range);
const body = new Body([]);
const assignment = new AssignmentStatement(undefined, ident, expr, undefined);
const block = new Block([], range);
const expression = new ExpressionStatement(expr);
const comment = new CommentStatement([token]);
const exitFor = new ExitForStatement({ exitFor: token });
const exitWhile = new ExitWhileStatement({ exitWhile: token });
const funs = new FunctionStatement(ident, new FunctionExpression([], undefined, block, token, token, token, token), undefined);
const funs = new FunctionStatement(ident, new FunctionExpression([], block, token, token, token, token), undefined);
const ifs = new IfStatement({ if: token }, expr, block, []);
const increment = new IncrementStatement(expr, token);
const print = new PrintStatement({ print: token }, []);
Expand All @@ -47,22 +43,21 @@ describe('reflection', () => {
const returns = new ReturnStatement({ return: token });
const ends = new EndStatement({ end: token });
const stop = new StopStatement({ stop: token });
const fors = new ForStatement({ for: token, to: token, endFor: token }, assignment, expr, expr, block);
const fors = new ForStatement(token, assignment, token, expr, block, token, token, expr);
const foreach = new ForEachStatement({ forEach: token, in: token, endFor: token }, token, expr, block);
const whiles = new WhileStatement({ while: token, endWhile: token }, expr, block);
const dottedSet = new DottedSetStatement(expr, ident, expr);
const indexedSet = new IndexedSetStatement(expr, expr, expr, token, token);
const library = new LibraryStatement({ library: token, filePath: token });
const namespace = new NamespaceStatement(token, new NamespacedVariableNameExpression(createIdentifier('a', pos)), body, token);
const namespace = new NamespaceStatement(token, new NamespacedVariableNameExpression(createIdentifier('a', range)), body, token);
const cls = new ClassStatement(token, ident, [], token);
const imports = new ImportStatement(token, token);

it('isStatement', () => {
expect(isStatement(library)).to.be.true;
expect(
isStatement(
new LiteralExpression(new BrsString('test'), Range.create(0, 0, 0, 0)
)
createStringLiteral('test')
)
).to.be.false;
//doesn't fail for undefined
Expand Down Expand Up @@ -172,12 +167,9 @@ describe('reflection', () => {
});

describe('Expressions', () => {
const pos = Position.create(0, 0);
const range = createRange(pos);
const ident = createToken(TokenKind.Identifier, pos, 'a');
const expr = createStringLiteral('', pos);
const token = createToken(TokenKind.StringLiteral, pos, '');
const value = new BrsString('');
const ident = createToken(TokenKind.Identifier, 'a', range);
const expr = createStringLiteral('', range);
const token = createToken(TokenKind.StringLiteral, '', range);
const block = new Block([], range);
const charCode: Token & { charCode: number } = {
kind: TokenKind.EscapedCharCodeLiteral,
Expand All @@ -187,15 +179,15 @@ describe('reflection', () => {
charCode: 0,
leadingWhitespace: ''
};
const nsVar = new NamespacedVariableNameExpression(createIdentifier('a', pos));
const nsVar = new NamespacedVariableNameExpression(createIdentifier('a', range));
const binary = new BinaryExpression(expr, token, expr);
const call = new CallExpression(expr, token, token, [], undefined);
const fun = new FunctionExpression([], undefined, block, token, token, token, token);
const fun = new FunctionExpression([], block, token, token, token, token);
const dottedGet = new DottedGetExpression(expr, ident, token);
const xmlAttrGet = new XmlAttributeGetExpression(expr, ident, token);
const indexedGet = new IndexedGetExpression(expr, expr, token, token);
const grouping = new GroupingExpression({ left: token, right: token }, expr);
const literal = new LiteralExpression(value, range);
const literal = createStringLiteral('test');
const escapedCarCode = new EscapedCharCodeLiteralExpression(charCode);
const arrayLit = new ArrayLiteralExpression([], token, token);
const aaLit = new AALiteralExpression([], token, token);
Expand Down
92 changes: 48 additions & 44 deletions src/astUtils/reflection.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,18 @@
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, Statement } from '../parser/Statement';
import type { LiteralExpression, Expression, BinaryExpression, CallExpression, FunctionExpression, NamespacedVariableNameExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression } from '../parser/Expression';
import type { BrsString, BrsInvalid, BrsBoolean, RoString, RoArray, RoAssociativeArray, RoSGNode, FunctionParameterExpression } from '../brsTypes';
import { ValueKind } from '../brsTypes';
import type { BrsNumber } from '../brsTypes/BrsNumber';
import type { LiteralExpression, Expression, BinaryExpression, CallExpression, FunctionExpression, NamespacedVariableNameExpression, DottedGetExpression, XmlAttributeGetExpression, IndexedGetExpression, GroupingExpression, EscapedCharCodeLiteralExpression, ArrayLiteralExpression, AALiteralExpression, UnaryExpression, VariableExpression, SourceLiteralExpression, NewExpression, CallfuncExpression, TemplateStringQuasiExpression, TemplateStringExpression, TaggedTemplateStringExpression, AnnotationExpression, FunctionParameterExpression } from '../parser/Expression';
import type { BrsFile } from '../files/BrsFile';
import type { XmlFile } from '../files/XmlFile';
import { InternalWalkMode } from './visitors';
import type { FunctionType } from '../types/FunctionType';
import type { BscFile, File } from '../interfaces';
import type { StringType } from '../types/StringType';
import { InvalidType } from '../types/InvalidType';
import { VoidType } from '../types/VoidType';
import { InternalWalkMode } from './visitors';
import { FunctionType } from '../types/FunctionType';
import { StringType } from '../types/StringType';
import { BooleanType } from '../types/BooleanType';
import { IntegerType } from '../types/IntegerType';
import { LongIntegerType } from '../types/LongIntegerType';
import { FloatType } from '../types/FloatType';
import { DoubleType } from '../types/DoubleType';

// File reflection

Expand Down Expand Up @@ -195,56 +199,56 @@ export function isAnnotationExpression(element: Statement | Expression | undefin
return element?.constructor.name === 'AnnotationExpression';
}

// Value/Type reflection

export function isInvalid(value: any): value is BrsInvalid {
return value?.kind === ValueKind.Invalid;
// BscType reflection
export function isStringType(value: any): value is StringType {
return value?.constructor.name === StringType.name;
}
export function isBoolean(value: any): value is BrsBoolean {
return value?.kind === ValueKind.Boolean;
export function isFunctionType(e: any): e is FunctionType {
return e?.constructor.name === FunctionType.name;
}
export function isString(value: any): value is BrsString {
return value?.kind === ValueKind.String;
export function isBooleanType(e: any): e is BooleanType {
return e?.constructor.name === BooleanType.name;
}
export function isNumber(value: any): value is BrsNumber {
return value && (
value.kind === ValueKind.Int32 ||
value.kind === ValueKind.Int64 ||
value.kind === ValueKind.Float ||
value.kind === ValueKind.Double
);
export function isIntegerType(e: any): e is IntegerType {
return e?.constructor.name === IntegerType.name;
}
export function isStringType(value: any): value is StringType {
return value?.constructor.name === 'StringType';
export function isLongIntegerType(e: any): e is LongIntegerType {
return e?.constructor.name === LongIntegerType.name;
}

export function isFunctionType(e: any): e is FunctionType {
return e?.constructor.name === 'FunctionType';
export function isFloatType(e: any): e is FloatType {
return e?.constructor.name === FloatType.name;
}
export function isRoArray(e: any): e is RoArray {
return e?.constructor.name === 'RoArray';
export function isDoubleType(e: any): e is DoubleType {
return e?.constructor.name === DoubleType.name;
}
export function isRoAssociativeArray(e: any): e is RoAssociativeArray {
return e?.constructor.name === 'RoAssociativeArray';
export function isInvalidType(e: any): e is InvalidType {
return e?.constructor.name === InvalidType.name;
}
export function isRoSGNode(e: any): e is RoSGNode {
return e?.constructor.name === 'RoSGNode';
export function isVoidType(e: any): e is VoidType {
return e?.constructor.name === VoidType.name;
}

const numberConstructorNames = [
IntegerType.name,
LongIntegerType.name,
FloatType.name,
DoubleType.name
];
export function isNumberType(e: any): e is IntegerType | LongIntegerType | FloatType | DoubleType {
return numberConstructorNames.includes(e?.constructor.name);
}

// Literal reflection

export function isLiteralInvalid(e: any): e is LiteralExpression & { value: BrsInvalid } {
return isLiteralExpression(e) && isInvalid(e.value);
}
export function isLiteralBoolean(e: any): e is LiteralExpression & { value: BrsBoolean } {
return isLiteralExpression(e) && isBoolean(e.value);
export function isLiteralInvalid(e: any): e is LiteralExpression & { type: InvalidType } {
return isLiteralExpression(e) && isLiteralInvalid(e.type);
}
export function isLiteralString(e: any): e is LiteralExpression & { value: BrsString } {
return isLiteralExpression(e) && isString(e.value);
export function isLiteralBoolean(e: any): e is LiteralExpression & { type: BooleanType } {
return isLiteralExpression(e) && isBooleanType(e.type);
}
export function isLiteralNumber(e: any): e is LiteralExpression & { value: BrsNumber } {
return isLiteralExpression(e) && isNumber(e.value);
export function isLiteralString(e: any): e is LiteralExpression & { type: StringType } {
return isLiteralExpression(e) && isStringType(e.type);
}
export function isRoString(s: any): s is RoString {
return s.constructor.name === 'RoString';
export function isLiteralNumber(e: any): e is LiteralExpression & { type: IntegerType | LongIntegerType | FloatType | DoubleType } {
return isLiteralExpression(e) && isNumberType(e.type);
}
Loading

0 comments on commit 8d29542

Please sign in to comment.