From 821aa3563a990adbd7a07a381d7cb384a265c573 Mon Sep 17 00:00:00 2001 From: Alex Zenla Date: Thu, 14 Sep 2023 14:16:08 -0700 Subject: [PATCH] frontend: implement basic scope analysis --- .../kotlin/gay/pizza/pork/frontend/World.kt | 5 ++ .../frontend/scope/CompilationUnitScope.kt | 36 +++++++++++ .../pizza/pork/frontend/scope/ScopeSymbol.kt | 11 ++++ .../pork/frontend/scope/VisibleScopeSymbol.kt | 8 +++ .../pizza/pork/frontend/scope/WorldScope.kt | 24 +++++++ .../kotlin/gay/pizza/pork/minimal/Tool.kt | 17 +++-- .../pork/parser/DiscardNodeAttribution.kt | 3 +- .../gay/pizza/pork/parser/NodeAttribution.kt | 3 +- .../kotlin/gay/pizza/pork/parser/Parser.kt | 63 +++++++++---------- .../gay/pizza/pork/parser/ParserBase.kt | 4 +- .../pork/parser/ParserNodeAttribution.kt | 3 +- .../gay/pizza/pork/idea/PorkElementTypes.kt | 3 + .../pork/idea/PsiBuilderMarkAttribution.kt | 7 ++- .../kotlin/gay/pizza/pork/tool/RootCommand.kt | 3 +- .../pizza/pork/tool/ScopeAnalysisCommand.kt | 27 ++++++++ 15 files changed, 172 insertions(+), 45 deletions(-) create mode 100644 frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/CompilationUnitScope.kt create mode 100644 frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt create mode 100644 frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/VisibleScopeSymbol.kt create mode 100644 frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/WorldScope.kt create mode 100644 tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt index 32fe291..b26ba0b 100644 --- a/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/World.kt @@ -9,6 +9,7 @@ import gay.pizza.pork.parser.Tokenizer class World(val importSource: ImportSource) { private val internalUnits = mutableMapOf() + private val importedUnits = mutableMapOf>() val units: List get() = internalUnits.values.toList() @@ -37,6 +38,7 @@ class World(val importSource: ImportSource) { val importedUnit = loadOneUnit(importLocator) units.add(importedUnit) } + importedUnits[unit] = units return units } @@ -46,6 +48,9 @@ class World(val importSource: ImportSource) { return unit } + fun importedBy(unit: CompilationUnit): Set = + importedUnits[unit] ?: emptySet() + private fun pickContentSource(form: String): ContentSource = importSource.provideContentSource(form) diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/CompilationUnitScope.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/CompilationUnitScope.kt new file mode 100644 index 0000000..c71eae4 --- /dev/null +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/CompilationUnitScope.kt @@ -0,0 +1,36 @@ +package gay.pizza.pork.frontend.scope + +import gay.pizza.pork.ast.CompilationUnit +import gay.pizza.pork.ast.Symbol + +class CompilationUnitScope(val worldScope: WorldScope, val unit: CompilationUnit) { + val externalSymbols = mutableSetOf() + val internalSymbols = mutableSetOf() + + fun index() { + for (definition in unit.definitions) { + val scopeSymbol = ScopeSymbol(unit, definition) + if (definition.modifiers.export) { + externalSymbols.add(scopeSymbol) + } + internalSymbols.add(scopeSymbol) + } + } + + fun findInternallyVisibleSymbols(): Set { + val allSymbols = mutableMapOf() + val imports = worldScope.world.importedBy(unit) + for (import in imports) { + val scope = worldScope.index(import) + for (importedSymbol in scope.externalSymbols) { + allSymbols[importedSymbol.symbol] = VisibleScopeSymbol(unit, importedSymbol) + } + } + + for (internalSymbol in internalSymbols) { + allSymbols[internalSymbol.symbol] = VisibleScopeSymbol(unit, internalSymbol) + } + + return allSymbols.values.toSet() + } +} diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt new file mode 100644 index 0000000..6a5f462 --- /dev/null +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/ScopeSymbol.kt @@ -0,0 +1,11 @@ +package gay.pizza.pork.frontend.scope + +import gay.pizza.pork.ast.Definition +import gay.pizza.pork.ast.Node + +class ScopeSymbol( + val compilationUnit: Node, + val definition: Definition +) { + val symbol = definition.symbol +} diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/VisibleScopeSymbol.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/VisibleScopeSymbol.kt new file mode 100644 index 0000000..b4a2fdd --- /dev/null +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/VisibleScopeSymbol.kt @@ -0,0 +1,8 @@ +package gay.pizza.pork.frontend.scope + +import gay.pizza.pork.ast.CompilationUnit + +class VisibleScopeSymbol(val visibleToUnit: CompilationUnit, val scopeSymbol: ScopeSymbol) { + val isInternalSymbol: Boolean + get() = visibleToUnit == scopeSymbol.compilationUnit +} diff --git a/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/WorldScope.kt b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/WorldScope.kt new file mode 100644 index 0000000..c993a9f --- /dev/null +++ b/frontend/src/main/kotlin/gay/pizza/pork/frontend/scope/WorldScope.kt @@ -0,0 +1,24 @@ +package gay.pizza.pork.frontend.scope + +import gay.pizza.pork.ast.CompilationUnit +import gay.pizza.pork.frontend.World + +class WorldScope(val world: World) { + private val compilationUnitScopes = mutableMapOf() + + fun indexAll() { + for (unit in world.units) { + index(unit) + } + } + + fun index(unit: CompilationUnit): CompilationUnitScope = + scope(unit).apply { + index() + } + + fun scope(unit: CompilationUnit): CompilationUnitScope = + compilationUnitScopes.computeIfAbsent(unit) { + CompilationUnitScope(this, unit) + } +} diff --git a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt index 3d52759..e27dce4 100644 --- a/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt +++ b/minimal/src/main/kotlin/gay/pizza/pork/minimal/Tool.kt @@ -19,6 +19,9 @@ abstract class Tool { abstract fun createContentSource(): ContentSource abstract fun rootFilePath(): String + val rootImportLocator: ImportLocator + get() = ImportLocator("local", rootFilePath()) + fun tokenize(): TokenStream = Tokenizer(createCharSource()).tokenize() @@ -33,16 +36,20 @@ abstract class Tool { fun visit(visitor: NodeVisitor): T = visitor.visit(parse()) fun loadMainFunction(scope: Scope, setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction { + val world = buildWorld() + val evaluator = Evaluator(world, scope) + setupEvaluator(evaluator) + val resultingScope = evaluator.evaluate(rootImportLocator) + return resultingScope.value("main") as CallableFunction + } + + fun buildWorld(): World { val fileContentSource = createContentSource() val dynamicImportSource = DynamicImportSource() dynamicImportSource.addContentSource("std", PorkStdlib) dynamicImportSource.addContentSource("local", fileContentSource) dynamicImportSource.addContentSource("java", JavaAutogenContentSource) - val world = World(dynamicImportSource) - val evaluator = Evaluator(world, scope) - setupEvaluator(evaluator) - val resultingScope = evaluator.evaluate(ImportLocator("local", rootFilePath())) - return resultingScope.value("main") as CallableFunction + return World(dynamicImportSource) } fun run(scope: Scope, quiet: Boolean = false) { diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/DiscardNodeAttribution.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/DiscardNodeAttribution.kt index 5a63b5b..95f0b5e 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/DiscardNodeAttribution.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/DiscardNodeAttribution.kt @@ -1,10 +1,11 @@ package gay.pizza.pork.parser import gay.pizza.pork.ast.Node +import gay.pizza.pork.ast.NodeType object DiscardNodeAttribution : NodeAttribution { override fun push(token: Token) {} override fun adopt(node: T) {} - override fun guarded(block: () -> T): T = + override fun guarded(type: NodeType?, block: () -> T): T = block() } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/NodeAttribution.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/NodeAttribution.kt index 1a5fc80..878e936 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/NodeAttribution.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/NodeAttribution.kt @@ -1,9 +1,10 @@ package gay.pizza.pork.parser import gay.pizza.pork.ast.Node +import gay.pizza.pork.ast.NodeType interface NodeAttribution { fun push(token: Token) fun adopt(node: T) - fun guarded(block: () -> T): T + fun guarded(type: NodeType?, block: () -> T): T } diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt index 56114c5..f943f42 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/Parser.kt @@ -7,7 +7,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : private var storedSymbol: Symbol? = null private var storedDefinitionModifiers: DefinitionModifiers? = null - override fun parseBlock(): Block = guarded { + override fun parseBlock(): Block = guarded(NodeType.Block) { expect(TokenType.LeftCurly) val items = collect(TokenType.RightCurly) { parseExpression() @@ -45,7 +45,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : } if (expression is SymbolReference && peek(TokenType.Equals)) { - return@guarded guarded { + return@guarded guarded(NodeType.SetAssignment) { attribution.adopt(expression) expect(TokenType.Equals) val value = parseExpression() @@ -73,7 +73,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : TokenType.Or ) ) { - guarded { + guarded(NodeType.InfixOperation) { val infixToken = next() val infixOperator = ParserHelpers.convertInfixOperator(infixToken) InfixOperation(expression, infixOperator, parseExpression()) @@ -81,7 +81,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : } else expression } - override fun parseBooleanLiteral(): BooleanLiteral = guarded { + override fun parseBooleanLiteral(): BooleanLiteral = guarded(NodeType.BooleanLiteral) { if (next(TokenType.True)) { BooleanLiteral(true) } else if (next(TokenType.False)) { @@ -91,12 +91,12 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : } } - override fun parseBreak(): Break = guarded { + override fun parseBreak(): Break = guarded(NodeType.Break) { expect(TokenType.Break) Break() } - override fun parseCompilationUnit(): CompilationUnit = guarded { + override fun parseCompilationUnit(): CompilationUnit = guarded(NodeType.CompilationUnit) { val declarations = mutableListOf() val definitions = mutableListOf() var declarationAccepted = true @@ -118,7 +118,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : CompilationUnit(declarations, definitions) } - override fun parseContinue(): Continue = guarded { + override fun parseContinue(): Continue = guarded(NodeType.Continue) { expect(TokenType.Continue) Continue() } @@ -167,11 +167,11 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : maybeParseDefinition() ?: throw ParseError("Unable to parse definition") } - override fun parseDoubleLiteral(): DoubleLiteral = guarded { + override fun parseDoubleLiteral(): DoubleLiteral = guarded(NodeType.DoubleLiteral) { DoubleLiteral(expect(TokenType.NumberLiteral).text.toDouble()) } - override fun parseForIn(): ForIn = guarded { + override fun parseForIn(): ForIn = guarded(NodeType.ForIn) { expect(TokenType.For) val symbol = parseSymbol() expect(TokenType.In) @@ -180,11 +180,10 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : ForIn(symbol, value, block) } - override fun parseFunctionCall(): FunctionCall = guarded { + override fun parseFunctionCall(): FunctionCall = parseFunctionCall(null) - } - fun parseFunctionCall(target: Symbol?): FunctionCall = guarded { + fun parseFunctionCall(target: Symbol?): FunctionCall = guarded(NodeType.FunctionCall) { val symbol = target ?: parseSymbol() val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { parseExpression() @@ -193,7 +192,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : FunctionCall(symbol, arguments) } - override fun parseFunctionDefinition(): FunctionDefinition = guarded { + override fun parseFunctionDefinition(): FunctionDefinition = guarded(NodeType.FunctionDefinition) { val modifiers = storedDefinitionModifiers ?: parseDefinitionModifiers() expect(TokenType.Func) val name = parseSymbol() @@ -218,7 +217,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : FunctionDefinition(modifiers, name, arguments, block, native) } - override fun parseIf(): If = guarded { + override fun parseIf(): If = guarded(NodeType.If) { expect(TokenType.If) val condition = parseExpression() val thenBlock = parseBlock() @@ -229,7 +228,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : If(condition, thenBlock, elseBlock) } - override fun parseImportDeclaration(): ImportDeclaration = guarded { + override fun parseImportDeclaration(): ImportDeclaration = guarded(NodeType.ImportDeclaration) { expect(TokenType.Import) val form = parseSymbol() val components = oneAndContinuedBy(TokenType.Dot) { @@ -238,7 +237,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : ImportDeclaration(form, components) } - override fun parseInfixOperation(): InfixOperation = guarded { + override fun parseInfixOperation(): InfixOperation = guarded(NodeType.InfixOperation) { val infixToken = next() val infixOperator = ParserHelpers.convertInfixOperator(infixToken) InfixOperation(parseExpression(), infixOperator, parseExpression()) @@ -258,11 +257,11 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : } } - override fun parseIntegerLiteral(): IntegerLiteral = guarded { + override fun parseIntegerLiteral(): IntegerLiteral = guarded(NodeType.IntegerLiteral) { IntegerLiteral(expect(TokenType.NumberLiteral).text.toInt()) } - override fun parseLetAssignment(): LetAssignment = guarded { + override fun parseLetAssignment(): LetAssignment = guarded(NodeType.LetAssignment) { expect(TokenType.Let) val symbol = parseSymbol() expect(TokenType.Equals) @@ -270,7 +269,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : LetAssignment(symbol, value) } - override fun parseLetDefinition(): LetDefinition = guarded { + override fun parseLetDefinition(): LetDefinition = guarded(NodeType.LetDefinition) { val definitionModifiers = storedDefinitionModifiers ?: parseDefinitionModifiers() expect(TokenType.Let) val name = parseSymbol() @@ -279,7 +278,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : LetDefinition(definitionModifiers, name, value) } - override fun parseListLiteral(): ListLiteral = guarded { + override fun parseListLiteral(): ListLiteral = guarded(NodeType.ListLiteral) { expect(TokenType.LeftBracket) val items = collect(TokenType.RightBracket, TokenType.Comma) { parseExpression() @@ -288,50 +287,50 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : ListLiteral(items) } - override fun parseLongLiteral(): LongLiteral = guarded { + override fun parseLongLiteral(): LongLiteral = guarded(NodeType.LongLiteral) { LongLiteral(expect(TokenType.NumberLiteral).text.toLong()) } - override fun parseNative(): Native = guarded { + override fun parseNative(): Native = guarded(NodeType.Native) { expect(TokenType.Native) val form = parseSymbol() val definition = parseStringLiteral() Native(form, definition) } - override fun parseNoneLiteral(): NoneLiteral = guarded { + override fun parseNoneLiteral(): NoneLiteral = guarded(NodeType.NoneLiteral) { expect(TokenType.None) NoneLiteral() } - override fun parseParentheses(): Parentheses = guarded { + override fun parseParentheses(): Parentheses = guarded(NodeType.Parentheses) { expect(TokenType.LeftParentheses) val expression = parseExpression() expect(TokenType.RightParentheses) Parentheses(expression) } - override fun parsePrefixOperation(): PrefixOperation = guarded { + override fun parsePrefixOperation(): PrefixOperation = guarded(NodeType.PrefixOperation) { expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) { PrefixOperation(ParserHelpers.convertPrefixOperator(it), parseExpression()) } } - override fun parseSetAssignment(): SetAssignment = guarded { + override fun parseSetAssignment(): SetAssignment = guarded(NodeType.SetAssignment) { val symbol = storedSymbol ?: parseSymbol() expect(TokenType.Equals) val value = parseExpression() SetAssignment(symbol, value) } - override fun parseStringLiteral(): StringLiteral = guarded { + override fun parseStringLiteral(): StringLiteral = guarded(NodeType.StringLiteral) { expect(TokenType.StringLiteral) { val content = StringEscape.unescape(StringEscape.unquote(it.text)) StringLiteral(content) } } - override fun parseSuffixOperation(): SuffixOperation = guarded { + override fun parseSuffixOperation(): SuffixOperation = guarded(NodeType.SuffixOperation) { val reference = parseSymbolReference() expect(TokenType.PlusPlus, TokenType.MinusMinus) { SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference) @@ -352,15 +351,15 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : } } - override fun parseSymbol(): Symbol = guarded { + override fun parseSymbol(): Symbol = guarded(NodeType.Symbol) { expect(TokenType.Symbol) { Symbol(it.text) } } - override fun parseSymbolReference(): SymbolReference = guarded { + override fun parseSymbolReference(): SymbolReference = guarded(NodeType.SymbolReference) { SymbolReference(parseSymbol()) } - override fun parseVarAssignment(): VarAssignment = guarded { + override fun parseVarAssignment(): VarAssignment = guarded(NodeType.VarAssignment) { expect(TokenType.Var) val symbol = parseSymbol() expect(TokenType.Equals) @@ -368,7 +367,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) : VarAssignment(symbol, value) } - override fun parseWhile(): While = guarded { + override fun parseWhile(): While = guarded(NodeType.While) { expect(TokenType.While) val condition = parseExpression() val block = parseBlock() diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt index d5b959f..96b8127 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserBase.kt @@ -2,6 +2,7 @@ package gay.pizza.pork.parser import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.NodeParser +import gay.pizza.pork.ast.NodeType abstract class ParserBase(val source: TokenSource, val attribution: NodeAttribution) : NodeParser { class ExpectedTokenError(got: Token, vararg expectedTypes: TokenType) : ParseError( @@ -9,7 +10,8 @@ abstract class ParserBase(val source: TokenSource, val attribution: NodeAttribut " but got type ${got.type} '${got.text}'" ) - protected fun guarded(block: () -> T): T = attribution.guarded(block) + protected fun guarded(type: NodeType? = null, block: () -> T): T = + attribution.guarded(type, block) protected fun collect( peeking: TokenType, diff --git a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserNodeAttribution.kt b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserNodeAttribution.kt index b7c4f29..8655524 100644 --- a/parser/src/main/kotlin/gay/pizza/pork/parser/ParserNodeAttribution.kt +++ b/parser/src/main/kotlin/gay/pizza/pork/parser/ParserNodeAttribution.kt @@ -1,6 +1,7 @@ package gay.pizza.pork.parser import gay.pizza.pork.ast.Node +import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.data open class ParserNodeAttribution : NodeAttribution { @@ -22,7 +23,7 @@ open class ParserNodeAttribution : NodeAttribution { } } - override fun guarded(block: () -> T): T { + override fun guarded(type: NodeType?, block: () -> T): T { var store = mutableListOf() current = store stack.add(store) diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkElementTypes.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkElementTypes.kt index 8341c19..8154f73 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkElementTypes.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PorkElementTypes.kt @@ -1,7 +1,10 @@ package gay.pizza.pork.idea +import com.intellij.extapi.psi.ASTWrapperPsiElement +import com.intellij.psi.PsiElement import com.intellij.psi.tree.IElementType import com.intellij.psi.tree.TokenSet +import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.NodeType import gay.pizza.pork.parser.TokenType diff --git a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt index 733f63b..aed09fc 100644 --- a/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt +++ b/support/pork-idea/src/main/kotlin/gay/pizza/pork/idea/PsiBuilderMarkAttribution.kt @@ -2,6 +2,7 @@ package gay.pizza.pork.idea import com.intellij.lang.PsiBuilder import gay.pizza.pork.ast.Node +import gay.pizza.pork.ast.NodeType import gay.pizza.pork.parser.ParseError import gay.pizza.pork.parser.ParserNodeAttribution import java.util.IdentityHashMap @@ -9,14 +10,14 @@ import java.util.IdentityHashMap class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() { private val map = IdentityHashMap() - override fun guarded(block: () -> T): T { + override fun guarded(type: NodeType?, block: () -> T): T { val marker = builder.mark() val result = try { - val item = super.guarded(block) + val item = super.guarded(type, block) marker.done(PorkElementTypes.elementTypeFor(item.type)) item } catch (e: PsiBuilderTokenSource.BadCharacterError) { - marker.error("Bad character.") + marker.error("Invalid character") while (!builder.eof()) { builder.advanceLexer() } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt index 810d455..ad3a829 100644 --- a/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/RootCommand.kt @@ -15,7 +15,8 @@ class RootCommand : CliktCommand( ReprintCommand(), ParseCommand(), AstCommand(), - AttributeCommand() + AttributeCommand(), + ScopeAnalysisCommand() ) } diff --git a/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt b/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt new file mode 100644 index 0000000..dc73135 --- /dev/null +++ b/tool/src/main/kotlin/gay/pizza/pork/tool/ScopeAnalysisCommand.kt @@ -0,0 +1,27 @@ +package gay.pizza.pork.tool + +import com.github.ajalt.clikt.core.CliktCommand +import com.github.ajalt.clikt.parameters.arguments.argument +import gay.pizza.dough.fs.PlatformFsProvider +import gay.pizza.pork.frontend.scope.WorldScope +import gay.pizza.pork.minimal.FileTool + +class ScopeAnalysisCommand : CliktCommand(help = "Run Scope Analysis", name = "scope-analysis") { + val path by argument("file") + + override fun run() { + val tool = FileTool(PlatformFsProvider.resolve(path)) + val world = tool.buildWorld() + val root = world.load(tool.rootImportLocator) + val scope = WorldScope(world).apply { index(root) } + val rootScope = scope.scope(root) + val visibleScopeSymbols = rootScope.findInternallyVisibleSymbols() + for (visibleScopeSymbol in visibleScopeSymbols) { + println( + "symbol ${visibleScopeSymbol.scopeSymbol.symbol.id} " + + "type=${visibleScopeSymbol.scopeSymbol.definition.type.name} " + + "internal=${visibleScopeSymbol.isInternalSymbol}" + ) + } + } +}