• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

rokucommunity / brighterscript / #15048

01 Jan 2026 11:17PM UTC coverage: 87.048% (-0.9%) from 87.907%
#15048

push

web-flow
Merge 02ba2bb57 into 2ea4d2108

14498 of 17595 branches covered (82.4%)

Branch coverage included in aggregate %.

192 of 261 new or added lines in 12 files covered. (73.56%)

897 existing lines in 48 files now uncovered.

15248 of 16577 relevant lines covered (91.98%)

24112.76 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

79.85
/src/astUtils/creators.ts
1
import type { Location } from 'vscode-languageserver';
2
import type { Identifier, Token } from '../lexer/Token';
3
import type { SGToken } from '../parser/SGTypes';
4
import { SGAttribute, SGComponent, SGInterface, SGInterfaceField, SGInterfaceFunction, SGScript } from '../parser/SGTypes';
1✔
5
import { TokenKind } from '../lexer/TokenKind';
1✔
6
import type { Expression, Statement } from '../parser/AstNode';
7
import { LiteralExpression, CallExpression, DottedGetExpression, VariableExpression, FunctionExpression } from '../parser/Expression';
1✔
8
import { AssignmentStatement, Block, DottedSetStatement, IfStatement, IndexedSetStatement, MethodStatement } from '../parser/Statement';
1✔
9

10
const tokenDefaults = {
1✔
11
    [TokenKind.BackTick]: '`',
12
    [TokenKind.Backslash]: '\\',
13
    [TokenKind.BackslashEqual]: '\\=',
14
    [TokenKind.Callfunc]: '@.',
15
    [TokenKind.Caret]: '^',
16
    [TokenKind.Colon]: ':',
17
    [TokenKind.Comma]: ',',
18
    [TokenKind.Comment]: '\'',
19
    [TokenKind.Dollar]: '$',
20
    [TokenKind.Dot]: '.',
21
    [TokenKind.EndClass]: 'end class',
22
    [TokenKind.EndEnum]: 'end enum',
23
    [TokenKind.EndFor]: 'end for',
24
    [TokenKind.EndFunction]: 'end function',
25
    [TokenKind.EndIf]: 'end if',
26
    [TokenKind.EndInterface]: 'end interface',
27
    [TokenKind.EndNamespace]: 'end namespace',
28
    [TokenKind.EndSub]: 'end sub',
29
    [TokenKind.EndTry]: 'end try',
30
    [TokenKind.EndWhile]: 'end while',
31
    [TokenKind.Equal]: '=',
32
    [TokenKind.Greater]: '>',
33
    [TokenKind.GreaterEqual]: '>=',
34
    [TokenKind.HashConst]: '#const',
35
    [TokenKind.HashElse]: '#else',
36
    [TokenKind.HashElseIf]: '#else if',
37
    [TokenKind.HashEndIf]: '#end if',
38
    [TokenKind.HashError]: '#error',
39
    [TokenKind.HashIf]: '#if',
40
    [TokenKind.LeftCurlyBrace]: '{',
41
    [TokenKind.LeftParen]: '(',
42
    [TokenKind.LeftShift]: '<<',
43
    [TokenKind.LeftShiftEqual]: '<<=',
44
    [TokenKind.LeftSquareBracket]: '[',
45
    [TokenKind.Less]: '<',
46
    [TokenKind.LessEqual]: '<=',
47
    [TokenKind.LessGreater]: '<>',
48
    [TokenKind.LineNumLiteral]: 'LINE_NUM',
49
    [TokenKind.Minus]: '-',
50
    [TokenKind.MinusEqual]: '-=',
51
    [TokenKind.MinusMinus]: '--',
52
    [TokenKind.Newline]: '\n',
53
    [TokenKind.PkgLocationLiteral]: 'PKG_LOCATION',
54
    [TokenKind.PkgPathLiteral]: 'PKG_PATH',
55
    [TokenKind.Plus]: '+',
56
    [TokenKind.PlusEqual]: '+=',
57
    [TokenKind.PlusPlus]: '++',
58
    [TokenKind.Question]: '?',
59
    [TokenKind.QuestionQuestion]: '??',
60
    [TokenKind.RightCurlyBrace]: '}',
61
    [TokenKind.RightParen]: ')',
62
    [TokenKind.RightShift]: '>>',
63
    [TokenKind.RightShiftEqual]: '>>=',
64
    [TokenKind.RightSquareBracket]: ']',
65
    [TokenKind.Semicolon]: ';',
66
    [TokenKind.SourceFilePathLiteral]: 'SOURCE_FILE_PATH',
67
    [TokenKind.SourceFunctionNameLiteral]: 'SOURCE_FUNCTION_NAME',
68
    [TokenKind.SourceNamespaceRootNameLiteral]: 'SOURCE_NAMESPACE_ROOT_NAME',
69
    [TokenKind.SourceNamespaceNameLiteral]: 'SOURCE_NAMESPACE_NAME',
70
    [TokenKind.SourceLineNumLiteral]: 'SOURCE_LINE_NUM',
71
    [TokenKind.SourceLocationLiteral]: 'SOURCE_LOCATION',
72
    [TokenKind.Star]: '*',
73
    [TokenKind.StarEqual]: '*=',
74
    [TokenKind.Tab]: '\t',
75
    [TokenKind.TemplateStringExpressionBegin]: '${',
76
    [TokenKind.TemplateStringExpressionEnd]: '}',
77
    [TokenKind.Whitespace]: ' '
78
};
79

80
export function createToken<T extends TokenKind>(kind: T, text?: string, location?: Location): Token & { kind: T } {
1✔
81
    return {
2,252,659✔
82
        kind: kind,
83
        text: text ?? tokenDefaults[kind as string] ?? kind.toString().toLowerCase(),
13,515,954✔
84
        isReserved: !text || text === kind.toString(),
4,494,964✔
85
        location: location,
86
        leadingWhitespace: '',
87
        leadingTrivia: []
88
    };
89
}
90

91
export function createIdentifier(name: string, location?: Location): Identifier {
1✔
92
    return {
255✔
93
        kind: TokenKind.Identifier,
94
        text: name,
95
        isReserved: false,
96
        location: location,
97
        leadingWhitespace: '',
98
        leadingTrivia: []
99
    };
100
}
101

102
export function createVariableExpression(ident: string, location?: Location): VariableExpression {
1✔
103
    return new VariableExpression({ name: createToken(TokenKind.Identifier, ident, location) });
26✔
104
}
105

106
export function createDottedIdentifier(path: string[], location?: Location): DottedGetExpression {
1✔
107
    const ident = path.pop();
1✔
108
    const obj = path.length > 1 ? createDottedIdentifier(path, location) : createVariableExpression(path[0], location);
1!
109
    return new DottedGetExpression({
1✔
110
        obj: obj,
111
        name: createToken(TokenKind.Identifier, ident, location),
112
        dot: createToken(TokenKind.Dot, '.', location)
113
    });
114
}
115

116
/**
117
 * Create a StringLiteralExpression. The TokenKind.StringLiteral token value includes the leading and trailing doublequote during lexing.
118
 * Since brightscript doesn't support strings with quotes in them, we can safely auto-detect and wrap the value in quotes in this function.
119
 * @param value - the string value. (value will be wrapped in quotes if they are missing)
120
 */
121
export function createStringLiteral(value: string, location?: Location) {
1✔
122
    //wrap the value in double quotes
123
    if (!value.startsWith('"') && !value.endsWith('"')) {
20✔
124
        value = '"' + value + '"';
17✔
125
    }
126
    return new LiteralExpression({ value: createToken(TokenKind.StringLiteral, value, location) });
20✔
127
}
128
export function createIntegerLiteral(value: string, location?: Location) {
1✔
129
    return new LiteralExpression({ value: createToken(TokenKind.IntegerLiteral, value, location) });
13✔
130
}
131
export function createFloatLiteral(value: string, location?: Location) {
1✔
132
    return new LiteralExpression({ value: createToken(TokenKind.FloatLiteral, value, location) });
3✔
133
}
134
export function createDoubleLiteral(value: string, location?: Location) {
1✔
135
    return new LiteralExpression({ value: createToken(TokenKind.DoubleLiteral, value, location) });
3✔
136
}
137
export function createLongIntegerLiteral(value: string, location?: Location) {
1✔
138
    return new LiteralExpression({ value: createToken(TokenKind.LongIntegerLiteral, value, location) });
3✔
139
}
140
export function createInvalidLiteral(value?: string, location?: Location) {
1✔
141
    return new LiteralExpression({ value: createToken(TokenKind.Invalid, value, location) });
12✔
142
}
143
export function createBooleanLiteral(value: string, location?: Location) {
1✔
144
    return new LiteralExpression({ value: createToken(value === 'true' ? TokenKind.True : TokenKind.False, value, location) });
10✔
145
}
146
export function createFunctionExpression(kind: TokenKind.Sub | TokenKind.Function) {
1✔
147
    return new FunctionExpression({
30✔
148
        parameters: [],
149
        body: new Block({ statements: [] }),
150
        functionType: createToken(kind),
151
        endFunctionType: kind === TokenKind.Sub ? createToken(TokenKind.EndSub) : createToken(TokenKind.EndFunction),
30!
152
        leftParen: createToken(TokenKind.LeftParen),
153
        rightParen: createToken(TokenKind.RightParen)
154
    });
155
}
156

157
export function createMethodStatement(name: string, kind: TokenKind.Sub | TokenKind.Function = TokenKind.Function, modifiers?: Token[]) {
1!
158
    return new MethodStatement({
30✔
159
        modifiers: modifiers,
160
        name: createIdentifier(name),
161
        func: createFunctionExpression(kind)
162
    });
163
}
164

165
export function createCall(callee: Expression, args?: Expression[]) {
1✔
166
    return new CallExpression({
3✔
167
        callee: callee,
168
        openingParen: createToken(TokenKind.LeftParen, '('),
169
        closingParen: createToken(TokenKind.RightParen, ')'),
170
        args: args
171
    });
172
}
173

174
export function createSGToken(text: string, location?: Location) {
1✔
175
    return {
1✔
176
        text: text,
177
        range: location
178
    } as SGToken;
179
}
180

181
/**
182
 * Create an SGAttribute without any ranges
183
 */
184
export function createSGAttribute(keyName: string, value: string) {
1✔
185
    return new SGAttribute({
71✔
186
        key: { text: keyName },
187
        equals: { text: '=' },
188
        openingQuote: { text: '"' },
189
        value: { text: value },
190
        closingQuote: { text: '"' }
191
    });
192
}
193

194
export function createSGInterfaceField(id: string, attributes: { type?: string; alias?: string; value?: string; onChange?: string; alwaysNotify?: string } = {}) {
1!
UNCOV
195
    const attrs = [
×
196
        createSGAttribute('id', id)
197
    ];
UNCOV
198
    for (let key in attributes) {
×
UNCOV
199
        attrs.push(
×
200
            createSGAttribute(key, attributes[key])
201
        );
202
    }
UNCOV
203
    return new SGInterfaceField({
×
204
        startTagOpen: { text: '<' },
205
        startTagName: { text: 'field' },
206
        attributes: attrs,
207
        startTagClose: { text: '/>' }
208
    });
209
}
210

211
export function createSGComponent(name: string, parentName?: string) {
1✔
UNCOV
212
    const attributes = [
×
213
        createSGAttribute('name', name)
214
    ];
UNCOV
215
    if (parentName) {
×
UNCOV
216
        attributes.push(
×
217
            createSGAttribute('extends', parentName)
218
        );
219
    }
UNCOV
220
    return new SGComponent({
×
221
        startTagOpen: { text: '<' },
222
        startTagName: { text: 'component' },
223
        attributes: attributes,
224
        startTagClose: { text: '>' },
225
        elements: [],
226
        endTagOpen: { text: '</' },
227
        endTagName: { text: 'component' },
228
        endTagClose: { text: '>' }
229
    });
230
}
231

232
export function createSGInterfaceFunction(functionName: string) {
1✔
UNCOV
233
    return new SGInterfaceFunction({
×
234
        startTagOpen: { text: '<' },
235
        startTagName: { text: 'function' },
236
        attributes: [createSGAttribute('name', functionName)],
237
        startTagClose: { text: '/>' }
238
    });
239
}
240

241
export function createSGInterface() {
1✔
UNCOV
242
    return new SGInterface({
×
243
        startTagOpen: { text: '<' },
244
        startTagName: { text: 'interface' },
245
        attributes: [],
246
        startTagClose: { text: '>' },
247
        elements: [],
248
        endTagOpen: { text: '</' },
249
        endTagName: { text: 'interface' },
250
        endTagClose: { text: '>' }
251
    });
252
}
253

254
export function createSGScript(attributes: { type?: string; uri?: string }) {
1✔
255
    const attrs = [] as SGAttribute[];
35✔
256
    for (let key in attributes) {
35✔
257
        attrs.push(
70✔
258
            createSGAttribute(key, attributes[key])
259
        );
260
    }
261
    return new SGScript({
35✔
262
        startTagOpen: { text: '<' },
263
        startTagName: { text: 'script' },
264
        attributes: attrs,
265
        startTagClose: { text: '/>' }
266
    });
267
}
268

269
export function createIfStatement(options: {
1✔
270
    if?: Token;
271
    condition: Expression;
272
    then?: Token;
273
    thenBranch: Block;
274
    else?: Token;
275
    elseBranch?: IfStatement | Block;
276
    endIf?: Token;
277
}) {
278
    return new IfStatement(
27✔
279
        {
280
            if: options.if ?? createToken(TokenKind.If),
81!
281
            condition: options.condition,
282
            then: options.then ?? createToken(TokenKind.Then),
81!
283
            thenBranch: options.thenBranch,
284
            else: options.else ?? createToken(TokenKind.Else),
81!
285
            elseBranch: options.elseBranch,
286
            endIf: options.endIf ?? createToken(TokenKind.EndIf)
81!
287
        }
288
    );
289
}
290

291
export function createBlock(options: { statements: Statement[] }) {
1✔
292
    return new Block(options);
54✔
293
}
294

295
export function createAssignmentStatement(options: {
1✔
296
    name: Identifier | string;
297
    equals?: Token;
298
    value: Expression;
299
}) {
300
    return new AssignmentStatement({
34✔
301
        equals: options.equals ?? createToken(TokenKind.Equal),
102!
302
        name: typeof options.name === 'string' ? createIdentifier(options.name) : options.name,
34!
303
        value: options.value
304
    });
305
}
306

307
export function createDottedSetStatement(options: {
1✔
308
    obj: Expression;
309
    dot?: Token;
310
    name: Identifier | string;
311
    equals?: Token;
312
    value: Expression;
313
}) {
314
    return new DottedSetStatement({
6✔
315
        obj: options.obj,
316
        name: typeof options.name === 'string' ? createIdentifier(options.name) : options.name,
6!
317
        value: options.value,
318
        dot: options.dot,
319
        equals: options.equals ?? createToken(TokenKind.Equal)
18!
320
    });
321
}
322

323
export function createIndexedSetStatement(options: {
1✔
324
    obj: Expression;
325
    openingSquare?: Token;
326
    indexes: Expression[];
327
    closingSquare?: Token;
328
    equals?: Token;
329
    value: Expression;
330
}) {
331
    return new IndexedSetStatement({
6✔
332
        obj: options.obj,
333
        indexes: options.indexes,
334
        value: options.value,
335
        openingSquare: options.openingSquare ?? createToken(TokenKind.LeftSquareBracket),
18!
336
        closingSquare: options.closingSquare ?? createToken(TokenKind.RightSquareBracket),
18!
337
        equals: options.equals ?? createToken(TokenKind.Equal)
18!
338
    });
339
}
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2026 Coveralls, Inc