mirror of
https://github.com/irmen/prog8.git
synced 2024-09-29 08:57:51 +00:00
cleaned up some symbol visibilities
This commit is contained in:
parent
a6c3251668
commit
ebd38f27e6
@ -35,7 +35,7 @@ fun main(args: Array<String>) {
|
|||||||
compileMain(args)
|
compileMain(args)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printSoftwareHeader(what: String) {
|
internal fun printSoftwareHeader(what: String) {
|
||||||
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
val buildVersion = object {}.javaClass.getResource("/version.txt").readText().trim()
|
||||||
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
println("\nProg8 $what v$buildVersion by Irmen de Jong (irmen@razorvine.net)")
|
||||||
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
println("This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html\n")
|
||||||
@ -190,7 +190,7 @@ private fun compileMain(args: Array<String>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun determineCompilationOptions(program: Program): CompilationOptions {
|
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||||
val mainModule = program.modules.first()
|
val mainModule = program.modules.first()
|
||||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||||
|
@ -848,6 +848,60 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
fun isMemoryMapped(namespace: INameScope): Boolean =
|
fun isMemoryMapped(namespace: INameScope): Boolean =
|
||||||
memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type==VarDeclType.MEMORY)
|
memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type==VarDeclType.MEMORY)
|
||||||
|
|
||||||
|
fun isSameAs(value: IExpression): Boolean {
|
||||||
|
return when {
|
||||||
|
this.memoryAddress!=null -> false
|
||||||
|
this.register!=null -> value is RegisterExpr && value.register==this.register
|
||||||
|
this.identifier!=null -> value is IdentifierReference && value.nameInSource==this.identifier.nameInSource
|
||||||
|
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||||
|
value.identifier.nameInSource==this.arrayindexed.identifier.nameInSource &&
|
||||||
|
value.arrayspec.size()!=null &&
|
||||||
|
this.arrayindexed.arrayspec.size()!=null &&
|
||||||
|
value.arrayspec.size()==this.arrayindexed.arrayspec.size()
|
||||||
|
else -> false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
||||||
|
if(this===other)
|
||||||
|
return true
|
||||||
|
if(this.register!=null && other.register!=null)
|
||||||
|
return this.register==other.register
|
||||||
|
if(this.identifier!=null && other.identifier!=null)
|
||||||
|
return this.identifier.nameInSource==other.identifier.nameInSource
|
||||||
|
if(this.memoryAddress!=null && other.memoryAddress!=null) {
|
||||||
|
val addr1 = this.memoryAddress!!.addressExpression.constValue(program)
|
||||||
|
val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
|
||||||
|
return addr1!=null && addr2!=null && addr1==addr2
|
||||||
|
}
|
||||||
|
if(this.arrayindexed!=null && other.arrayindexed!=null) {
|
||||||
|
if(this.arrayindexed.identifier.nameInSource == other.arrayindexed.identifier.nameInSource) {
|
||||||
|
val x1 = this.arrayindexed.arrayspec.index.constValue(program)
|
||||||
|
val x2 = other.arrayindexed.arrayspec.index.constValue(program)
|
||||||
|
return x1!=null && x2!=null && x1==x2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
|
fun isNotMemory(namespace: INameScope): Boolean {
|
||||||
|
if(this.register!=null)
|
||||||
|
return true
|
||||||
|
if(this.memoryAddress!=null)
|
||||||
|
return false
|
||||||
|
if(this.arrayindexed!=null) {
|
||||||
|
val targetStmt = this.arrayindexed.identifier.targetVarDecl(namespace)
|
||||||
|
if(targetStmt!=null)
|
||||||
|
return targetStmt.type!=VarDeclType.MEMORY
|
||||||
|
}
|
||||||
|
if(this.identifier!=null) {
|
||||||
|
val targetStmt = this.identifier.targetVarDecl(namespace)
|
||||||
|
if(targetStmt!=null)
|
||||||
|
return targetStmt.type!=VarDeclType.MEMORY
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -857,6 +911,29 @@ interface IExpression: Node {
|
|||||||
fun process(processor: IAstProcessor): IExpression
|
fun process(processor: IAstProcessor): IExpression
|
||||||
fun referencesIdentifier(name: String): Boolean
|
fun referencesIdentifier(name: String): Boolean
|
||||||
fun resultingDatatype(program: Program): DataType?
|
fun resultingDatatype(program: Program): DataType?
|
||||||
|
|
||||||
|
fun same(other: IExpression): Boolean {
|
||||||
|
if(this===other)
|
||||||
|
return true
|
||||||
|
when(this) {
|
||||||
|
is RegisterExpr ->
|
||||||
|
return (other is RegisterExpr && other.register==this.register)
|
||||||
|
is IdentifierReference ->
|
||||||
|
return (other is IdentifierReference && other.nameInSource==this.nameInSource)
|
||||||
|
is PrefixExpression ->
|
||||||
|
return (other is PrefixExpression && other.operator==this.operator && other.expression.same(this.expression))
|
||||||
|
is BinaryExpression ->
|
||||||
|
return (other is BinaryExpression && other.operator==this.operator
|
||||||
|
&& other.left.same(this.left)
|
||||||
|
&& other.right.same(this.right))
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
return (other is ArrayIndexedExpression && other.identifier.nameInSource == this.identifier.nameInSource
|
||||||
|
&& other.arrayspec.index.same(this.arrayspec.index))
|
||||||
|
}
|
||||||
|
is LiteralValue -> return (other is LiteralValue && other==this)
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -5,7 +5,6 @@ import prog8.compiler.HeapValues
|
|||||||
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.optimizing.same
|
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import java.io.File
|
import java.io.File
|
||||||
|
|
||||||
@ -13,7 +12,7 @@ import java.io.File
|
|||||||
* General checks on the Ast
|
* General checks on the Ast
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun Program.checkValid(compilerOptions: CompilationOptions) {
|
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
||||||
val checker = AstChecker(this, compilerOptions)
|
val checker = AstChecker(this, compilerOptions)
|
||||||
checker.process(this)
|
checker.process(this)
|
||||||
printErrors(checker.result(), name)
|
printErrors(checker.result(), name)
|
||||||
@ -269,9 +268,9 @@ private class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
if(subroutine.isAsmSubroutine) {
|
if(subroutine.isAsmSubroutine) {
|
||||||
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
|
if(subroutine.asmParameterRegisters.size != subroutine.parameters.size)
|
||||||
err("number of asm parameter registers is not the same as number of parameters")
|
err("number of asm parameter registers is not the isSameAs as number of parameters")
|
||||||
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
|
if(subroutine.asmReturnvaluesRegisters.size != subroutine.returntypes.size)
|
||||||
err("number of return registers is not the same as number of return values")
|
err("number of return registers is not the isSameAs as number of return values")
|
||||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||||
if(param.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
if(param.second.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||||
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE)
|
if (param.first.type != DataType.UBYTE && param.first.type != DataType.BYTE)
|
||||||
@ -845,7 +844,7 @@ private class AstChecker(private val program: Program,
|
|||||||
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
|
checkResult.add(ExpressionError("swap requires 2 args of identical type", position))
|
||||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||||
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
|
checkResult.add(ExpressionError("swap requires 2 variables, not constant value(s)", position))
|
||||||
else if(same(args[0], args[1]))
|
else if(args[0].same(args[1]))
|
||||||
checkResult.add(ExpressionError("swap should have 2 different args", position))
|
checkResult.add(ExpressionError("swap should have 2 different args", position))
|
||||||
else if(dt1 !in NumericDatatypes)
|
else if(dt1 !in NumericDatatypes)
|
||||||
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
checkResult.add(ExpressionError("swap requires args of numerical type", position))
|
||||||
|
@ -8,7 +8,7 @@ import prog8.functions.BuiltinFunctions
|
|||||||
* Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
|
* Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun Program.checkIdentifiers() {
|
internal fun Program.checkIdentifiers() {
|
||||||
val checker = AstIdentifiersChecker(namespace)
|
val checker = AstIdentifiersChecker(namespace)
|
||||||
checker.process(this)
|
checker.process(this)
|
||||||
|
|
||||||
@ -53,7 +53,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
|||||||
|
|
||||||
private var blocks: MutableMap<String, Block> = mutableMapOf()
|
private var blocks: MutableMap<String, Block> = mutableMapOf()
|
||||||
|
|
||||||
fun result(): List<AstException> {
|
internal fun result(): List<AstException> {
|
||||||
return checkResult
|
return checkResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,7 +4,7 @@ package prog8.ast
|
|||||||
* Checks for the occurrence of recursive subroutine calls
|
* Checks for the occurrence of recursive subroutine calls
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun Program.checkRecursion() {
|
internal fun Program.checkRecursion() {
|
||||||
val checker = AstRecursionChecker(namespace)
|
val checker = AstRecursionChecker(namespace)
|
||||||
checker.process(this)
|
checker.process(this)
|
||||||
printErrors(checker.result(), name)
|
printErrors(checker.result(), name)
|
||||||
@ -84,7 +84,7 @@ private class DirectedGraph<VT> {
|
|||||||
private class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
|
private class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
|
||||||
private val callGraph = DirectedGraph<INameScope>()
|
private val callGraph = DirectedGraph<INameScope>()
|
||||||
|
|
||||||
fun result(): List<AstException> {
|
internal fun result(): List<AstException> {
|
||||||
val cycle = callGraph.checkForCycle()
|
val cycle = callGraph.checkForCycle()
|
||||||
if(cycle.isEmpty())
|
if(cycle.isEmpty())
|
||||||
return emptyList()
|
return emptyList()
|
||||||
|
@ -5,7 +5,7 @@ package prog8.ast
|
|||||||
* Checks that are specific for imported modules.
|
* Checks that are specific for imported modules.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
fun Module.checkImportedValid() {
|
internal fun Module.checkImportedValid() {
|
||||||
this.linkParents()
|
this.linkParents()
|
||||||
val checker = ImportedAstChecker()
|
val checker = ImportedAstChecker()
|
||||||
checker.process(this)
|
checker.process(this)
|
||||||
@ -16,7 +16,7 @@ fun Module.checkImportedValid() {
|
|||||||
private class ImportedAstChecker : IAstProcessor {
|
private class ImportedAstChecker : IAstProcessor {
|
||||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||||
|
|
||||||
fun result(): List<SyntaxError> {
|
internal fun result(): List<SyntaxError> {
|
||||||
return checkResult
|
return checkResult
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.ast
|
package prog8.ast
|
||||||
|
|
||||||
fun Program.reorderStatements() {
|
internal fun Program.reorderStatements() {
|
||||||
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
||||||
initvalueCreator.process(this)
|
initvalueCreator.process(this)
|
||||||
|
|
||||||
@ -8,7 +8,7 @@ fun Program.reorderStatements() {
|
|||||||
checker.process(this)
|
checker.process(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
|
internal const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
|
||||||
|
|
||||||
|
|
||||||
private class StatementReorderer(private val program: Program): IAstProcessor {
|
private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||||
@ -167,7 +167,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun sortConstantAssignments(statements: MutableList<IStatement>) {
|
private fun sortConstantAssignments(statements: MutableList<IStatement>) {
|
||||||
// sort assignments by datatype and value, so multiple initializations with the same value can be optimized (to load the value just once)
|
// sort assignments by datatype and value, so multiple initializations with the isSameAs value can be optimized (to load the value just once)
|
||||||
val result = mutableListOf<IStatement>()
|
val result = mutableListOf<IStatement>()
|
||||||
val stmtIter = statements.iterator()
|
val stmtIter = statements.iterator()
|
||||||
for(stmt in stmtIter) {
|
for(stmt in stmtIter) {
|
||||||
|
@ -7,7 +7,6 @@ import prog8.compiler.intermediate.Opcode
|
|||||||
import prog8.compiler.intermediate.Value
|
import prog8.compiler.intermediate.Value
|
||||||
import prog8.compiler.intermediate.branchOpcodes
|
import prog8.compiler.intermediate.branchOpcodes
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.optimizing.same
|
|
||||||
import prog8.parser.tryGetEmbeddedResource
|
import prog8.parser.tryGetEmbeddedResource
|
||||||
import prog8.stackvm.Syscall
|
import prog8.stackvm.Syscall
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -68,7 +67,7 @@ class HeapValues {
|
|||||||
if (str.length > 255)
|
if (str.length > 255)
|
||||||
throw IllegalArgumentException("string length must be 0-255")
|
throw IllegalArgumentException("string length must be 0-255")
|
||||||
|
|
||||||
// strings are 'interned' and shared if they're the same
|
// strings are 'interned' and shared if they're the isSameAs
|
||||||
val value = HeapValue(type, str, null, null)
|
val value = HeapValue(type, str, null, null)
|
||||||
|
|
||||||
val existing = heap.filter { it.value==value }.map { it.key }.firstOrNull()
|
val existing = heap.filter { it.value==value }.map { it.key }.firstOrNull()
|
||||||
@ -147,10 +146,7 @@ data class CompilationOptions(val output: OutputType,
|
|||||||
|
|
||||||
internal class Compiler(private val program: Program): IAstProcessor {
|
internal class Compiler(private val program: Program): IAstProcessor {
|
||||||
|
|
||||||
val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source)
|
private val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source)
|
||||||
val namespace = program.namespace
|
|
||||||
val heap = program.heap
|
|
||||||
|
|
||||||
private var generatedLabelSequenceNumber = 0
|
private var generatedLabelSequenceNumber = 0
|
||||||
private val breakStmtLabelStack : Stack<String> = Stack()
|
private val breakStmtLabelStack : Stack<String> = Stack()
|
||||||
private val continueStmtLabelStack : Stack<String> = Stack()
|
private val continueStmtLabelStack : Stack<String> = Stack()
|
||||||
@ -644,7 +640,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
translateBinaryOperator(expr.operator, commonDt)
|
translateBinaryOperator(expr.operator, commonDt)
|
||||||
}
|
}
|
||||||
is FunctionCall -> {
|
is FunctionCall -> {
|
||||||
val target = expr.target.targetStatement(namespace)
|
val target = expr.target.targetStatement(program.namespace)
|
||||||
if(target is BuiltinFunctionStatementPlaceholder) {
|
if(target is BuiltinFunctionStatementPlaceholder) {
|
||||||
// call to a builtin function (some will just be an opcode!)
|
// call to a builtin function (some will just be an opcode!)
|
||||||
val funcname = expr.target.nameInSource[0]
|
val funcname = expr.target.nameInSource[0]
|
||||||
@ -731,7 +727,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(identifierRef: IdentifierReference) {
|
private fun translate(identifierRef: IdentifierReference) {
|
||||||
val target = identifierRef.targetStatement(namespace)
|
val target = identifierRef.targetStatement(program.namespace)
|
||||||
when (target) {
|
when (target) {
|
||||||
is VarDecl -> {
|
is VarDecl -> {
|
||||||
when (target.type) {
|
when (target.type) {
|
||||||
@ -760,7 +756,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
|
|
||||||
private fun translate(stmt: FunctionCallStatement) {
|
private fun translate(stmt: FunctionCallStatement) {
|
||||||
prog.line(stmt.position)
|
prog.line(stmt.position)
|
||||||
val targetStmt = stmt.target.targetStatement(namespace)!!
|
val targetStmt = stmt.target.targetStatement(program.namespace)!!
|
||||||
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
if(targetStmt is BuiltinFunctionStatementPlaceholder) {
|
||||||
val funcname = stmt.target.nameInSource[0]
|
val funcname = stmt.target.nameInSource[0]
|
||||||
translateBuiltinFunctionCall(funcname, stmt.arglist)
|
translateBuiltinFunctionCall(funcname, stmt.arglist)
|
||||||
@ -819,7 +815,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
"any", "all" -> {
|
"any", "all" -> {
|
||||||
// 1 array argument, type determines the exact syscall to use
|
// 1 array argument, type determines the exact syscall to use
|
||||||
val arg=args.single() as IdentifierReference
|
val arg=args.single() as IdentifierReference
|
||||||
val target=arg.targetVarDecl(namespace)!!
|
val target=arg.targetVarDecl(program.namespace)!!
|
||||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||||
prog.instr(Opcode.PUSH_BYTE, length)
|
prog.instr(Opcode.PUSH_BYTE, length)
|
||||||
when (arg.resultingDatatype(program)) {
|
when (arg.resultingDatatype(program)) {
|
||||||
@ -832,7 +828,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
"avg" -> {
|
"avg" -> {
|
||||||
// 1 array argument, type determines the exact syscall to use
|
// 1 array argument, type determines the exact syscall to use
|
||||||
val arg=args.single() as IdentifierReference
|
val arg=args.single() as IdentifierReference
|
||||||
val target=arg.targetVarDecl(namespace)!!
|
val target=arg.targetVarDecl(program.namespace)!!
|
||||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||||
val arrayDt=arg.resultingDatatype(program)
|
val arrayDt=arg.resultingDatatype(program)
|
||||||
prog.instr(Opcode.PUSH_BYTE, length)
|
prog.instr(Opcode.PUSH_BYTE, length)
|
||||||
@ -863,7 +859,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
"min", "max", "sum" -> {
|
"min", "max", "sum" -> {
|
||||||
// 1 array argument, type determines the exact syscall to use
|
// 1 array argument, type determines the exact syscall to use
|
||||||
val arg=args.single() as IdentifierReference
|
val arg=args.single() as IdentifierReference
|
||||||
val target=arg.targetVarDecl(namespace)!!
|
val target=arg.targetVarDecl(program.namespace)!!
|
||||||
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
val length=Value(DataType.UBYTE, target.arraysize!!.size()!!)
|
||||||
prog.instr(Opcode.PUSH_BYTE, length)
|
prog.instr(Opcode.PUSH_BYTE, length)
|
||||||
when (arg.resultingDatatype(program)) {
|
when (arg.resultingDatatype(program)) {
|
||||||
@ -976,7 +972,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
throw AstException("swap requires 2 args of identical type")
|
throw AstException("swap requires 2 args of identical type")
|
||||||
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||||
throw AstException("swap requires 2 variables, not constant value(s)")
|
throw AstException("swap requires 2 variables, not constant value(s)")
|
||||||
if(same(args[0], args[1]))
|
if(args[0].same(args[1]))
|
||||||
throw AstException("swap should have 2 different args")
|
throw AstException("swap should have 2 different args")
|
||||||
if(dt1 !in NumericDatatypes)
|
if(dt1 !in NumericDatatypes)
|
||||||
throw AstException("swap requires args of numerical type")
|
throw AstException("swap requires args of numerical type")
|
||||||
@ -1384,7 +1380,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(arrayindexed: ArrayIndexedExpression, write: Boolean) {
|
private fun translate(arrayindexed: ArrayIndexedExpression, write: Boolean) {
|
||||||
val variable = arrayindexed.identifier.targetVarDecl(namespace)!!
|
val variable = arrayindexed.identifier.targetVarDecl(program.namespace)!!
|
||||||
translate(arrayindexed.arrayspec.index)
|
translate(arrayindexed.arrayspec.index)
|
||||||
if (write)
|
if (write)
|
||||||
prog.instr(opcodeWriteindexedvar(variable.datatype), callLabel = variable.scopedname)
|
prog.instr(opcodeWriteindexedvar(variable.datatype), callLabel = variable.scopedname)
|
||||||
@ -1415,7 +1411,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
jumpAddress = Value(DataType.UWORD, stmt.address)
|
jumpAddress = Value(DataType.UWORD, stmt.address)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
val target = stmt.identifier!!.targetStatement(namespace)!!
|
val target = stmt.identifier!!.targetStatement(program.namespace)!!
|
||||||
jumpLabel = when(target) {
|
jumpLabel = when(target) {
|
||||||
is Label -> target.scopedname
|
is Label -> target.scopedname
|
||||||
is Subroutine -> target.scopedname
|
is Subroutine -> target.scopedname
|
||||||
@ -1435,14 +1431,14 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register!!.name)
|
"--" -> prog.instr(Opcode.DEC_VAR_UB, callLabel = stmt.target.register!!.name)
|
||||||
}
|
}
|
||||||
stmt.target.identifier != null -> {
|
stmt.target.identifier != null -> {
|
||||||
val targetStatement = stmt.target.identifier!!.targetVarDecl(namespace)!!
|
val targetStatement = stmt.target.identifier!!.targetVarDecl(program.namespace)!!
|
||||||
when(stmt.operator) {
|
when(stmt.operator) {
|
||||||
"++" -> prog.instr(opcodeIncvar(targetStatement.datatype), callLabel = targetStatement.scopedname)
|
"++" -> prog.instr(opcodeIncvar(targetStatement.datatype), callLabel = targetStatement.scopedname)
|
||||||
"--" -> prog.instr(opcodeDecvar(targetStatement.datatype), callLabel = targetStatement.scopedname)
|
"--" -> prog.instr(opcodeDecvar(targetStatement.datatype), callLabel = targetStatement.scopedname)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
stmt.target.arrayindexed != null -> {
|
stmt.target.arrayindexed != null -> {
|
||||||
val variable = stmt.target.arrayindexed!!.identifier.targetVarDecl(namespace)!!
|
val variable = stmt.target.arrayindexed!!.identifier.targetVarDecl(program.namespace)!!
|
||||||
translate(stmt.target.arrayindexed!!.arrayspec.index)
|
translate(stmt.target.arrayindexed!!.arrayspec.index)
|
||||||
when(stmt.operator) {
|
when(stmt.operator) {
|
||||||
"++" -> prog.instr(opcodeIncArrayindexedVar(variable.datatype), callLabel = variable.scopedname)
|
"++" -> prog.instr(opcodeIncArrayindexedVar(variable.datatype), callLabel = variable.scopedname)
|
||||||
@ -1502,7 +1498,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
DataType.STR, DataType.STR_S -> pushHeapVarAddress(stmt.value, true)
|
DataType.STR, DataType.STR_S -> pushHeapVarAddress(stmt.value, true)
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_F -> {
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.ARRAY_W, DataType.ARRAY_UW, DataType.ARRAY_F -> {
|
||||||
if (stmt.value is IdentifierReference) {
|
if (stmt.value is IdentifierReference) {
|
||||||
val vardecl = (stmt.value as IdentifierReference).targetVarDecl(namespace)!!
|
val vardecl = (stmt.value as IdentifierReference).targetVarDecl(program.namespace)!!
|
||||||
prog.removeLastInstruction()
|
prog.removeLastInstruction()
|
||||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||||
}
|
}
|
||||||
@ -1539,7 +1535,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
when (value) {
|
when (value) {
|
||||||
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)")
|
is LiteralValue -> throw CompilerException("can only push address of string or array (value on the heap)")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val vardecl = value.targetVarDecl(namespace)!!
|
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||||
if(removeLastOpcode) prog.removeLastInstruction()
|
if(removeLastOpcode) prog.removeLastInstruction()
|
||||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||||
}
|
}
|
||||||
@ -1551,7 +1547,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
when (value) {
|
when (value) {
|
||||||
is LiteralValue -> throw CompilerException("can only push address of float that is a variable on the heap")
|
is LiteralValue -> throw CompilerException("can only push address of float that is a variable on the heap")
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val vardecl = value.targetVarDecl(namespace)!!
|
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
|
else -> throw CompilerException("can only take address of a the float as constant literal or variable")
|
||||||
@ -1559,7 +1555,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
private fun translateMultiReturnAssignment(stmt: Assignment) {
|
||||||
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(namespace)
|
val targetStmt = (stmt.value as? FunctionCall)?.target?.targetStatement(program.namespace)
|
||||||
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
if(targetStmt is Subroutine && targetStmt.isAsmSubroutine) {
|
||||||
// this is the only case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
// this is the only case where multiple assignment targets are allowed: a call to an asmsub with multiple return values
|
||||||
// the return values are already on the stack (the subroutine call puts them there)
|
// the return values are already on the stack (the subroutine call puts them there)
|
||||||
@ -1575,7 +1571,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
private fun popValueIntoTarget(assignTarget: AssignTarget, datatype: DataType) {
|
||||||
when {
|
when {
|
||||||
assignTarget.identifier != null -> {
|
assignTarget.identifier != null -> {
|
||||||
val target = assignTarget.identifier.targetStatement(namespace)!!
|
val target = assignTarget.identifier.targetStatement(program.namespace)!!
|
||||||
if (target is VarDecl) {
|
if (target is VarDecl) {
|
||||||
when (target.type) {
|
when (target.type) {
|
||||||
VarDeclType.VAR -> {
|
VarDeclType.VAR -> {
|
||||||
@ -1630,13 +1626,13 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
loopVarName = reg.name
|
loopVarName = reg.name
|
||||||
loopVarDt = DataType.UBYTE
|
loopVarDt = DataType.UBYTE
|
||||||
} else {
|
} else {
|
||||||
val loopvar = loop.loopVar!!.targetVarDecl(namespace)!!
|
val loopvar = loop.loopVar!!.targetVarDecl(program.namespace)!!
|
||||||
loopVarName = loopvar.scopedname
|
loopVarName = loopvar.scopedname
|
||||||
loopVarDt = loopvar.datatype
|
loopVarDt = loopvar.datatype
|
||||||
}
|
}
|
||||||
|
|
||||||
if(loop.iterable is RangeExpr) {
|
if(loop.iterable is RangeExpr) {
|
||||||
val range = (loop.iterable as RangeExpr).toConstantIntegerRange(heap)
|
val range = (loop.iterable as RangeExpr).toConstantIntegerRange(program.heap)
|
||||||
if(range!=null) {
|
if(range!=null) {
|
||||||
// loop over a range with constant start, last and step values
|
// loop over a range with constant start, last and step values
|
||||||
if (range.isEmpty())
|
if (range.isEmpty())
|
||||||
@ -1679,7 +1675,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
when {
|
when {
|
||||||
loop.iterable is IdentifierReference -> {
|
loop.iterable is IdentifierReference -> {
|
||||||
val idRef = loop.iterable as IdentifierReference
|
val idRef = loop.iterable as IdentifierReference
|
||||||
val vardecl = idRef.targetVarDecl(namespace)!!
|
val vardecl = idRef.targetVarDecl(program.namespace)!!
|
||||||
val iterableValue = vardecl.value as LiteralValue
|
val iterableValue = vardecl.value as LiteralValue
|
||||||
if(!iterableValue.isIterable(program))
|
if(!iterableValue.isIterable(program))
|
||||||
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
throw CompilerException("loop over something that isn't iterable ${loop.iterable}")
|
||||||
@ -1703,16 +1699,16 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
when(iterableValue.type) {
|
when(iterableValue.type) {
|
||||||
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
|
!in IterableDatatypes -> throw CompilerException("non-iterableValue type")
|
||||||
DataType.STR, DataType.STR_S -> {
|
DataType.STR, DataType.STR_S -> {
|
||||||
numElements = iterableValue.strvalue(heap).length
|
numElements = iterableValue.strvalue(program.heap).length
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).arraysize
|
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
numElements = iterableValue.arrayvalue?.size ?: heap.get(iterableValue.heapId!!).arraysize
|
numElements = iterableValue.arrayvalue?.size ?: program.heap.get(iterableValue.heapId!!).arraysize
|
||||||
if(numElements>255) throw CompilerException("string length > 255")
|
if(numElements>255) throw CompilerException("string length > 255")
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("weird datatype")
|
else -> throw CompilerException("weird datatype")
|
||||||
@ -1943,7 +1939,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
lvTarget.linkParents(body)
|
lvTarget.linkParents(body)
|
||||||
val targetStatement: VarDecl? =
|
val targetStatement: VarDecl? =
|
||||||
if(lvTarget.identifier!=null) {
|
if(lvTarget.identifier!=null) {
|
||||||
lvTarget.identifier.targetVarDecl(namespace)
|
lvTarget.identifier.targetVarDecl(program.namespace)
|
||||||
} else {
|
} else {
|
||||||
null
|
null
|
||||||
}
|
}
|
||||||
@ -2139,7 +2135,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(addrof: AddressOf) {
|
private fun translate(addrof: AddressOf) {
|
||||||
val target = addrof.identifier.targetVarDecl(namespace)!!
|
val target = addrof.identifier.targetVarDecl(program.namespace)!!
|
||||||
if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes|| target.datatype==DataType.FLOAT) {
|
if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes|| target.datatype==DataType.FLOAT) {
|
||||||
pushHeapVarAddress(addrof.identifier, false)
|
pushHeapVarAddress(addrof.identifier, false)
|
||||||
}
|
}
|
||||||
@ -2176,7 +2172,7 @@ fun loadAsmIncludeFile(filename: String, source: Path): String {
|
|||||||
?: throw IllegalArgumentException("library file '$filename' not found")
|
?: throw IllegalArgumentException("library file '$filename' not found")
|
||||||
resource.bufferedReader().use { it.readText() }
|
resource.bufferedReader().use { it.readText() }
|
||||||
} else {
|
} else {
|
||||||
// first try in the same folder as where the containing file was imported from
|
// first try in the isSameAs folder as where the containing file was imported from
|
||||||
val sib = source.resolveSibling(filename)
|
val sib = source.resolveSibling(filename)
|
||||||
if (sib.toFile().isFile)
|
if (sib.toFile().isFile)
|
||||||
sib.toFile().readText()
|
sib.toFile().readText()
|
||||||
|
@ -16,7 +16,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
fun available() = free.size
|
fun available() = free.size
|
||||||
|
|
||||||
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
||||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"same scopedname can't be allocated twice"}
|
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
||||||
|
|
||||||
val size =
|
val size =
|
||||||
when (datatype) {
|
when (datatype) {
|
||||||
|
@ -118,7 +118,7 @@ class Value(val type: DataType, numericvalueOrHeapId: Number) {
|
|||||||
|
|
||||||
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): Value {
|
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): Value {
|
||||||
if(leftDt!=rightDt)
|
if(leftDt!=rightDt)
|
||||||
throw ValueException("left and right datatypes are not the same")
|
throw ValueException("left and right datatypes are not the isSameAs")
|
||||||
if(result.toDouble() < 0 ) {
|
if(result.toDouble() < 0 ) {
|
||||||
return when(leftDt) {
|
return when(leftDt) {
|
||||||
DataType.UBYTE, DataType.UWORD -> {
|
DataType.UBYTE, DataType.UWORD -> {
|
||||||
|
@ -16,7 +16,8 @@ import kotlin.math.abs
|
|||||||
class AssemblyError(msg: String) : RuntimeException(msg)
|
class AssemblyError(msg: String) : RuntimeException(msg)
|
||||||
|
|
||||||
|
|
||||||
class AsmGen(val options: CompilationOptions, val program: IntermediateProgram, val heap: HeapValues, val zeropage: Zeropage) {
|
class AsmGen(private val options: CompilationOptions, private val program: IntermediateProgram,
|
||||||
|
private val heap: HeapValues, private val zeropage: Zeropage) {
|
||||||
private val globalFloatConsts = mutableMapOf<Double, String>()
|
private val globalFloatConsts = mutableMapOf<Double, String>()
|
||||||
private val assemblyLines = mutableListOf<String>()
|
private val assemblyLines = mutableListOf<String>()
|
||||||
private lateinit var block: IntermediateProgram.ProgramBlock
|
private lateinit var block: IntermediateProgram.ProgramBlock
|
||||||
|
@ -64,7 +64,7 @@ fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>
|
|||||||
|
|
||||||
fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Int> {
|
fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Int> {
|
||||||
|
|
||||||
// optimize sequential assignments of the same value to various targets (bytes, words, floats)
|
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
|
||||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||||
// @todo a better place to do this is in the Compiler instead and work on opcodes, and never even create the inefficient asm...
|
// @todo a better place to do this is in the Compiler instead and work on opcodes, and never even create the inefficient asm...
|
||||||
|
|
||||||
@ -86,7 +86,7 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
val thirdvalue = fifth.substring(4)
|
val thirdvalue = fifth.substring(4)
|
||||||
val fourthvalue = sixth.substring(4)
|
val fourthvalue = sixth.substring(4)
|
||||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
// lda/ldy sta/sty twice the same word --> remove second lda/ldy pair (fifth and sixth lines)
|
// lda/ldy sta/sty twice the isSameAs word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||||
removeLines.add(pair[4].index)
|
removeLines.add(pair[4].index)
|
||||||
removeLines.add(pair[5].index)
|
removeLines.add(pair[5].index)
|
||||||
}
|
}
|
||||||
@ -96,7 +96,7 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
val firstvalue = first.substring(4)
|
val firstvalue = first.substring(4)
|
||||||
val secondvalue = third.substring(4)
|
val secondvalue = third.substring(4)
|
||||||
if(firstvalue==secondvalue) {
|
if(firstvalue==secondvalue) {
|
||||||
// lda value / sta ? / lda same-value / sta ? -> remove second lda (third line)
|
// lda value / sta ? / lda isSameAs-value / sta ? -> remove second lda (third line)
|
||||||
removeLines.add(pair[2].index)
|
removeLines.add(pair[2].index)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -277,7 +277,7 @@ private fun collectionArgOutputBoolean(args: List<IExpression>, position: Positi
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||||
// 1 arg, type = float or int, result type= same as argument type
|
// 1 arg, type = float or int, result type= isSameAs as argument type
|
||||||
if(args.size!=1)
|
if(args.size!=1)
|
||||||
throw SyntaxError("abs requires one numeric argument", position)
|
throw SyntaxError("abs requires one numeric argument", position)
|
||||||
|
|
||||||
|
@ -16,7 +16,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
|||||||
private val reportedErrorMessages = mutableSetOf<String>()
|
private val reportedErrorMessages = mutableSetOf<String>()
|
||||||
|
|
||||||
fun addError(x: AstException) {
|
fun addError(x: AstException) {
|
||||||
// check that we don't add the same error more than once
|
// check that we don't add the isSameAs error more than once
|
||||||
if(x.toString() !in reportedErrorMessages) {
|
if(x.toString() !in reportedErrorMessages) {
|
||||||
reportedErrorMessages.add(x.toString())
|
reportedErrorMessages.add(x.toString())
|
||||||
errors.add(x)
|
errors.add(x)
|
||||||
@ -330,7 +330,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
|||||||
{
|
{
|
||||||
// @todo this implements only a small set of possible reorderings for now
|
// @todo this implements only a small set of possible reorderings for now
|
||||||
if(expr.operator==subExpr.operator) {
|
if(expr.operator==subExpr.operator) {
|
||||||
// both operators are the same.
|
// both operators are the isSameAs.
|
||||||
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
// If + or *, we can simply swap the const of expr and Var in subexpr.
|
||||||
if(expr.operator=="+" || expr.operator=="*") {
|
if(expr.operator=="+" || expr.operator=="*") {
|
||||||
if(leftIsConst) {
|
if(leftIsConst) {
|
||||||
|
@ -4,7 +4,7 @@ import prog8.ast.*
|
|||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
|
|
||||||
|
|
||||||
fun Program.constantFold() {
|
internal fun Program.constantFold() {
|
||||||
val optimizer = ConstantFolding(this)
|
val optimizer = ConstantFolding(this)
|
||||||
try {
|
try {
|
||||||
optimizer.process(this)
|
optimizer.process(this)
|
||||||
@ -26,7 +26,7 @@ fun Program.constantFold() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
||||||
val optimizer = StatementOptimizer(this, optimizeInlining)
|
val optimizer = StatementOptimizer(this, optimizeInlining)
|
||||||
optimizer.process(this)
|
optimizer.process(this)
|
||||||
for(scope in optimizer.scopesToFlatten.reversed()) {
|
for(scope in optimizer.scopesToFlatten.reversed()) {
|
||||||
@ -42,7 +42,7 @@ fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
|||||||
return optimizer.optimizationsDone
|
return optimizer.optimizationsDone
|
||||||
}
|
}
|
||||||
|
|
||||||
fun Program.simplifyExpressions() : Int {
|
internal fun Program.simplifyExpressions() : Int {
|
||||||
val optimizer = SimplifyExpressions(this)
|
val optimizer = SimplifyExpressions(this)
|
||||||
optimizer.process(this)
|
optimizer.process(this)
|
||||||
return optimizer.optimizationsDone
|
return optimizer.optimizationsDone
|
||||||
|
@ -8,7 +8,7 @@ import kotlin.math.log2
|
|||||||
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
|
todo advanced expression optimization: common (sub) expression elimination (turn common expressions into single subroutine call + introduce variable to hold it)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class SimplifyExpressions(private val program: Program) : IAstProcessor {
|
internal class SimplifyExpressions(private val program: Program) : IAstProcessor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
|
|
||||||
override fun process(assignment: Assignment): IStatement {
|
override fun process(assignment: Assignment): IStatement {
|
||||||
@ -295,8 +295,8 @@ class SimplifyExpressions(private val program: Program) : IAstProcessor {
|
|||||||
|
|
||||||
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
|
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
|
||||||
return when {
|
return when {
|
||||||
same(subBinExpr.left, x) -> subBinExpr.right
|
subBinExpr.left.same(x) -> subBinExpr.right
|
||||||
same(subBinExpr.right, x) -> subBinExpr.left
|
subBinExpr.right.same(x) -> subBinExpr.left
|
||||||
else -> null
|
else -> null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -12,7 +12,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
|
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
|
||||||
*/
|
*/
|
||||||
|
|
||||||
class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstProcessor {
|
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstProcessor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
private set
|
private set
|
||||||
var scopesToFlatten = mutableListOf<INameScope>()
|
var scopesToFlatten = mutableListOf<INameScope>()
|
||||||
@ -50,7 +50,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun inlineSubroutine(sub: Subroutine, caller: Node) {
|
private fun inlineSubroutine(sub: Subroutine, caller: Node) {
|
||||||
// if the sub is called multiple times from the same scope, we can't inline (would result in duplicate definitions)
|
// if the sub is called multiple times from the isSameAs scope, we can't inline (would result in duplicate definitions)
|
||||||
// (unless we add a sequence number to all vars/labels and references to them in the inlined code, but I skip that for now)
|
// (unless we add a sequence number to all vars/labels and references to them in the inlined code, but I skip that for now)
|
||||||
val scope = caller.definingScope()
|
val scope = caller.definingScope()
|
||||||
if(sub.calledBy.count { it.definingScope()===scope } > 1)
|
if(sub.calledBy.count { it.definingScope()===scope } > 1)
|
||||||
@ -171,7 +171,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
||||||
// removes 'duplicate' assignments that assign the same target
|
// removes 'duplicate' assignments that assign the isSameAs target
|
||||||
val linesToRemove = mutableListOf<Int>()
|
val linesToRemove = mutableListOf<Int>()
|
||||||
var previousAssignmentLine: Int? = null
|
var previousAssignmentLine: Int? = null
|
||||||
for (i in 0 until statements.size) {
|
for (i in 0 until statements.size) {
|
||||||
@ -182,9 +182,9 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
continue
|
continue
|
||||||
} else {
|
} else {
|
||||||
val prev = statements[previousAssignmentLine] as Assignment
|
val prev = statements[previousAssignmentLine] as Assignment
|
||||||
if (prev.targets.size == 1 && stmt.targets.size == 1 && same(prev.targets[0], stmt.targets[0])) {
|
if (prev.targets.size == 1 && stmt.targets.size == 1 && prev.targets[0].isSameAs(stmt.targets[0], program)) {
|
||||||
// get rid of the previous assignment, if the target is not MEMORY
|
// get rid of the previous assignment, if the target is not MEMORY
|
||||||
if (isNotMemory(prev.targets[0]))
|
if (prev.targets[0].isNotMemory(program.namespace))
|
||||||
linesToRemove.add(previousAssignmentLine)
|
linesToRemove.add(previousAssignmentLine)
|
||||||
}
|
}
|
||||||
previousAssignmentLine = i
|
previousAssignmentLine = i
|
||||||
@ -195,25 +195,6 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
return linesToRemove
|
return linesToRemove
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun isNotMemory(target: AssignTarget): Boolean {
|
|
||||||
if(target.register!=null)
|
|
||||||
return true
|
|
||||||
if(target.memoryAddress!=null)
|
|
||||||
return false
|
|
||||||
if(target.arrayindexed!=null) {
|
|
||||||
val targetStmt = target.arrayindexed.identifier.targetVarDecl(program.namespace)
|
|
||||||
if(targetStmt!=null)
|
|
||||||
return targetStmt.type!=VarDeclType.MEMORY
|
|
||||||
}
|
|
||||||
if(target.identifier!=null) {
|
|
||||||
val targetStmt = target.identifier.targetVarDecl(program.namespace)
|
|
||||||
if(targetStmt!=null)
|
|
||||||
return targetStmt.type!=VarDeclType.MEMORY
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
@ -467,7 +448,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
|
|
||||||
if(assignment.targets.size==1) {
|
if(assignment.targets.size==1) {
|
||||||
val target=assignment.targets[0]
|
val target=assignment.targets[0]
|
||||||
if(same(target, assignment.value)) {
|
if(target.isSameAs(assignment.value)) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement(assignment.position)
|
return NopStatement(assignment.position)
|
||||||
}
|
}
|
||||||
@ -477,7 +458,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
|
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
|
||||||
if(cv==null) {
|
if(cv==null) {
|
||||||
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
|
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
|
||||||
if (same(bexpr.left, bexpr.right) && same(target, bexpr.left)) {
|
if (bexpr.left.same(bexpr.right) && target.isSameAs(bexpr.left)) {
|
||||||
bexpr.operator = "*"
|
bexpr.operator = "*"
|
||||||
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
|
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
@ -485,7 +466,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
if (same(target, bexpr.left)) {
|
if (target.isSameAs(bexpr.left)) {
|
||||||
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
// remove assignments that have no effect X=X , X+=0, X-=0, X*=1, X/=1, X//=1, A |= 0, A ^= 0, A<<=0, etc etc
|
||||||
// A = A <operator> B
|
// A = A <operator> B
|
||||||
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type
|
val vardeclDt = (target.identifier?.targetVarDecl(program.namespace))?.type
|
||||||
@ -616,64 +597,7 @@ class StatementOptimizer(private val program: Program, private val optimizeInlin
|
|||||||
|
|
||||||
return super.process(label)
|
return super.process(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun same(target: AssignTarget, value: IExpression): Boolean {
|
|
||||||
return when {
|
|
||||||
target.memoryAddress!=null -> false
|
|
||||||
target.register!=null -> value is RegisterExpr && value.register==target.register
|
|
||||||
target.identifier!=null -> value is IdentifierReference && value.nameInSource==target.identifier.nameInSource
|
|
||||||
target.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
|
||||||
value.identifier.nameInSource==target.arrayindexed.identifier.nameInSource &&
|
|
||||||
value.arrayspec.size()!=null &&
|
|
||||||
target.arrayindexed.arrayspec.size()!=null &&
|
|
||||||
value.arrayspec.size()==target.arrayindexed.arrayspec.size()
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun same(target1: AssignTarget, target2: AssignTarget): Boolean {
|
|
||||||
if(target1===target2)
|
|
||||||
return true
|
|
||||||
if(target1.register!=null && target2.register!=null)
|
|
||||||
return target1.register==target2.register
|
|
||||||
if(target1.identifier!=null && target2.identifier!=null)
|
|
||||||
return target1.identifier.nameInSource==target2.identifier.nameInSource
|
|
||||||
if(target1.memoryAddress!=null && target2.memoryAddress!=null) {
|
|
||||||
val addr1 = target1.memoryAddress!!.addressExpression.constValue(program)
|
|
||||||
val addr2 = target2.memoryAddress!!.addressExpression.constValue(program)
|
|
||||||
return addr1!=null && addr2!=null && addr1==addr2
|
|
||||||
}
|
|
||||||
if(target1.arrayindexed!=null && target2.arrayindexed!=null) {
|
|
||||||
if(target1.arrayindexed.identifier.nameInSource == target2.arrayindexed.identifier.nameInSource) {
|
|
||||||
val x1 = target1.arrayindexed.arrayspec.index.constValue(program)
|
|
||||||
val x2 = target2.arrayindexed.arrayspec.index.constValue(program)
|
|
||||||
return x1!=null && x2!=null && x1==x2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
fun same(left: IExpression, right: IExpression): Boolean {
|
|
||||||
if(left===right)
|
|
||||||
return true
|
|
||||||
when(left) {
|
|
||||||
is RegisterExpr ->
|
|
||||||
return (right is RegisterExpr && right.register==left.register)
|
|
||||||
is IdentifierReference ->
|
|
||||||
return (right is IdentifierReference && right.nameInSource==left.nameInSource)
|
|
||||||
is PrefixExpression ->
|
|
||||||
return (right is PrefixExpression && right.operator==left.operator && same(right.expression, left.expression))
|
|
||||||
is BinaryExpression ->
|
|
||||||
return (right is BinaryExpression && right.operator==left.operator
|
|
||||||
&& same(right.left, left.left)
|
|
||||||
&& same(right.right, left.right))
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
return (right is ArrayIndexedExpression && right.identifier.nameInSource == left.identifier.nameInSource
|
|
||||||
&& same(right.arrayspec.index, left.arrayspec.index))
|
|
||||||
}
|
|
||||||
is LiteralValue -> return (right is LiteralValue && right==left)
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
@ -4,7 +4,7 @@ import org.antlr.v4.runtime.CommonTokenStream
|
|||||||
import org.antlr.v4.runtime.Lexer
|
import org.antlr.v4.runtime.Lexer
|
||||||
|
|
||||||
|
|
||||||
class CommentHandlingTokenStream(lexer: Lexer) : CommonTokenStream(lexer) {
|
internal class CommentHandlingTokenStream(lexer: Lexer) : CommonTokenStream(lexer) {
|
||||||
|
|
||||||
data class Comment(val type: String, val line: Int, val comment: String)
|
data class Comment(val type: String, val line: Int, val comment: String)
|
||||||
|
|
||||||
|
@ -8,7 +8,7 @@ import java.nio.file.Path
|
|||||||
import java.nio.file.Paths
|
import java.nio.file.Paths
|
||||||
|
|
||||||
|
|
||||||
class ParsingFailedError(override var message: String) : Exception(message)
|
internal class ParsingFailedError(override var message: String) : Exception(message)
|
||||||
|
|
||||||
|
|
||||||
private class LexerErrorListener: BaseErrorListener() {
|
private class LexerErrorListener: BaseErrorListener() {
|
||||||
@ -25,7 +25,7 @@ internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexe
|
|||||||
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||||
|
|
||||||
|
|
||||||
fun importModule(program: Program, filePath: Path): Module {
|
internal fun importModule(program: Program, filePath: Path): Module {
|
||||||
print("importing '${moduleName(filePath.fileName)}'")
|
print("importing '${moduleName(filePath.fileName)}'")
|
||||||
if(filePath.parent!=null) {
|
if(filePath.parent!=null) {
|
||||||
var importloc = filePath.toString()
|
var importloc = filePath.toString()
|
||||||
@ -43,14 +43,14 @@ fun importModule(program: Program, filePath: Path): Module {
|
|||||||
return importModule(program, input, filePath, filePath.parent==null)
|
return importModule(program, input, filePath, filePath.parent==null)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun importLibraryModule(program: Program, name: String): Module? {
|
internal fun importLibraryModule(program: Program, name: String): Module? {
|
||||||
val import = Directive("%import", listOf(
|
val import = Directive("%import", listOf(
|
||||||
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0 ,0))
|
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0 ,0))
|
||||||
), Position("<<<implicit-import>>>", 0, 0 ,0))
|
), Position("<<<implicit-import>>>", 0, 0 ,0))
|
||||||
return executeImportDirective(program, import, Paths.get(""))
|
return executeImportDirective(program, import, Paths.get(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
internal fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||||
val moduleName = moduleName(modulePath.fileName)
|
val moduleName = moduleName(modulePath.fileName)
|
||||||
val lexer = CustomLexer(modulePath, stream)
|
val lexer = CustomLexer(modulePath, stream)
|
||||||
val lexerErrors = LexerErrorListener()
|
val lexerErrors = LexerErrorListener()
|
||||||
@ -133,6 +133,6 @@ private fun executeImportDirective(program: Program, import: Directive, source:
|
|||||||
return importedModule
|
return importedModule
|
||||||
}
|
}
|
||||||
|
|
||||||
fun tryGetEmbeddedResource(name: String): InputStream? {
|
internal fun tryGetEmbeddedResource(name: String): InputStream? {
|
||||||
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
||||||
}
|
}
|
||||||
|
@ -122,7 +122,7 @@ class Program (val name: String,
|
|||||||
val instructions = mutableListOf<Instruction>()
|
val instructions = mutableListOf<Instruction>()
|
||||||
val labels = mutableMapOf<String, Instruction>()
|
val labels = mutableMapOf<String, Instruction>()
|
||||||
val splitpattern = Pattern.compile("\\s+")
|
val splitpattern = Pattern.compile("\\s+")
|
||||||
val nextInstructionLabels = Stack<String>() // more than one label can occur on the same line
|
val nextInstructionLabels = Stack<String>() // more than one label can occur on the isSameAs line
|
||||||
|
|
||||||
while(true) {
|
while(true) {
|
||||||
val (lineNr, line) = lines.next()
|
val (lineNr, line) = lines.next()
|
||||||
|
@ -636,7 +636,7 @@ class TestStackVmOpcodes {
|
|||||||
assertEquals(DataType.UBYTE, rndb2.type)
|
assertEquals(DataType.UBYTE, rndb2.type)
|
||||||
assertEquals(DataType.UWORD, rndw.type)
|
assertEquals(DataType.UWORD, rndw.type)
|
||||||
assertEquals(DataType.FLOAT, rndf.type)
|
assertEquals(DataType.FLOAT, rndf.type)
|
||||||
assertNotEquals(rndb1.integerValue(), rndb2.integerValue()) // this *sometimes* fails when the two random numbers are the same by pure chance
|
assertNotEquals(rndb1.integerValue(), rndb2.integerValue()) // this *sometimes* fails when the two random numbers are the isSameAs by pure chance
|
||||||
assertTrue(rndf.numericValue().toDouble() > 0.0 && rndf.numericValue().toDouble() < 1.0)
|
assertTrue(rndf.numericValue().toDouble() > 0.0 && rndf.numericValue().toDouble() < 1.0)
|
||||||
|
|
||||||
vm.step(2)
|
vm.step(2)
|
||||||
|
Loading…
Reference in New Issue
Block a user