diff --git a/docs/lang/syntax.md b/docs/lang/syntax.md index 4c8b9af0..a6f3ed73 100644 --- a/docs/lang/syntax.md +++ b/docs/lang/syntax.md @@ -66,7 +66,7 @@ TODO ### Alias definitions -`alias = ` +`alias = [!]` Sets an alias for a global name. Unless shadowed by a local name, the alias will point to the given global object: @@ -86,6 +86,14 @@ Unless shadowed by a local name, the alias will point to the given global object Aliases can be used for variables, arrays, constants, functions, and types, but not for text encodings, array formats or keywords. +If the alias definition is followed by a `!`, then the alias overrides any other definition of that name. +This allows for overriding definitions of library functions by another library: + + void f() {} + void g() {} + alias f = g! + // now the original f is removed and all calls to f will call g instead + ### Array declarations An array is a continuous sequence of bytes in memory. diff --git a/src/main/scala/millfork/env/Environment.scala b/src/main/scala/millfork/env/Environment.scala index 378b54b3..e2b23148 100644 --- a/src/main/scala/millfork/env/Environment.scala +++ b/src/main/scala/millfork/env/Environment.scala @@ -1331,6 +1331,24 @@ class Environment(val parent: Option[Environment], val prefix: String, val cpuFa } nameCheck(params) } + + def getBooleanConstant(literal: String): Option[Boolean] = + maybeGet[TypedThing](literal).flatMap(_.typ match { + case ConstantBooleanType(_, x) => Some(x) + case _ => None + }) + + def isAlias(name: String): Boolean = { + things.get(name).map(_.isInstanceOf[Alias]).orElse(parent.map(_.isAlias(name))).getOrElse(false) + } + + def getAliases: Map[String, String] = { + things.values.flatMap { + case Alias(a, b, _) => Some(a -> b) + case _ => None + }.toMap ++ parent.map(_.getAliases).getOrElse(Map.empty) + } + } object Environment { diff --git a/src/main/scala/millfork/node/CallGraph.scala b/src/main/scala/millfork/node/CallGraph.scala index c4fc9b37..008ec07d 100644 --- a/src/main/scala/millfork/node/CallGraph.scala +++ b/src/main/scala/millfork/node/CallGraph.scala @@ -33,14 +33,18 @@ abstract class CallGraph(program: Program, log: Logger) { protected val multiaccessibleFunctions = mutable.Set[String]() protected val everCalledFunctions = mutable.Set[String]() protected val allFunctions = mutable.Set[String]() + protected val aliases = mutable.Map[String, String]() entryPoints += "main" program.declarations.foreach(s => add(None, Nil, s)) + everCalledFunctions ++= everCalledFunctions.flatMap(aliases.get) everCalledFunctions.retain(allFunctions) fillOut() def add(currentFunction: Option[String], callingFunctions: List[String], node: Node): Unit = { node match { + case AliasDefinitionStatement(name, target, _) => + aliases += name -> target case f: FunctionDeclarationStatement => allFunctions += f.name if (f.address.isDefined || f.interrupt) entryPoints += f.name @@ -67,6 +71,14 @@ abstract class CallGraph(program: Program, log: Logger) { def fillOut(): Unit = { + + callEdges ++= callEdges.flatMap { + case (a,b) => aliases.get(b).map(a -> _) + } + paramEdges ++= paramEdges.flatMap { + case (a,b) => aliases.get(b).map(a -> _) + } + var changed = true while (changed) { changed = false diff --git a/src/main/scala/millfork/node/Node.scala b/src/main/scala/millfork/node/Node.scala index e59c0055..a0c2f53e 100644 --- a/src/main/scala/millfork/node/Node.scala +++ b/src/main/scala/millfork/node/Node.scala @@ -192,7 +192,9 @@ sealed trait Statement extends Node { def getAllExpressions: List[Expression] } -sealed trait DeclarationStatement extends Statement +sealed trait DeclarationStatement extends Statement { + def name: String +} case class TypeDefinitionStatement(name: String, parent: String) extends DeclarationStatement { override def getAllExpressions: List[Expression] = Nil @@ -261,7 +263,7 @@ case class ProcessedContents(processor: String, values: ArrayContents) extends A ProcessedContents(processor, values.replaceVariable(variableToReplace, expression)) } -case class AliasDefinitionStatement(name: String, target: String) extends DeclarationStatement { +case class AliasDefinitionStatement(name: String, target: String, important: Boolean) extends DeclarationStatement { override def getAllExpressions: List[Expression] = Nil } @@ -283,6 +285,8 @@ case class ParameterDeclaration(typ: String, case class ImportStatement(filename: String) extends DeclarationStatement { override def getAllExpressions: List[Expression] = Nil + + override def name: String = "" } case class FunctionDeclarationStatement(name: String, diff --git a/src/main/scala/millfork/node/Program.scala b/src/main/scala/millfork/node/Program.scala index b3fa361e..20c7bd7c 100644 --- a/src/main/scala/millfork/node/Program.scala +++ b/src/main/scala/millfork/node/Program.scala @@ -7,6 +7,17 @@ import millfork.node.opt.NodeOptimization * @author Karol Stasiak */ case class Program(declarations: List[DeclarationStatement]) { + def applyImportantAliases: Program = { + val importantAliases = declarations.flatMap{ + case AliasDefinitionStatement(name, _, true) => Some(name) + case _ => None + }.toSet + Program(declarations.filter{ + case AliasDefinitionStatement(_, _, true) => true + case d => !importantAliases(d.name) + }) + } + def applyNodeOptimization(o: NodeOptimization, options: CompilationOptions) = Program(o.optimize(declarations, options).asInstanceOf[List[DeclarationStatement]]) def +(p:Program): Program = Program(this.declarations ++ p.declarations) } diff --git a/src/main/scala/millfork/node/opt/UnusedFunctions.scala b/src/main/scala/millfork/node/opt/UnusedFunctions.scala index 74313268..65c2c7ad 100644 --- a/src/main/scala/millfork/node/opt/UnusedFunctions.scala +++ b/src/main/scala/millfork/node/opt/UnusedFunctions.scala @@ -29,7 +29,7 @@ object UnusedFunctions extends NodeOptimization { override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = { val aliases = nodes.flatMap{ - case AliasDefinitionStatement(source, target) => Some(source -> target) + case AliasDefinitionStatement(source, target, _) => Some(source -> target) case _ => None }.toMap val panicRequired = options.flags(CompilationFlag.CheckIndexOutOfBounds) diff --git a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala index 3e8f27c9..2306528d 100644 --- a/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala +++ b/src/main/scala/millfork/node/opt/UnusedGlobalVariables.scala @@ -12,7 +12,7 @@ object UnusedGlobalVariables extends NodeOptimization { override def optimize(nodes: List[Node], options: CompilationOptions): List[Node] = { val aliases = nodes.flatMap{ - case AliasDefinitionStatement(source, target) => Some(source -> target) + case AliasDefinitionStatement(source, target, _) => Some(source -> target) case _ => None }.toMap // TODO: volatile diff --git a/src/main/scala/millfork/output/AbstractAssembler.scala b/src/main/scala/millfork/output/AbstractAssembler.scala index 4c3616a5..f7e37d4a 100644 --- a/src/main/scala/millfork/output/AbstractAssembler.scala +++ b/src/main/scala/millfork/output/AbstractAssembler.scala @@ -197,8 +197,9 @@ abstract class AbstractAssembler[T <: AbstractCode](private val program: Program val compiledFunctions = mutable.Map[String, CompiledFunction[T]]() val recommendedCompilationOrder = callGraph.recommendedCompilationOrder val niceFunctionProperties = mutable.Set[(NiceFunctionProperty, String)]() + val aliases = env.getAliases recommendedCompilationOrder.foreach { f => - env.maybeGet[NormalFunction](f).foreach { function => + if (!env.isAlias(f)) env.maybeGet[NormalFunction](f).foreach { function => val code = compileFunction(function, optimizations, options, inlinedFunctions, labelMap.toMap, niceFunctionProperties.toSet) val strippedCodeForInlining = for { limit <- potentiallyInlineable.get(f) diff --git a/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala b/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala index 5f4956c7..c2b1aec0 100644 --- a/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala +++ b/src/main/scala/millfork/parser/AbstractSourceLoadingQueue.scala @@ -37,7 +37,7 @@ abstract class AbstractSourceLoadingQueue[T](val initialFilenames: List[String], } } options.log.assertNoErrors("Parse failed") - parsedModules.values.reduce(_ + _) + parsedModules.values.reduce(_ + _).applyImportantAliases } def lookupModuleFile(includePath: List[String], moduleName: String, position: Option[Position]): String = { diff --git a/src/main/scala/millfork/parser/MfParser.scala b/src/main/scala/millfork/parser/MfParser.scala index 041d5fa2..9218b9d0 100644 --- a/src/main/scala/millfork/parser/MfParser.scala +++ b/src/main/scala/millfork/parser/MfParser.scala @@ -213,7 +213,8 @@ abstract class MfParser[T](fileId: String, input: String, currentDirectory: Stri p <- position() name <- "alias" ~ !letterOrDigit ~/ SWS ~ identifier ~ HWS target <- "=" ~/ HWS ~/ identifier ~/ HWS - } yield Seq(AliasDefinitionStatement(name, target).pos(p)) + important <- "!".!.? ~/ HWS + } yield Seq(AliasDefinitionStatement(name, target, important.isDefined).pos(p)) def fastAlignmentForArrays: MemoryAlignment def fastAlignmentForFunctions: MemoryAlignment diff --git a/src/test/scala/millfork/test/emu/EmuRun.scala b/src/test/scala/millfork/test/emu/EmuRun.scala index c2f0eebe..d89d8473 100644 --- a/src/test/scala/millfork/test/emu/EmuRun.scala +++ b/src/test/scala/millfork/test/emu/EmuRun.scala @@ -176,7 +176,7 @@ class EmuRun(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimization], tmp += EmuRun.cachedBcd tmp } - val program = nodeOptimizations.foldLeft(withLibraries)((p, opt) => p.applyNodeOptimization(opt, options)) + val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options)) val callGraph = new StandardCallGraph(program, log) val env = new Environment(None, "", CpuFamily.M6502, options.jobContext) env.collectDeclarations(program, options) diff --git a/src/test/scala/millfork/test/emu/EmuZ80Run.scala b/src/test/scala/millfork/test/emu/EmuZ80Run.scala index 27c02059..91f590bb 100644 --- a/src/test/scala/millfork/test/emu/EmuZ80Run.scala +++ b/src/test/scala/millfork/test/emu/EmuZ80Run.scala @@ -98,7 +98,7 @@ class EmuZ80Run(cpu: millfork.Cpu.Value, nodeOptimizations: List[NodeOptimizatio tmp += EmuZ80Run.cachedMath(cpu) tmp } - val program = nodeOptimizations.foldLeft(withLibraries)((p, opt) => p.applyNodeOptimization(opt, options)) + val program = nodeOptimizations.foldLeft(withLibraries.applyImportantAliases)((p, opt) => p.applyNodeOptimization(opt, options)) val callGraph = new StandardCallGraph(program, log) val env = new Environment(None, "", CpuFamily.I80, options.jobContext) env.collectDeclarations(program, options)