From 5e528db965c874d1687c6b79c45537acbf8c3667 Mon Sep 17 00:00:00 2001 From: Bronley Plumb Date: Thu, 15 Oct 2026 15:17:30 -0400 Subject: [PATCH] Rename Locatable props to startPosition, endPosition and sourceInfo `source` collided with other fields that mean something else (LSP `Diagnostic.source`, plugin RawCodeStatement `source` strings), which broke the `'source' in x` checks, and `pos` only named the start. `SourceInfo.lineStarts` is now `linePositions`. Done with the TypeScript language service rename, plus the string checks, untyped test literals, comments and docs it can't see. Co-Authored-By: Claude Opus 5.5 --- docs/plugins.md | 6 +- src/astUtils/creators.spec.ts | 4 +- src/astUtils/creators.ts | 12 +- src/astUtils/reflection.spec.ts | 6 +- src/bscPlugin/validation/BrsFileValidator.ts | 2 +- src/files/BrsFile.ts | 10 +- src/lexer/Lexer.ts | 12 +- src/lexer/Token.ts | 18 +- src/lexer/TokenObject.ts | 6 +- src/parser/AstNode.spec.ts | 2 +- src/parser/AstNode.ts | 18 +- src/parser/Expression.ts | 216 ++++++------- src/parser/Parser.ts | 16 +- src/parser/Statement.ts | 308 +++++++++---------- src/parser/TranspileState.spec.ts | 2 +- src/parser/TranspileState.ts | 18 +- src/parser/tests/Parser.spec.ts | 6 +- src/testHelpers.spec.ts | 4 +- src/util.spec.ts | 26 +- src/util.ts | 120 ++++---- 20 files changed, 408 insertions(+), 404 deletions(-) diff --git a/docs/plugins.md b/docs/plugins.md index 02c53d96d..6beefba8e 100644 --- a/docs/plugins.md +++ b/docs/plugins.md @@ -582,9 +582,9 @@ export default function () { ``` ## Locations -To keep memory down, tokens and AST nodes don't store a `Location`. Instead, each one stores where it starts and ends, counted from the start of the file (`pos` and `end`), plus a reference to info about the file it was parsed from (`source`), which is shared by everything from the same parse. +To keep memory down, tokens and AST nodes don't store a `Location`. Instead, each one stores where it starts and ends, counted from the start of the file (`startPosition` and `endPosition`), plus a reference to info about the file it was parsed from (`sourceInfo`), which is shared by everything from the same parse. `sourceInfo.linePositions` holds where each line of the file starts, which is how positions get converted to lines and characters. -Positions are the gaps between characters, like an editor cursor (and like an LSP `Position`). For a file that starts with `print`, the `print` token's `pos` is `0` (before the `p`) and its `end` is `5` (after the `t`), so `end - pos` is its length. +Positions are the gaps between characters, like an editor cursor (and like an LSP `Position`). For a file that starts with `print`, the `print` token's `startPosition` is `0` (before the `p`) and its `endPosition` is `5` (after the `t`), so `endPosition - startPosition` is its length. Build the line/character `Location` on demand with `util.getLocation()`: @@ -595,7 +595,7 @@ const location = util.getLocation(funcStmt.tokens.name); // { uri, range } `util.getLocation()` returns `undefined` for synthetic tokens and nodes (ones that were created in code rather than parsed from a file), since they have no position. A synthetic node won't get a source map entry, and diagnostics can't point at it. A position describes where the code came from in the original source, so it's used as-is for source maps and diagnostics. Don't change the position of a parsed token or node. Only give one to a synthetic token or node you're creating: -- **Borrow it from a real token or node in the same file** (preferred): `util.setBounds(node, someToken)`. This shares the file's real `source`, so the node combines correctly with the rest of that file's AST. The `create*` functions do the same when given a token or node: `createToken(TokenKind.Identifier, 'name', someToken)`. +- **Borrow it from a real token or node in the same file** (preferred): `util.setBounds(node, someToken)`. This shares the file's real `sourceInfo`, so the node combines correctly with the rest of that file's AST. The `create*` functions do the same when given a token or node: `createToken(TokenKind.Identifier, 'name', someToken)`. - **From a `Location` in a file**: `util.setLocation(node, location, file.sourceInfo)`. This maps the location into that file, so the node combines with the rest of its AST. If the location is in a different file (or its lines aren't in the file), the node is left without a position. To pass one to a `create*` function, convert it first: `createToken(TokenKind.Identifier, 'name', util.setLocation({} as Locatable, location, file.sourceInfo))`. Code parsed from a snippet (i.e. `Parser.parse('...')` without a `srcPath`) has positions, but no uri. Those positions aren't in the file the code is added to, so lookups by position in that file (hover, definition, etc.) skip it, it isn't included in the bounds of nodes that also contain code from a file, and it doesn't get source map entries. diff --git a/src/astUtils/creators.spec.ts b/src/astUtils/creators.spec.ts index a01570996..a9ffb3296 100644 --- a/src/astUtils/creators.spec.ts +++ b/src/astUtils/creators.spec.ts @@ -27,12 +27,12 @@ describe('creators', () => { it('copies the bounds of a locatable', () => { const original = Lexer.scan('\n abc').tokens[1]; const token = createToken(TokenKind.Identifier, 'xyz', original); - expect(token.source).to.equal(original.source); + expect(token.sourceInfo).to.equal(original.sourceInfo); expect(util.getLocation(token)).to.eql(util.getLocation(original)); }); it('accepts a location from util.setLocation', () => { - const sourceInfo = Lexer.scan('\n\n abc', { srcPath: 'source/main.brs' }).tokens[0].source; + const sourceInfo = Lexer.scan('\n\n abc', { srcPath: 'source/main.brs' }).tokens[0].sourceInfo; const location = util.createLocation(2, 4, 2, 7, sourceInfo.uri); const locatable = util.setLocation({} as Locatable, location, sourceInfo); expect(util.getLocation(createToken(TokenKind.Identifier, 'xyz', locatable))).to.eql(location); diff --git a/src/astUtils/creators.ts b/src/astUtils/creators.ts index 44cf26942..ccd516e26 100644 --- a/src/astUtils/creators.ts +++ b/src/astUtils/creators.ts @@ -83,9 +83,9 @@ export function createToken(kind: T, text?: string, locatab kind, text ?? (tokenDefaults[kind as string] as string) ?? kind.toString().toLowerCase(), !text || text === kind.toString(), - locatable?.pos, - locatable?.end, - locatable?.source, + locatable?.startPosition, + locatable?.endPosition, + locatable?.sourceInfo, [] ) as Token as Token & { kind: T }; } @@ -95,9 +95,9 @@ export function createIdentifier(name: string, locatable?: Locatable): Identifie TokenKind.Identifier, name, false, - locatable?.pos, - locatable?.end, - locatable?.source, + locatable?.startPosition, + locatable?.endPosition, + locatable?.sourceInfo, [] ) as Identifier; } diff --git a/src/astUtils/reflection.spec.ts b/src/astUtils/reflection.spec.ts index af1569618..345c62177 100644 --- a/src/astUtils/reflection.spec.ts +++ b/src/astUtils/reflection.spec.ts @@ -190,9 +190,9 @@ describe('reflection', () => { kind: TokenKind.EscapedCharCodeLiteral, text: '0', isReserved: false, - pos: undefined, - end: undefined, - source: undefined, + startPosition: undefined, + endPosition: undefined, + sourceInfo: undefined, charCode: 0, leadingTrivia: [] }; diff --git a/src/bscPlugin/validation/BrsFileValidator.ts b/src/bscPlugin/validation/BrsFileValidator.ts index 13d4a16f7..d059d05b5 100644 --- a/src/bscPlugin/validation/BrsFileValidator.ts +++ b/src/bscPlugin/validation/BrsFileValidator.ts @@ -582,7 +582,7 @@ export class BrsFileValidator { const addVariable = (nameToken: Token | undefined, fallback: Locatable) => { const name = nameToken?.text?.toLowerCase(); if (name && !variableLocatables.has(name)) { - variableLocatables.set(name, nameToken.source ? nameToken : fallback); + variableLocatables.set(name, nameToken.sourceInfo ? nameToken : fallback); } }; // count all the parameters as they count as variables diff --git a/src/files/BrsFile.ts b/src/files/BrsFile.ts index 9af1affb9..ac029461f 100644 --- a/src/files/BrsFile.ts +++ b/src/files/BrsFile.ts @@ -305,14 +305,14 @@ export class BrsFile implements BscFile { public get sourceInfo(): SourceInfo | undefined { const tokens = this.parser?.tokens; //every token from the same parse shares the same source, and the last token (`Eof`) always exists - return tokens?.[tokens.length - 1]?.source; + return tokens?.[tokens.length - 1]?.sourceInfo; } /** * Does the token contain the given offset (inclusive on both ends, excluding any trailing newline) */ private tokenContainsOffset(token: Token, offset: number) { - return token.pos <= offset && offset <= util.getContentEnd(token); + return token.startPosition <= offset && offset <= util.getContentEnd(token); } /** @@ -339,7 +339,7 @@ export class BrsFile implements BscFile { return undefined; } for (let token of this.parser.tokens) { - if (offset < token.pos) { + if (offset < token.startPosition) { return token; } } @@ -362,7 +362,7 @@ export class BrsFile implements BscFile { this.ast.walk((node) => { const latestContainer = containingNode; //bsc walks depth-first - if (node.source === source && node.pos <= offset && offset <= node.end) { + if (node.sourceInfo === source && node.startPosition <= offset && offset <= node.endPosition) { containingNode = node; } //we had a match before, and don't now. this means we've finished walking down the whole way, and found our match @@ -897,7 +897,7 @@ export class BrsFile implements BscFile { return token; } //if the position less than this token range, then this position touches no token, - if (offset < token.pos) { + if (offset < token.startPosition) { let t = tokens[i - 1]; //return the token or the first token return t ? t : tokens[0]; diff --git a/src/lexer/Lexer.ts b/src/lexer/Lexer.ts index 0da580cba..0a64cb938 100644 --- a/src/lexer/Lexer.ts +++ b/src/lexer/Lexer.ts @@ -83,7 +83,7 @@ export class Lexer { this.tokens = []; this.diagnostics = []; this.sourceInfo = this.options.trackLocations - ? { uri: util.pathToUri(options?.srcPath), lineStarts: [0] } + ? { uri: util.pathToUri(options?.srcPath), linePositions: [0] } : undefined; while (!this.isAtEnd()) { this.scanToken(); @@ -408,7 +408,7 @@ export class Lexer { this.addToken(TokenKind.Newline); this.start = this.current; - this.sourceInfo?.lineStarts.push(this.current); + this.sourceInfo?.linePositions.push(this.current); } /** @@ -524,7 +524,7 @@ export class Lexer { token.charCode = 10; //track the start of the next line - this.sourceInfo?.lineStarts.push(this.current); + this.sourceInfo?.linePositions.push(this.current); continue; } else if (this.check('\r') && this.peekNext() === '\n') { this.templateQuasiString(); @@ -538,7 +538,7 @@ export class Lexer { token.charCode = 10; //track the start of the next line - this.sourceInfo?.lineStarts.push(this.current); + this.sourceInfo?.linePositions.push(this.current); continue; //a lone `\r` is a newline too (the same as everywhere else in the lexer, and in LSP) @@ -550,7 +550,7 @@ export class Lexer { token.charCode = 13; //track the start of the next line - this.sourceInfo?.lineStarts.push(this.current); + this.sourceInfo?.linePositions.push(this.current); continue; //escaped chars @@ -1197,7 +1197,7 @@ export class Lexer { * @returns the location of `text` */ private locationOf(): Location { - return util.getLocation({ pos: this.start, end: this.current, source: this.sourceInfo }); + return util.getLocation({ startPosition: this.start, endPosition: this.current, sourceInfo: this.sourceInfo }); } } diff --git a/src/lexer/Token.ts b/src/lexer/Token.ts index 783ee3bec..def76d04e 100644 --- a/src/lexer/Token.ts +++ b/src/lexer/Token.ts @@ -30,18 +30,19 @@ export interface Locatable { /** * Where this item starts, counted from the start of the source. * - * Positions are the gaps between characters, like an editor cursor. For the source `print`, `pos` is `0` (before the `p`) and `end` is `5` (after the `t`). + * Positions are the gaps between characters, like an editor cursor. For the source `print`, `startPosition` is `0` (before the `p`) and `endPosition` is `5` (after the `t`). * Counted in UTF-16 code units, the same as an LSP `Position.character` */ - pos: number; + startPosition: number; /** - * Where this item ends: the gap just after its last character (see `pos`). `end - pos` is the item's length + * Where this item ends: the gap just after its last character, counted from the start of the source (see `startPosition`). + * `endPosition - startPosition` is the item's length */ - end: number; + endPosition: number; /** * Info about the source this item was parsed from. Shared by every item from the same parse. `undefined` for synthetic items */ - source: SourceInfo | undefined; + sourceInfo: SourceInfo | undefined; } /** @@ -53,9 +54,12 @@ export interface SourceInfo { */ uri: string; /** - * Where each line starts (the gap before its first character), counted the same way as `Locatable.pos` + * Where each line starts: the gap before its first character, counted from the start of the source in UTF-16 code units + * (the same as `Locatable.startPosition`). `linePositions[0]` is always `0`, and the index of each entry is its 0-based line number. + * + * For the source `a = 1\nb = 2`, this is `[0, 6]`. `util.getLocation()` uses it to convert a position into a line/character `Position` */ - lineStarts: number[]; + linePositions: number[]; } /** diff --git a/src/lexer/TokenObject.ts b/src/lexer/TokenObject.ts index e66b7a7eb..13b4757c1 100644 --- a/src/lexer/TokenObject.ts +++ b/src/lexer/TokenObject.ts @@ -12,9 +12,9 @@ export class TokenObject implements Token { public kind: TokenKind, public text: string, public isReserved: boolean, - public pos: number, - public end: number, - public source: SourceInfo | undefined, + public startPosition: number, + public endPosition: number, + public sourceInfo: SourceInfo | undefined, public leadingTrivia: Token[] | undefined ) { } diff --git a/src/parser/AstNode.spec.ts b/src/parser/AstNode.spec.ts index 3ee923cdc..c45f07144 100644 --- a/src/parser/AstNode.spec.ts +++ b/src/parser/AstNode.spec.ts @@ -510,7 +510,7 @@ describe('AstNode', () => { //skip these properties if ( - ['parent', 'symbolTable', 'pos', 'end', 'source'].includes(key) || + ['parent', 'symbolTable', 'startPosition', 'endPosition', 'sourceInfo'].includes(key) || //this is a circular reference property or the `returnType` prop, skip it (isFunctionExpression(original) && (key === 'functionStatement' || key === 'returnType')) || //circular reference property for annotations diff --git a/src/parser/AstNode.ts b/src/parser/AstNode.ts index 15117c64a..f1de57ec1 100644 --- a/src/parser/AstNode.ts +++ b/src/parser/AstNode.ts @@ -19,18 +19,18 @@ import { isBlock, isBody, isFunctionParameterExpression } from '../astUtils/refl export abstract class AstNode implements Locatable { public abstract kind: AstNodeKind; /** - * Where this node starts, counted from the start of its source (see `Locatable.pos`). + * Where this node starts, counted from the start of its source (see `Locatable.startPosition`). * Use `util.getLocation()` to get the line/character `Location` */ - public abstract pos: number; + public abstract startPosition: number; /** - * Where this node ends: the gap just after its last character (see `Locatable.end`) + * Where this node ends: the gap just after its last character (see `Locatable.endPosition`) */ - public abstract end: number; + public abstract endPosition: number; /** * Info about the source this node was parsed from. `undefined` for synthetic nodes */ - public abstract source: SourceInfo | undefined; + public abstract sourceInfo: SourceInfo | undefined; /** * @deprecated use `util.getLocation(node)`. This is computed on every read @@ -170,13 +170,13 @@ export abstract class AstNode implements Locatable { //a position only means something within one source (the one this search starts from), so only nodes from that source can match. //Nodes without a source (i.e. a synthetic `if` wrapping real code) or from another source (i.e. code parsed from a snippet) never match //on their own, but the walk still steps into them, so the real code inside them can still be found - const searchSource = this.source; + const searchSource = this.sourceInfo; return this.findChild((node) => { - if (!node?.source || (searchSource && node.source !== searchSource)) { + if (!node?.sourceInfo || (searchSource && node.sourceInfo !== searchSource)) { return; } - const offset = util.getOffset(node.source, position); - if (offset !== undefined && node.pos <= offset && offset <= node.end) { + const offset = util.getOffset(node.sourceInfo, position); + if (offset !== undefined && node.startPosition <= offset && offset <= node.endPosition) { return node.findChildAtPosition(position, options) ?? node; } }, options); diff --git a/src/parser/Expression.ts b/src/parser/Expression.ts index 40fa2542e..1ec543ae3 100644 --- a/src/parser/Expression.ts +++ b/src/parser/Expression.ts @@ -61,9 +61,9 @@ export class BinaryExpression extends Expression { public readonly kind = AstNodeKind.BinaryExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState): TranspileResult { return [ @@ -157,9 +157,9 @@ export class CallExpression extends Expression { public readonly kind = AstNodeKind.CallExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** `a.b()` -> `a.b` (not the args) */ public get previousInChain() { @@ -323,9 +323,9 @@ export class FunctionExpression extends Expression implements TypedefProvider { return this.tokens.endFunctionType?.leadingTrivia; } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState, name?: Identifier, includeBody = true) { let results = [] as TranspileResult; @@ -586,9 +586,9 @@ export class FunctionParameterExpression extends Expression { return paramType; } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState) { let result: TranspileResult = [ @@ -700,9 +700,9 @@ export class DottedGetExpression extends Expression { public readonly kind = AstNodeKind.DottedGetExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** `a.b` -> `a` */ public get previousInChain() { @@ -810,9 +810,9 @@ export class XmlAttributeGetExpression extends Expression { public readonly obj: Expression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** `a@b` -> `a` */ public get previousInChain() { @@ -913,9 +913,9 @@ export class IndexedGetExpression extends Expression { return this.indexes?.[0]; } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** `a[i]` -> `a` (not the index) */ public get previousInChain() { @@ -1002,9 +1002,9 @@ export class GroupingExpression extends Expression { public readonly kind = AstNodeKind.GroupingExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { if (isTypecastExpression(this.expression)) { @@ -1059,14 +1059,14 @@ export class LiteralExpression extends Expression { public readonly kind = AstNodeKind.LiteralExpression; - public get pos() { - return this.tokens.value.pos; + public get startPosition() { + return this.tokens.value.startPosition; } - public get end() { - return this.tokens.value.end; + public get endPosition() { + return this.tokens.value.endPosition; } - public get source() { - return this.tokens.value.source; + public get sourceInfo() { + return this.tokens.value.sourceInfo; } public getType(options?: GetTypeOptions) { @@ -1132,9 +1132,9 @@ export class PrintSeparatorExpression extends Expression { public readonly kind = AstNodeKind.PrintSeparatorExpression; - public pos: number; - public end: number; - public source: SourceInfo | undefined; + public startPosition: number; + public endPosition: number; + public sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -1178,9 +1178,9 @@ export class EscapedCharCodeLiteralExpression extends Expression { readonly value: Token & { charCode: number }; }; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -1225,9 +1225,9 @@ export class ArrayLiteralExpression extends Expression { public readonly kind = AstNodeKind.ArrayLiteralExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result: TranspileResult = []; @@ -1311,9 +1311,9 @@ export class AAMemberExpression extends Expression { public readonly kind = AstNodeKind.AAMemberExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly key: Token; @@ -1388,9 +1388,9 @@ export class AAIndexedMemberExpression extends Expression { public readonly kind = AstNodeKind.AAIndexedMemberExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return []; @@ -1440,9 +1440,9 @@ export class AALiteralExpression extends Expression { public readonly kind = AstNodeKind.AALiteralExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result: TranspileResult = []; @@ -1559,9 +1559,9 @@ export class UnaryExpression extends Expression { public readonly kind = AstNodeKind.UnaryExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly operator: Token; @@ -1627,9 +1627,9 @@ export class VariableExpression extends Expression { public readonly kind = AstNodeKind.VariableExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * @deprecated use `tokens.name` instead @@ -1721,9 +1721,9 @@ export class SourceLiteralExpression extends Expression { util.setBounds(this, this.tokens.value); } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly kind = AstNodeKind.SourceLiteralExpression; @@ -1871,9 +1871,9 @@ export class NewExpression extends Expression { public readonly kind = AstNodeKind.NewExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly new?: Token; @@ -1980,9 +1980,9 @@ export class CallfuncExpression extends Expression { public readonly kind = AstNodeKind.CallfuncExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** `a@.b()` -> `a` (not the args) */ public get previousInChain() { @@ -2068,9 +2068,9 @@ export class TemplateStringQuasiExpression extends Expression { public readonly expressions: Array; public readonly kind = AstNodeKind.TemplateStringQuasiExpression; - readonly pos: number; - readonly end: number; - readonly source: SourceInfo | undefined; + readonly startPosition: number; + readonly endPosition: number; + readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState, skipEmptyStrings = true) { let result = [] as TranspileResult; @@ -2137,9 +2137,9 @@ export class TemplateStringExpression extends Expression { public readonly quasis: TemplateStringQuasiExpression[]; public readonly expressions: Expression[]; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public getType(options: GetTypeOptions) { return StringType.instance; @@ -2263,9 +2263,9 @@ export class TaggedTemplateStringExpression extends Expression { public readonly quasis: TemplateStringQuasiExpression[]; public readonly expressions: Expression[]; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -2359,14 +2359,14 @@ export class AnnotationExpression extends Expression { readonly name: Token; }; - public get pos() { - return this.getBounds().pos; + public get startPosition() { + return this.getBounds().startPosition; } - public get end() { - return this.getBounds().end; + public get endPosition() { + return this.getBounds().endPosition; } - public get source() { - return this.getBounds().source; + public get sourceInfo() { + return this.getBounds().sourceInfo; } private getBounds() { //this needs to be computed on demand because `call` is assigned after construction @@ -2446,9 +2446,9 @@ export class TernaryExpression extends Expression { public readonly kind = AstNodeKind.TernaryExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly questionMark?: Token; @@ -2571,9 +2571,9 @@ export class NullCoalescingExpression extends Expression { public readonly kind = AstNodeKind.NullCoalescingExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly questionQuestion?: Token; @@ -2685,14 +2685,14 @@ export class RegexLiteralExpression extends Expression { readonly regexLiteral: Token; }; - public get pos() { - return this.tokens?.regexLiteral?.pos; + public get startPosition() { + return this.tokens?.regexLiteral?.startPosition; } - public get end() { - return this.tokens?.regexLiteral?.end; + public get endPosition() { + return this.tokens?.regexLiteral?.endPosition; } - public get source() { - return this.tokens?.regexLiteral?.source; + public get sourceInfo() { + return this.tokens?.regexLiteral?.sourceInfo; } public transpile(state: BrsTranspileState): TranspileResult { @@ -2829,9 +2829,9 @@ export class TypeExpression extends Expression implements TypedefProvider { */ public readonly resolvedType?: BscType; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { //roku built-in names (rosgnode*, etc.) collapse to `dynamic` at transpile. @@ -2927,9 +2927,9 @@ export class TypecastExpression extends Expression { public typeExpression?: TypeExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { return this.obj.transpile(state); @@ -2993,9 +2993,9 @@ export class TypedArrayExpression extends Expression { public readonly kind = AstNodeKind.TypedArrayExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { //typed arrays (e.g. `float[]`, `float[][][]`) collapse to `dynamic` since @@ -3053,9 +3053,9 @@ export class InlineInterfaceExpression extends Expression { public readonly kind = AstNodeKind.InlineInterfaceExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; get leadingTrivia(): Token[] { return this.tokens.open?.leadingTrivia; @@ -3134,9 +3134,9 @@ export class InlineInterfaceMemberExpression extends Expression { public readonly typeExpression?: TypeExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; get leadingTrivia(): Token[] { return this.tokens.optional?.leadingTrivia ?? this.tokens.name.leadingTrivia; @@ -3214,9 +3214,9 @@ export class TypedFunctionTypeExpression extends Expression { public readonly params: FunctionParameterExpression[]; public readonly returnType?: TypeExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { return [this.getType({ flags: SymbolTypeFlag.typetime }).toTypeString()]; diff --git a/src/parser/Parser.ts b/src/parser/Parser.ts index 7baa704fb..99c7eed33 100644 --- a/src/parser/Parser.ts +++ b/src/parser/Parser.ts @@ -921,9 +921,9 @@ export class Parser { TokenKind.Function, 'function', true, - this.peek().pos, - this.peek().end, - this.peek().source, + this.peek().startPosition, + this.peek().endPosition, + this.peek().sourceInfo, [] ); } @@ -1381,10 +1381,10 @@ export class Parser { const originalToken = exitToken; exitToken = createToken(TokenKind.Exit, exitText, originalToken); - exitToken.end = exitToken.pos + 4; + exitToken.endPosition = exitToken.startPosition + 4; this.tokens[this.current - 1] = exitToken; const newLoopToken = createToken(TokenKind.While, whileText, originalToken); - newLoopToken.pos += 4; + newLoopToken.startPosition += 4; this.tokens.splice(this.current, 0, newLoopToken); } @@ -1684,7 +1684,7 @@ export class Parser { this.diagnostics.push({ ...DiagnosticMessages.expectedIdentifier('typecast'), location: { - uri: typecastToken?.source?.uri, + uri: typecastToken?.sourceInfo?.uri, range: util.createBoundingRange(typecastToken, this.peek()) } }); @@ -1864,7 +1864,7 @@ export class Parser { this.diagnostics.push({ ...DiagnosticMessages.unterminatedTemplateExpression(), location: { - uri: openingBacktick?.source?.uri, + uri: openingBacktick?.sourceInfo?.uri, range: util.createBoundingRange(openingBacktick, this.peek()) } }); @@ -1883,7 +1883,7 @@ export class Parser { this.diagnostics.push({ ...DiagnosticMessages.unterminatedTemplateString(), location: { - uri: openingBacktick?.source?.uri, + uri: openingBacktick?.sourceInfo?.uri, range: util.createBoundingRange(openingBacktick, this.peek()) } }); diff --git a/src/parser/Statement.ts b/src/parser/Statement.ts index dc9f35cfa..7d0ce4eea 100644 --- a/src/parser/Statement.ts +++ b/src/parser/Statement.ts @@ -38,9 +38,9 @@ export class EmptyStatement extends Statement { /** * Create a negative range to indicate this is an interpolated location */ - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly kind = AstNodeKind.EmptyStatement; @@ -74,18 +74,18 @@ export class Body extends Statement implements TypedefProvider { public readonly symbolTable = new SymbolTable('Body', () => this.parent?.getSymbolTable()); - public get pos() { - return this.getBounds().pos; + public get startPosition() { + return this.getBounds().startPosition; } - public get end() { - return this.getBounds().end; + public get endPosition() { + return this.getBounds().endPosition; } - public get source() { - return this.getBounds().source; + public get sourceInfo() { + return this.getBounds().sourceInfo; } private getBounds() { //this needs to be lazy because the body has its statements pushed to it after being constructed - if (!this._bounds?.source) { + if (!this._bounds?.sourceInfo) { this._bounds = util.setBounds(this._bounds ?? {} as Locatable, ...(this.statements ?? [])); } return this._bounds; @@ -186,9 +186,9 @@ export class AssignmentStatement extends Statement { public readonly kind = AstNodeKind.AssignmentStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -263,9 +263,9 @@ export class AugmentedAssignmentStatement extends Statement { public readonly kind = AstNodeKind.AugmentedAssignmentStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -411,23 +411,23 @@ export class Block extends Statement { ); firstBitAfter = this.parent.parent.tokens.endTry; } - if (lastBitBefore?.source && firstBitAfter?.source) { - return { pos: lastBitBefore.end, end: firstBitAfter.pos, source: lastBitBefore.source }; + if (lastBitBefore?.sourceInfo && firstBitAfter?.sourceInfo) { + return { startPosition: lastBitBefore.endPosition, endPosition: firstBitAfter.startPosition, sourceInfo: lastBitBefore.sourceInfo }; } } - public get pos() { - return this.getBounds()?.pos; + public get startPosition() { + return this.getBounds()?.startPosition; } - public get end() { - return this.getBounds()?.end; + public get endPosition() { + return this.getBounds()?.endPosition; } - public get source() { - return this.getBounds()?.source; + public get sourceInfo() { + return this.getBounds()?.sourceInfo; } private getBounds() { //this needs to be lazy because the body has its statements pushed to it after being constructed - if (!this._bounds?.source) { + if (!this._bounds?.sourceInfo) { this._bounds = this.buildBounds(); } return this._bounds; @@ -511,9 +511,9 @@ export class ExpressionStatement extends Statement { public readonly expression: Expression; public readonly kind = AstNodeKind.ExpressionStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -571,9 +571,9 @@ export class ExitStatement extends Statement { public readonly kind = AstNodeKind.ExitStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -625,9 +625,9 @@ export class FunctionStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.FunctionStatement as AstNodeKind; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * Get the name of this expression based on the parse mode @@ -749,9 +749,9 @@ export class IfStatement extends Statement { public readonly kind = AstNodeKind.IfStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; get isInline() { const allLeadingTrivia = [ @@ -886,9 +886,9 @@ export class IncrementStatement extends Statement { public readonly kind = AstNodeKind.IncrementStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -974,9 +974,9 @@ export class PrintStatement extends Statement { return { valueCount: valueCount, commaCount: commaCount }; } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [ @@ -1062,9 +1062,9 @@ export class DimStatement extends Statement { public readonly kind = AstNodeKind.DimStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState) { let result: TranspileResult = [ @@ -1142,9 +1142,9 @@ export class GotoStatement extends Statement { public readonly kind = AstNodeKind.GotoStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -1193,9 +1193,9 @@ export class LabelStatement extends Statement { }; public readonly kind = AstNodeKind.LabelStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public get leadingTrivia(): Token[] { return this.tokens.name.leadingTrivia; @@ -1245,9 +1245,9 @@ export class ReturnStatement extends Statement { public readonly value?: Expression; public readonly kind = AstNodeKind.ReturnStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -1297,9 +1297,9 @@ export class EndStatement extends Statement { }; public readonly kind = AstNodeKind.EndStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -1338,9 +1338,9 @@ export class StopStatement extends Statement { public readonly kind = AstNodeKind.StopStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -1414,9 +1414,9 @@ export class ForStatement extends Statement { public readonly kind = AstNodeKind.ForStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -1545,9 +1545,9 @@ export class ForEachStatement extends Statement { public readonly kind = AstNodeKind.ForEachStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -1662,9 +1662,9 @@ export class WhileStatement extends Statement { public readonly kind = AstNodeKind.WhileStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -1755,9 +1755,9 @@ export class DottedSetStatement extends Statement { public readonly kind = AstNodeKind.DottedSetStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { //if the value is a compound assignment, don't add the obj, dot, name, or operator...the expression will handle that @@ -1851,9 +1851,9 @@ export class IndexedSetStatement extends Statement { public readonly kind = AstNodeKind.IndexedSetStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { const result = []; @@ -1933,9 +1933,9 @@ export class LibraryStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.LibraryStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let result = [] as TranspileResult; @@ -2009,9 +2009,9 @@ export class NamespaceStatement extends Statement implements TypedefProvider { return this.getName(ParseMode.BrighterScript); } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public getName(parseMode: ParseMode) { const sep = parseMode === ParseMode.BrighterScript ? '.' : '_'; @@ -2122,10 +2122,10 @@ export class ImportStatement extends Statement implements TypedefProvider { if (this.tokens.path) { //remove quotes this.filePath = this.tokens.path.text.replace(/"/g, ''); - if (this.tokens.path.source) { + if (this.tokens.path.sourceInfo) { //adjust the range to exclude the quotes - this.tokens.path.pos++; - this.tokens.path.end--; + this.tokens.path.startPosition++; + this.tokens.path.endPosition--; } } } @@ -2137,9 +2137,9 @@ export class ImportStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.ImportStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly filePath: string; @@ -2222,9 +2222,9 @@ export class InterfaceStatement extends Statement implements TypedefProvider { readonly endInterface?: Token; }; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public get fields(): InterfaceFieldStatement[] { return this.body.filter(x => isInterfaceFieldStatement(x)) as InterfaceFieldStatement[]; @@ -2430,9 +2430,9 @@ export class InterfaceFieldStatement extends Statement implements TypedefProvide public readonly typeExpression?: TypeExpression; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public readonly tokens: { readonly name: Identifier; @@ -2551,9 +2551,9 @@ export class InterfaceMethodStatement extends Statement implements TypedefProvid public readonly kind = AstNodeKind.InterfaceMethodStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * Get the name of this method. */ @@ -2781,9 +2781,9 @@ export class ClassStatement extends Statement implements TypedefProvider { public readonly methods = [] as MethodStatement[]; public readonly fields = [] as FieldStatement[]; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * Register all members (methods and fields) found in the given statements, @@ -3426,9 +3426,9 @@ export class MethodStatement extends FunctionStatement { return this.modifiers.find(x => accessModifiers.includes(x.kind)); } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * Get the name of this method. @@ -3527,9 +3527,9 @@ export class MethodStatement extends FunctionStatement { kind: TokenKind.Identifier, text: 'super', isReserved: false, - pos: state.classStatement.tokens.name.pos, - end: state.classStatement.tokens.name.end, - source: state.classStatement.tokens.name.source, + startPosition: state.classStatement.tokens.name.startPosition, + endPosition: state.classStatement.tokens.name.endPosition, + sourceInfo: state.classStatement.tokens.name.sourceInfo, leadingTrivia: [] } }), @@ -3537,18 +3537,18 @@ export class MethodStatement extends FunctionStatement { kind: TokenKind.LeftParen, text: '(', isReserved: false, - pos: state.classStatement.tokens.name.pos, - end: state.classStatement.tokens.name.end, - source: state.classStatement.tokens.name.source, + startPosition: state.classStatement.tokens.name.startPosition, + endPosition: state.classStatement.tokens.name.endPosition, + sourceInfo: state.classStatement.tokens.name.sourceInfo, leadingTrivia: [] }, closingParen: { kind: TokenKind.RightParen, text: ')', isReserved: false, - pos: state.classStatement.tokens.name.pos, - end: state.classStatement.tokens.name.end, - source: state.classStatement.tokens.name.source, + startPosition: state.classStatement.tokens.name.startPosition, + endPosition: state.classStatement.tokens.name.endPosition, + sourceInfo: state.classStatement.tokens.name.sourceInfo, leadingTrivia: [] }, args: [] @@ -3738,9 +3738,9 @@ export class FieldStatement extends Statement implements TypedefProvider { DynamicType.instance; } - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public get leadingTrivia(): Token[] { return this.tokens.accessModifier?.leadingTrivia ?? this.tokens.optional?.leadingTrivia ?? this.tokens.name.leadingTrivia; @@ -3849,9 +3849,9 @@ export class TryCatchStatement extends Statement { public readonly kind = AstNodeKind.TryCatchStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { return [ @@ -3923,9 +3923,9 @@ export class CatchStatement extends Statement { public readonly kind = AstNodeKind.CatchStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState): TranspileResult { return [ @@ -3986,9 +3986,9 @@ export class ThrowStatement extends Statement { public readonly kind = AstNodeKind.ThrowStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public transpile(state: BrsTranspileState) { const result = [ @@ -4063,9 +4063,9 @@ export class EnumStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.EnumStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public getMembers() { const result = [] as EnumMemberStatement[]; @@ -4263,9 +4263,9 @@ export class EnumMemberStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.EnumMemberStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; /** * The name of the member @@ -4366,9 +4366,9 @@ export class ConstStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.ConstStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; public get name() { return this.tokens.name.text; @@ -4473,9 +4473,9 @@ export class ContinueStatement extends Statement { public readonly kind = AstNodeKind.ContinueStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { //when targeting firmware without native `continue` support, rewrite into a jump to the @@ -4544,9 +4544,9 @@ export class TypecastStatement extends Statement { public readonly kind = AstNodeKind.TypecastStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { //the typecast statement is a comment just for debugging purposes @@ -4607,9 +4607,9 @@ export class ConditionalCompileErrorStatement extends Statement { public readonly kind = AstNodeKind.ConditionalCompileErrorStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -4673,9 +4673,9 @@ export class AliasStatement extends Statement { public readonly kind = AstNodeKind.AliasStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { //transpile to a comment just for debugging purposes @@ -4758,9 +4758,9 @@ export class ConditionalCompileStatement extends Statement { public readonly kind = AstNodeKind.ConditionalCompileStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { let results = [] as TranspileResult; @@ -4902,9 +4902,9 @@ export class ConditionalCompileConstStatement extends Statement { public readonly kind = AstNodeKind.ConditionalCompileConstStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { return [ @@ -4973,9 +4973,9 @@ export class TypeStatement extends Statement implements TypedefProvider { public readonly kind = AstNodeKind.TypeStatement; - public readonly pos: number; - public readonly end: number; - public readonly source: SourceInfo | undefined; + public readonly startPosition: number; + public readonly endPosition: number; + public readonly sourceInfo: SourceInfo | undefined; transpile(state: BrsTranspileState) { //type statements have no runtime representation, so they're stripped entirely diff --git a/src/parser/TranspileState.spec.ts b/src/parser/TranspileState.spec.ts index cfb09fc78..1e8362dc9 100644 --- a/src/parser/TranspileState.spec.ts +++ b/src/parser/TranspileState.spec.ts @@ -18,7 +18,7 @@ describe('TranspileState', () => { it('does not map code parsed from a snippet, since its position is not in any file', () => { const state = new TranspileState(srcPath, {}); const token = Lexer.scan('\n print').tokens[1]; - expect(token.source).to.exist; + expect(token.sourceInfo).to.exist; expect(state.tokenToSourceNode(token).line).to.be.null; expect(state.sourceNode(token, 'print').line).to.be.null; }); diff --git a/src/parser/TranspileState.ts b/src/parser/TranspileState.ts index a0f76bc5b..816260bf3 100644 --- a/src/parser/TranspileState.ts +++ b/src/parser/TranspileState.ts @@ -8,9 +8,9 @@ import { util } from '../util'; import type { TranspileResult } from '../interfaces'; interface TranspileToken { - pos?: number; - end?: number; - source?: SourceInfo; + startPosition?: number; + endPosition?: number; + sourceInfo?: SourceInfo; /** * Location for tokens that aren't `Locatable` (i.e. SGTokens) */ @@ -69,7 +69,7 @@ export class TranspileState { public newline = '\n'; private getSource(locatable: RangeLike) { - let srcPath = (locatable as Locatable)?.source?.uri ?? (locatable as { location: Location })?.location?.uri ?? (locatable as Location).uri; + let srcPath = (locatable as Locatable)?.sourceInfo?.uri ?? (locatable as { location: Location })?.location?.uri ?? (locatable as Location).uri; if (srcPath) { srcPath = util.uriToPath(srcPath); //if a sourceRoot is specified, use that instead of the rootDir @@ -90,7 +90,7 @@ export class TranspileState { */ public sourceNode(locatable: RangeLike, code: string | SourceNode | TranspileResult): SourceNode { //source maps only need the start, so skip building a whole `Location` for locatables - const start = (locatable && 'source' in locatable) + const start = (locatable && 'sourceInfo' in locatable) ? this.getLocatableStart(locatable) : util.extractRange(locatable)?.start; return util.sourceNodeFromTranspileResult( @@ -123,7 +123,7 @@ export class TranspileState { * Get the start position of a token. Source maps only need the start, so this skips building a whole `Location` */ private getTokenStart(token: TranspileToken): Position | undefined { - return token?.source + return token?.sourceInfo ? this.getLocatableStart(token as Locatable) : token?.location?.range?.start; } @@ -133,15 +133,15 @@ export class TranspileState { * that position belongs to. Mapping it would point at the same lines of whatever file is being transpiled, so leave it unmapped instead */ private getLocatableStart(locatable: Locatable): Position | undefined { - return locatable.source?.uri ? util.getStartPosition(locatable) : undefined; + return locatable.sourceInfo?.uri ? util.getStartPosition(locatable) : undefined; } /** * Does this token span more than one line? (a trailing newline doesn't count) */ private isMultiLine(token: TranspileToken, start: Position) { - if (token.source) { - const nextLineStart = token.source.lineStarts[start.line + 1]; + if (token.sourceInfo) { + const nextLineStart = token.sourceInfo.linePositions[start.line + 1]; return nextLineStart !== undefined && nextLineStart <= util.getContentEnd(token as Locatable); } return token.location?.range?.end?.line > start.line; diff --git a/src/parser/tests/Parser.spec.ts b/src/parser/tests/Parser.spec.ts index 5b8e9a759..000d45dc9 100644 --- a/src/parser/tests/Parser.spec.ts +++ b/src/parser/tests/Parser.spec.ts @@ -11,9 +11,9 @@ export function token(kind: TokenKind, text?: string): Token { kind: kind, text: text!, isReserved: ReservedWords.has((text ?? '').toLowerCase()), - pos: undefined, - end: undefined, - source: undefined, + startPosition: undefined, + endPosition: undefined, + sourceInfo: undefined, leadingTrivia: [] }; } diff --git a/src/testHelpers.spec.ts b/src/testHelpers.spec.ts index f4aa5676d..9bd6af53e 100644 --- a/src/testHelpers.spec.ts +++ b/src/testHelpers.spec.ts @@ -37,13 +37,13 @@ export const trim = undent; /** * A shared source for hand-built test tokens, where every line is 1000 characters long */ -const testSource: SourceInfo = { uri: undefined, lineStarts: Array.from({ length: 1000 }, (x, i) => i * 1000) }; +const testSource: SourceInfo = { uri: undefined, linePositions: Array.from({ length: 1000 }, (x, i) => i * 1000) }; /** * Create a `Locatable` for a hand-built test token from line/character positions */ export function testLocatable(startLine: number, startCharacter: number, endLine: number, endCharacter: number): Locatable { - return { pos: (startLine * 1000) + startCharacter, end: (endLine * 1000) + endCharacter, source: testSource }; + return { startPosition: (startLine * 1000) + startCharacter, endPosition: (endLine * 1000) + endCharacter, sourceInfo: testSource }; } const sinon = createSandbox(); diff --git a/src/util.spec.ts b/src/util.spec.ts index 04a361b3c..575e7ba6a 100644 --- a/src/util.spec.ts +++ b/src/util.spec.ts @@ -1135,8 +1135,8 @@ describe('util', () => { }); it('clears the bounds when nothing has a source', () => { - const result = util.setBounds({ pos: 1, end: 2, source: testLocatable(0, 0, 0, 0).source }, createToken(TokenKind.Identifier, 'a')); - expect(result).to.eql({ pos: undefined, end: undefined, source: undefined }); + const result = util.setBounds({ startPosition: 1, endPosition: 2, sourceInfo: testLocatable(0, 0, 0, 0).sourceInfo }, createToken(TokenKind.Identifier, 'a')); + expect(result).to.eql({ startPosition: undefined, endPosition: undefined, sourceInfo: undefined }); }); it('does not end at the start of the next line when the last locatable is a LF newline', () => { @@ -1185,7 +1185,7 @@ describe('util', () => { }); describe('getOffset', () => { - const source = Lexer.scan('ab\ncd\nef').tokens[0].source; + const source = Lexer.scan('ab\ncd\nef').tokens[0].sourceInfo; it('converts positions to offsets', () => { expect(util.getOffset(source, util.createPosition(0, 0))).to.eql(0); @@ -1209,37 +1209,37 @@ describe('util', () => { it('maps the location into the source, so it combines with that source', () => { const tokens = Lexer.scan('a = 1\nbb = 2', { srcPath: srcPath }).tokens; - const sourceInfo = tokens[0].source; + const sourceInfo = tokens[0].sourceInfo; const location = util.createLocation(1, 0, 1, 2, sourceInfo.uri); const result = util.setLocation({} as Locatable, location, sourceInfo); - expect(result.source).to.equal(sourceInfo); - expect(result.pos).to.eql(6); - expect(result.end).to.eql(8); + expect(result.sourceInfo).to.equal(sourceInfo); + expect(result.startPosition).to.eql(6); + expect(result.endPosition).to.eql(8); expect(util.getLocation(result)).to.eql(location); expect(util.getLocation(util.setBounds({} as Locatable, tokens[0], result)).range).to.eql(util.createRange(0, 0, 1, 2)); }); it('clears the position when given no location or source', () => { - const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].source; - expect(util.setLocation(testLocatable(0, 0, 0, 1), undefined, sourceInfo)).to.eql({ pos: undefined, end: undefined, source: undefined }); - expect(util.setLocation(testLocatable(0, 0, 0, 1), util.createLocation(0, 0, 0, 1), undefined)).to.eql({ pos: undefined, end: undefined, source: undefined }); + const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].sourceInfo; + expect(util.setLocation(testLocatable(0, 0, 0, 1), undefined, sourceInfo)).to.eql({ startPosition: undefined, endPosition: undefined, sourceInfo: undefined }); + expect(util.setLocation(testLocatable(0, 0, 0, 1), util.createLocation(0, 0, 0, 1), undefined)).to.eql({ startPosition: undefined, endPosition: undefined, sourceInfo: undefined }); }); it('clears the position for a location in a different file', () => { - const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].source; + const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].sourceInfo; const result = util.setLocation(testLocatable(0, 0, 0, 1), util.createLocation(0, 0, 0, 1, 'file:///other.brs'), sourceInfo); expect(util.getLocation(result)).to.be.undefined; }); it('clears the position for lines that are not in the source', () => { - const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].source; + const sourceInfo = Lexer.scan('a = 1', { srcPath: srcPath }).tokens[0].sourceInfo; //bsc builds ranges like this for comment flags that run to the end of the file const result = util.setLocation({} as Locatable, util.createLocation(0, 0, Number.MAX_SAFE_INTEGER, 0, sourceInfo.uri), sourceInfo); expect(util.getLocation(result)).to.be.undefined; }); it('keeps a character past the end of a line on that line', () => { - const sourceInfo = Lexer.scan('a = 1\nb = 2', { srcPath: srcPath }).tokens[0].source; + const sourceInfo = Lexer.scan('a = 1\nb = 2', { srcPath: srcPath }).tokens[0].sourceInfo; //bsc builds ranges like this for whole-file diagnostics const result = util.setLocation({} as Locatable, util.createLocation(0, 0, 0, Number.MAX_VALUE, sourceInfo.uri), sourceInfo); expect(util.getLocation(result).range.end.line).to.eql(0); diff --git a/src/util.ts b/src/util.ts index 24b8b50e3..aaca7f74b 100644 --- a/src/util.ts +++ b/src/util.ts @@ -1070,7 +1070,7 @@ export class Util { public extractRange(rangeIsh: RangeLike): Range | undefined { if (!rangeIsh) { return undefined; - } else if ('source' in rangeIsh) { + } else if ('sourceInfo' in rangeIsh) { return this.getLocation(rangeIsh)?.range; } else if ('location' in rangeIsh) { return rangeIsh.location?.range; @@ -1268,9 +1268,9 @@ export class Util { token.kind, token.text, token.isReserved, - token.pos, - token.end, - token.source, + token.startPosition, + token.endPosition, + token.sourceInfo, token.leadingTrivia ? token.leadingTrivia.map(x => this.cloneToken(x)) : undefined ) as Token; //handle those tokens that have charCode @@ -1296,7 +1296,7 @@ export class Util { let range: Range; if (!locatable) { continue; - } else if ('source' in locatable) { + } else if ('sourceInfo' in locatable) { const location = this.getLocation(locatable); range = location?.range; if (!uri) { @@ -1354,30 +1354,30 @@ export class Util { } /** - * Set the `pos`, `end`, and `source` of `target` to the bounds of all the locatables. - * Offsets from different sources can't be compared, so only locatables from one source are included: the first one that has a uri, + * Set the `startPosition`, `endPosition`, and `sourceInfo` of `target` to the bounds of all the locatables. + * Positions from different sources can't be compared, so only locatables from one source are included: the first one that has a uri, * or the first one if none of them do. That way, code parsed from a snippet (which has no uri) never takes over the bounds of real code from a file. * A trailing newline is not included in the bounds, so the target never ends at the start of the next line */ public setBounds(target: T, ...locatables: Array): T { - let pos: number; - let end: number; - let source: SourceInfo; + let startPosition: number; + let endPosition: number; + let sourceInfo: SourceInfo; for (const locatable of locatables) { - if (!locatable?.source) { + if (!locatable?.sourceInfo) { continue; - } else if (!source || (!source.uri && locatable.source.uri)) { - source = locatable.source; - pos = locatable.pos; - end = this.getContentEnd(locatable); - } else if (locatable.source === source) { - pos = Math.min(pos, locatable.pos); - end = Math.max(end, this.getContentEnd(locatable)); - } - } - target.pos = pos; - target.end = end; - target.source = source; + } else if (!sourceInfo || (!sourceInfo.uri && locatable.sourceInfo.uri)) { + sourceInfo = locatable.sourceInfo; + startPosition = locatable.startPosition; + endPosition = this.getContentEnd(locatable); + } else if (locatable.sourceInfo === sourceInfo) { + startPosition = Math.min(startPosition, locatable.startPosition); + endPosition = Math.max(endPosition, this.getContentEnd(locatable)); + } + } + target.startPosition = startPosition; + target.endPosition = endPosition; + target.sourceInfo = sourceInfo; return target; } @@ -1391,22 +1391,22 @@ export class Util { */ public setLocation(target: T, location: Location | undefined, sourceInfo: SourceInfo | undefined): T { const range = location?.range; - const pos = this.getOffset(sourceInfo, range?.start); - const end = this.getOffset(sourceInfo, range?.end); - if (pos === undefined || end === undefined || (location.uri && location.uri !== sourceInfo.uri)) { - target.source = undefined; - target.pos = undefined; - target.end = undefined; + const startPosition = this.getOffset(sourceInfo, range?.start); + const endPosition = this.getOffset(sourceInfo, range?.end); + if (startPosition === undefined || endPosition === undefined || (location.uri && location.uri !== sourceInfo.uri)) { + target.sourceInfo = undefined; + target.startPosition = undefined; + target.endPosition = undefined; } else { - target.source = sourceInfo; - target.pos = pos; - target.end = end; + target.sourceInfo = sourceInfo; + target.startPosition = startPosition; + target.endPosition = endPosition; } return target; } /** - * Get the end offset of a locatable, excluding any trailing newline (i.e. a `Newline` token, or an escaped newline in a template string) + * Get the end position of a locatable, excluding any trailing newline (i.e. a `Newline` token, or an escaped newline in a template string) */ public getContentEnd(locatable: Locatable): number { const text = (locatable as Token).text; @@ -1414,73 +1414,73 @@ export class Util { const lastCharCode = text.charCodeAt(text.length - 1); // \n (and \r\n) if (lastCharCode === 10) { - return locatable.end - (text.length > 1 && text.charCodeAt(text.length - 2) === 13 ? 2 : 1); + return locatable.endPosition - (text.length > 1 && text.charCodeAt(text.length - 2) === 13 ? 2 : 1); // \r } else if (lastCharCode === 13) { - return locatable.end - 1; + return locatable.endPosition - 1; } } - return locatable.end; + return locatable.endPosition; } /** - * Build a line/character `Location` for the locatable. Returns `undefined` for synthetic items (those without a `source`) + * Build a line/character `Location` for the locatable. Returns `undefined` for synthetic items (those without a `sourceInfo`) */ public getLocation(locatable: Locatable): Location | undefined { - const source = locatable?.source; - if (!source) { + const sourceInfo = locatable?.sourceInfo; + if (!sourceInfo) { return undefined; } - const lineStarts = source.lineStarts; - const startLine = this.getLineIndex(lineStarts, locatable.pos, 0); - let endLine = this.getLineIndex(lineStarts, locatable.end, startLine); + const linePositions = sourceInfo.linePositions; + const startLine = this.getLineIndex(linePositions, locatable.startPosition, 0); + let endLine = this.getLineIndex(linePositions, locatable.endPosition, startLine); //a token that ends with a newline ends on its own line rather than at the start of the next one - if (endLine > startLine && lineStarts[endLine] === locatable.end && this.getContentEnd(locatable) !== locatable.end) { + if (endLine > startLine && linePositions[endLine] === locatable.endPosition && this.getContentEnd(locatable) !== locatable.endPosition) { endLine--; } return { - uri: source.uri, - range: this.createRange(startLine, locatable.pos - lineStarts[startLine], endLine, locatable.end - lineStarts[endLine]) + uri: sourceInfo.uri, + range: this.createRange(startLine, locatable.startPosition - linePositions[startLine], endLine, locatable.endPosition - linePositions[endLine]) }; } /** - * Get just the start `Position` of a locatable. Cheaper than `getLocation()` when the end isn't needed. - * Returns `undefined` for synthetic items (those without a `source`) + * Get just the start line/character `Position` of a locatable. Cheaper than `getLocation()` when the end isn't needed. + * Returns `undefined` for synthetic items (those without a `sourceInfo`) */ public getStartPosition(locatable: Locatable): Position | undefined { - const lineStarts = locatable?.source?.lineStarts; - if (!lineStarts) { + const linePositions = locatable?.sourceInfo?.linePositions; + if (!linePositions) { return undefined; } - const line = this.getLineIndex(lineStarts, locatable.pos, 0); - return this.createPosition(line, locatable.pos - lineStarts[line]); + const line = this.getLineIndex(linePositions, locatable.startPosition, 0); + return this.createPosition(line, locatable.startPosition - linePositions[line]); } /** - * Convert a line/character `Position` into an absolute offset within the given source. + * Convert a line/character `Position` into a position counted from the start of the source (see `Locatable.startPosition`). * A character past the end of its line is clamped to that line, so it never lands on the next line. * Returns `undefined` if the source or the position's line is missing */ - public getOffset(source: SourceInfo | undefined, position: Position | undefined): number | undefined { - const lineStarts = source?.lineStarts; - const lineStart = lineStarts?.[position?.line]; + public getOffset(sourceInfo: SourceInfo | undefined, position: Position | undefined): number | undefined { + const linePositions = sourceInfo?.linePositions; + const lineStart = linePositions?.[position?.line]; if (lineStart === undefined) { return undefined; } const offset = lineStart + position.character; - const nextLineStart = lineStarts[position.line + 1]; + const nextLineStart = linePositions[position.line + 1]; return nextLineStart !== undefined && offset >= nextLineStart ? nextLineStart - 1 : offset; } /** - * Binary search `lineStarts` (beginning at line index `low`) for the index of the line that contains `offset` + * Binary search `linePositions` (beginning at line index `low`) for the index of the line that contains `position` */ - private getLineIndex(lineStarts: number[], offset: number, low: number) { - let high = lineStarts.length - 1; + private getLineIndex(linePositions: number[], position: number, low: number) { + let high = linePositions.length - 1; while (low < high) { const mid = Math.floor((low + high + 1) / 2); - if (lineStarts[mid] <= offset) { + if (linePositions[mid] <= position) { low = mid; } else { high = mid - 1;