mirror of
https://github.com/irmen/prog8.git
synced 2024-11-27 03:50:27 +00:00
refactor
This commit is contained in:
parent
a6d789cfbc
commit
b6e2b36692
@ -6,7 +6,6 @@ import prog8.ast.processing.*
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||
import prog8.optimizer.AssignmentTransformer
|
||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
||||
|
||||
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||
@ -38,10 +37,9 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
|
||||
caster.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Program.simplifyNumericCasts() {
|
||||
val fixer = TypecastsSimplifier(this)
|
||||
internal fun Program.verifyFunctionArgTypes() {
|
||||
val fixer = VerifyFunctionArgTypes(this)
|
||||
fixer.visit(this)
|
||||
fixer.applyModifications()
|
||||
}
|
||||
|
||||
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
||||
@ -83,7 +81,8 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
||||
}
|
||||
}
|
||||
|
||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
||||
val flattener = FlattenAnonymousScopesAndNopRemover()
|
||||
flattener.visit(this)
|
||||
internal fun Program.variousCleanups() {
|
||||
val process = VariousCleanups()
|
||||
process.visit(this)
|
||||
process.applyModifications()
|
||||
}
|
||||
|
@ -7,7 +7,6 @@ import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
|
||||
@ -247,52 +246,3 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
class TypecastsSimplifier(val program: Program) : AstWalker() {
|
||||
/*
|
||||
* Typecasts of a numeric literal value can be replaced by the numeric value of the type directly.
|
||||
*/
|
||||
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.expression is NumericLiteralValue) {
|
||||
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification>
|
||||
= checkCallArgTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
|
||||
|
||||
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification>
|
||||
= checkCallArgTypes(functionCall as IFunctionCall, functionCall.definingScope())
|
||||
|
||||
private fun checkCallArgTypes(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
when(target) {
|
||||
is Subroutine -> {
|
||||
val paramtypes = target.parameters.map { it.type }
|
||||
if(argtypes!=paramtypes)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for(x in argtypes.zip(paramtypes)) {
|
||||
if(x.first !in x.second)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
println("**** $target")
|
||||
return noModifications
|
||||
}
|
||||
|
||||
}
|
||||
|
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
43
compiler/src/prog8/ast/processing/VariousCleanups.kt
Normal file
@ -0,0 +1,43 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.NopStatement
|
||||
|
||||
|
||||
internal class VariousCleanups: AstWalker() {
|
||||
private val noModifications = emptyList<IAstModification>()
|
||||
|
||||
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||
return listOf(IAstModification.Remove(nopStatement, parent))
|
||||
}
|
||||
|
||||
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||
return if(parent is INameScope)
|
||||
listOf(ScopeFlatten(scope, parent as INameScope))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
|
||||
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
||||
override fun perform() {
|
||||
val idx = into.statements.indexOf(scope)
|
||||
if(idx>=0) {
|
||||
into.statements.addAll(idx+1, scope.statements)
|
||||
into.statements.remove(scope)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(typecast.expression is NumericLiteralValue) {
|
||||
val value = (typecast.expression as NumericLiteralValue).cast(typecast.type)
|
||||
return listOf(IAstModification.ReplaceNode(typecast, value, parent))
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
42
compiler/src/prog8/ast/processing/VerifyFunctionArgTypes.kt
Normal file
@ -0,0 +1,42 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||
|
||||
override fun visit(functionCall: FunctionCall)
|
||||
= checkTypes(functionCall as IFunctionCall, functionCall.definingScope())
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement)
|
||||
= checkTypes(functionCallStatement as IFunctionCall, functionCallStatement.definingScope())
|
||||
|
||||
private fun checkTypes(call: IFunctionCall, scope: INameScope) {
|
||||
val argtypes = call.args.map { it.inferType(program).typeOrElse(DataType.STRUCT) }
|
||||
val target = call.target.targetStatement(scope)
|
||||
when(target) {
|
||||
is Subroutine -> {
|
||||
val paramtypes = target.parameters.map { it.type }
|
||||
if(argtypes!=paramtypes)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
}
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
val paramtypes = func.parameters.map { it.possibleDatatypes }
|
||||
for(x in argtypes.zip(paramtypes)) {
|
||||
if(x.first !in x.second)
|
||||
throw CompilerException("parameter type mismatch $call")
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
}
|
@ -146,10 +146,10 @@ private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptio
|
||||
errors.handle()
|
||||
programAst.constantFold(errors)
|
||||
errors.handle()
|
||||
programAst.removeNopsFlattenAnonScopes()
|
||||
programAst.reorderStatements()
|
||||
programAst.addTypecasts(errors)
|
||||
errors.handle()
|
||||
programAst.variousCleanups()
|
||||
programAst.checkValid(compilerOptions, errors)
|
||||
errors.handle()
|
||||
programAst.checkIdentifiers(errors)
|
||||
@ -180,12 +180,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
||||
errors.handle()
|
||||
programAst.addTypecasts(errors)
|
||||
errors.handle()
|
||||
programAst.simplifyNumericCasts()
|
||||
programAst.removeNopsFlattenAnonScopes()
|
||||
programAst.variousCleanups()
|
||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||
errors.handle()
|
||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||
errors.handle()
|
||||
programAst.verifyFunctionArgTypes()
|
||||
}
|
||||
|
||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||
|
@ -364,11 +364,18 @@ internal class AssignmentAsmGen(private val program: Program, private val errors
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val sourceName = asmgen.asmIdentifierName(assign.value as IdentifierReference)
|
||||
TODO("membyte = variable $assign")
|
||||
when(assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda $sourceName | sta $hexAddr")
|
||||
else -> TODO("membyte aug.assign variable $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex()
|
||||
asmgen.out(" lda $memory | sta $hexAddr")
|
||||
when(assign.aug_op) {
|
||||
"setvalue" -> asmgen.out(" lda $memory | sta $hexAddr")
|
||||
else -> TODO("membyte aug.assign memread $assign")
|
||||
}
|
||||
return true
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
|
@ -1,46 +0,0 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.NopStatement
|
||||
import prog8.ast.statements.Statement
|
||||
|
||||
internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor {
|
||||
private var scopesToFlatten = mutableListOf<INameScope>()
|
||||
private val nopStatements = mutableListOf<NopStatement>()
|
||||
|
||||
override fun visit(program: Program) {
|
||||
super.visit(program)
|
||||
for(scope in scopesToFlatten.reversed()) {
|
||||
val namescope = scope.parent as INameScope
|
||||
val idx = namescope.statements.indexOf(scope as Statement)
|
||||
if(idx>=0) {
|
||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
||||
nop.parent = namescope as Node
|
||||
namescope.statements[idx] = nop
|
||||
namescope.statements.addAll(idx, scope.statements)
|
||||
scope.statements.forEach { it.parent = namescope }
|
||||
visit(nop)
|
||||
}
|
||||
}
|
||||
|
||||
this.nopStatements.forEach {
|
||||
it.definingScope().remove(it)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(scope: AnonymousScope) {
|
||||
if(scope.parent is INameScope) {
|
||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
||||
}
|
||||
|
||||
return super.visit(scope)
|
||||
}
|
||||
|
||||
override fun visit(nopStatement: NopStatement) {
|
||||
nopStatements.add(nopStatement)
|
||||
}
|
||||
}
|
@ -9,7 +9,7 @@
|
||||
|
||||
|
||||
|
||||
; TODO fix wrong block behavior at bottom when compiled without optimizations (codegen issue).
|
||||
; TODO fix noCollision() at bottom when compiled without optimizations (codegen issue).
|
||||
|
||||
|
||||
main {
|
||||
@ -112,6 +112,7 @@ waitkey:
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if dropypos>ypos {
|
||||
ypos = dropypos
|
||||
sound.blockdrop()
|
||||
@ -548,6 +549,7 @@ blocklogic {
|
||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||
ubyte i
|
||||
for i in 15 downto 0 {
|
||||
; TODO FIX THIS when compiling without optimizations (codegen problem: clobbering register arguments):
|
||||
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||
return false
|
||||
}
|
||||
|
@ -5,30 +5,43 @@
|
||||
%option enable_floats
|
||||
|
||||
|
||||
; TODO: fix register argument clobbering when calling asmsubs.
|
||||
; for instance if the first arg goes into Y, and the second in A,
|
||||
; but when calculating the second argument clobbers Y, the first argument gets destroyed.
|
||||
|
||||
main {
|
||||
|
||||
sub start() {
|
||||
ubyte xx = 10
|
||||
float ff = 4
|
||||
float ff2 = 4
|
||||
function(20, calculate())
|
||||
asmfunction(20, calculate())
|
||||
|
||||
; xx /= 2
|
||||
;
|
||||
; xx /= 3
|
||||
;
|
||||
; xx *= 2
|
||||
; xx *= 3
|
||||
c64.CHROUT('\n')
|
||||
|
||||
;ff **= 2.0
|
||||
;ff **= 3.0
|
||||
|
||||
ff = ff2 ** 2.0
|
||||
ff = ff2 ** 3.0
|
||||
|
||||
; xx = xx % 5
|
||||
; xx %= 5
|
||||
if @($0400)==@($0402) and @($0401) == @($0403) {
|
||||
c64scr.print("ok: results are same\n")
|
||||
} else {
|
||||
c64scr.print("error: result differ; arg got clobbered\n")
|
||||
}
|
||||
}
|
||||
|
||||
sub function(ubyte a1, ubyte a2) {
|
||||
; non-asm function passes via stack, this is ok
|
||||
@($0400) = a1
|
||||
@($0401) = a2
|
||||
}
|
||||
|
||||
asmsub asmfunction(ubyte a1 @ Y, ubyte a2 @ A) {
|
||||
; asm-function passes via registers, risk of clobbering
|
||||
%asm {{
|
||||
sty $0402
|
||||
sta $0403
|
||||
}}
|
||||
}
|
||||
|
||||
sub calculate() -> ubyte {
|
||||
Y = 99
|
||||
return Y
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user