simplify DirectiveArg

This commit is contained in:
Irmen de Jong 2025-02-02 04:35:20 +01:00
parent 216825b98a
commit 75ddcda5f3
11 changed files with 43 additions and 42 deletions

View File

@ -367,10 +367,10 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
val toplevelModule = program.toplevelModule val toplevelModule = program.toplevelModule
val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive) val outputDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%output" } as? Directive)
val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive) val launcherDirective = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" } as? Directive)
val outputTypeStr = outputDirective?.args?.single()?.name?.uppercase() val outputTypeStr = outputDirective?.args?.single()?.string?.uppercase()
val launcherTypeStr = launcherDirective?.args?.single()?.name?.uppercase() val launcherTypeStr = launcherDirective?.args?.single()?.string?.uppercase()
val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" } val zpoption: String? = (toplevelModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
as? Directive)?.args?.single()?.name?.uppercase() as? Directive)?.args?.single()?.string?.uppercase()
val allOptions = program.modules.flatMap { it.options() }.toSet() val allOptions = program.modules.flatMap { it.options() }.toSet()
val floatsEnabled = "enable_floats" in allOptions val floatsEnabled = "enable_floats" in allOptions
var noSysInit = "no_sysinit" in allOptions var noSysInit = "no_sysinit" in allOptions

View File

@ -46,7 +46,7 @@ class ModuleImporter(private val program: Program,
fun importImplicitLibraryModule(name: String): Module? { fun importImplicitLibraryModule(name: String): Module? {
val import = Directive("%import", listOf( val import = Directive("%import", listOf(
DirectiveArg("", name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0)) DirectiveArg(name, 42u, position = Position("<<<implicit-import>>>", 0, 0, 0))
), Position("<<<implicit-import>>>", 0, 0, 0)) ), Position("<<<implicit-import>>>", 0, 0, 0))
return executeImportDirective(import, null) return executeImportDirective(import, null)
} }
@ -75,9 +75,7 @@ class ModuleImporter(private val program: Program,
private fun executeImportDirective(import: Directive, importingModule: Module?): Module? { private fun executeImportDirective(import: Directive, importingModule: Module?): Module? {
if(import.directive!="%import" || import.args.size!=1) if(import.directive!="%import" || import.args.size!=1)
throw SyntaxError("invalid import directive", import.position) throw SyntaxError("invalid import directive", import.position)
if(!import.args[0].str.isNullOrEmpty() || import.args[0].name==null) val moduleName = import.args[0].string!!
throw SyntaxError("%import requires unquoted module name", import.position)
val moduleName = import.args[0].name!!
if("$moduleName.p8" == import.position.file) if("$moduleName.p8" == import.position.file)
throw SyntaxError("cannot import self", import.position) throw SyntaxError("cannot import self", import.position)

View File

@ -955,24 +955,24 @@ internal class AstChecker(private val program: Program,
"%output" -> { "%output" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in OutputType.entries.map {it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].string !in OutputType.entries.map {it.name.lowercase()})
err("invalid output directive type") err("invalid output directive type")
} }
"%launcher" -> { "%launcher" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name !in CbmPrgLauncherType.entries.map{it.name.lowercase()}) if(directive.args.size!=1 || directive.args[0].string !in CbmPrgLauncherType.entries.map{it.name.lowercase()})
err("invalid launcher directive type") err("invalid launcher directive type")
} }
"%zeropage" -> { "%zeropage" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || if(directive.args.size!=1 ||
directive.args[0].name != "basicsafe" && directive.args[0].string != "basicsafe" &&
directive.args[0].name != "floatsafe" && directive.args[0].string != "floatsafe" &&
directive.args[0].name != "kernalsafe" && directive.args[0].string != "kernalsafe" &&
directive.args[0].name != "dontuse" && directive.args[0].string != "dontuse" &&
directive.args[0].name != "full") directive.args[0].string != "full")
err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, dontuse, or full") err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, dontuse, or full")
} }
"%zpreserved", "%zpallowed" -> { "%zpreserved", "%zpallowed" -> {
@ -998,9 +998,9 @@ internal class AstChecker(private val program: Program,
"%import" -> { "%import" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
if(directive.args.size!=1 || directive.args[0].name==null) if(directive.args.size!=1 || directive.args[0].string==null)
err("invalid import directive, expected module name argument") err("invalid import directive, expected module name argument")
if(directive.args[0].name == (directive.parent as? Module)?.name) if(directive.args[0].string == (directive.parent as? Module)?.name)
err("invalid import directive, cannot import itself") err("invalid import directive, cannot import itself")
} }
"%breakpoint" -> { "%breakpoint" -> {
@ -1012,44 +1012,44 @@ internal class AstChecker(private val program: Program,
"%asminclude" -> { "%asminclude" -> {
if(directive.parent !is INameScope || directive.parent is Module) if(directive.parent !is INameScope || directive.parent is Module)
err("this directive can't be used here") err("this directive can't be used here")
if(directive.args.size!=1 || directive.args[0].str==null) if(directive.args.size!=1 || directive.args[0].string==null)
err("invalid asminclude directive, expected argument: \"filename\"") err("invalid asminclude directive, expected argument: \"filename\"")
checkFileExists(directive, directive.args[0].str!!) checkFileExists(directive, directive.args[0].string!!)
} }
"%asmbinary" -> { "%asmbinary" -> {
if(directive.parent !is INameScope || directive.parent is Module) if(directive.parent !is INameScope || directive.parent is Module)
err("this directive can't be used here") err("this directive can't be used here")
val errormsg = "invalid asmbinary directive, expected arguments: \"filename\" [, offset [, length ] ]" val errormsg = "invalid asmbinary directive, expected arguments: \"filename\" [, offset [, length ] ]"
if(directive.args.isEmpty()) err(errormsg) if(directive.args.isEmpty()) err(errormsg)
else if(directive.args[0].str==null) err(errormsg) else if(directive.args[0].string==null) err(errormsg)
else if(directive.args.size>=2 && directive.args[1].int==null) err(errormsg) else if(directive.args.size>=2 && directive.args[1].int==null) err(errormsg)
else if(directive.args.size==3 && directive.args[2].int==null) err(errormsg) else if(directive.args.size==3 && directive.args[2].int==null) err(errormsg)
else if(directive.args.size>3) err(errormsg) else if(directive.args.size>3) err(errormsg)
else checkFileExists(directive, directive.args[0].str!!) else checkFileExists(directive, directive.args[0].string!!)
} }
"%option" -> { "%option" -> {
if(directive.parent !is Block && directive.parent !is Module) if(directive.parent !is Block && directive.parent !is Module)
err("this directive may only occur in a block or at module level") err("this directive may only occur in a block or at module level")
if(directive.args.isEmpty()) if(directive.args.isEmpty())
err("missing option directive argument(s)") err("missing option directive argument(s)")
else if(directive.args.map{it.name in arrayOf("enable_floats", "force_output", "no_sysinit", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")}.any { !it }) else if(directive.args.map{it.string in arrayOf("enable_floats", "force_output", "no_sysinit", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")}.any { !it })
err("invalid option directive argument(s)") err("invalid option directive argument(s)")
if(directive.parent is Block) { if(directive.parent is Block) {
if(directive.args.any {it.name !in arrayOf("force_output", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")}) if(directive.args.any {it.string !in arrayOf("force_output", "merge", "verafxmuls", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for blocks") err("using an option that is not valid for blocks")
} }
if(directive.parent is Module) { if(directive.parent is Module) {
if(directive.args.any {it.name !in arrayOf("enable_floats", "no_sysinit", "no_symbol_prefixing", "ignore_unused")}) if(directive.args.any {it.string !in arrayOf("enable_floats", "no_sysinit", "no_symbol_prefixing", "ignore_unused")})
err("using an option that is not valid for modules") err("using an option that is not valid for modules")
} }
if(directive.args.any { it.name=="verafxmuls" } && compilerOptions.compTarget.name != Cx16Target.NAME) if(directive.args.any { it.string=="verafxmuls" } && compilerOptions.compTarget.name != Cx16Target.NAME)
err("verafx option is only valid on cx16 target") err("verafx option is only valid on cx16 target")
} }
"%encoding" -> { "%encoding" -> {
if(directive.parent !is Module) if(directive.parent !is Module)
err("this directive may only occur at module level") err("this directive may only occur at module level")
val allowedEncodings = Encoding.entries.map {it.prefix} val allowedEncodings = Encoding.entries.map {it.prefix}
if(directive.args.size!=1 || directive.args[0].name !in allowedEncodings) if(directive.args.size!=1 || directive.args[0].string !in allowedEncodings)
err("invalid encoding directive, expected one of $allowedEncodings") err("invalid encoding directive, expected one of $allowedEncodings")
} }
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)

View File

@ -201,13 +201,13 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
val directives = srcBlock.statements.filterIsInstance<Directive>() val directives = srcBlock.statements.filterIsInstance<Directive>()
for (directive in directives.filter { it.directive == "%option" }) { for (directive in directives.filter { it.directive == "%option" }) {
for (arg in directive.args) { for (arg in directive.args) {
when (arg.name) { when (arg.string) {
"no_symbol_prefixing" -> noSymbolPrefixing = true "no_symbol_prefixing" -> noSymbolPrefixing = true
"ignore_unused" -> ignoreUnused = true "ignore_unused" -> ignoreUnused = true
"force_output" -> forceOutput = true "force_output" -> forceOutput = true
"merge" -> { /* ignore this one */ } "merge" -> { /* ignore this one */ }
"verafxmuls" -> veraFxMuls = true "verafxmuls" -> veraFxMuls = true
else -> throw FatalAstException("weird directive option: ${arg.name}") else -> throw FatalAstException("weird directive option: ${arg.string}")
} }
} }
} }
@ -253,7 +253,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
PtAlign(align, directive.position) PtAlign(align, directive.position)
} }
"%asmbinary" -> { "%asmbinary" -> {
val filename = directive.args[0].str!! val filename = directive.args[0].string!!
val offset: UInt? = if(directive.args.size>=2) directive.args[1].int!! else null val offset: UInt? = if(directive.args.size>=2) directive.args[1].int!! else null
val length: UInt? = if(directive.args.size>=3) directive.args[2].int!! else null val length: UInt? = if(directive.args.size>=3) directive.args[2].int!! else null
val abspath = if(File(filename).isFile) { val abspath = if(File(filename).isFile) {
@ -268,7 +268,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
throw FatalAstException("included file doesn't exist") throw FatalAstException("included file doesn't exist")
} }
"%asminclude" -> { "%asminclude" -> {
val result = loadAsmIncludeFile(directive.args[0].str!!, directive.definingModule.source) val result = loadAsmIncludeFile(directive.args[0].string!!, directive.definingModule.source)
val assembly = result.getOrElse { throw it } val assembly = result.getOrElse { throw it }
PtInlineAssembly(assembly.trimEnd().trimStart('\r', '\n'), false, directive.position) PtInlineAssembly(assembly.trimEnd().trimStart('\r', '\n'), false, directive.position)
} }

View File

@ -17,7 +17,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
override fun after(block: Block, parent: Node): Iterable<IAstModification> { override fun after(block: Block, parent: Node): Iterable<IAstModification> {
val inheritOptions = block.definingModule.options() intersect setOf("no_symbol_prefixing", "ignore_unused") subtract block.options() val inheritOptions = block.definingModule.options() intersect setOf("no_symbol_prefixing", "ignore_unused") subtract block.options()
if(inheritOptions.isNotEmpty()) { if(inheritOptions.isNotEmpty()) {
val directive = Directive("%option", inheritOptions.map{ DirectiveArg(null, it, null, block.position) }, block.position) val directive = Directive("%option", inheritOptions.map{ DirectiveArg(it, null, block.position) }, block.position)
return listOf(IAstModification.InsertFirst(directive, block)) return listOf(IAstModification.InsertFirst(directive, block))
} }

View File

@ -107,8 +107,7 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
for(arg in directive.args) { for(arg in directive.args) {
when { when {
arg.int!=null -> output(arg.int.toString()) arg.int!=null -> output(arg.int.toString())
arg.name!=null -> output(arg.name) arg.string!=null -> output(arg.string)
arg.str!=null -> output("\"${arg.str}\"")
} }
if(arg!==directive.args.last()) if(arg!==directive.args.last())
output(",") output(",")

View File

@ -328,7 +328,7 @@ open class Module(final override val statements: MutableList<Statement>,
override fun toString() = "Module(name=$name, pos=$position, lib=${isLibrary})" override fun toString() = "Module(name=$name, pos=$position, lib=${isLibrary})"
override fun referencesIdentifier(nameInSource: List<String>): Boolean = statements.any { it.referencesIdentifier(nameInSource) } override fun referencesIdentifier(nameInSource: List<String>): Boolean = statements.any { it.referencesIdentifier(nameInSource) }
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet() fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.string!!}.toSet()
fun accept(visitor: IAstVisitor) = visitor.visit(this) fun accept(visitor: IAstVisitor) = visitor.visit(this)
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
@ -336,7 +336,7 @@ open class Module(final override val statements: MutableList<Statement>,
val textEncoding: Encoding by lazy { val textEncoding: Encoding by lazy {
val encoding = (statements.singleOrNull { it is Directive && it.directive == "%encoding" } as? Directive) val encoding = (statements.singleOrNull { it is Directive && it.directive == "%encoding" } as? Directive)
if(encoding!=null) if(encoding!=null)
Encoding.entries.first { it.prefix==encoding.args[0].name } Encoding.entries.first { it.prefix==encoding.args[0].string }
else else
program.encoding.defaultEncoding program.encoding.defaultEncoding
} }

View File

@ -25,7 +25,7 @@ class Program(val name: String,
// insert a container module for all interned strings later // insert a container module for all interned strings later
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName)) val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY) val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
val directive = Directive("%option", listOf(DirectiveArg(null,"no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY) val directive = Directive("%option", listOf(DirectiveArg("no_symbol_prefixing", null, Position.DUMMY)), Position.DUMMY)
block.statements.add(directive) block.statements.add(directive)
directive.linkParents(block) directive.linkParents(block)
internedStringsModule.statements.add(block) internedStringsModule.statements.add(block)

View File

@ -445,9 +445,13 @@ internal fun DirectiveContext.toAst() : Directive =
private fun DirectiveargContext.toAst() : DirectiveArg { private fun DirectiveargContext.toAst() : DirectiveArg {
val str = stringliteral() val str = stringliteral()
if(str?.encoding?.text!=null) if(str!=null) {
throw SyntaxError("don't use a string encoding for directive arguments", toPosition()) if (str.encoding?.text != null)
return DirectiveArg(str?.text?.substring(1, text.length-1), identifier()?.text, integerliteral()?.toAst()?.number?.toUInt(), toPosition()) throw SyntaxError("don't use a string encoding for directive arguments", toPosition())
return DirectiveArg(str.text.substring(1, text.length-1), integerliteral()?.toAst()?.number?.toUInt(), toPosition())
}
return DirectiveArg(identifier()?.text, integerliteral()?.toAst()?.number?.toUInt(), toPosition())
} }
private fun IntegerliteralContext.toAst(): NumericLiteralNode { private fun IntegerliteralContext.toAst(): NumericLiteralNode {

View File

@ -94,7 +94,7 @@ class Block(override val name: String,
override fun toString() = "Block(name=$name, address=$address, ${statements.size} statements)" override fun toString() = "Block(name=$name, address=$address, ${statements.size} statements)"
override fun referencesIdentifier(nameInSource: List<String>): Boolean = statements.any { it.referencesIdentifier(nameInSource) } override fun referencesIdentifier(nameInSource: List<String>): Boolean = statements.any { it.referencesIdentifier(nameInSource) }
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet() fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.string!!}.toSet()
} }
// note: a Directive is not strictly always Statement (in module scope, it's a Module Element rather) // note: a Directive is not strictly always Statement (in module scope, it's a Module Element rather)
@ -132,14 +132,14 @@ data class Directive(val directive: String, val args: List<DirectiveArg>, overri
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent) override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
} }
data class DirectiveArg(val str: String?, val name: String?, val int: UInt?, override val position: Position) : Node { data class DirectiveArg(val string: String?, val int: UInt?, override val position: Position) : Node {
override lateinit var parent: Node override lateinit var parent: Node
override fun linkParents(parent: Node) { override fun linkParents(parent: Node) {
this.parent = parent this.parent = parent
} }
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here") override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
override fun copy() = DirectiveArg(str, name, int, position) override fun copy() = DirectiveArg(string, int, position)
override fun referencesIdentifier(nameInSource: List<String>): Boolean = false override fun referencesIdentifier(nameInSource: List<String>): Boolean = false
} }

View File

@ -56,7 +56,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
override fun visit(directive: Directive) { override fun visit(directive: Directive) {
val thisModule = directive.definingModule val thisModule = directive.definingModule
if (directive.directive == "%import") { if (directive.directive == "%import") {
val importedModule = program.modules.singleOrNull { it.name == directive.args[0].name } // the module may no longer exist at all due to optimizations val importedModule = program.modules.singleOrNull { it.name == directive.args[0].string } // the module may no longer exist at all due to optimizations
if(importedModule!=null) { if(importedModule!=null) {
imports[thisModule] = imports.getValue(thisModule) + importedModule imports[thisModule] = imports.getValue(thisModule) + importedModule
importedBy[importedModule] = importedBy.getValue(importedModule) + thisModule importedBy[importedModule] = importedBy.getValue(importedModule) + thisModule