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
This commit is contained in:
Irmen de Jong 2019-07-08 21:32:32 +02:00
parent c970d899fa
commit 2b3382ff8e
27 changed files with 1077 additions and 497 deletions

View File

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

View File

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

View File

@ -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<String>, 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? {

View File

@ -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<AstException> = 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<Directive>().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<IStatement>(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<IExpression>, 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? {

View File

@ -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<AstException> = mutableListOf()
private var blocks: MutableMap<String, Block> = 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<String, Pair<LiteralValue, VarDecl>>()
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)
}
}

View File

@ -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<INameScope>()
internal fun result(): List<AstException> {
@ -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)
}

View File

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

View File

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

View File

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

View File

@ -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<SyntaxError> = mutableListOf()
internal fun result(): List<SyntaxError> {
@ -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<IStatement> = 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)

View File

@ -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<Subroutine>()
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<IStatement>) {
@ -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)
}
}

View File

@ -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<INameScope, MutableMap<String, VarDecl>>()
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)

View File

@ -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<DirectiveArg>, 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<IExpression>, 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<IExpression>, 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<AssignTarget>, 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<IStatement>,
@ -443,7 +462,9 @@ class AnonymousScope(override var statements: MutableList<IStatement>,
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)
}

View File

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

View File

@ -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<IStatement>) {
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)
}

View File

@ -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()

View File

@ -381,7 +381,7 @@ class AsmGen(private val options: CompilationOptions, private val program: Inter
private fun makeArrayFillDataSigned(value: RuntimeValue): List<String> {
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)

View File

@ -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<Module, List<Module>>().withDefault { mutableListOf() }
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
@ -20,7 +20,7 @@ class CallGraph(private val program: Program): IAstProcessor {
val usedSymbols = mutableSetOf<IStatement>()
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) {

View File

@ -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<AstException> = 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

View File

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

View File

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

View File

@ -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<INameScope>()
@ -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<IStatement>): MutableList<Int> {
@ -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)
}
}

View File

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

View File

@ -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) {

View File

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

View File

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

View File

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