mirror of
https://github.com/irmen/prog8.git
synced 2025-01-26 19:30:59 +00:00
'Program' is not an ast Node
This commit is contained in:
parent
d7d2eefa4f
commit
3767b4bbe7
@ -24,7 +24,7 @@ import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
class CompilationResult(val success: Boolean,
|
||||
val programAst: Program,
|
||||
val program: Program,
|
||||
val programName: String,
|
||||
val compTarget: ICompilationTarget,
|
||||
val importedFiles: List<Path>)
|
||||
@ -39,7 +39,7 @@ fun compileProgram(filepath: Path,
|
||||
outputDir: Path,
|
||||
errors: IErrorReporter = ErrorReporter()): CompilationResult {
|
||||
var programName = ""
|
||||
lateinit var programAst: Program
|
||||
lateinit var program: Program
|
||||
lateinit var importedFiles: List<Path>
|
||||
|
||||
val compTarget =
|
||||
@ -52,31 +52,31 @@ fun compileProgram(filepath: Path,
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
// import main module and everything it needs
|
||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, sourceDirs)
|
||||
val (programresult, compilationOptions, imported) = parseImports(filepath, errors, compTarget, sourceDirs)
|
||||
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
||||
compilationOptions.optimize = optimize
|
||||
programAst = ast
|
||||
program = programresult
|
||||
importedFiles = imported
|
||||
processAst(programAst, errors, compilationOptions)
|
||||
processAst(program, errors, compilationOptions)
|
||||
if (compilationOptions.optimize)
|
||||
optimizeAst(
|
||||
programAst,
|
||||
program,
|
||||
errors,
|
||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||
compTarget
|
||||
)
|
||||
postprocessAst(programAst, errors, compilationOptions)
|
||||
postprocessAst(program, errors, compilationOptions)
|
||||
|
||||
// println("*********** AST BEFORE ASSEMBLYGEN *************")
|
||||
// printAst(programAst)
|
||||
// printAst(program)
|
||||
|
||||
if (writeAssembly) {
|
||||
val result = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
||||
val result = writeAssembly(program, errors, outputDir, compilationOptions)
|
||||
when (result) {
|
||||
is WriteAssemblyResult.Ok -> programName = result.filename
|
||||
is WriteAssemblyResult.Fail -> {
|
||||
System.err.println(result.error)
|
||||
return CompilationResult(false, programAst, programName, compTarget, importedFiles)
|
||||
return CompilationResult(false, program, programName, compTarget, importedFiles)
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -84,7 +84,7 @@ fun compileProgram(filepath: Path,
|
||||
System.out.flush()
|
||||
System.err.flush()
|
||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||
return CompilationResult(true, programAst, programName, compTarget, importedFiles)
|
||||
return CompilationResult(true, program, programName, compTarget, importedFiles)
|
||||
} catch (px: ParseError) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||
@ -155,20 +155,20 @@ fun parseImports(filepath: Path,
|
||||
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
||||
println("Compiler target: ${compTarget.name}. Parsing...")
|
||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||
val programAst = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
|
||||
bf.program = programAst
|
||||
val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
|
||||
bf.program = program
|
||||
|
||||
val importer = ModuleImporter(programAst, compTarget.name, errors, sourceDirs)
|
||||
val importer = ModuleImporter(program, compTarget.name, errors, sourceDirs)
|
||||
val importedModuleResult = importer.importModule(filepath)
|
||||
importedModuleResult.onFailure { throw it }
|
||||
errors.report()
|
||||
|
||||
val importedFiles = programAst.modules.map { it.source }
|
||||
val importedFiles = program.modules.map { it.source }
|
||||
.filter { it.isFromFilesystem }
|
||||
.map { Path(it.origin) }
|
||||
val compilerOptions = determineCompilationOptions(programAst, compTarget)
|
||||
val compilerOptions = determineCompilationOptions(program, compTarget)
|
||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||
throw ParsingFailedError("${program.modules.first().position} BASIC launcher requires output type PRG.")
|
||||
|
||||
// depending on the machine and compiler options we may have to include some libraries
|
||||
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
||||
@ -178,7 +178,7 @@ fun parseImports(filepath: Path,
|
||||
importer.importLibraryModule("math")
|
||||
importer.importLibraryModule("prog8_lib")
|
||||
errors.report()
|
||||
return Triple(programAst, compilerOptions, importedFiles)
|
||||
return Triple(program, compilerOptions, importedFiles)
|
||||
}
|
||||
|
||||
fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||
@ -241,44 +241,44 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
||||
)
|
||||
}
|
||||
|
||||
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
// perform initial syntax checks and processings
|
||||
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||
programAst.preprocessAst()
|
||||
programAst.checkIdentifiers(errors, compilerOptions)
|
||||
program.preprocessAst()
|
||||
program.checkIdentifiers(errors, compilerOptions)
|
||||
errors.report()
|
||||
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
|
||||
// NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur?
|
||||
// Also they might be optimized away eventually in codegen or by the assembler even
|
||||
programAst.charLiteralsToUByteLiterals(compilerOptions.compTarget)
|
||||
programAst.constantFold(errors, compilerOptions.compTarget)
|
||||
program.charLiteralsToUByteLiterals(compilerOptions.compTarget)
|
||||
program.constantFold(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.reorderStatements(errors)
|
||||
program.reorderStatements(errors)
|
||||
errors.report()
|
||||
programAst.addTypecasts(errors)
|
||||
program.addTypecasts(errors)
|
||||
errors.report()
|
||||
programAst.variousCleanups(programAst, errors)
|
||||
program.variousCleanups(program, errors)
|
||||
errors.report()
|
||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||
program.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
programAst.checkIdentifiers(errors, compilerOptions)
|
||||
program.checkIdentifiers(errors, compilerOptions)
|
||||
errors.report()
|
||||
}
|
||||
|
||||
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||
private fun optimizeAst(program: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||
// optimize the parse tree
|
||||
println("Optimizing...")
|
||||
|
||||
val remover = UnusedCodeRemover(programAst, errors, compTarget)
|
||||
remover.visit(programAst)
|
||||
val remover = UnusedCodeRemover(program, errors, compTarget)
|
||||
remover.visit(program)
|
||||
remover.applyModifications()
|
||||
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = programAst.simplifyExpressions()
|
||||
val optsDone2 = programAst.splitBinaryExpressions(compTarget)
|
||||
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget)
|
||||
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
val optsDone1 = program.simplifyExpressions()
|
||||
val optsDone2 = program.splitBinaryExpressions(compTarget)
|
||||
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
||||
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||
errors.report()
|
||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||
break
|
||||
@ -287,17 +287,17 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions:
|
||||
errors.report()
|
||||
}
|
||||
|
||||
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
programAst.addTypecasts(errors)
|
||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||
program.addTypecasts(errors)
|
||||
errors.report()
|
||||
programAst.variousCleanups(programAst, errors)
|
||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
||||
program.variousCleanups(program, errors)
|
||||
program.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
||||
errors.report()
|
||||
val callGraph = CallGraph(programAst)
|
||||
val callGraph = CallGraph(program)
|
||||
callGraph.checkRecursiveCalls(errors)
|
||||
errors.report()
|
||||
programAst.verifyFunctionArgTypes()
|
||||
programAst.moveMainAndStartToFirst()
|
||||
program.verifyFunctionArgTypes()
|
||||
program.moveMainAndStartToFirst()
|
||||
}
|
||||
|
||||
private sealed class WriteAssemblyResult {
|
||||
@ -305,20 +305,20 @@ private sealed class WriteAssemblyResult {
|
||||
class Fail(val error: String): WriteAssemblyResult()
|
||||
}
|
||||
|
||||
private fun writeAssembly(programAst: Program,
|
||||
private fun writeAssembly(program: Program,
|
||||
errors: IErrorReporter,
|
||||
outputDir: Path,
|
||||
compilerOptions: CompilationOptions
|
||||
): WriteAssemblyResult {
|
||||
// asm generation directly from the Ast
|
||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||
program.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
||||
errors.report()
|
||||
|
||||
// printAst(programAst)
|
||||
// printAst(program)
|
||||
|
||||
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
||||
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
||||
programAst,
|
||||
program,
|
||||
errors,
|
||||
compilerOptions.compTarget.machine.zeropage,
|
||||
compilerOptions,
|
||||
@ -338,10 +338,10 @@ private fun writeAssembly(programAst: Program,
|
||||
}
|
||||
}
|
||||
|
||||
fun printAst(programAst: Program) {
|
||||
fun printAst(program: Program) {
|
||||
println()
|
||||
val printer = AstToSourceTextConverter(::print, programAst)
|
||||
printer.visit(programAst)
|
||||
val printer = AstToSourceTextConverter(::print, program)
|
||||
printer.visit(program)
|
||||
println()
|
||||
}
|
||||
|
||||
|
@ -27,7 +27,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(mainBlocks.size>1)
|
||||
errors.err("more than one 'main' block", mainBlocks[0].position)
|
||||
if(mainBlocks.isEmpty())
|
||||
errors.err("there is no 'main' block", program.modules.firstOrNull()?.position ?: program.position)
|
||||
errors.err("there is no 'main' block", program.modules.firstOrNull()?.position ?: Position.DUMMY)
|
||||
|
||||
for(mainBlock in mainBlocks) {
|
||||
val startSub = mainBlock.subScope("start") as? Subroutine
|
||||
|
@ -26,11 +26,11 @@ class TestCallgraph {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||
val graph = CallGraph(result.programAst)
|
||||
val graph = CallGraph(result.program)
|
||||
|
||||
assertEquals(1, graph.imports.size)
|
||||
assertEquals(1, graph.importedBy.size)
|
||||
val toplevelModule = result.programAst.toplevelModule
|
||||
val toplevelModule = result.program.toplevelModule
|
||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||
assertEquals("string", importedModule.name)
|
||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||
@ -45,7 +45,7 @@ class TestCallgraph {
|
||||
assertFalse(sub in graph.calls)
|
||||
assertFalse(sub in graph.calledBy)
|
||||
|
||||
if(sub === result.programAst.entrypoint)
|
||||
if(sub === result.program.entrypoint)
|
||||
assertFalse(graph.unused(sub), "start() should always be marked as used to avoid having it removed")
|
||||
else
|
||||
assertTrue(graph.unused(sub))
|
||||
@ -66,11 +66,11 @@ class TestCallgraph {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||
val graph = CallGraph(result.programAst)
|
||||
val graph = CallGraph(result.program)
|
||||
|
||||
assertEquals(1, graph.imports.size)
|
||||
assertEquals(1, graph.importedBy.size)
|
||||
val toplevelModule = result.programAst.toplevelModule
|
||||
val toplevelModule = result.program.toplevelModule
|
||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||
assertEquals("string", importedModule.name)
|
||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||
|
@ -34,7 +34,7 @@ class TestCompilerOnCharLit {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||
|
||||
@ -58,7 +58,7 @@ class TestCompilerOnCharLit {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||
|
||||
@ -93,7 +93,7 @@ class TestCompilerOnCharLit {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||
|
||||
|
@ -37,7 +37,7 @@ class TestCompilerOnImportsAndIncludes {
|
||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
||||
.assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val strLits = startSub.statements
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
@ -62,7 +62,7 @@ class TestCompilerOnImportsAndIncludes {
|
||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
||||
.assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val args = startSub.statements
|
||||
.filterIsInstance<FunctionCallStatement>()
|
||||
|
@ -43,7 +43,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val decl = startSub
|
||||
.statements.filterIsInstance<VarDecl>()[0]
|
||||
@ -72,7 +72,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val decl = startSub
|
||||
.statements.filterIsInstance<VarDecl>()[0]
|
||||
@ -154,7 +154,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val iterable = startSub
|
||||
.statements.filterIsInstance<ForLoop>()
|
||||
@ -185,7 +185,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val rangeExpr = startSub
|
||||
.statements.filterIsInstance<ForLoop>()
|
||||
@ -212,7 +212,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val rangeExpr = startSub
|
||||
.statements.filterIsInstance<ForLoop>()
|
||||
@ -256,7 +256,7 @@ class TestCompilerOnRanges {
|
||||
}
|
||||
""").assertSuccess()
|
||||
|
||||
val program = result.programAst
|
||||
val program = result.program
|
||||
val startSub = program.entrypoint
|
||||
val iterable = startSub
|
||||
.statements.filterIsInstance<ForLoop>()
|
||||
|
@ -30,9 +30,9 @@ main {
|
||||
}
|
||||
}
|
||||
""").assertSuccess()
|
||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
|
||||
val moduleNames = result.programAst.modules.map { it.name }
|
||||
val moduleNames = result.program.modules.map { it.name }
|
||||
assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first")
|
||||
assertEquals(listOf(
|
||||
"prog8_interned_strings",
|
||||
@ -44,7 +44,7 @@ main {
|
||||
"prog8_lib"
|
||||
), moduleNames.drop(1), "module order in parse tree")
|
||||
|
||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
}
|
||||
|
||||
@Test
|
||||
@ -61,8 +61,8 @@ main {
|
||||
}
|
||||
}
|
||||
""").assertSuccess()
|
||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
val options = determineCompilationOptions(result.programAst, C64Target)
|
||||
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||
val options = determineCompilationOptions(result.program, C64Target)
|
||||
assertTrue(options.floats)
|
||||
assertEquals(ZeropageType.DONTUSE, options.zeropage)
|
||||
assertTrue(options.noSysInit)
|
||||
|
@ -26,10 +26,10 @@ class TestOptimization {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||
val toplevelModule = result.programAst.toplevelModule
|
||||
val toplevelModule = result.program.toplevelModule
|
||||
val mainBlock = toplevelModule.statements.single() as Block
|
||||
val startSub = mainBlock.statements.single() as Subroutine
|
||||
assertSame(result.programAst.entrypoint, startSub)
|
||||
assertSame(result.program.entrypoint, startSub)
|
||||
assertEquals("start", startSub.name, "only start sub should remain")
|
||||
assertTrue(startSub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
||||
}
|
||||
@ -48,11 +48,11 @@ class TestOptimization {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||
val toplevelModule = result.programAst.toplevelModule
|
||||
val toplevelModule = result.program.toplevelModule
|
||||
val mainBlock = toplevelModule.statements.single() as Block
|
||||
val startSub = mainBlock.statements[0] as Subroutine
|
||||
val emptySub = mainBlock.statements[1] as Subroutine
|
||||
assertSame(result.programAst.entrypoint, startSub)
|
||||
assertSame(result.program.entrypoint, startSub)
|
||||
assertEquals("start", startSub.name)
|
||||
assertEquals("empty", emptySub.name)
|
||||
assertTrue(emptySub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
||||
|
@ -2,19 +2,35 @@ package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.GlobalNamespace
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.C64Target
|
||||
import prog8tests.helpers.assertSuccess
|
||||
import prog8tests.helpers.compileText
|
||||
import kotlin.test.assertEquals
|
||||
import kotlin.test.assertFalse
|
||||
import kotlin.test.assertTrue
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestScoping {
|
||||
|
||||
@Test
|
||||
fun testModulesParentIsGlobalNamespace() {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
|
||||
val module = result.program.toplevelModule
|
||||
assertIs<GlobalNamespace>(module.parent)
|
||||
assertSame(result.program, module.program)
|
||||
assertIs<ParentSentinel>(module.parent.parent)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testAnonScopeVarsMovedIntoSubroutineScope() {
|
||||
val src = """
|
||||
@ -29,7 +45,7 @@ class TestScoping {
|
||||
"""
|
||||
|
||||
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
|
||||
val module = result.programAst.toplevelModule
|
||||
val module = result.program.toplevelModule
|
||||
val mainBlock = module.statements.single() as Block
|
||||
val start = mainBlock.statements.single() as Subroutine
|
||||
val repeatbody = start.statements.filterIsInstance<RepeatLoop>().single().body
|
||||
@ -100,7 +116,7 @@ class TestScoping {
|
||||
"""
|
||||
|
||||
val result = compileText(C64Target, false, src, writeAssembly = true).assertSuccess()
|
||||
val module = result.programAst.toplevelModule
|
||||
val module = result.program.toplevelModule
|
||||
val mainBlock = module.statements.single() as Block
|
||||
val start = mainBlock.statements.single() as Subroutine
|
||||
val labels = start.statements.filterIsInstance<Label>()
|
||||
|
@ -46,7 +46,7 @@ class TestSubroutines {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
val module = result.programAst.toplevelModule
|
||||
val module = result.program.toplevelModule
|
||||
val mainBlock = module.statements.single() as Block
|
||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||
@ -94,7 +94,7 @@ class TestSubroutines {
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
||||
val module = result.programAst.toplevelModule
|
||||
val module = result.program.toplevelModule
|
||||
val mainBlock = module.statements.single() as Block
|
||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||
@ -170,7 +170,7 @@ class TestSubroutines {
|
||||
"""
|
||||
|
||||
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||
val module = result.programAst.toplevelModule
|
||||
val module = result.program.toplevelModule
|
||||
val mainBlock = module.statements.single() as Block
|
||||
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||
|
@ -3,12 +3,9 @@ package prog8.ast
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.StringLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
import prog8.compilerinterface.IStringEncoding
|
||||
import prog8.parser.SourceCode
|
||||
|
||||
const val internedStringsModuleName = "prog8_interned_strings"
|
||||
@ -284,121 +281,6 @@ interface Node {
|
||||
}
|
||||
|
||||
|
||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||
|
||||
class Program(val name: String,
|
||||
val builtinFunctions: IBuiltinFunctions,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding
|
||||
): Node {
|
||||
private val _modules = mutableListOf<Module>()
|
||||
|
||||
val modules: List<Module> = _modules
|
||||
val namespace: GlobalNamespace = GlobalNamespace(modules)
|
||||
|
||||
init {
|
||||
// insert a container module for all interned strings later
|
||||
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
|
||||
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
||||
internedStringsModule.statements.add(block)
|
||||
|
||||
_modules.add(0, internedStringsModule)
|
||||
internedStringsModule.linkParents(namespace)
|
||||
internedStringsModule.program = this
|
||||
}
|
||||
|
||||
fun addModule(module: Module): Program {
|
||||
require(null == _modules.firstOrNull { it.name == module.name })
|
||||
{ "module '${module.name}' already present" }
|
||||
_modules.add(module)
|
||||
module.linkIntoProgram(this)
|
||||
return this
|
||||
}
|
||||
|
||||
fun removeModule(module: Module) = _modules.remove(module)
|
||||
|
||||
fun moveModuleToFront(module: Module): Program {
|
||||
require(_modules.contains(module))
|
||||
{ "Not a module of this program: '${module.name}'"}
|
||||
_modules.remove(module)
|
||||
_modules.add(0, module)
|
||||
return this
|
||||
}
|
||||
|
||||
val allBlocks: List<Block>
|
||||
get() = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||
|
||||
val entrypoint: Subroutine
|
||||
get() {
|
||||
val mainBlocks = allBlocks.filter { it.name == "main" }
|
||||
return when (mainBlocks.size) {
|
||||
0 -> throw FatalAstException("no 'main' block")
|
||||
1 -> mainBlocks[0].subScope("start") as Subroutine
|
||||
else -> throw FatalAstException("more than one 'main' block")
|
||||
}
|
||||
}
|
||||
|
||||
val toplevelModule: Module
|
||||
get() = modules.first { it.name!=internedStringsModuleName }
|
||||
|
||||
val definedLoadAddress: Int
|
||||
get() = toplevelModule.loadAddress
|
||||
|
||||
var actualLoadAddress: Int = 0
|
||||
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||
|
||||
fun internString(string: StringLiteralValue): List<String> {
|
||||
// Move a string literal into the internal, deduplicated, string pool
|
||||
// replace it with a variable declaration that points to the entry in the pool.
|
||||
|
||||
if(string.parent is VarDecl) {
|
||||
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
||||
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
||||
}
|
||||
|
||||
fun getScopedName(string: StringLiteralValue): List<String> {
|
||||
val internedStringsBlock = modules
|
||||
.first { it.name == internedStringsModuleName }.statements
|
||||
.first { it is Block && it.name == internedStringsModuleName } as Block
|
||||
val varName = "string_${internedStringsBlock.statements.size}"
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
|
||||
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
|
||||
)
|
||||
internedStringsBlock.statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
return listOf(internedStringsModuleName, decl.name)
|
||||
}
|
||||
|
||||
val key = Pair(string.value, string.altEncoding)
|
||||
val existing = internedStringsUnique[key]
|
||||
if (existing != null)
|
||||
return existing
|
||||
|
||||
val scopedName = getScopedName(string)
|
||||
internedStringsUnique[key] = scopedName
|
||||
return scopedName
|
||||
}
|
||||
|
||||
override val position: Position = Position.DUMMY
|
||||
override var parent: Node
|
||||
get() = throw FatalAstException("program has no parent")
|
||||
set(_) = throw FatalAstException("can't set parent of program")
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
modules.forEach {
|
||||
it.linkParents(namespace)
|
||||
}
|
||||
}
|
||||
|
||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||
require(node is Module && replacement is Module)
|
||||
val idx = _modules.indexOfFirst { it===node }
|
||||
_modules[idx] = replacement
|
||||
replacement.linkIntoProgram(this)
|
||||
}
|
||||
}
|
||||
|
||||
open class Module(final override var statements: MutableList<Statement>,
|
||||
final override val position: Position,
|
||||
val source: SourceCode) : Node, INameScope {
|
||||
|
112
compilerAst/src/prog8/ast/Program.kt
Normal file
112
compilerAst/src/prog8/ast/Program.kt
Normal file
@ -0,0 +1,112 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.StringLiteralValue
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
import prog8.compilerinterface.IMemSizer
|
||||
import prog8.compilerinterface.IStringEncoding
|
||||
import prog8.parser.SourceCode
|
||||
|
||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||
|
||||
class Program(val name: String,
|
||||
val builtinFunctions: IBuiltinFunctions,
|
||||
val memsizer: IMemSizer,
|
||||
val encoding: IStringEncoding
|
||||
) {
|
||||
private val _modules = mutableListOf<Module>()
|
||||
|
||||
val modules: List<Module> = _modules
|
||||
val namespace: GlobalNamespace = GlobalNamespace(modules)
|
||||
|
||||
init {
|
||||
// insert a container module for all interned strings later
|
||||
val internedStringsModule =
|
||||
Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
|
||||
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
||||
internedStringsModule.statements.add(block)
|
||||
|
||||
_modules.add(0, internedStringsModule)
|
||||
internedStringsModule.linkParents(namespace)
|
||||
internedStringsModule.program = this
|
||||
}
|
||||
|
||||
fun addModule(module: Module): Program {
|
||||
require(null == _modules.firstOrNull { it.name == module.name })
|
||||
{ "module '${module.name}' already present" }
|
||||
_modules.add(module)
|
||||
module.linkIntoProgram(this)
|
||||
return this
|
||||
}
|
||||
|
||||
fun removeModule(module: Module) = _modules.remove(module)
|
||||
|
||||
fun moveModuleToFront(module: Module): Program {
|
||||
require(_modules.contains(module))
|
||||
{ "Not a module of this program: '${module.name}'"}
|
||||
_modules.remove(module)
|
||||
_modules.add(0, module)
|
||||
return this
|
||||
}
|
||||
|
||||
val allBlocks: List<Block>
|
||||
get() = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||
|
||||
val entrypoint: Subroutine
|
||||
get() {
|
||||
val mainBlocks = allBlocks.filter { it.name == "main" }
|
||||
return when (mainBlocks.size) {
|
||||
0 -> throw FatalAstException("no 'main' block")
|
||||
1 -> mainBlocks[0].subScope("start") as Subroutine
|
||||
else -> throw FatalAstException("more than one 'main' block")
|
||||
}
|
||||
}
|
||||
|
||||
val toplevelModule: Module
|
||||
get() = modules.first { it.name!= internedStringsModuleName }
|
||||
|
||||
val definedLoadAddress: Int
|
||||
get() = toplevelModule.loadAddress
|
||||
|
||||
var actualLoadAddress: Int = 0
|
||||
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||
|
||||
fun internString(string: StringLiteralValue): List<String> {
|
||||
// Move a string literal into the internal, deduplicated, string pool
|
||||
// replace it with a variable declaration that points to the entry in the pool.
|
||||
|
||||
if(string.parent is VarDecl) {
|
||||
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
||||
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
||||
}
|
||||
|
||||
fun getScopedName(string: StringLiteralValue): List<String> {
|
||||
val internedStringsBlock = modules
|
||||
.first { it.name == internedStringsModuleName }.statements
|
||||
.first { it is Block && it.name == internedStringsModuleName } as Block
|
||||
val varName = "string_${internedStringsBlock.statements.size}"
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
|
||||
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
|
||||
)
|
||||
internedStringsBlock.statements.add(decl)
|
||||
decl.linkParents(internedStringsBlock)
|
||||
return listOf(internedStringsModuleName, decl.name)
|
||||
}
|
||||
|
||||
val key = Pair(string.value, string.altEncoding)
|
||||
val existing = internedStringsUnique[key]
|
||||
if (existing != null)
|
||||
return existing
|
||||
|
||||
val scopedName = getScopedName(string)
|
||||
internedStringsUnique[key] = scopedName
|
||||
return scopedName
|
||||
}
|
||||
}
|
@ -2,6 +2,7 @@ package prog8.ast.walk
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
|
||||
@ -105,7 +106,7 @@ abstract class AstWalker {
|
||||
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(program: Program): Iterable<IAstModification> = noModifications
|
||||
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@ -146,7 +147,7 @@ abstract class AstWalker {
|
||||
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(program: Program): Iterable<IAstModification> = noModifications
|
||||
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@ -196,9 +197,9 @@ abstract class AstWalker {
|
||||
}
|
||||
|
||||
fun visit(program: Program) {
|
||||
track(before(program, program), program, program)
|
||||
program.modules.forEach { it.accept(this, program) }
|
||||
track(after(program, program), program, program)
|
||||
track(before(program), ParentSentinel, program.namespace)
|
||||
program.modules.forEach { it.accept(this, program.namespace) }
|
||||
track(after(program), ParentSentinel, program.namespace)
|
||||
}
|
||||
|
||||
fun visit(module: Module, parent: Node) {
|
||||
|
Loading…
x
Reference in New Issue
Block a user