UNPKG

@bscotch/gml-parser

Version:

A parser for GML (GameMaker Language) files for programmatic manipulation and analysis of GameMaker projects.

147 lines 5.18 kB
import { Diagnostic, } from './project.diagnostics.js'; import { Range, } from './project.location.js'; import { Type } from './types.js'; import { assert } from './util.js'; export const diagnosticCollections = [ 'GLOBAL_SELF', 'UNDECLARED_GLOBAL_REFERENCE', 'INVALID_OPERATION', 'JSDOC_MISMATCH', ]; export class SignifierProcessor { file; localScopeStack = []; selfStack = []; /** The current ScopeRange, updated as we push/pop local and self */ scope; position; unusedJsdoc; /** * For the current node, the "definitive" self. This is the * self that is used definitionally, e.g. a constructor's self * or a Create event's self. This is only set when we are inside * a "definitive" location for the self, so that we can use this * value to determine if currentSelf is also the definitiveSelf, * or if we are accessing that self externally to where it is defined. */ definitiveSelfStack = []; constructor(file) { this.file = file; this.scope = file.scopes[0]; assert(this.scope, 'SymbolProcessor constructor: File must have a global scope'); this.localScopeStack.push(this.scope.local); this.selfStack.push(this.scope.self); this.position = this.scope.start; this.definitiveSelfStack.push(this.file.definitiveSelf); } consumeJsdoc() { const docs = this.unusedJsdoc; this.unusedJsdoc = undefined; return docs; } /** * If a single node is provided, create a range from it. * If two are provided, use the start of the first and end * of the second to create the range. */ range(loc, endLoc) { const start = this.position.at(loc); const end = this.position.atEnd(endLoc || loc); return new Range(start, end); } addDiagnostic(kind, where, message, severity = 'warning') { this.file.addDiagnostic(kind, new Diagnostic(message, Range.from(this.file, where), severity)); } get fullScope() { return { local: this.currentLocalScope, self: this.currentSelf, definitiveSelf: this.currentDefinitiveSelf, global: this.project.self, selfIsGlobal: this.currentSelf === this.project.self, }; } get asset() { return this.file.asset; } get project() { return this.asset.project; } get globalSelf() { return this.project.self; } get currentLocalScope() { return this.localScopeStack.at(-1); } get currentSelf() { return this.selfStack.at(-1) || this.project.self; } get currentDefinitiveSelf() { return this.definitiveSelfStack.at(-1); } get outerSelf() { return this.selfStack.at(-2) || this.project.self; } nextScope(token, fromTokenEnd) { this.scope = this.scope.createNext(token, fromTokenEnd); this.file.scopes.push(this.scope); return this.scope; } /** * After parsing the entire file, the last scope might not * have an appropriate end position. Set it here! */ setLastScopeEnd(rootNode) { const lastScope = this.file.scopes.at(-1); lastScope.end = this.scope.end.atEnd(rootNode); if (lastScope.end.offset < lastScope.start.offset) { lastScope.end = lastScope.start; } } createStruct(token, endToken) { return new Type('Struct'); } pushScope(startToken, self, localScope, fromTokenEnd) { this.localScopeStack.push(localScope); this.selfStack.push(self); this.nextScope(startToken, fromTokenEnd); this.scope.self = self; this.scope.local = localScope; } popScope(nextScopeToken, nextScopeStartsFromTokenEnd) { this.localScopeStack.pop(); this.selfStack.pop(); this.nextScope(nextScopeToken, nextScopeStartsFromTokenEnd).local = this.currentLocalScope; this.scope.self = this.currentSelf; } pushLocalScope(startToken, fromTokenEnd, localScope = this.createStruct(startToken)) { this.localScopeStack.push(localScope); this.nextScope(startToken, fromTokenEnd).local = localScope; } pushSelfScope(startToken, self, fromTokenEnd, options) { this.selfStack.push(self); const nextScope = this.nextScope(startToken, fromTokenEnd); nextScope.self = self; if (options?.accessorScope) { nextScope.isDotAccessor = true; } } popLocalScope(nextScopeToken, nextScopeStartsFromTokenEnd) { this.localScopeStack.pop(); this.nextScope(nextScopeToken, nextScopeStartsFromTokenEnd).local = this.currentLocalScope; } popSelfScope(nextScopeToken, nextScopeStartsFromTokenEnd) { this.selfStack.pop(); this.nextScope(nextScopeToken, nextScopeStartsFromTokenEnd).self = this.currentSelf; } pushDefinitiveSelf(self) { this.definitiveSelfStack.push(self); } popDefinitiveSelf() { this.definitiveSelfStack.pop(); } } //# sourceMappingURL=visitor.processor.js.map