frontend: implement basic scope analysis

This commit is contained in:
Alex Zenla 2023-09-14 14:16:08 -07:00
parent aadc8282a5
commit 821aa3563a
Signed by: alex
GPG Key ID: C0780728420EBFE5
15 changed files with 172 additions and 45 deletions

View File

@ -9,6 +9,7 @@ import gay.pizza.pork.parser.Tokenizer
class World(val importSource: ImportSource) { class World(val importSource: ImportSource) {
private val internalUnits = mutableMapOf<String, CompilationUnit>() private val internalUnits = mutableMapOf<String, CompilationUnit>()
private val importedUnits = mutableMapOf<CompilationUnit, Set<CompilationUnit>>()
val units: List<CompilationUnit> val units: List<CompilationUnit>
get() = internalUnits.values.toList() get() = internalUnits.values.toList()
@ -37,6 +38,7 @@ class World(val importSource: ImportSource) {
val importedUnit = loadOneUnit(importLocator) val importedUnit = loadOneUnit(importLocator)
units.add(importedUnit) units.add(importedUnit)
} }
importedUnits[unit] = units
return units return units
} }
@ -46,6 +48,9 @@ class World(val importSource: ImportSource) {
return unit return unit
} }
fun importedBy(unit: CompilationUnit): Set<CompilationUnit> =
importedUnits[unit] ?: emptySet()
private fun pickContentSource(form: String): ContentSource = private fun pickContentSource(form: String): ContentSource =
importSource.provideContentSource(form) importSource.provideContentSource(form)

View File

@ -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<ScopeSymbol>()
val internalSymbols = mutableSetOf<ScopeSymbol>()
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<VisibleScopeSymbol> {
val allSymbols = mutableMapOf<Symbol, VisibleScopeSymbol>()
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()
}
}

View File

@ -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
}

View File

@ -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
}

View File

@ -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<CompilationUnit, CompilationUnitScope>()
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)
}
}

View File

@ -19,6 +19,9 @@ abstract class Tool {
abstract fun createContentSource(): ContentSource abstract fun createContentSource(): ContentSource
abstract fun rootFilePath(): String abstract fun rootFilePath(): String
val rootImportLocator: ImportLocator
get() = ImportLocator("local", rootFilePath())
fun tokenize(): TokenStream = fun tokenize(): TokenStream =
Tokenizer(createCharSource()).tokenize() Tokenizer(createCharSource()).tokenize()
@ -33,16 +36,20 @@ abstract class Tool {
fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse()) fun <T> visit(visitor: NodeVisitor<T>): T = visitor.visit(parse())
fun loadMainFunction(scope: Scope, setupEvaluator: Evaluator.() -> Unit = {}): CallableFunction { 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 fileContentSource = createContentSource()
val dynamicImportSource = DynamicImportSource() val dynamicImportSource = DynamicImportSource()
dynamicImportSource.addContentSource("std", PorkStdlib) dynamicImportSource.addContentSource("std", PorkStdlib)
dynamicImportSource.addContentSource("local", fileContentSource) dynamicImportSource.addContentSource("local", fileContentSource)
dynamicImportSource.addContentSource("java", JavaAutogenContentSource) dynamicImportSource.addContentSource("java", JavaAutogenContentSource)
val world = World(dynamicImportSource) return World(dynamicImportSource)
val evaluator = Evaluator(world, scope)
setupEvaluator(evaluator)
val resultingScope = evaluator.evaluate(ImportLocator("local", rootFilePath()))
return resultingScope.value("main") as CallableFunction
} }
fun run(scope: Scope, quiet: Boolean = false) { fun run(scope: Scope, quiet: Boolean = false) {

View File

@ -1,10 +1,11 @@
package gay.pizza.pork.parser package gay.pizza.pork.parser
import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeType
object DiscardNodeAttribution : NodeAttribution { object DiscardNodeAttribution : NodeAttribution {
override fun push(token: Token) {} override fun push(token: Token) {}
override fun <T : Node> adopt(node: T) {} override fun <T : Node> adopt(node: T) {}
override fun <T : Node> guarded(block: () -> T): T = override fun <T : Node> guarded(type: NodeType?, block: () -> T): T =
block() block()
} }

View File

@ -1,9 +1,10 @@
package gay.pizza.pork.parser package gay.pizza.pork.parser
import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeType
interface NodeAttribution { interface NodeAttribution {
fun push(token: Token) fun push(token: Token)
fun <T: Node> adopt(node: T) fun <T: Node> adopt(node: T)
fun <T: Node> guarded(block: () -> T): T fun <T: Node> guarded(type: NodeType?, block: () -> T): T
} }

View File

@ -7,7 +7,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
private var storedSymbol: Symbol? = null private var storedSymbol: Symbol? = null
private var storedDefinitionModifiers: DefinitionModifiers? = null private var storedDefinitionModifiers: DefinitionModifiers? = null
override fun parseBlock(): Block = guarded { override fun parseBlock(): Block = guarded(NodeType.Block) {
expect(TokenType.LeftCurly) expect(TokenType.LeftCurly)
val items = collect(TokenType.RightCurly) { val items = collect(TokenType.RightCurly) {
parseExpression() parseExpression()
@ -45,7 +45,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
} }
if (expression is SymbolReference && peek(TokenType.Equals)) { if (expression is SymbolReference && peek(TokenType.Equals)) {
return@guarded guarded { return@guarded guarded(NodeType.SetAssignment) {
attribution.adopt(expression) attribution.adopt(expression)
expect(TokenType.Equals) expect(TokenType.Equals)
val value = parseExpression() val value = parseExpression()
@ -73,7 +73,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
TokenType.Or TokenType.Or
) )
) { ) {
guarded { guarded(NodeType.InfixOperation) {
val infixToken = next() val infixToken = next()
val infixOperator = ParserHelpers.convertInfixOperator(infixToken) val infixOperator = ParserHelpers.convertInfixOperator(infixToken)
InfixOperation(expression, infixOperator, parseExpression()) InfixOperation(expression, infixOperator, parseExpression())
@ -81,7 +81,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
} else expression } else expression
} }
override fun parseBooleanLiteral(): BooleanLiteral = guarded { override fun parseBooleanLiteral(): BooleanLiteral = guarded(NodeType.BooleanLiteral) {
if (next(TokenType.True)) { if (next(TokenType.True)) {
BooleanLiteral(true) BooleanLiteral(true)
} else if (next(TokenType.False)) { } 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) expect(TokenType.Break)
Break() Break()
} }
override fun parseCompilationUnit(): CompilationUnit = guarded { override fun parseCompilationUnit(): CompilationUnit = guarded(NodeType.CompilationUnit) {
val declarations = mutableListOf<Declaration>() val declarations = mutableListOf<Declaration>()
val definitions = mutableListOf<Definition>() val definitions = mutableListOf<Definition>()
var declarationAccepted = true var declarationAccepted = true
@ -118,7 +118,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
CompilationUnit(declarations, definitions) CompilationUnit(declarations, definitions)
} }
override fun parseContinue(): Continue = guarded { override fun parseContinue(): Continue = guarded(NodeType.Continue) {
expect(TokenType.Continue) expect(TokenType.Continue)
Continue() Continue()
} }
@ -167,11 +167,11 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
maybeParseDefinition() ?: throw ParseError("Unable to parse definition") 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()) DoubleLiteral(expect(TokenType.NumberLiteral).text.toDouble())
} }
override fun parseForIn(): ForIn = guarded { override fun parseForIn(): ForIn = guarded(NodeType.ForIn) {
expect(TokenType.For) expect(TokenType.For)
val symbol = parseSymbol() val symbol = parseSymbol()
expect(TokenType.In) expect(TokenType.In)
@ -180,11 +180,10 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
ForIn(symbol, value, block) ForIn(symbol, value, block)
} }
override fun parseFunctionCall(): FunctionCall = guarded { override fun parseFunctionCall(): FunctionCall =
parseFunctionCall(null) parseFunctionCall(null)
}
fun parseFunctionCall(target: Symbol?): FunctionCall = guarded { fun parseFunctionCall(target: Symbol?): FunctionCall = guarded(NodeType.FunctionCall) {
val symbol = target ?: parseSymbol() val symbol = target ?: parseSymbol()
val arguments = collect(TokenType.RightParentheses, TokenType.Comma) { val arguments = collect(TokenType.RightParentheses, TokenType.Comma) {
parseExpression() parseExpression()
@ -193,7 +192,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
FunctionCall(symbol, arguments) FunctionCall(symbol, arguments)
} }
override fun parseFunctionDefinition(): FunctionDefinition = guarded { override fun parseFunctionDefinition(): FunctionDefinition = guarded(NodeType.FunctionDefinition) {
val modifiers = storedDefinitionModifiers ?: parseDefinitionModifiers() val modifiers = storedDefinitionModifiers ?: parseDefinitionModifiers()
expect(TokenType.Func) expect(TokenType.Func)
val name = parseSymbol() val name = parseSymbol()
@ -218,7 +217,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
FunctionDefinition(modifiers, name, arguments, block, native) FunctionDefinition(modifiers, name, arguments, block, native)
} }
override fun parseIf(): If = guarded { override fun parseIf(): If = guarded(NodeType.If) {
expect(TokenType.If) expect(TokenType.If)
val condition = parseExpression() val condition = parseExpression()
val thenBlock = parseBlock() val thenBlock = parseBlock()
@ -229,7 +228,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
If(condition, thenBlock, elseBlock) If(condition, thenBlock, elseBlock)
} }
override fun parseImportDeclaration(): ImportDeclaration = guarded { override fun parseImportDeclaration(): ImportDeclaration = guarded(NodeType.ImportDeclaration) {
expect(TokenType.Import) expect(TokenType.Import)
val form = parseSymbol() val form = parseSymbol()
val components = oneAndContinuedBy(TokenType.Dot) { val components = oneAndContinuedBy(TokenType.Dot) {
@ -238,7 +237,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
ImportDeclaration(form, components) ImportDeclaration(form, components)
} }
override fun parseInfixOperation(): InfixOperation = guarded { override fun parseInfixOperation(): InfixOperation = guarded(NodeType.InfixOperation) {
val infixToken = next() val infixToken = next()
val infixOperator = ParserHelpers.convertInfixOperator(infixToken) val infixOperator = ParserHelpers.convertInfixOperator(infixToken)
InfixOperation(parseExpression(), infixOperator, parseExpression()) 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()) IntegerLiteral(expect(TokenType.NumberLiteral).text.toInt())
} }
override fun parseLetAssignment(): LetAssignment = guarded { override fun parseLetAssignment(): LetAssignment = guarded(NodeType.LetAssignment) {
expect(TokenType.Let) expect(TokenType.Let)
val symbol = parseSymbol() val symbol = parseSymbol()
expect(TokenType.Equals) expect(TokenType.Equals)
@ -270,7 +269,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
LetAssignment(symbol, value) LetAssignment(symbol, value)
} }
override fun parseLetDefinition(): LetDefinition = guarded { override fun parseLetDefinition(): LetDefinition = guarded(NodeType.LetDefinition) {
val definitionModifiers = storedDefinitionModifiers ?: parseDefinitionModifiers() val definitionModifiers = storedDefinitionModifiers ?: parseDefinitionModifiers()
expect(TokenType.Let) expect(TokenType.Let)
val name = parseSymbol() val name = parseSymbol()
@ -279,7 +278,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
LetDefinition(definitionModifiers, name, value) LetDefinition(definitionModifiers, name, value)
} }
override fun parseListLiteral(): ListLiteral = guarded { override fun parseListLiteral(): ListLiteral = guarded(NodeType.ListLiteral) {
expect(TokenType.LeftBracket) expect(TokenType.LeftBracket)
val items = collect(TokenType.RightBracket, TokenType.Comma) { val items = collect(TokenType.RightBracket, TokenType.Comma) {
parseExpression() parseExpression()
@ -288,50 +287,50 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
ListLiteral(items) ListLiteral(items)
} }
override fun parseLongLiteral(): LongLiteral = guarded { override fun parseLongLiteral(): LongLiteral = guarded(NodeType.LongLiteral) {
LongLiteral(expect(TokenType.NumberLiteral).text.toLong()) LongLiteral(expect(TokenType.NumberLiteral).text.toLong())
} }
override fun parseNative(): Native = guarded { override fun parseNative(): Native = guarded(NodeType.Native) {
expect(TokenType.Native) expect(TokenType.Native)
val form = parseSymbol() val form = parseSymbol()
val definition = parseStringLiteral() val definition = parseStringLiteral()
Native(form, definition) Native(form, definition)
} }
override fun parseNoneLiteral(): NoneLiteral = guarded { override fun parseNoneLiteral(): NoneLiteral = guarded(NodeType.NoneLiteral) {
expect(TokenType.None) expect(TokenType.None)
NoneLiteral() NoneLiteral()
} }
override fun parseParentheses(): Parentheses = guarded { override fun parseParentheses(): Parentheses = guarded(NodeType.Parentheses) {
expect(TokenType.LeftParentheses) expect(TokenType.LeftParentheses)
val expression = parseExpression() val expression = parseExpression()
expect(TokenType.RightParentheses) expect(TokenType.RightParentheses)
Parentheses(expression) Parentheses(expression)
} }
override fun parsePrefixOperation(): PrefixOperation = guarded { override fun parsePrefixOperation(): PrefixOperation = guarded(NodeType.PrefixOperation) {
expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) { expect(TokenType.Not, TokenType.Plus, TokenType.Minus, TokenType.Tilde) {
PrefixOperation(ParserHelpers.convertPrefixOperator(it), parseExpression()) PrefixOperation(ParserHelpers.convertPrefixOperator(it), parseExpression())
} }
} }
override fun parseSetAssignment(): SetAssignment = guarded { override fun parseSetAssignment(): SetAssignment = guarded(NodeType.SetAssignment) {
val symbol = storedSymbol ?: parseSymbol() val symbol = storedSymbol ?: parseSymbol()
expect(TokenType.Equals) expect(TokenType.Equals)
val value = parseExpression() val value = parseExpression()
SetAssignment(symbol, value) SetAssignment(symbol, value)
} }
override fun parseStringLiteral(): StringLiteral = guarded { override fun parseStringLiteral(): StringLiteral = guarded(NodeType.StringLiteral) {
expect(TokenType.StringLiteral) { expect(TokenType.StringLiteral) {
val content = StringEscape.unescape(StringEscape.unquote(it.text)) val content = StringEscape.unescape(StringEscape.unquote(it.text))
StringLiteral(content) StringLiteral(content)
} }
} }
override fun parseSuffixOperation(): SuffixOperation = guarded { override fun parseSuffixOperation(): SuffixOperation = guarded(NodeType.SuffixOperation) {
val reference = parseSymbolReference() val reference = parseSymbolReference()
expect(TokenType.PlusPlus, TokenType.MinusMinus) { expect(TokenType.PlusPlus, TokenType.MinusMinus) {
SuffixOperation(ParserHelpers.convertSuffixOperator(it), reference) 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) } expect(TokenType.Symbol) { Symbol(it.text) }
} }
override fun parseSymbolReference(): SymbolReference = guarded { override fun parseSymbolReference(): SymbolReference = guarded(NodeType.SymbolReference) {
SymbolReference(parseSymbol()) SymbolReference(parseSymbol())
} }
override fun parseVarAssignment(): VarAssignment = guarded { override fun parseVarAssignment(): VarAssignment = guarded(NodeType.VarAssignment) {
expect(TokenType.Var) expect(TokenType.Var)
val symbol = parseSymbol() val symbol = parseSymbol()
expect(TokenType.Equals) expect(TokenType.Equals)
@ -368,7 +367,7 @@ class Parser(source: TokenSource, attribution: NodeAttribution) :
VarAssignment(symbol, value) VarAssignment(symbol, value)
} }
override fun parseWhile(): While = guarded { override fun parseWhile(): While = guarded(NodeType.While) {
expect(TokenType.While) expect(TokenType.While)
val condition = parseExpression() val condition = parseExpression()
val block = parseBlock() val block = parseBlock()

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.parser
import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeParser import gay.pizza.pork.ast.NodeParser
import gay.pizza.pork.ast.NodeType
abstract class ParserBase(val source: TokenSource, val attribution: NodeAttribution) : NodeParser { abstract class ParserBase(val source: TokenSource, val attribution: NodeAttribution) : NodeParser {
class ExpectedTokenError(got: Token, vararg expectedTypes: TokenType) : ParseError( 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}'" " but got type ${got.type} '${got.text}'"
) )
protected fun <T: Node> guarded(block: () -> T): T = attribution.guarded(block) protected fun <T: Node> guarded(type: NodeType? = null, block: () -> T): T =
attribution.guarded(type, block)
protected fun <T> collect( protected fun <T> collect(
peeking: TokenType, peeking: TokenType,

View File

@ -1,6 +1,7 @@
package gay.pizza.pork.parser package gay.pizza.pork.parser
import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.ast.data import gay.pizza.pork.ast.data
open class ParserNodeAttribution : NodeAttribution { open class ParserNodeAttribution : NodeAttribution {
@ -22,7 +23,7 @@ open class ParserNodeAttribution : NodeAttribution {
} }
} }
override fun <T : Node> guarded(block: () -> T): T { override fun <T : Node> guarded(type: NodeType?, block: () -> T): T {
var store = mutableListOf<Token>() var store = mutableListOf<Token>()
current = store current = store
stack.add(store) stack.add(store)

View File

@ -1,7 +1,10 @@
package gay.pizza.pork.idea 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.IElementType
import com.intellij.psi.tree.TokenSet import com.intellij.psi.tree.TokenSet
import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeType import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.parser.TokenType import gay.pizza.pork.parser.TokenType

View File

@ -2,6 +2,7 @@ package gay.pizza.pork.idea
import com.intellij.lang.PsiBuilder import com.intellij.lang.PsiBuilder
import gay.pizza.pork.ast.Node import gay.pizza.pork.ast.Node
import gay.pizza.pork.ast.NodeType
import gay.pizza.pork.parser.ParseError import gay.pizza.pork.parser.ParseError
import gay.pizza.pork.parser.ParserNodeAttribution import gay.pizza.pork.parser.ParserNodeAttribution
import java.util.IdentityHashMap import java.util.IdentityHashMap
@ -9,14 +10,14 @@ import java.util.IdentityHashMap
class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() { class PsiBuilderMarkAttribution(val builder: PsiBuilder) : ParserNodeAttribution() {
private val map = IdentityHashMap<Node, Node>() private val map = IdentityHashMap<Node, Node>()
override fun <T : Node> guarded(block: () -> T): T { override fun <T : Node> guarded(type: NodeType?, block: () -> T): T {
val marker = builder.mark() val marker = builder.mark()
val result = try { val result = try {
val item = super.guarded(block) val item = super.guarded(type, block)
marker.done(PorkElementTypes.elementTypeFor(item.type)) marker.done(PorkElementTypes.elementTypeFor(item.type))
item item
} catch (e: PsiBuilderTokenSource.BadCharacterError) { } catch (e: PsiBuilderTokenSource.BadCharacterError) {
marker.error("Bad character.") marker.error("Invalid character")
while (!builder.eof()) { while (!builder.eof()) {
builder.advanceLexer() builder.advanceLexer()
} }

View File

@ -15,7 +15,8 @@ class RootCommand : CliktCommand(
ReprintCommand(), ReprintCommand(),
ParseCommand(), ParseCommand(),
AstCommand(), AstCommand(),
AttributeCommand() AttributeCommand(),
ScopeAnalysisCommand()
) )
} }

View File

@ -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}"
)
}
}
}