From 2b3382ff8e220f3c5e8db96f2ac5bbe7a203bc35 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Mon, 8 Jul 2019 21:32:32 +0200 Subject: [PATCH] cleaned up the ast processing: - visitor pattern names are now used for the interfaces and the methods - separated a modifying and a read-only ast visitor There is now also an AstPrinter that produces original source code back from an AST --- compiler/src/prog8/ast/Interfaces.kt | 9 +- compiler/src/prog8/ast/base/Extensions.kt | 14 +- .../prog8/ast/expressions/AstExpressions.kt | 36 +- .../src/prog8/ast/processing/AstChecker.kt | 90 ++--- .../ast/processing/AstIdentifiersChecker.kt | 48 +-- .../ast/processing/AstRecursionChecker.kt | 10 +- .../ast/processing/IAstModifyingVisitor.kt | 196 +++++++++ .../src/prog8/ast/processing/IAstProcessor.kt | 196 --------- .../src/prog8/ast/processing/IAstVisitor.kt | 150 +++++++ ...r.kt => ImportedModuleDirectiveRemover.kt} | 10 +- .../ast/processing/StatementReorderer.kt | 38 +- .../VarInitValueAndAddressOfCreator.kt | 14 +- .../src/prog8/ast/statements/AstStatements.kt | 83 ++-- .../src/prog8/compiler/AstToSourceCode.kt | 373 ++++++++++++++++++ compiler/src/prog8/compiler/Compiler.kt | 54 ++- compiler/src/prog8/compiler/Main.kt | 9 + .../src/prog8/compiler/target/c64/AsmGen.kt | 6 +- compiler/src/prog8/optimizer/CallGraph.kt | 46 +-- .../src/prog8/optimizer/ConstantFolding.kt | 52 +-- compiler/src/prog8/optimizer/Extensions.kt | 8 +- .../prog8/optimizer/SimplifyExpressions.kt | 28 +- .../src/prog8/optimizer/StatementOptimizer.kt | 70 ++-- compiler/src/prog8/parser/ModuleParsing.kt | 4 +- compiler/src/prog8/vm/astvm/AstVm.kt | 2 +- .../src/prog8/vm/astvm/VariablesCreator.kt | 16 +- compiler/src/prog8/vm/stackvm/StackVm.kt | 6 +- examples/test.p8 | 6 +- 27 files changed, 1077 insertions(+), 497 deletions(-) create mode 100644 compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt delete mode 100644 compiler/src/prog8/ast/processing/IAstProcessor.kt create mode 100644 compiler/src/prog8/ast/processing/IAstVisitor.kt rename compiler/src/prog8/ast/processing/{ImportedAstChecker.kt => ImportedModuleDirectiveRemover.kt} (78%) create mode 100644 compiler/src/prog8/compiler/AstToSourceCode.kt diff --git a/compiler/src/prog8/ast/Interfaces.kt b/compiler/src/prog8/ast/Interfaces.kt index 10e1fff14..a227854e3 100644 --- a/compiler/src/prog8/ast/Interfaces.kt +++ b/compiler/src/prog8/ast/Interfaces.kt @@ -2,7 +2,8 @@ package prog8.ast import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor +import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* interface Node { @@ -33,7 +34,8 @@ interface Node { } interface IStatement : Node { - fun process(processor: IAstProcessor) : IStatement + fun accept(processor: IAstModifyingVisitor) : IStatement + fun accept(processor: IAstVisitor) fun makeScopedName(name: String): String { // easy way out is to always return the full scoped name. // it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now. @@ -165,7 +167,8 @@ interface INameScope { interface IExpression: Node { fun constValue(program: Program): LiteralValue? - fun process(processor: IAstProcessor): IExpression + fun accept(processor: IAstModifyingVisitor): IExpression + fun accept(processor: IAstVisitor) fun referencesIdentifier(name: String): Boolean fun inferType(program: Program): DataType? diff --git a/compiler/src/prog8/ast/base/Extensions.kt b/compiler/src/prog8/ast/base/Extensions.kt index 5eb1af007..c3bf4eb24 100644 --- a/compiler/src/prog8/ast/base/Extensions.kt +++ b/compiler/src/prog8/ast/base/Extensions.kt @@ -18,35 +18,35 @@ internal const val autoHeapValuePrefix = "auto_heap_value_" internal fun Program.checkValid(compilerOptions: CompilationOptions) { val checker = AstChecker(this, compilerOptions) - checker.process(this) + checker.visit(this) printErrors(checker.result(), name) } internal fun Program.reorderStatements() { val initvalueCreator = VarInitValueAndAddressOfCreator(namespace) - initvalueCreator.process(this) + initvalueCreator.visit(this) val checker = StatementReorderer(this) - checker.process(this) + checker.visit(this) } internal fun Module.checkImportedValid() { - val checker = ImportedAstChecker() - checker.process(this) + val checker = ImportedModuleDirectiveRemover() + checker.visit(this) printErrors(checker.result(), name) } internal fun Program.checkRecursion() { val checker = AstRecursionChecker(namespace) - checker.process(this) + checker.visit(this) printErrors(checker.result(), name) } internal fun Program.checkIdentifiers() { val checker = AstIdentifiersChecker(namespace) - checker.process(this) + checker.visit(this) if(modules.map {it.name}.toSet().size != modules.size) { throw FatalAstException("modules should all be unique") diff --git a/compiler/src/prog8/ast/expressions/AstExpressions.kt b/compiler/src/prog8/ast/expressions/AstExpressions.kt index f936cde88..65fa7d969 100644 --- a/compiler/src/prog8/ast/expressions/AstExpressions.kt +++ b/compiler/src/prog8/ast/expressions/AstExpressions.kt @@ -2,7 +2,8 @@ package prog8.ast.expressions import prog8.ast.* import prog8.ast.base.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor +import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* import prog8.compiler.HeapValues import prog8.compiler.IntegerOrAddressOf @@ -26,7 +27,8 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri } override fun constValue(program: Program): LiteralValue? = null - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name) override fun inferType(program: Program): DataType? = expression.inferType(program) @@ -51,7 +53,8 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I // binary expression should actually have been optimized away into a single value, before const value was requested... override fun constValue(program: Program): LiteralValue? = null - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name) override fun inferType(program: Program): DataType? { val leftDt = left.inferType(program) @@ -218,7 +221,8 @@ class ArrayIndexedExpression(val identifier: IdentifierReference, } override fun constValue(program: Program): LiteralValue? = null - override fun process(processor: IAstProcessor): IExpression = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name) override fun inferType(program: Program): DataType? { @@ -251,7 +255,8 @@ class TypecastExpression(var expression: IExpression, var type: DataType, val im expression.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name) override fun inferType(program: Program): DataType? = type override fun constValue(program: Program): LiteralValue? { @@ -278,7 +283,8 @@ data class AddressOf(val identifier: IdentifierReference, override val position: override fun constValue(program: Program): LiteralValue? = null override fun referencesIdentifier(name: String) = false override fun inferType(program: Program) = DataType.UWORD - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression { @@ -289,7 +295,8 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position this.addressExpression.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String) = false override fun inferType(program: Program): DataType? = DataType.UBYTE override fun constValue(program: Program): LiteralValue? = null @@ -407,7 +414,8 @@ open class LiteralValue(val type: DataType, return this } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { val vstr = when(type) { @@ -575,7 +583,8 @@ class RangeExpr(var from: IExpression, } override fun constValue(program: Program): LiteralValue? = null - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name) override fun inferType(program: Program): DataType? { val fromDt=from.inferType(program) @@ -643,7 +652,8 @@ class RegisterExpr(val register: Register, override val position: Position) : IE } override fun constValue(program: Program): LiteralValue? = null - override fun process(processor: IAstProcessor) = this + override fun accept(processor: IAstModifyingVisitor) = this + override fun accept(processor: IAstVisitor) {} override fun referencesIdentifier(name: String): Boolean = false override fun toString(): String { return "RegisterExpr(register=$register, pos=$position)" @@ -684,7 +694,8 @@ data class IdentifierReference(val nameInSource: List, override val posi return "IdentifierRef($nameInSource)" } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time? override fun inferType(program: Program): DataType? { @@ -749,7 +760,8 @@ class FunctionCall(override var target: IdentifierReference, return "FunctionCall(target=$target, pos=$position)" } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)} override fun inferType(program: Program): DataType? { diff --git a/compiler/src/prog8/ast/processing/AstChecker.kt b/compiler/src/prog8/ast/processing/AstChecker.kt index a354b48fa..c96ca0f21 100644 --- a/compiler/src/prog8/ast/processing/AstChecker.kt +++ b/compiler/src/prog8/ast/processing/AstChecker.kt @@ -13,7 +13,7 @@ import prog8.functions.BuiltinFunctions import java.io.File internal class AstChecker(private val program: Program, - private val compilerOptions: CompilationOptions) : IAstProcessor { + private val compilerOptions: CompilationOptions) : IAstModifyingVisitor { private val checkResult: MutableList = mutableListOf() private val heapStringSentinel: Int init { @@ -25,7 +25,7 @@ internal class AstChecker(private val program: Program, return checkResult } - override fun process(program: Program) { + override fun visit(program: Program) { assert(program === this.program) // there must be a single 'main' block with a 'start' subroutine for the program entry point. val mainBlocks = program.modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block } @@ -74,11 +74,11 @@ internal class AstChecker(private val program: Program, } } - super.process(program) + super.visit(program) } - override fun process(module: Module) { - super.process(module) + override fun visit(module: Module) { + super.visit(module) val directives = module.statements.filterIsInstance().groupBy { it.directive } directives.filter { it.value.size > 1 }.forEach{ entry -> when(entry.key) { @@ -88,7 +88,7 @@ internal class AstChecker(private val program: Program, } } - override fun process(returnStmt: Return): IStatement { + override fun visit(returnStmt: Return): IStatement { val expectedReturnValues = returnStmt.definingSubroutine()?.returntypes ?: emptyList() if(expectedReturnValues.size != returnStmt.values.size) { // if the return value is a function call, check the result of that call instead @@ -105,10 +105,10 @@ internal class AstChecker(private val program: Program, if(rv.first.value!=valueDt) checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index + 1} doesn't match subroutine return type ${rv.first.value}", rv.second.position)) } - return super.process(returnStmt) + return super.visit(returnStmt) } - override fun process(forLoop: ForLoop): IStatement { + override fun visit(forLoop: ForLoop): IStatement { if(forLoop.body.containsNoCodeNorVars()) printWarning("for loop body is empty", forLoop.position) @@ -156,10 +156,10 @@ internal class AstChecker(private val program: Program, } } } - return super.process(forLoop) + return super.visit(forLoop) } - override fun process(jump: Jump): IStatement { + override fun visit(jump: Jump): IStatement { if(jump.identifier!=null) { val targetStatement = checkFunctionOrLabelExists(jump.identifier, jump) if(targetStatement!=null) { @@ -170,29 +170,29 @@ internal class AstChecker(private val program: Program, if(jump.address!=null && (jump.address < 0 || jump.address > 65535)) checkResult.add(SyntaxError("jump address must be valid integer 0..\$ffff", jump.position)) - return super.process(jump) + return super.visit(jump) } - override fun process(block: Block): IStatement { + override fun visit(block: Block): IStatement { if(block.address!=null && (block.address<0 || block.address>65535)) { checkResult.add(SyntaxError("block memory address must be valid integer 0..\$ffff", block.position)) } - return super.process(block) + return super.visit(block) } - override fun process(label: Label): IStatement { + override fun visit(label: Label): IStatement { // scope check if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) { checkResult.add(SyntaxError("Labels can only be defined in the scope of a block, a loop body, or within another subroutine", label.position)) } - return super.process(label) + return super.visit(label) } /** * Check subroutine definition */ - override fun process(subroutine: Subroutine): IStatement { + override fun visit(subroutine: Subroutine): IStatement { fun err(msg: String) { checkResult.add(SyntaxError(msg, subroutine.position)) } @@ -204,7 +204,7 @@ internal class AstChecker(private val program: Program, if(uniqueNames.size!=subroutine.parameters.size) err("parameter names must be unique") - super.process(subroutine) + super.visit(subroutine) // user-defined subroutines can only have zero or one return type // (multiple return values are only allowed for asm subs) @@ -323,7 +323,7 @@ internal class AstChecker(private val program: Program, * Assignment target must be register, or a variable name * Also check data type compatibility and number of values */ - override fun process(assignment: Assignment): IStatement { + override fun visit(assignment: Assignment): IStatement { // assigning from a functioncall COULD return multiple values (from an asm subroutine) if(assignment.value is FunctionCall) { @@ -347,7 +347,7 @@ internal class AstChecker(private val program: Program, for (target in assignment.targets) { resultingAssignment = processAssignmentTarget(resultingAssignment, target) } - return super.process(resultingAssignment) + return super.visit(resultingAssignment) } private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment { @@ -428,7 +428,7 @@ internal class AstChecker(private val program: Program, return assignment } - override fun process(addressOf: AddressOf): IExpression { + override fun visit(addressOf: AddressOf): IExpression { val variable=addressOf.identifier.targetVarDecl(program.namespace) if(variable==null) checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position)) @@ -438,13 +438,13 @@ internal class AstChecker(private val program: Program, } if(addressOf.scopedname==null) throw FatalAstException("the scopedname of AddressOf should have been set by now $addressOf") - return super.process(addressOf) + return super.visit(addressOf) } /** * Check the variable declarations (values within range etc) */ - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl): IStatement { fun err(msg: String, position: Position?=null) { checkResult.add(SyntaxError(msg, position ?: decl.position)) } @@ -504,7 +504,7 @@ internal class AstChecker(private val program: Program, else -> err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}") // const fold should have provided it! } - return super.process(decl) + return super.visit(decl) } when { decl.value is RangeExpr -> { @@ -522,7 +522,7 @@ internal class AstChecker(private val program: Program, } else -> { err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}") - return super.process(decl) + return super.visit(decl) } } } @@ -554,13 +554,13 @@ internal class AstChecker(private val program: Program, } } - return super.process(decl) + return super.visit(decl) } /** * check the arguments of the directive */ - override fun process(directive: Directive): IStatement { + override fun visit(directive: Directive): IStatement { fun err(msg: String) { checkResult.add(SyntaxError(msg, directive.position)) } @@ -631,7 +631,7 @@ internal class AstChecker(private val program: Program, } else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position) } - return super.process(directive) + return super.visit(directive) } private fun checkFileExists(directive: Directive, filename: String) { @@ -642,7 +642,7 @@ internal class AstChecker(private val program: Program, checkResult.add(NameError("included file not found: $filename", directive.position)) } - override fun process(literalValue: LiteralValue): LiteralValue { + override fun visit(literalValue: LiteralValue): LiteralValue { if(!compilerOptions.floats && literalValue.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) { checkResult.add(SyntaxError("floating point used, but that is not enabled via options", literalValue.position)) } @@ -653,7 +653,7 @@ internal class AstChecker(private val program: Program, ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position) checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, program.heap) - val lv = super.process(literalValue) + val lv = super.visit(literalValue) when(lv.type) { in StringDatatypes -> { if(lv.heapId==null) @@ -668,17 +668,17 @@ internal class AstChecker(private val program: Program, return lv } - override fun process(expr: PrefixExpression): IExpression { + override fun visit(expr: PrefixExpression): IExpression { if(expr.operator=="-") { val dt = expr.inferType(program) if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) { checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position)) } } - return super.process(expr) + return super.visit(expr) } - override fun process(expr: BinaryExpression): IExpression { + override fun visit(expr: BinaryExpression): IExpression { val leftDt = expr.left.inferType(program) val rightDt = expr.right.inferType(program) @@ -717,20 +717,20 @@ internal class AstChecker(private val program: Program, checkResult.add(ExpressionError("left operand is not numeric", expr.left.position)) if(rightDt!in NumericDatatypes) checkResult.add(ExpressionError("right operand is not numeric", expr.right.position)) - return super.process(expr) + return super.visit(expr) } - override fun process(typecast: TypecastExpression): IExpression { + override fun visit(typecast: TypecastExpression): IExpression { if(typecast.type in IterableDatatypes) checkResult.add(ExpressionError("cannot type cast to string or array type", typecast.position)) - return super.process(typecast) + return super.visit(typecast) } - override fun process(range: RangeExpr): IExpression { + override fun visit(range: RangeExpr): IExpression { fun err(msg: String) { checkResult.add(SyntaxError(msg, range.position)) } - super.process(range) + super.visit(range) val from = range.from.constValue(program) val to = range.to.constValue(program) val stepLv = range.step.constValue(program) ?: LiteralValue(DataType.UBYTE, 1, position = range.position) @@ -767,7 +767,7 @@ internal class AstChecker(private val program: Program, return range } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { // this function call is (part of) an expression, which should be in a statement somewhere. val stmtOfExpression = findParentNode(functionCall) ?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}") @@ -775,16 +775,16 @@ internal class AstChecker(private val program: Program, val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression) if(targetStatement!=null) checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position) - return super.process(functionCall) + return super.visit(functionCall) } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement): IStatement { val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement) if(targetStatement!=null) checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position) if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty()) printWarning("result value of subroutine call is discarded", functionCallStatement.position) - return super.process(functionCallStatement) + return super.visit(functionCallStatement) } private fun checkFunctionCall(target: IStatement, args: List, position: Position) { @@ -853,7 +853,7 @@ internal class AstChecker(private val program: Program, } } - override fun process(postIncrDecr: PostIncrDecr): IStatement { + override fun visit(postIncrDecr: PostIncrDecr): IStatement { if(postIncrDecr.target.identifier != null) { val targetName = postIncrDecr.target.identifier!!.nameInSource val target = program.namespace.lookup(targetName, postIncrDecr) @@ -879,10 +879,10 @@ internal class AstChecker(private val program: Program, } else if(postIncrDecr.target.memoryAddress != null) { // a memory location can always be ++/-- } - return super.process(postIncrDecr) + return super.visit(postIncrDecr) } - override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression { + override fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression { val target = arrayIndexedExpression.identifier.targetStatement(program.namespace) if(target is VarDecl) { if(target.datatype !in IterableDatatypes) @@ -909,7 +909,7 @@ internal class AstChecker(private val program: Program, if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE) checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position)) - return super.process(arrayIndexedExpression) + return super.visit(arrayIndexedExpression) } private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? { diff --git a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt index 58e0898ca..78205bbb3 100644 --- a/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt +++ b/compiler/src/prog8/ast/processing/AstIdentifiersChecker.kt @@ -8,7 +8,7 @@ import prog8.ast.statements.* import prog8.functions.BuiltinFunctions -internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor { +internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstModifyingVisitor { private val checkResult: MutableList = mutableListOf() private var blocks: MutableMap = mutableMapOf() @@ -21,32 +21,32 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)) } - override fun process(module: Module) { + override fun visit(module: Module) { blocks.clear() // blocks may be redefined within a different module - super.process(module) + super.visit(module) } - override fun process(block: Block): IStatement { + override fun visit(block: Block): IStatement { val existing = blocks[block.name] if(existing!=null) nameError(block.name, block.position, existing) else blocks[block.name] = block - return super.process(block) + return super.visit(block) } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") { // lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte" val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position) typecast.linkParents(functionCall.parent) - return super.process(typecast) + return super.visit(typecast) } - return super.process(functionCall) + return super.visit(functionCall) } - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl): IStatement { // first, check if there are datatype errors on the vardecl decl.datatypeErrors.forEach { checkResult.add(it) } @@ -59,10 +59,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr if (existing != null && existing !== decl) nameError(decl.name, decl.position, existing) - return super.process(decl) + return super.visit(decl) } - override fun process(subroutine: Subroutine): IStatement { + override fun visit(subroutine: Subroutine): IStatement { if(subroutine.name in BuiltinFunctions) { // the builtin functions can't be redefined checkResult.add(NameError("builtin function cannot be redefined", subroutine.position)) @@ -106,10 +106,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr } } } - return super.process(subroutine) + return super.visit(subroutine) } - override fun process(label: Label): IStatement { + override fun visit(label: Label): IStatement { if(label.name in BuiltinFunctions) { // the builtin functions can't be redefined checkResult.add(NameError("builtin function cannot be redefined", label.position)) @@ -118,10 +118,10 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr if (existing != null && existing !== label) nameError(label.name, label.position, existing) } - return super.process(label) + return super.visit(label) } - override fun process(forLoop: ForLoop): IStatement { + override fun visit(forLoop: ForLoop): IStatement { // If the for loop has a decltype, it means to declare the loopvar inside the loop body // rather than reusing an already declared loopvar from an outer scope. // For loops that loop over an interable variable (instead of a range of numbers) get an @@ -158,16 +158,16 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr } } } - return super.process(forLoop) + return super.visit(forLoop) } - override fun process(assignTarget: AssignTarget): AssignTarget { + override fun visit(assignTarget: AssignTarget): AssignTarget { if(assignTarget.register== Register.X) printWarning("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position) - return super.process(assignTarget) + return super.visit(assignTarget) } - override fun process(returnStmt: Return): IStatement { + override fun visit(returnStmt: Return): IStatement { if(returnStmt.values.isNotEmpty()) { // possibly adjust any literal values returned, into the desired returning data type val subroutine = returnStmt.definingSubroutine()!! @@ -188,14 +188,14 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr } returnStmt.values = newValues } - return super.process(returnStmt) + return super.visit(returnStmt) } internal val anonymousVariablesFromHeap = mutableMapOf>() - override fun process(literalValue: LiteralValue): LiteralValue { + override fun visit(literalValue: LiteralValue): LiteralValue { if(literalValue.heapId!=null && literalValue.parent !is VarDecl) { // a literal value that's not declared as a variable, which refers to something on the heap. // we need to introduce an auto-generated variable for this to be able to refer to the value! @@ -203,14 +203,14 @@ internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstPr isArray = false, autoGenerated = false, position = literalValue.position) anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable) } - return super.process(literalValue) + return super.visit(literalValue) } - override fun process(addressOf: AddressOf): IExpression { + override fun visit(addressOf: AddressOf): IExpression { // register the scoped name of the referenced identifier val variable= addressOf.identifier.targetVarDecl(namespace) ?: return addressOf addressOf.scopedname = variable.scopedname - return super.process(addressOf) + return super.visit(addressOf) } } diff --git a/compiler/src/prog8/ast/processing/AstRecursionChecker.kt b/compiler/src/prog8/ast/processing/AstRecursionChecker.kt index e2cb75ebf..02d64e70f 100644 --- a/compiler/src/prog8/ast/processing/AstRecursionChecker.kt +++ b/compiler/src/prog8/ast/processing/AstRecursionChecker.kt @@ -7,7 +7,7 @@ import prog8.ast.statements.FunctionCallStatement import prog8.ast.statements.Subroutine -internal class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor { +internal class AstRecursionChecker(private val namespace: INameScope) : IAstVisitor { private val callGraph = DirectedGraph() internal fun result(): List { @@ -18,7 +18,7 @@ internal class AstRecursionChecker(private val namespace: INameScope) : IAstProc return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain")) } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement) { val scope = functionCallStatement.definingScope() val targetStatement = functionCallStatement.target.targetStatement(namespace) if(targetStatement!=null) { @@ -28,10 +28,10 @@ internal class AstRecursionChecker(private val namespace: INameScope) : IAstProc } callGraph.add(scope, targetScope) } - return super.process(functionCallStatement) + super.visit(functionCallStatement) } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall) { val scope = functionCall.definingScope() val targetStatement = functionCall.target.targetStatement(namespace) if(targetStatement!=null) { @@ -41,7 +41,7 @@ internal class AstRecursionChecker(private val namespace: INameScope) : IAstProc } callGraph.add(scope, targetScope) } - return super.process(functionCall) + super.visit(functionCall) } diff --git a/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt new file mode 100644 index 000000000..e0ebcb1b0 --- /dev/null +++ b/compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt @@ -0,0 +1,196 @@ +package prog8.ast.processing + +import prog8.ast.* +import prog8.ast.expressions.* +import prog8.ast.statements.* + +interface IAstModifyingVisitor { + fun visit(program: Program) { + program.modules.forEach { visit(it) } + } + + fun visit(module: Module) { + module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList() + } + + fun visit(expr: PrefixExpression): IExpression { + expr.expression = expr.expression.accept(this) + return expr + } + + fun visit(expr: BinaryExpression): IExpression { + expr.left = expr.left.accept(this) + expr.right = expr.right.accept(this) + return expr + } + + fun visit(directive: Directive): IStatement { + return directive + } + + fun visit(block: Block): IStatement { + block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList() + return block + } + + fun visit(decl: VarDecl): IStatement { + decl.value = decl.value?.accept(this) + decl.arraysize?.accept(this) + return decl + } + + fun visit(subroutine: Subroutine): IStatement { + subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList() + return subroutine + } + + fun visit(functionCall: FunctionCall): IExpression { + val newtarget = functionCall.target.accept(this) + if(newtarget is IdentifierReference) + functionCall.target = newtarget + functionCall.arglist = functionCall.arglist.map { it.accept(this) }.toMutableList() + return functionCall + } + + fun visit(functionCallStatement: FunctionCallStatement): IStatement { + val newtarget = functionCallStatement.target.accept(this) + if(newtarget is IdentifierReference) + functionCallStatement.target = newtarget + functionCallStatement.arglist = functionCallStatement.arglist.map { it.accept(this) }.toMutableList() + return functionCallStatement + } + + fun visit(identifier: IdentifierReference): IExpression { + // note: this is an identifier that is used in an expression. + // other identifiers are simply part of the other statements (such as jumps, subroutine defs etc) + return identifier + } + + fun visit(jump: Jump): IStatement { + if(jump.identifier!=null) { + val ident = jump.identifier.accept(this) + if(ident is IdentifierReference && ident!==jump.identifier) { + return Jump(null, ident, null, jump.position) + } + } + return jump + } + + fun visit(ifStatement: IfStatement): IStatement { + ifStatement.condition = ifStatement.condition.accept(this) + ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope + ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope + return ifStatement + } + + fun visit(branchStatement: BranchStatement): IStatement { + branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope + branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope + return branchStatement + } + + fun visit(range: RangeExpr): IExpression { + range.from = range.from.accept(this) + range.to = range.to.accept(this) + range.step = range.step.accept(this) + return range + } + + fun visit(label: Label): IStatement { + return label + } + + fun visit(literalValue: LiteralValue): LiteralValue { + if(literalValue.arrayvalue!=null) { + for(av in literalValue.arrayvalue.withIndex()) { + val newvalue = av.value.accept(this) + literalValue.arrayvalue[av.index] = newvalue + } + } + return literalValue + } + + fun visit(assignment: Assignment): IStatement { + assignment.targets = assignment.targets.map { it.accept(this) } + assignment.value = assignment.value.accept(this) + return assignment + } + + fun visit(postIncrDecr: PostIncrDecr): IStatement { + postIncrDecr.target = postIncrDecr.target.accept(this) + return postIncrDecr + } + + fun visit(contStmt: Continue): IStatement { + return contStmt + } + + fun visit(breakStmt: Break): IStatement { + return breakStmt + } + + fun visit(forLoop: ForLoop): IStatement { + forLoop.loopVar?.accept(this) + forLoop.iterable = forLoop.iterable.accept(this) + forLoop.body = forLoop.body.accept(this) as AnonymousScope + return forLoop + } + + fun visit(whileLoop: WhileLoop): IStatement { + whileLoop.condition = whileLoop.condition.accept(this) + whileLoop.body = whileLoop.body.accept(this) as AnonymousScope + return whileLoop + } + + fun visit(repeatLoop: RepeatLoop): IStatement { + repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this) + repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope + return repeatLoop + } + + fun visit(returnStmt: Return): IStatement { + returnStmt.values = returnStmt.values.map { it.accept(this) } + return returnStmt + } + + fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression { + arrayIndexedExpression.identifier.accept(this) + arrayIndexedExpression.arrayspec.accept(this) + return arrayIndexedExpression + } + + fun visit(assignTarget: AssignTarget): AssignTarget { + assignTarget.arrayindexed?.accept(this) + assignTarget.identifier?.accept(this) + assignTarget.memoryAddress?.let { visit(it) } + return assignTarget + } + + fun visit(scope: AnonymousScope): IStatement { + scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList() + return scope + } + + fun visit(typecast: TypecastExpression): IExpression { + typecast.expression = typecast.expression.accept(this) + return typecast + } + + fun visit(memread: DirectMemoryRead): IExpression { + memread.addressExpression = memread.addressExpression.accept(this) + return memread + } + + fun visit(memwrite: DirectMemoryWrite) { + memwrite.addressExpression = memwrite.addressExpression.accept(this) + } + + fun visit(addressOf: AddressOf): IExpression { + addressOf.identifier.accept(this) + return addressOf + } + + fun visit(inlineAssembly: InlineAssembly): IStatement { + return inlineAssembly + } +} diff --git a/compiler/src/prog8/ast/processing/IAstProcessor.kt b/compiler/src/prog8/ast/processing/IAstProcessor.kt deleted file mode 100644 index 3594e7a56..000000000 --- a/compiler/src/prog8/ast/processing/IAstProcessor.kt +++ /dev/null @@ -1,196 +0,0 @@ -package prog8.ast.processing - -import prog8.ast.* -import prog8.ast.expressions.* -import prog8.ast.statements.* - -interface IAstProcessor { - fun process(program: Program) { - program.modules.forEach { process(it) } - } - - fun process(module: Module) { - module.statements = module.statements.asSequence().map { it.process(this) }.toMutableList() - } - - fun process(expr: PrefixExpression): IExpression { - expr.expression = expr.expression.process(this) - return expr - } - - fun process(expr: BinaryExpression): IExpression { - expr.left = expr.left.process(this) - expr.right = expr.right.process(this) - return expr - } - - fun process(directive: Directive): IStatement { - return directive - } - - fun process(block: Block): IStatement { - block.statements = block.statements.asSequence().map { it.process(this) }.toMutableList() - return block - } - - fun process(decl: VarDecl): IStatement { - decl.value = decl.value?.process(this) - decl.arraysize?.process(this) - return decl - } - - fun process(subroutine: Subroutine): IStatement { - subroutine.statements = subroutine.statements.asSequence().map { it.process(this) }.toMutableList() - return subroutine - } - - fun process(functionCall: FunctionCall): IExpression { - val newtarget = functionCall.target.process(this) - if(newtarget is IdentifierReference) - functionCall.target = newtarget - functionCall.arglist = functionCall.arglist.map { it.process(this) }.toMutableList() - return functionCall - } - - fun process(functionCallStatement: FunctionCallStatement): IStatement { - val newtarget = functionCallStatement.target.process(this) - if(newtarget is IdentifierReference) - functionCallStatement.target = newtarget - functionCallStatement.arglist = functionCallStatement.arglist.map { it.process(this) }.toMutableList() - return functionCallStatement - } - - fun process(identifier: IdentifierReference): IExpression { - // note: this is an identifier that is used in an expression. - // other identifiers are simply part of the other statements (such as jumps, subroutine defs etc) - return identifier - } - - fun process(jump: Jump): IStatement { - if(jump.identifier!=null) { - val ident = jump.identifier.process(this) - if(ident is IdentifierReference && ident!==jump.identifier) { - return Jump(null, ident, null, jump.position) - } - } - return jump - } - - fun process(ifStatement: IfStatement): IStatement { - ifStatement.condition = ifStatement.condition.process(this) - ifStatement.truepart = ifStatement.truepart.process(this) as AnonymousScope - ifStatement.elsepart = ifStatement.elsepart.process(this) as AnonymousScope - return ifStatement - } - - fun process(branchStatement: BranchStatement): IStatement { - branchStatement.truepart = branchStatement.truepart.process(this) as AnonymousScope - branchStatement.elsepart = branchStatement.elsepart.process(this) as AnonymousScope - return branchStatement - } - - fun process(range: RangeExpr): IExpression { - range.from = range.from.process(this) - range.to = range.to.process(this) - range.step = range.step.process(this) - return range - } - - fun process(label: Label): IStatement { - return label - } - - fun process(literalValue: LiteralValue): LiteralValue { - if(literalValue.arrayvalue!=null) { - for(av in literalValue.arrayvalue.withIndex()) { - val newvalue = av.value.process(this) - literalValue.arrayvalue[av.index] = newvalue - } - } - return literalValue - } - - fun process(assignment: Assignment): IStatement { - assignment.targets = assignment.targets.map { it.process(this) } - assignment.value = assignment.value.process(this) - return assignment - } - - fun process(postIncrDecr: PostIncrDecr): IStatement { - postIncrDecr.target = postIncrDecr.target.process(this) - return postIncrDecr - } - - fun process(contStmt: Continue): IStatement { - return contStmt - } - - fun process(breakStmt: Break): IStatement { - return breakStmt - } - - fun process(forLoop: ForLoop): IStatement { - forLoop.loopVar?.process(this) - forLoop.iterable = forLoop.iterable.process(this) - forLoop.body = forLoop.body.process(this) as AnonymousScope - return forLoop - } - - fun process(whileLoop: WhileLoop): IStatement { - whileLoop.condition = whileLoop.condition.process(this) - whileLoop.body = whileLoop.body.process(this) as AnonymousScope - return whileLoop - } - - fun process(repeatLoop: RepeatLoop): IStatement { - repeatLoop.untilCondition = repeatLoop.untilCondition.process(this) - repeatLoop.body = repeatLoop.body.process(this) as AnonymousScope - return repeatLoop - } - - fun process(returnStmt: Return): IStatement { - returnStmt.values = returnStmt.values.map { it.process(this) } - return returnStmt - } - - fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression { - arrayIndexedExpression.identifier.process(this) - arrayIndexedExpression.arrayspec.process(this) - return arrayIndexedExpression - } - - fun process(assignTarget: AssignTarget): AssignTarget { - assignTarget.arrayindexed?.process(this) - assignTarget.identifier?.process(this) - assignTarget.memoryAddress?.let { process(it) } - return assignTarget - } - - fun process(scope: AnonymousScope): IStatement { - scope.statements = scope.statements.asSequence().map { it.process(this) }.toMutableList() - return scope - } - - fun process(typecast: TypecastExpression): IExpression { - typecast.expression = typecast.expression.process(this) - return typecast - } - - fun process(memread: DirectMemoryRead): IExpression { - memread.addressExpression = memread.addressExpression.process(this) - return memread - } - - fun process(memwrite: DirectMemoryWrite) { - memwrite.addressExpression = memwrite.addressExpression.process(this) - } - - fun process(addressOf: AddressOf): IExpression { - addressOf.identifier.process(this) - return addressOf - } - - fun process(inlineAssembly: InlineAssembly): IStatement { - return inlineAssembly - } -} diff --git a/compiler/src/prog8/ast/processing/IAstVisitor.kt b/compiler/src/prog8/ast/processing/IAstVisitor.kt new file mode 100644 index 000000000..ecd7c5e17 --- /dev/null +++ b/compiler/src/prog8/ast/processing/IAstVisitor.kt @@ -0,0 +1,150 @@ +package prog8.ast.processing + +import prog8.ast.* +import prog8.ast.expressions.* +import prog8.ast.statements.* + +interface IAstVisitor { + fun visit(program: Program) { + program.modules.forEach { visit(it) } + } + + fun visit(module: Module) { + module.statements.forEach{ it.accept(this) } + } + + fun visit(expr: PrefixExpression) { + expr.expression.accept(this) + } + + fun visit(expr: BinaryExpression) { + expr.left.accept(this) + expr.right.accept(this) + } + + fun visit(directive: Directive) { + } + + fun visit(block: Block) { + block.statements.forEach { it.accept(this) } + } + + fun visit(decl: VarDecl) { + decl.value?.accept(this) + decl.arraysize?.accept(this) + } + + fun visit(subroutine: Subroutine) { + subroutine.statements.forEach { it.accept(this) } + } + + fun visit(functionCall: FunctionCall) { + functionCall.target.accept(this) + functionCall.arglist.forEach { it.accept(this) } + } + + fun visit(functionCallStatement: FunctionCallStatement) { + functionCallStatement.target.accept(this) + functionCallStatement.arglist.forEach { it.accept(this) } + } + + fun visit(identifier: IdentifierReference) { + } + + fun visit(jump: Jump) { + jump.identifier?.accept(this) + } + + fun visit(ifStatement: IfStatement) { + ifStatement.condition.accept(this) + ifStatement.truepart.accept(this) + ifStatement.elsepart.accept(this) + } + + fun visit(branchStatement: BranchStatement) { + branchStatement.truepart.accept(this) + branchStatement.elsepart.accept(this) + } + + fun visit(range: RangeExpr) { + range.from.accept(this) + range.to.accept(this) + range.step.accept(this) + } + + fun visit(label: Label) { + } + + fun visit(literalValue: LiteralValue) { + literalValue.arrayvalue?.let { it.forEach { v->v.accept(this) }} + } + + fun visit(assignment: Assignment) { + assignment.targets.forEach { it.accept(this) } + assignment.value.accept(this) + } + + fun visit(postIncrDecr: PostIncrDecr) { + postIncrDecr.target.accept(this) + } + + fun visit(contStmt: Continue) { + } + + fun visit(breakStmt: Break) { + } + + fun visit(forLoop: ForLoop) { + forLoop.loopVar?.accept(this) + forLoop.iterable.accept(this) + forLoop.body.accept(this) + } + + fun visit(whileLoop: WhileLoop) { + whileLoop.condition.accept(this) + whileLoop.body.accept(this) + } + + fun visit(repeatLoop: RepeatLoop) { + repeatLoop.untilCondition.accept(this) + repeatLoop.body.accept(this) + } + + fun visit(returnStmt: Return) { + returnStmt.values.forEach { it.accept(this) } + } + + fun visit(arrayIndexedExpression: ArrayIndexedExpression) { + arrayIndexedExpression.identifier.accept(this) + arrayIndexedExpression.arrayspec.accept(this) + } + + fun visit(assignTarget: AssignTarget) { + assignTarget.arrayindexed?.accept(this) + assignTarget.identifier?.accept(this) + assignTarget.memoryAddress?.accept(this) + } + + fun visit(scope: AnonymousScope) { + scope.statements.forEach { it.accept(this) } + } + + fun visit(typecast: TypecastExpression) { + typecast.expression.accept(this) + } + + fun visit(memread: DirectMemoryRead) { + memread.addressExpression.accept(this) + } + + fun visit(memwrite: DirectMemoryWrite) { + memwrite.addressExpression.accept(this) + } + + fun visit(addressOf: AddressOf) { + addressOf.identifier.accept(this) + } + + fun visit(inlineAssembly: InlineAssembly) { + } +} diff --git a/compiler/src/prog8/ast/processing/ImportedAstChecker.kt b/compiler/src/prog8/ast/processing/ImportedModuleDirectiveRemover.kt similarity index 78% rename from compiler/src/prog8/ast/processing/ImportedAstChecker.kt rename to compiler/src/prog8/ast/processing/ImportedModuleDirectiveRemover.kt index 67938ea4e..c6156e2e3 100644 --- a/compiler/src/prog8/ast/processing/ImportedAstChecker.kt +++ b/compiler/src/prog8/ast/processing/ImportedModuleDirectiveRemover.kt @@ -5,7 +5,7 @@ import prog8.ast.base.SyntaxError import prog8.ast.base.printWarning import prog8.ast.statements.Directive -internal class ImportedAstChecker : IAstProcessor { +internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor { private val checkResult: MutableList = mutableListOf() internal fun result(): List { @@ -13,15 +13,15 @@ internal class ImportedAstChecker : IAstProcessor { } /** - * Module check: most global directives don't apply for imported modules + * Most global directives don't apply for imported modules, so remove them */ - override fun process(module: Module) { - super.process(module) + override fun visit(module: Module) { + super.visit(module) val newStatements : MutableList = mutableListOf() val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address") for (sourceStmt in module.statements) { - val stmt = sourceStmt.process(this) + val stmt = sourceStmt.accept(this) if(stmt is Directive && stmt.parent is Module) { if(stmt.directive in moduleLevelDirectives) { printWarning("ignoring module directive because it was imported", stmt.position, stmt.directive) diff --git a/compiler/src/prog8/ast/processing/StatementReorderer.kt b/compiler/src/prog8/ast/processing/StatementReorderer.kt index 1807c363d..b689d048b 100644 --- a/compiler/src/prog8/ast/processing/StatementReorderer.kt +++ b/compiler/src/prog8/ast/processing/StatementReorderer.kt @@ -11,7 +11,7 @@ import prog8.ast.expressions.TypecastExpression import prog8.ast.statements.* import prog8.functions.BuiltinFunctions -internal class StatementReorderer(private val program: Program): IAstProcessor { +internal class StatementReorderer(private val program: Program): IAstModifyingVisitor { // Reorders the statements in a way the compiler needs. // - 'main' block must be the very first statement UNLESS it has an address set. // - blocks are ordered by address, where blocks without address are put at the end. @@ -28,8 +28,8 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option") - override fun process(module: Module) { - super.process(module) + override fun visit(module: Module) { + super.visit(module) val (blocks, other) = module.statements.partition { it is Block } module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList() @@ -60,7 +60,7 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { sortConstantAssignments(module.statements) } - override fun process(block: Block): IStatement { + override fun visit(block: Block): IStatement { val subroutines = block.statements.filterIsInstance() var numSubroutinesAtEnd = 0 @@ -123,11 +123,11 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { block.statements.removeAt(index) } - return super.process(block) + return super.visit(block) } - override fun process(subroutine: Subroutine): IStatement { - super.process(subroutine) + override fun visit(subroutine: Subroutine): IStatement { + super.visit(subroutine) sortConstantAssignments(subroutine.statements) @@ -153,13 +153,13 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { return subroutine } - override fun process(scope: AnonymousScope): AnonymousScope { - scope.statements = scope.statements.map { it.process(this)}.toMutableList() + override fun visit(scope: AnonymousScope): AnonymousScope { + scope.statements = scope.statements.map { it.accept(this)}.toMutableList() sortConstantAssignments(scope.statements) return scope } - override fun process(expr: BinaryExpression): IExpression { + override fun visit(expr: BinaryExpression): IExpression { val leftDt = expr.left.inferType(program) val rightDt = expr.right.inferType(program) if(leftDt!=null && rightDt!=null && leftDt!=rightDt) { @@ -179,7 +179,7 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { } } } - return super.process(expr) + return super.visit(expr) } private fun sortConstantAssignments(statements: MutableList) { @@ -205,7 +205,7 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { statements.addAll(result) } - override fun process(assignment: Assignment): IStatement { + override fun visit(assignment: Assignment): IStatement { val target=assignment.singleTarget if(target!=null) { // see if a typecast is needed to convert the value's type into the proper target type @@ -220,17 +220,17 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { } } else TODO("multi-target assign") - return super.process(assignment) + return super.visit(assignment) } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement): IStatement { checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope()) - return super.process(functionCallStatement) + return super.visit(functionCallStatement) } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { checkFunctionCallArguments(functionCall, functionCall.definingScope()) - return super.process(functionCall) + return super.visit(functionCall) } private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) { @@ -301,11 +301,11 @@ internal class StatementReorderer(private val program: Program): IAstProcessor { return Pair(sorted, trailing) } - override fun process(typecast: TypecastExpression): IExpression { + override fun visit(typecast: TypecastExpression): IExpression { // warn about any implicit type casts to Float, because that may not be intended if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) { printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position) } - return super.process(typecast) + return super.visit(typecast) } } diff --git a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt index a95a0aefc..c76ef5aff 100644 --- a/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt +++ b/compiler/src/prog8/ast/processing/VarInitValueAndAddressOfCreator.kt @@ -9,7 +9,7 @@ import prog8.ast.expressions.IdentifierReference import prog8.ast.expressions.LiteralValue import prog8.ast.statements.* -internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstProcessor { +internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor { // For VarDecls that declare an initialization value: // Replace the vardecl with an assignment (to set the initial value), // and add a new vardecl with the default constant value of that type (usually zero) to the scope. @@ -23,9 +23,9 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope private val vardeclsToAdd = mutableMapOf>() - override fun process(module: Module) { + override fun visit(module: Module) { vardeclsToAdd.clear() - super.process(module) + super.visit(module) // add any new vardecls to the various scopes for(decl in vardeclsToAdd) @@ -35,8 +35,8 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope } } - override fun process(decl: VarDecl): IStatement { - super.process(decl) + override fun visit(decl: VarDecl): IStatement { + super.visit(decl) if(decl.type!= VarDeclType.VAR || decl.value==null) return decl @@ -62,7 +62,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope return decl } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { val targetStatement = functionCall.target.targetSubroutine(namespace) if(targetStatement!=null) { var node: Node = functionCall @@ -73,7 +73,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope return functionCall } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement): IStatement { val targetStatement = functionCallStatement.target.targetSubroutine(namespace) if(targetStatement!=null) addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement) diff --git a/compiler/src/prog8/ast/statements/AstStatements.kt b/compiler/src/prog8/ast/statements/AstStatements.kt index 3352336ab..61e7c99f1 100644 --- a/compiler/src/prog8/ast/statements/AstStatements.kt +++ b/compiler/src/prog8/ast/statements/AstStatements.kt @@ -3,14 +3,16 @@ package prog8.ast.statements import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor +import prog8.ast.processing.IAstVisitor import prog8.compiler.HeapValues class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement { override var parent: Node = ParentSentinel override fun linkParents(parent: Node) {} - override fun process(processor: IAstProcessor): IStatement = this + override fun accept(processor: IAstModifyingVisitor): IStatement = this + override fun accept(processor: IAstVisitor) { } override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder override val expensiveToInline = false } @@ -33,7 +35,8 @@ class Block(override val name: String, statements.forEach {it.linkParents(this)} } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "Block(name=$name, address=$address, ${statements.size} statements)" @@ -51,7 +54,8 @@ data class Directive(val directive: String, val args: List, overri args.forEach{it.linkParents(this)} } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node { @@ -70,7 +74,8 @@ data class Label(val name: String, override val position: Position) : IStatement this.parent = parent } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "Label(name=$name, pos=$position)" @@ -88,7 +93,8 @@ open class Return(var values: List, override val position: Position values.forEach {it.linkParents(this)} } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "Return(values: $values, pos=$position)" @@ -96,7 +102,8 @@ open class Return(var values: List, override val position: Position } class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) { - override fun process(processor: IAstProcessor) = this + override fun accept(processor: IAstModifyingVisitor) = this + override fun accept(processor: IAstVisitor) {} override fun toString(): String { return "ReturnFromIrq(pos=$position)" @@ -111,7 +118,8 @@ class Continue(override val position: Position) : IStatement { this.parent=parent } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class Break(override val position: Position) : IStatement { @@ -122,7 +130,8 @@ class Break(override val position: Position) : IStatement { this.parent=parent } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class VarDecl(val type: VarDeclType, @@ -159,7 +168,8 @@ class VarDecl(val type: VarDeclType, value?.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) val scopedname: String by lazy { makeScopedName(name) } @@ -198,8 +208,11 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node } } - fun process(processor: IAstProcessor) { - index = index.process(processor) + fun accept(processor: IAstModifyingVisitor) { + index = index.accept(processor) + } + fun accept(processor: IAstVisitor) { + index.accept(processor) } override fun toString(): String { @@ -220,7 +233,8 @@ open class Assignment(var targets: List, val aug_op : String?, var value.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)") @@ -251,7 +265,8 @@ data class AssignTarget(val register: Register?, memoryAddress?.linkParents(this) } - fun process(processor: IAstProcessor) = processor.process(this) + fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + fun accept(processor: IAstVisitor) = processor.visit(this) companion object { fun fromExpr(expr: IExpression): AssignTarget { @@ -366,7 +381,8 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val target.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "PostIncrDecr(op: $operator, target: $target, pos=$position)" @@ -385,7 +401,8 @@ class Jump(val address: Int?, identifier?.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)" @@ -405,7 +422,8 @@ class FunctionCallStatement(override var target: IdentifierReference, arglist.forEach { it.linkParents(this) } } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "FunctionCallStatement(target=$target, pos=$position)" @@ -420,7 +438,8 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS this.parent = parent } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class AnonymousScope(override var statements: MutableList, @@ -443,7 +462,9 @@ class AnonymousScope(override var statements: MutableList, this.parent = parent statements.forEach { it.linkParents(this) } } - override fun process(processor: IAstProcessor) = processor.process(this) + + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class NopStatement(override val position: Position): IStatement { @@ -454,7 +475,8 @@ class NopStatement(override val position: Position): IStatement { this.parent = parent } - override fun process(processor: IAstProcessor) = this + override fun accept(processor: IAstModifyingVisitor) = this + override fun accept(processor: IAstVisitor) {} } // the subroutine class covers both the normal user-defined subroutines, @@ -487,7 +509,8 @@ class Subroutine(override val name: String, statements.forEach { it.linkParents(this) } } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)" @@ -559,7 +582,8 @@ class IfStatement(var condition: IExpression, elsepart.linkParents(this) } - override fun process(processor: IAstProcessor): IStatement = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class BranchStatement(var condition: BranchCondition, @@ -576,7 +600,8 @@ class BranchStatement(var condition: BranchCondition, elsepart.linkParents(this) } - override fun process(processor: IAstProcessor): IStatement = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class ForLoop(val loopRegister: Register?, @@ -596,7 +621,8 @@ class ForLoop(val loopRegister: Register?, body.linkParents(this) } - override fun process(processor: IAstProcessor) = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) override fun toString(): String { return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)" @@ -619,7 +645,8 @@ class WhileLoop(var condition: IExpression, body.linkParents(this) } - override fun process(processor: IAstProcessor): IStatement = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class RepeatLoop(var body: AnonymousScope, @@ -634,7 +661,8 @@ class RepeatLoop(var body: AnonymousScope, body.linkParents(this) } - override fun process(processor: IAstProcessor): IStatement = processor.process(this) + override fun accept(processor: IAstModifyingVisitor) = processor.visit(this) + override fun accept(processor: IAstVisitor) = processor.visit(this) } class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : Node { @@ -648,4 +676,7 @@ class DirectMemoryWrite(var addressExpression: IExpression, override val positio override fun toString(): String { return "DirectMemoryWrite($addressExpression)" } + + fun accept(processor: IAstVisitor) = processor.visit(this) + fun accept(processor: IAstModifyingVisitor) = processor.visit(this) } diff --git a/compiler/src/prog8/compiler/AstToSourceCode.kt b/compiler/src/prog8/compiler/AstToSourceCode.kt new file mode 100644 index 000000000..6311d6b27 --- /dev/null +++ b/compiler/src/prog8/compiler/AstToSourceCode.kt @@ -0,0 +1,373 @@ +package prog8.compiler + +import prog8.ast.IFunctionCall +import prog8.ast.Module +import prog8.ast.Program +import prog8.ast.base.* +import prog8.ast.expressions.* +import prog8.ast.processing.IAstVisitor +import prog8.ast.statements.* + +class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor { + var scopelevel = 0 + + fun indent(s: String) = " ".repeat(scopelevel) + s + fun outputln(text: String) = output(text + "\n") + fun outputlni(s: Any) = outputln(indent(s.toString())) + fun outputi(s: Any) = output(indent(s.toString())) + + override fun visit(program: Program) { + outputln("============= PROGRAM ${program.name} (FROM AST) ===============") + super.visit(program) + outputln("============= END PROGRAM ${program.name} (FROM AST) ===========") + } + + override fun visit(module: Module) { + if(!module.isLibraryModule) { + outputln("; ----------- module: ${module.name} -----------") + super.visit(module) + } + else outputln("; library module skipped: ${module.name}") + } + + override fun visit(block: Block) { + val addr = if(block.address!=null) block.address.toHex() else "" + outputln("~ ${block.name} $addr {") + scopelevel++ + for(stmt in block.statements) { + outputi("") + stmt.accept(this) + output("\n") + } + scopelevel-- + outputln("}\n") + } + + override fun visit(expr: PrefixExpression) { + if(expr.operator.any { it.isLetter() }) + output(" ${expr.operator} ") + else + output(expr.operator) + expr.expression.accept(this) + } + + override fun visit(expr: BinaryExpression) { + expr.left.accept(this) + if(expr.operator.any { it.isLetter() }) + output(" ${expr.operator} ") + else + output(expr.operator) + expr.right.accept(this) + } + + override fun visit(directive: Directive) { + output("${directive.directive} ") + for(arg in directive.args) { + when { + arg.int!=null -> output(arg.int.toString()) + arg.name!=null -> output(arg.name) + arg.str!=null -> output("\"${arg.str}\"") + } + if(arg!==directive.args.last()) + output(",") + } + output("\n") + } + + fun datatypeString(dt: DataType): String { + return when(dt) { + in NumericDatatypes -> dt.toString().toLowerCase() + in StringDatatypes -> dt.toString().toLowerCase() + DataType.ARRAY_UB -> "ubyte[" + DataType.ARRAY_B -> "byte[" + DataType.ARRAY_UW -> "uword[" + DataType.ARRAY_W -> "word[" + DataType.ARRAY_F -> "float[" + else -> "?????" + } + } + override fun visit(decl: VarDecl) { + if(decl.autoGenerated) { + // skip autogenerated vardecl + return + } + + when(decl.type) { + VarDeclType.VAR -> {} + VarDeclType.CONST -> output("const ") + VarDeclType.MEMORY -> output("&") + } + output(datatypeString(decl.datatype)) + if(decl.arraysize!=null) { + decl.arraysize!!.index.accept(this) + } + if(decl.isArray) + output("]") + + if(decl.zeropage) + output(" @zp") + output(" ${decl.name} ") + if(decl.value!=null) { + output("= ") + decl.value?.accept(this) + } + } + + override fun visit(subroutine: Subroutine) { + output("\n") + if(subroutine.isAsmSubroutine) { + outputi("asmsub ${subroutine.name} (") + for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) { + val reg = + when { + true==param.second.stack -> "stack" + param.second.registerOrPair!=null -> param.second.registerOrPair.toString() + param.second.statusflag!=null -> param.second.statusflag.toString() + else -> "?????" + } + output("${datatypeString(param.first.type)} ${param.first.name} @$reg") + if(param.first!==subroutine.parameters.last()) + output(", ") + } + } + else { + outputi("sub ${subroutine.name} (") + for(param in subroutine.parameters) { + output("${datatypeString(param.type)} ${param.name}") + if(param!==subroutine.parameters.last()) + output(", ") + } + } + output(") ") + if(subroutine.asmClobbers.isNotEmpty()) { + output("-> clobbers (") + val regs = subroutine.asmClobbers.toList().sorted() + for(r in regs) { + output(r.toString()) + if(r!==regs.last()) + output(",") + } + output(") ") + } + if(subroutine.returntypes.any()) { + val rt = subroutine.returntypes.single() + output("-> ${datatypeString(rt)} ") + } + if(subroutine.asmAddress!=null) + outputln("= ${subroutine.asmAddress.toHex()}") + else { + outputln("{ ") + scopelevel++ + subroutine.statements.forEach { + outputi("") + it.accept(this) + output("\n") + } + scopelevel-- + outputi("}") + } + } + + override fun visit(functionCall: FunctionCall) { + printout(functionCall as IFunctionCall) + } + + override fun visit(functionCallStatement: FunctionCallStatement) { + printout(functionCallStatement as IFunctionCall) + } + + private fun printout(call: IFunctionCall) { + call.target.accept(this) + output("(") + for(arg in call.arglist) { + arg.accept(this) + if(arg!==call.arglist.last()) + output(", ") + } + output(")") + } + + override fun visit(identifier: IdentifierReference) { + output(identifier.nameInSource.joinToString(".")) + } + + override fun visit(jump: Jump) { + output("goto ") + when { + jump.address!=null -> output(jump.address.toHex()) + jump.generatedLabel!=null -> output(jump.generatedLabel) + jump.identifier!=null -> jump.identifier.accept(this) + } + } + + override fun visit(ifStatement: IfStatement) { + output("if ") + ifStatement.condition.accept(this) + output(" ") + ifStatement.truepart.accept(this) + if(ifStatement.elsepart.statements.isNotEmpty()) { + output(" else ") + ifStatement.elsepart.accept(this) + } + } + + override fun visit(branchStatement: BranchStatement) { + output("if_${branchStatement.condition.toString().toLowerCase()} ") + branchStatement.truepart.accept(this) + if(branchStatement.elsepart.statements.isNotEmpty()) { + output(" else ") + branchStatement.elsepart.accept(this) + } + } + + override fun visit(range: RangeExpr) { + range.from.accept(this) + output(" to ") + range.to.accept(this) + output(" step ") + range.step.accept(this) + output(" ") + } + + override fun visit(label: Label) { + output("\n") + output("${label.name}:") + } + + override fun visit(literalValue: LiteralValue) { + when { + literalValue.isNumeric -> output(literalValue.asNumericValue.toString()) + literalValue.isString -> output("\"${literalValue.strvalue}\"") + literalValue.isArray -> { + output("[") + for(v in literalValue.arrayvalue!!) { + v.accept(this) + if(v!==literalValue.arrayvalue.last()) + output(", ") + } + output("]") + } + } + } + + override fun visit(assignment: Assignment) { + assignment.singleTarget!!.accept(this) + if(assignment.aug_op!=null) + output(" ${assignment.aug_op} ") + else + output(" = ") + assignment.value.accept(this) + } + + override fun visit(postIncrDecr: PostIncrDecr) { + postIncrDecr.target.accept(this) + output(postIncrDecr.operator) + } + + override fun visit(contStmt: Continue) { + output("continue") + } + + override fun visit(breakStmt: Break) { + output("break") + } + + override fun visit(forLoop: ForLoop) { + output("for ") + if(forLoop.decltype!=null) { + output(datatypeString(forLoop.decltype)) + if (forLoop.zeropage) + output(" @zp ") + else + output(" ") + } + if(forLoop.loopRegister!=null) + output(forLoop.loopRegister.toString()) + else + forLoop.loopVar!!.accept(this) + output(" in ") + forLoop.iterable.accept(this) + output(" ") + forLoop.body.accept(this) + } + + override fun visit(whileLoop: WhileLoop) { + output("while ") + whileLoop.condition.accept(this) + output(" ") + whileLoop.body.accept(this) + } + + override fun visit(repeatLoop: RepeatLoop) { + outputln("repeat ") + repeatLoop.body.accept(this) + output(" until ") + repeatLoop.untilCondition.accept(this) + } + + override fun visit(returnStmt: Return) { + output("return ") + for(v in returnStmt.values) { + v.accept(this) + if(v!==returnStmt.values.last()) + output(", ") + } + } + + override fun visit(arrayIndexedExpression: ArrayIndexedExpression) { + arrayIndexedExpression.identifier.accept(this) + output("[") + arrayIndexedExpression.arrayspec.index.accept(this) + output("]") + } + + override fun visit(assignTarget: AssignTarget) { + if(assignTarget.register!=null) + output(assignTarget.register.toString()) + else { + assignTarget.memoryAddress?.accept(this) + assignTarget.identifier?.accept(this) + } + assignTarget.arrayindexed?.accept(this) + } + + override fun visit(scope: AnonymousScope) { + outputln("{") + scopelevel++ + scope.statements.forEach { + outputi("") + it.accept(this) + output("\n") + } + scopelevel-- + outputi("}") + } + + override fun visit(typecast: TypecastExpression) { + typecast.expression.accept(this) + output(" as ${datatypeString(typecast.type)} ") + } + + override fun visit(memread: DirectMemoryRead) { + output("@(") + memread.addressExpression.accept(this) + output(")") + } + + override fun visit(memwrite: DirectMemoryWrite) { + output("@(") + memwrite.addressExpression.accept(this) + output(")") + } + + override fun visit(addressOf: AddressOf) { + output("&") + addressOf.identifier.accept(this) + } + + override fun visit(inlineAssembly: InlineAssembly) { + outputlni("%asm {{") + outputln(inlineAssembly.assembly) + outputlni("}}") + } +} diff --git a/compiler/src/prog8/compiler/Compiler.kt b/compiler/src/prog8/compiler/Compiler.kt index 30a9763b3..d41825a87 100644 --- a/compiler/src/prog8/compiler/Compiler.kt +++ b/compiler/src/prog8/compiler/Compiler.kt @@ -4,7 +4,6 @@ import prog8.ast.* import prog8.ast.base.* import prog8.ast.base.RegisterOrPair.* import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor import prog8.ast.statements.* import prog8.compiler.intermediate.IntermediateProgram import prog8.compiler.intermediate.Opcode @@ -148,7 +147,7 @@ data class CompilationOptions(val output: OutputType, val floats: Boolean) -internal class Compiler(private val program: Program): IAstProcessor { +internal class Compiler(private val program: Program) { private val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source) private var generatedLabelSequenceNumber = 0 @@ -158,17 +157,19 @@ internal class Compiler(private val program: Program): IAstProcessor { fun compile(options: CompilationOptions) : IntermediateProgram { println("Creating stackVM code...") program.modules.forEach { - process(it) + it.statements.forEach { stmt-> + if(stmt is Block) + processBlock(stmt) + } } return prog } - override fun process(block: Block): IStatement { + private fun processBlock(block: Block) { prog.newBlock(block.name, block.address, block.options()) processVariables(block) prog.line(block.position) translate(block.statements) - return super.process(block) } private fun processVariables(scope: INameScope) { @@ -178,27 +179,6 @@ internal class Compiler(private val program: Program): IAstProcessor { processVariables(subscope.value) } - override fun process(subroutine: Subroutine): IStatement { - if(subroutine.asmAddress==null) { - prog.label(subroutine.scopedname, true) - prog.instr(Opcode.START_PROCDEF) - prog.line(subroutine.position) - // note: the caller has already written the arguments into the subroutine's parameter variables. - // note2: don't separate normal and VariableInitializationAssignment here, because the order strictly matters - translate(subroutine.statements) - val r= super.process(subroutine) - prog.instr(Opcode.END_PROCDEF) - return r - } else { - // asmsub - if(subroutine.containsCodeOrVars()) - throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine") - - prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case - return super.process(subroutine) - } - } - private fun translate(statements: List) { for (stmt: IStatement in statements) { generatedLabelSequenceNumber++ @@ -228,7 +208,8 @@ internal class Compiler(private val program: Program): IAstProcessor { } } } - is VarDecl, is Subroutine -> {} // skip this, already processed these. + is VarDecl -> {} // skip this, already processed these. + is Subroutine -> translate(stmt) is NopStatement -> {} is InlineAssembly -> translate(stmt) else -> TODO("translate statement $stmt to stackvm") @@ -236,6 +217,23 @@ internal class Compiler(private val program: Program): IAstProcessor { } } + private fun translate(subroutine: Subroutine) { + if(subroutine.asmAddress==null) { + prog.label(subroutine.scopedname, true) + prog.instr(Opcode.START_PROCDEF) + prog.line(subroutine.position) + // note: the caller has already written the arguments into the subroutine's parameter variables. + // note2: don't separate normal and VariableInitializationAssignment here, because the order strictly matters + translate(subroutine.statements) + prog.instr(Opcode.END_PROCDEF) + } else { + // asmsub + if(subroutine.containsCodeOrVars()) + throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine") + + prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case + } + } private fun opcodePush(dt: DataType): Opcode { return when (dt) { in ByteDatatypes -> Opcode.PUSH_BYTE @@ -1538,7 +1536,7 @@ internal class Compiler(private val program: Program): IAstProcessor { } private fun translate(stmt: Return) { - // put the return values on the stack, in reversed order. The caller will process them. + // put the return values on the stack, in reversed order. The caller will accept them. for(value in stmt.values.reversed()) { translate(value) } diff --git a/compiler/src/prog8/compiler/Main.kt b/compiler/src/prog8/compiler/Main.kt index 90d7826d1..cbbe61c79 100644 --- a/compiler/src/prog8/compiler/Main.kt +++ b/compiler/src/prog8/compiler/Main.kt @@ -55,6 +55,7 @@ fun compileProgram(filepath: Path, val time1 = measureTimeMillis { programAst.checkIdentifiers() } + //println(" time1: $time1") val time2 = measureTimeMillis { programAst.constantFold() @@ -63,6 +64,7 @@ fun compileProgram(filepath: Path, val time3 = measureTimeMillis { programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later } + printAst(programAst) //println(" time3: $time3") val time4 = measureTimeMillis { programAst.checkValid(compilerOptions) // check if tree is valid @@ -137,6 +139,13 @@ fun compileProgram(filepath: Path, return Pair(programAst, programName) } +fun printAst(programAst: Program) { + println() + val printer = AstToSourceCode(::print) + printer.visit(programAst) + println() +} + private fun determineCompilationOptions(program: Program): CompilationOptions { val mainModule = program.modules.first() diff --git a/compiler/src/prog8/compiler/target/c64/AsmGen.kt b/compiler/src/prog8/compiler/target/c64/AsmGen.kt index fb4dc3283..33880322c 100644 --- a/compiler/src/prog8/compiler/target/c64/AsmGen.kt +++ b/compiler/src/prog8/compiler/target/c64/AsmGen.kt @@ -381,7 +381,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter private fun makeArrayFillDataSigned(value: RuntimeValue): List { val array = heap.get(value.heapId!!).array!! - // note: array of signed value can never contain pointer-to type, so simply process values as being all integers + // note: array of signed value can never contain pointer-to type, so simply accept values as being all integers return if (value.type == DataType.ARRAY_B || value.type == DataType.ARRAY_W) { array.map { if(it.integer!!>=0) @@ -476,7 +476,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter Opcode.DISCARD_BYTE -> " inx" Opcode.DISCARD_WORD -> " inx" Opcode.DISCARD_FLOAT -> " inx | inx | inx" - Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to process it. + Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it. Opcode.INCLUDE_FILE -> { val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}" val length = if(ins.arg2==null) "" else ", ${ins.arg2.integerValue()}" @@ -1043,7 +1043,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter // add any matching patterns from the big list for(pattern in patterns) { if(pattern.sequence.size > segment.size || (pattern.altSequence!=null && pattern.altSequence.size > segment.size)) - continue // don't process patterns that don't fit + continue // don't accept patterns that don't fit val opcodesList = opcodes.subList(0, pattern.sequence.size) if(pattern.sequence == opcodesList) { val asm = pattern.asm(segment) diff --git a/compiler/src/prog8/optimizer/CallGraph.kt b/compiler/src/prog8/optimizer/CallGraph.kt index 459a49190..c2a12d9ae 100644 --- a/compiler/src/prog8/optimizer/CallGraph.kt +++ b/compiler/src/prog8/optimizer/CallGraph.kt @@ -6,12 +6,12 @@ import prog8.ast.base.VarDeclType import prog8.ast.base.initvarsSubName import prog8.ast.expressions.FunctionCall import prog8.ast.expressions.IdentifierReference -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstVisitor import prog8.ast.statements.* import prog8.compiler.loadAsmIncludeFile -class CallGraph(private val program: Program): IAstProcessor { +class CallGraph(private val program: Program): IAstVisitor { val modulesImporting = mutableMapOf>().withDefault { mutableListOf() } val modulesImportedBy = mutableMapOf>().withDefault { mutableListOf() } @@ -20,7 +20,7 @@ class CallGraph(private val program: Program): IAstProcessor { val usedSymbols = mutableSetOf() init { - process(program) + visit(program) } fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) { @@ -35,8 +35,8 @@ class CallGraph(private val program: Program): IAstProcessor { findSubs(scope) } - override fun process(program: Program) { - super.process(program) + override fun visit(program: Program) { + super.visit(program) program.modules.forEach { it.importedBy.clear() @@ -59,16 +59,16 @@ class CallGraph(private val program: Program): IAstProcessor { rootmodule.importedBy.add(rootmodule) // don't discard root module } - override fun process(block: Block): IStatement { + override fun visit(block: Block) { if(block.definingModule().isLibraryModule) { // make sure the block is not removed addNodeAndParentScopes(block) } - return super.process(block) + super.visit(block) } - override fun process(directive: Directive): IStatement { + override fun visit(directive: Directive) { val thisModule = directive.definingModule() if(directive.directive=="%import") { val importedModule: Module = program.modules.single { it.name==directive.args[0].name } @@ -80,16 +80,16 @@ class CallGraph(private val program: Program): IAstProcessor { scanAssemblyCode(asm, directive, scope) } - return super.process(directive) + super.visit(directive) } - override fun process(identifier: IdentifierReference): IExpression { + override fun visit(identifier: IdentifierReference) { // track symbol usage val target = identifier.targetStatement(this.program.namespace) if(target!=null) { addNodeAndParentScopes(target) } - return super.process(identifier) + super.visit(identifier) } private fun addNodeAndParentScopes(stmt: IStatement) { @@ -103,24 +103,24 @@ class CallGraph(private val program: Program): IAstProcessor { } while (node !is Module && node !is ParentSentinel) } - override fun process(subroutine: Subroutine): IStatement { + override fun visit(subroutine: Subroutine) { if((subroutine.name=="start" && subroutine.definingScope().name=="main") || subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) { // make sure the entrypoint is mentioned in the used symbols addNodeAndParentScopes(subroutine) } - return super.process(subroutine) + super.visit(subroutine) } - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl) { if(decl.autoGenerated || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) { // make sure autogenerated vardecls are in the used symbols addNodeAndParentScopes(decl) } - return super.process(decl) + super.visit(decl) } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall) { val otherSub = functionCall.target.targetSubroutine(program.namespace) if(otherSub!=null) { functionCall.definingSubroutine()?.let { thisSub -> @@ -128,10 +128,10 @@ class CallGraph(private val program: Program): IAstProcessor { subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall) } } - return super.process(functionCall) + super.visit(functionCall) } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement) { val otherSub = functionCallStatement.target.targetSubroutine(program.namespace) if(otherSub!=null) { functionCallStatement.definingSubroutine()?.let { thisSub -> @@ -139,10 +139,10 @@ class CallGraph(private val program: Program): IAstProcessor { subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement) } } - return super.process(functionCallStatement) + super.visit(functionCallStatement) } - override fun process(jump: Jump): IStatement { + override fun visit(jump: Jump) { val otherSub = jump.identifier?.targetSubroutine(program.namespace) if(otherSub!=null) { jump.definingSubroutine()?.let { thisSub -> @@ -150,14 +150,14 @@ class CallGraph(private val program: Program): IAstProcessor { subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump) } } - return super.process(jump) + super.visit(jump) } - override fun process(inlineAssembly: InlineAssembly): IStatement { + override fun visit(inlineAssembly: InlineAssembly) { // parse inline asm for subroutine calls (jmp, jsr) val scope = inlineAssembly.definingScope() scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope) - return super.process(inlineAssembly) + super.visit(inlineAssembly) } private fun scanAssemblyCode(asm: String, context: IStatement, scope: INameScope) { diff --git a/compiler/src/prog8/optimizer/ConstantFolding.kt b/compiler/src/prog8/optimizer/ConstantFolding.kt index d1997ffb1..d8456ef31 100644 --- a/compiler/src/prog8/optimizer/ConstantFolding.kt +++ b/compiler/src/prog8/optimizer/ConstantFolding.kt @@ -3,7 +3,7 @@ package prog8.optimizer import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.statements.* import prog8.compiler.HeapValues import prog8.compiler.IntegerOrAddressOf @@ -12,7 +12,7 @@ import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE import kotlin.math.floor -class ConstantFolding(private val program: Program) : IAstProcessor { +class ConstantFolding(private val program: Program) : IAstModifyingVisitor { var optimizationsDone: Int = 0 var errors : MutableList = mutableListOf() @@ -26,7 +26,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl): IStatement { // the initializer value can't refer to the variable itself (recursive definition) if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) { errors.add(ExpressionError("recursive var declaration", decl.position)) @@ -48,7 +48,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } else if(decl.arraysize?.size()==null) { - val size = decl.arraysize!!.index.process(this) + val size = decl.arraysize!!.index.accept(this) if(size is LiteralValue) { decl.arraysize = ArrayIndex(size, decl.position) optimizationsDone++ @@ -149,7 +149,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - return super.process(decl) + return super.visit(decl) } private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) { @@ -182,7 +182,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { /** * replace identifiers that refer to const value, with the value itself (if it's a simple type) */ - override fun process(identifier: IdentifierReference): IExpression { + override fun visit(identifier: IdentifierReference): IExpression { return try { val cval = identifier.constValue(program) ?: return identifier return if(cval.isNumeric) { @@ -197,9 +197,9 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { return try { - super.process(functionCall) + super.visit(functionCall) typeCastConstArguments(functionCall) functionCall.constValue(program) ?: functionCall } catch (ax: AstException) { @@ -208,8 +208,8 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { - super.process(functionCallStatement) + override fun visit(functionCallStatement: FunctionCallStatement): IStatement { + super.visit(functionCallStatement) typeCastConstArguments(functionCallStatement) return functionCallStatement } @@ -232,26 +232,26 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - override fun process(memread: DirectMemoryRead): IExpression { + override fun visit(memread: DirectMemoryRead): IExpression { // @( &thing ) --> thing val addrOf = memread.addressExpression as? AddressOf if(addrOf!=null) - return super.process(addrOf.identifier) - return super.process(memread) + return super.visit(addrOf.identifier) + return super.visit(memread) } /** - * Try to process a unary prefix expression. + * Try to accept a unary prefix expression. * Compile-time constant sub expressions will be evaluated on the spot. * For instance, the expression for "- 4.5" will be optimized into the float literal -4.5 */ - override fun process(expr: PrefixExpression): IExpression { + override fun visit(expr: PrefixExpression): IExpression { return try { - super.process(expr) + super.visit(expr) val subexpr = expr.expression if (subexpr is LiteralValue) { - // process prefixed literal values (such as -3, not true) + // accept prefixed literal values (such as -3, not true) return when { expr.operator == "+" -> subexpr expr.operator == "-" -> when { @@ -294,7 +294,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } /** - * Try to process a binary expression. + * Try to accept a binary expression. * Compile-time constant sub expressions will be evaluated on the spot. * For instance, "9 * (4 + 2)" will be optimized into the integer literal 54. * @@ -310,9 +310,9 @@ class ConstantFolding(private val program: Program) : IAstProcessor { * (X / c1) * c2 -> X / (c2/c1) * (X + c1) - c2 -> X + (c1-c2) */ - override fun process(expr: BinaryExpression): IExpression { + override fun visit(expr: BinaryExpression): IExpression { return try { - super.process(expr) + super.visit(expr) val leftconst = expr.left.constValue(program) val rightconst = expr.right.constValue(program) @@ -539,7 +539,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } } - override fun process(forLoop: ForLoop): IStatement { + override fun visit(forLoop: ForLoop): IStatement { fun adjustRangeDt(rangeFrom: LiteralValue, targetDt: DataType, rangeTo: LiteralValue, stepLiteral: LiteralValue?, range: RangeExpr): RangeExpr { val newFrom = rangeFrom.cast(targetDt) @@ -553,7 +553,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor { } // adjust the datatype of a range expression in for loops to the loop variable. - val resultStmt = super.process(forLoop) as ForLoop + val resultStmt = super.visit(forLoop) as ForLoop val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt val rangeFrom = iterableRange.from as? LiteralValue val rangeTo = iterableRange.to as? LiteralValue @@ -593,8 +593,8 @@ class ConstantFolding(private val program: Program) : IAstProcessor { return resultStmt } - override fun process(literalValue: LiteralValue): LiteralValue { - val litval = super.process(literalValue) + override fun visit(literalValue: LiteralValue): LiteralValue { + val litval = super.visit(literalValue) if(litval.isString) { // intern the string; move it into the heap if(litval.strvalue!!.length !in 1..255) @@ -651,8 +651,8 @@ class ConstantFolding(private val program: Program) : IAstProcessor { return litval } - override fun process(assignment: Assignment): IStatement { - super.process(assignment) + override fun visit(assignment: Assignment): IStatement { + super.visit(assignment) val lv = assignment.value as? LiteralValue if(lv!=null) { // see if we can promote/convert a literal value to the required datatype diff --git a/compiler/src/prog8/optimizer/Extensions.kt b/compiler/src/prog8/optimizer/Extensions.kt index 2a1ed5c11..12dd2ff4f 100644 --- a/compiler/src/prog8/optimizer/Extensions.kt +++ b/compiler/src/prog8/optimizer/Extensions.kt @@ -9,14 +9,14 @@ import prog8.parser.ParsingFailedError internal fun Program.constantFold() { val optimizer = ConstantFolding(this) try { - optimizer.process(this) + optimizer.visit(this) } catch (ax: AstException) { optimizer.addError(ax) } while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) { optimizer.optimizationsDone = 0 - optimizer.process(this) + optimizer.visit(this) } if(optimizer.errors.isNotEmpty()) { @@ -30,7 +30,7 @@ internal fun Program.constantFold() { internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int { val optimizer = StatementOptimizer(this, optimizeInlining) - optimizer.process(this) + optimizer.visit(this) for(scope in optimizer.scopesToFlatten.reversed()) { val namescope = scope.parent as INameScope val idx = namescope.statements.indexOf(scope as IStatement) @@ -46,6 +46,6 @@ internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int { internal fun Program.simplifyExpressions() : Int { val optimizer = SimplifyExpressions(this) - optimizer.process(this) + optimizer.visit(this) return optimizer.optimizationsDone } diff --git a/compiler/src/prog8/optimizer/SimplifyExpressions.kt b/compiler/src/prog8/optimizer/SimplifyExpressions.kt index cc17bb491..5395151e2 100644 --- a/compiler/src/prog8/optimizer/SimplifyExpressions.kt +++ b/compiler/src/prog8/optimizer/SimplifyExpressions.kt @@ -6,7 +6,7 @@ import prog8.ast.base.DataType import prog8.ast.base.IntegerDatatypes import prog8.ast.base.NumericDatatypes import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.statements.Assignment import kotlin.math.abs import kotlin.math.log2 @@ -18,24 +18,24 @@ import kotlin.math.log2 */ -internal class SimplifyExpressions(private val program: Program) : IAstProcessor { +internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor { var optimizationsDone: Int = 0 - override fun process(assignment: Assignment): IStatement { + override fun visit(assignment: Assignment): IStatement { if (assignment.aug_op != null) throw AstException("augmented assignments should have been converted to normal assignments before this optimizer") - return super.process(assignment) + return super.visit(assignment) } - override fun process(memread: DirectMemoryRead): IExpression { + override fun visit(memread: DirectMemoryRead): IExpression { // @( &thing ) --> thing val addrOf = memread.addressExpression as? AddressOf if(addrOf!=null) - return super.process(addrOf.identifier) - return super.process(memread) + return super.visit(addrOf.identifier) + return super.visit(memread) } - override fun process(typecast: TypecastExpression): IExpression { + override fun visit(typecast: TypecastExpression): IExpression { // remove redundant typecasts var tc = typecast while(true) { @@ -49,18 +49,18 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor return tc.expression } } - return super.process(tc) + return super.visit(tc) } optimizationsDone++ tc = expr } } - override fun process(expr: PrefixExpression): IExpression { + override fun visit(expr: PrefixExpression): IExpression { if (expr.operator == "+") { // +X --> X optimizationsDone++ - return expr.expression.process(this) + return expr.expression.accept(this) } else if (expr.operator == "not") { (expr.expression as? BinaryExpression)?.let { // NOT (...) -> invert ... @@ -100,11 +100,11 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor } } } - return super.process(expr) + return super.visit(expr) } - override fun process(expr: BinaryExpression): IExpression { - super.process(expr) + override fun visit(expr: BinaryExpression): IExpression { + super.visit(expr) val leftVal = expr.left.constValue(program) val rightVal = expr.right.constValue(program) val constTrue = LiteralValue.fromBoolean(true, expr.position) diff --git a/compiler/src/prog8/optimizer/StatementOptimizer.kt b/compiler/src/prog8/optimizer/StatementOptimizer.kt index f9715063f..3712e4778 100644 --- a/compiler/src/prog8/optimizer/StatementOptimizer.kt +++ b/compiler/src/prog8/optimizer/StatementOptimizer.kt @@ -3,7 +3,7 @@ package prog8.optimizer import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.* -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.statements.* import prog8.compiler.target.c64.Petscii import prog8.functions.BuiltinFunctions @@ -15,7 +15,7 @@ import kotlin.math.floor todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this */ -internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstProcessor { +internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor { var optimizationsDone: Int = 0 private set var scopesToFlatten = mutableListOf() @@ -27,12 +27,12 @@ internal class StatementOptimizer(private val program: Program, private val opti private var generatedLabelSequenceNumber = 0 } - override fun process(program: Program) { + override fun visit(program: Program) { removeUnusedCode(callgraph) if(optimizeInlining) { inlineSubroutines(callgraph) } - super.process(program) + super.visit(program) } private fun inlineSubroutines(callgraph: CallGraph) { @@ -131,7 +131,7 @@ internal class StatementOptimizer(private val program: Program, private val opti } } - override fun process(block: Block): IStatement { + override fun visit(block: Block): IStatement { if("force_output" !in block.options()) { if (block.containsNoCodeNorVars()) { optimizationsDone++ @@ -146,11 +146,11 @@ internal class StatementOptimizer(private val program: Program, private val opti } } - return super.process(block) + return super.visit(block) } - override fun process(subroutine: Subroutine): IStatement { - super.process(subroutine) + override fun visit(subroutine: Subroutine): IStatement { + super.visit(subroutine) val forceOutput = "force_output" in subroutine.definingBlock().options() if(subroutine.asmAddress==null && !forceOutput) { if(subroutine.containsNoCodeNorVars()) { @@ -186,7 +186,7 @@ internal class StatementOptimizer(private val program: Program, private val opti return subroutine } - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl): IStatement { val forceOutput = "force_output" in decl.definingBlock().options() if(decl !in callgraph.usedSymbols && !forceOutput) { if(decl.type!=VarDeclType.CONST) @@ -195,7 +195,7 @@ internal class StatementOptimizer(private val program: Program, private val opti return NopStatement(decl.position) // remove unused variable } - return super.process(decl) + return super.visit(decl) } private fun deduplicateAssignments(statements: List): MutableList { @@ -223,7 +223,7 @@ internal class StatementOptimizer(private val program: Program, private val opti return linesToRemove } - override fun process(functionCallStatement: FunctionCallStatement): IStatement { + override fun visit(functionCallStatement: FunctionCallStatement): IStatement { if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) { val functionName = functionCallStatement.target.nameInSource[0] if (functionName in pureBuiltinFunctions) { @@ -278,10 +278,10 @@ internal class StatementOptimizer(private val program: Program, private val opti } } - return super.process(functionCallStatement) + return super.visit(functionCallStatement) } - override fun process(functionCall: FunctionCall): IExpression { + override fun visit(functionCall: FunctionCall): IExpression { // if it calls a subroutine, // and the first instruction in the subroutine is a jump, call that jump target instead // if the first instruction in the subroutine is a return statement with constant value, replace with the constant value @@ -298,11 +298,11 @@ internal class StatementOptimizer(private val program: Program, private val opti return constval } } - return super.process(functionCall) + return super.visit(functionCall) } - override fun process(ifStatement: IfStatement): IStatement { - super.process(ifStatement) + override fun visit(ifStatement: IfStatement): IStatement { + super.visit(ifStatement) if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) { optimizationsDone++ @@ -335,8 +335,8 @@ internal class StatementOptimizer(private val program: Program, private val opti return ifStatement } - override fun process(forLoop: ForLoop): IStatement { - super.process(forLoop) + override fun visit(forLoop: ForLoop): IStatement { + super.visit(forLoop) if(forLoop.body.containsNoCodeNorVars()) { // remove empty for loop optimizationsDone++ @@ -365,8 +365,8 @@ internal class StatementOptimizer(private val program: Program, private val opti return forLoop } - override fun process(whileLoop: WhileLoop): IStatement { - super.process(whileLoop) + override fun visit(whileLoop: WhileLoop): IStatement { + super.visit(whileLoop) val constvalue = whileLoop.condition.constValue(program) if(constvalue!=null) { return if(constvalue.asBooleanValue){ @@ -391,8 +391,8 @@ internal class StatementOptimizer(private val program: Program, private val opti return whileLoop } - override fun process(repeatLoop: RepeatLoop): IStatement { - super.process(repeatLoop) + override fun visit(repeatLoop: RepeatLoop): IStatement { + super.visit(repeatLoop) val constvalue = repeatLoop.untilCondition.constValue(program) if(constvalue!=null) { return if(constvalue.asBooleanValue){ @@ -423,30 +423,30 @@ internal class StatementOptimizer(private val program: Program, private val opti private fun hasContinueOrBreak(scope: INameScope): Boolean { - class Searcher: IAstProcessor + class Searcher: IAstModifyingVisitor { var count=0 - override fun process(breakStmt: Break): IStatement { + override fun visit(breakStmt: Break): IStatement { count++ - return super.process(breakStmt) + return super.visit(breakStmt) } - override fun process(contStmt: Continue): IStatement { + override fun visit(contStmt: Continue): IStatement { count++ - return super.process(contStmt) + return super.visit(contStmt) } } val s=Searcher() for(stmt in scope.statements) { - stmt.process(s) + stmt.accept(s) if(s.count>0) return true } return s.count > 0 } - override fun process(jump: Jump): IStatement { + override fun visit(jump: Jump): IStatement { val subroutine = jump.identifier?.targetSubroutine(program.namespace) if(subroutine!=null) { // if the first instruction in the subroutine is another jump, shortcut this one @@ -470,7 +470,7 @@ internal class StatementOptimizer(private val program: Program, private val opti return jump } - override fun process(assignment: Assignment): IStatement { + override fun visit(assignment: Assignment): IStatement { if(assignment.aug_op!=null) throw AstException("augmented assignments should have been converted to normal assignments before this optimizer") @@ -600,10 +600,10 @@ internal class StatementOptimizer(private val program: Program, private val opti } } - return super.process(assignment) + return super.visit(assignment) } - override fun process(scope: AnonymousScope): IStatement { + override fun visit(scope: AnonymousScope): IStatement { val linesToRemove = deduplicateAssignments(scope.statements) if(linesToRemove.isNotEmpty()) { linesToRemove.reversed().forEach{scope.statements.removeAt(it)} @@ -613,17 +613,17 @@ internal class StatementOptimizer(private val program: Program, private val opti scopesToFlatten.add(scope) // get rid of the anonymous scope } - return super.process(scope) + return super.visit(scope) } - override fun process(label: Label): IStatement { + override fun visit(label: Label): IStatement { // remove duplicate labels val stmts = label.definingScope().statements val startIdx = stmts.indexOf(label) if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label) return NopStatement(label.position) - return super.process(label) + return super.visit(label) } } diff --git a/compiler/src/prog8/parser/ModuleParsing.kt b/compiler/src/prog8/parser/ModuleParsing.kt index 84ecccb73..a895e369a 100644 --- a/compiler/src/prog8/parser/ModuleParsing.kt +++ b/compiler/src/prog8/parser/ModuleParsing.kt @@ -46,7 +46,7 @@ internal fun importModule(program: Program, filePath: Path): Module { throw ParsingFailedError("No such file: $filePath") val input = CharStreams.fromPath(filePath) - return importModule(program, input, filePath, filePath.parent==null) + return importModule(program, input, filePath, false) } internal fun importLibraryModule(program: Program, name: String): Module? { @@ -77,7 +77,7 @@ internal fun importModule(program: Program, stream: CharStream, modulePath: Path moduleAst.linkParents(program.namespace) program.modules.add(moduleAst) - // process additional imports + // accept additional imports val lines = moduleAst.statements.toMutableList() lines.asSequence() .mapIndexed { i, it -> Pair(i, it) } diff --git a/compiler/src/prog8/vm/astvm/AstVm.kt b/compiler/src/prog8/vm/astvm/AstVm.kt index f9c58ffd3..872040741 100644 --- a/compiler/src/prog8/vm/astvm/AstVm.kt +++ b/compiler/src/prog8/vm/astvm/AstVm.kt @@ -165,7 +165,7 @@ class AstVm(val program: Program) { fun run() { try { val init = VariablesCreator(runtimeVariables, program.heap) - init.process(program) + init.visit(program) // initialize all global variables for (m in program.modules) { diff --git a/compiler/src/prog8/vm/astvm/VariablesCreator.kt b/compiler/src/prog8/vm/astvm/VariablesCreator.kt index 92127c0c9..b101bda20 100644 --- a/compiler/src/prog8/vm/astvm/VariablesCreator.kt +++ b/compiler/src/prog8/vm/astvm/VariablesCreator.kt @@ -3,14 +3,14 @@ package prog8.vm.astvm import prog8.ast.* import prog8.ast.base.* import prog8.ast.expressions.LiteralValue -import prog8.ast.processing.IAstProcessor +import prog8.ast.processing.IAstModifyingVisitor import prog8.ast.statements.VarDecl import prog8.compiler.HeapValues import prog8.vm.RuntimeValue -class VariablesCreator(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstProcessor { +class VariablesCreator(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstModifyingVisitor { - override fun process(program: Program) { + override fun visit(program: Program) { // define the three registers as global variables runtimeVariables.define(program.namespace, Register.A.name, RuntimeValue(DataType.UBYTE, 0)) runtimeVariables.define(program.namespace, Register.X.name, RuntimeValue(DataType.UBYTE, 255)) @@ -27,10 +27,10 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v program.namespace.statements.add(vdX) program.namespace.statements.add(vdY) - super.process(program) + super.visit(program) } - override fun process(decl: VarDecl): IStatement { + override fun visit(decl: VarDecl): IStatement { when(decl.type) { // we can assume the value in the vardecl already has been converted into a constant LiteralValue here. VarDeclType.VAR -> { @@ -44,14 +44,14 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v // consts should have been const-folded away } } - return super.process(decl) + return super.visit(decl) } -// override fun process(assignment: Assignment): IStatement { +// override fun accept(assignment: Assignment): IStatement { // if(assignment is VariableInitializationAssignment) { // println("INIT VAR $assignment") // } -// return super.process(assignment) +// return super.accept(assignment) // } } diff --git a/compiler/src/prog8/vm/stackvm/StackVm.kt b/compiler/src/prog8/vm/stackvm/StackVm.kt index b51794460..044bcf37b 100644 --- a/compiler/src/prog8/vm/stackvm/StackVm.kt +++ b/compiler/src/prog8/vm/stackvm/StackVm.kt @@ -2133,7 +2133,7 @@ class StackVm(private var traceOutputFile: String?) { if(length!=value.array!!.size) throw VmExecutionException("iterable length mismatch") if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") + throw VmExecutionException("stackvm cannot accept raw memory pointers") evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.max() ?: 0)) } Syscall.FUNC_MAX_W -> { @@ -2175,7 +2175,7 @@ class StackVm(private var traceOutputFile: String?) { if(length!=value.array!!.size) throw VmExecutionException("iterable length mismatch") if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") + throw VmExecutionException("stackvm cannot accept raw memory pointers") evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.min() ?: 0)) } Syscall.FUNC_MIN_W -> { @@ -2209,7 +2209,7 @@ class StackVm(private var traceOutputFile: String?) { if(length!=value.array!!.size) throw VmExecutionException("iterable length mismatch") if(value.array.any {it.addressOf!=null}) - throw VmExecutionException("stackvm cannot process raw memory pointers") + throw VmExecutionException("stackvm cannot accept raw memory pointers") evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum())) } Syscall.FUNC_SUM_UB -> { diff --git a/examples/test.p8 b/examples/test.p8 index 3c901cbf8..7b4910823 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -7,7 +7,8 @@ uword xw = 33 - xw = $d020 ; @todo gets removed in assembly!?!??!?!? + ; TODO reorderstatements fucks up the order of these + xw = $d020 ; @todo @(xw) = 1 ; @todo should turn border white @(xw) = 1 ; @todo should turn border white @@ -18,4 +19,7 @@ ; @(xw) = A } + + asmsub derp (ubyte arg @ X) -> clobbers(A, X) -> (ubyte @Y) = $a000 + }