mirror of
https://github.com/irmen/prog8.git
synced 2025-02-02 04:30:46 +00:00
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:
parent
c970d899fa
commit
dbe048158c
@ -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?
|
||||
|
||||
|
@ -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")
|
||||
|
@ -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? {
|
||||
|
@ -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? {
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
||||
|
||||
|
196
compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt
Normal file
196
compiler/src/prog8/ast/processing/IAstModifyingVisitor.kt
Normal 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
|
||||
}
|
||||
}
|
@ -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
|
||||
}
|
||||
}
|
150
compiler/src/prog8/ast/processing/IAstVisitor.kt
Normal file
150
compiler/src/prog8/ast/processing/IAstVisitor.kt
Normal 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) {
|
||||
}
|
||||
}
|
@ -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)
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
|
373
compiler/src/prog8/compiler/AstToSourceCode.kt
Normal file
373
compiler/src/prog8/compiler/AstToSourceCode.kt
Normal 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("}}")
|
||||
}
|
||||
}
|
@ -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)
|
||||
}
|
||||
|
@ -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()
|
||||
|
@ -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)
|
||||
|
@ -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) {
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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)
|
||||
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -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) }
|
||||
|
@ -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) {
|
||||
|
@ -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)
|
||||
// }
|
||||
|
||||
}
|
||||
|
@ -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 -> {
|
||||
|
@ -136,9 +136,8 @@ For other platforms it is very easy to compile it yourself (make ; make install)
|
||||
|
||||
A **Java runtime (jre or jdk), version 8 or newer** is required to run the packaged compiler.
|
||||
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead
|
||||
and for Windows it's possible to get that as well: for instance,
|
||||
`Azul's Zulu <https://www.azul.com/downloads/zulu/>`_ is a certified OpenJDK
|
||||
implementation available for various platforms.
|
||||
and for Windows it's possible to get that as well. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ for
|
||||
downloads.
|
||||
|
||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) to run the programs on. The compiler assumes the presence
|
||||
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
|
@ -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
|
||||
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user