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.CompilationOptions
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.optimizer.AssignmentTransformer
|
import prog8.optimizer.AssignmentTransformer
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||||
@ -38,10 +37,9 @@ internal fun Program.addTypecasts(errors: ErrorReporter) {
|
|||||||
caster.applyModifications()
|
caster.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyNumericCasts() {
|
internal fun Program.verifyFunctionArgTypes() {
|
||||||
val fixer = TypecastsSimplifier(this)
|
val fixer = VerifyFunctionArgTypes(this)
|
||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
fixer.applyModifications()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
internal fun Program.transformAssignments(errors: ErrorReporter) {
|
||||||
@ -83,7 +81,8 @@ internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
internal fun Program.variousCleanups() {
|
||||||
val flattener = FlattenAnonymousScopesAndNopRemover()
|
val process = VariousCleanups()
|
||||||
flattener.visit(this)
|
process.visit(this)
|
||||||
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -7,7 +7,6 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
@ -247,52 +246,3 @@ class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalke
|
|||||||
return noModifications
|
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()
|
errors.handle()
|
||||||
programAst.constantFold(errors)
|
programAst.constantFold(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.reorderStatements()
|
programAst.reorderStatements()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.variousCleanups()
|
||||||
programAst.checkValid(compilerOptions, errors)
|
programAst.checkValid(compilerOptions, errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkIdentifiers(errors)
|
programAst.checkIdentifiers(errors)
|
||||||
@ -180,12 +180,12 @@ private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerO
|
|||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.addTypecasts(errors)
|
programAst.addTypecasts(errors)
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.simplifyNumericCasts()
|
programAst.variousCleanups()
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
errors.handle()
|
errors.handle()
|
||||||
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||||
errors.handle()
|
errors.handle()
|
||||||
|
programAst.verifyFunctionArgTypes()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
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 -> {
|
is IdentifierReference -> {
|
||||||
val sourceName = asmgen.asmIdentifierName(assign.value as 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 -> {
|
is DirectMemoryRead -> {
|
||||||
val memory = (assign.value as DirectMemoryRead).addressExpression.constValue(program)!!.number.toHex()
|
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
|
return true
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
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 {
|
main {
|
||||||
@ -112,6 +112,7 @@ waitkey:
|
|||||||
break
|
break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if dropypos>ypos {
|
if dropypos>ypos {
|
||||||
ypos = dropypos
|
ypos = dropypos
|
||||||
sound.blockdrop()
|
sound.blockdrop()
|
||||||
@ -548,6 +549,7 @@ blocklogic {
|
|||||||
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
sub noCollision(ubyte xpos, ubyte ypos) -> ubyte {
|
||||||
ubyte i
|
ubyte i
|
||||||
for i in 15 downto 0 {
|
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
|
if currentBlock[i] and c64scr.getchr(xpos + (i&3), ypos+i/4)!=32
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -5,30 +5,43 @@
|
|||||||
%option enable_floats
|
%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 {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte xx = 10
|
function(20, calculate())
|
||||||
float ff = 4
|
asmfunction(20, calculate())
|
||||||
float ff2 = 4
|
|
||||||
|
|
||||||
; xx /= 2
|
c64.CHROUT('\n')
|
||||||
;
|
|
||||||
; xx /= 3
|
|
||||||
;
|
|
||||||
; xx *= 2
|
|
||||||
; xx *= 3
|
|
||||||
|
|
||||||
;ff **= 2.0
|
if @($0400)==@($0402) and @($0401) == @($0403) {
|
||||||
;ff **= 3.0
|
c64scr.print("ok: results are same\n")
|
||||||
|
} else {
|
||||||
ff = ff2 ** 2.0
|
c64scr.print("error: result differ; arg got clobbered\n")
|
||||||
ff = ff2 ** 3.0
|
}
|
||||||
|
|
||||||
; xx = xx % 5
|
|
||||||
; xx %= 5
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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