mirror of
				https://github.com/irmen/prog8.git
				synced 2025-11-03 19:16:13 +00:00 
			
		
		
		
	Merge branch 'master' into structs
# Conflicts: # codeGenCpu6502/src/prog8/codegen/cpu6502/AsmGen.kt # compiler/src/prog8/compiler/astprocessing/CodeDesugarer.kt # compiler/src/prog8/compiler/astprocessing/SimplifiedAstMaker.kt # compilerAst/src/prog8/ast/AstToSourceTextConverter.kt # compilerAst/src/prog8/ast/antlr/Antlr2Kotlin.kt # compilerAst/src/prog8/ast/walk/AstWalker.kt # compilerAst/src/prog8/ast/walk/IAstVisitor.kt # docs/source/todo.rst # examples/test.p8 # parser/src/main/antlr/Prog8ANTLR.g4
This commit is contained in:
		@@ -71,6 +71,7 @@ What does Prog8 provide?
 | 
				
			|||||||
- high-level program optimizations
 | 
					- high-level program optimizations
 | 
				
			||||||
- conditional branches that map 1:1 to cpu status flags
 | 
					- conditional branches that map 1:1 to cpu status flags
 | 
				
			||||||
- ``when`` statement to provide a concise jump table alternative to if/elseif chains
 | 
					- ``when`` statement to provide a concise jump table alternative to if/elseif chains
 | 
				
			||||||
 | 
					- ``on .. goto`` statement for fast jump tables
 | 
				
			||||||
- ``in`` expression for concise and efficient multi-value/containment check 
 | 
					- ``in`` expression for concise and efficient multi-value/containment check 
 | 
				
			||||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
 | 
					- ``defer`` statement to help write concise and robust subroutine cleanup logic
 | 
				
			||||||
- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
 | 
					- several specialized built-in functions such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,10 @@ import prog8.code.target.zp.C128Zeropage
 | 
				
			|||||||
import java.nio.file.Path
 | 
					import java.nio.file.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
					class C128Target: ICompilationTarget,
 | 
				
			||||||
 | 
					    IStringEncoding by Encoder(true),
 | 
				
			||||||
 | 
					    IMemSizer by NormalMemSizer(Mflpt5.FLOAT_MEM_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val name = NAME
 | 
					    override val name = NAME
 | 
				
			||||||
    override val defaultEncoding = Encoding.PETSCII
 | 
					    override val defaultEncoding = Encoding.PETSCII
 | 
				
			||||||
    override val libraryPath = null
 | 
					    override val libraryPath = null
 | 
				
			||||||
@@ -28,10 +31,10 @@ class C128Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by N
 | 
				
			|||||||
    override val PROGRAM_LOAD_ADDRESS = 0x1c01u
 | 
					    override val PROGRAM_LOAD_ADDRESS = 0x1c01u
 | 
				
			||||||
    override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
 | 
					    override val PROGRAM_MEMTOP_ADDRESS = 0xc000u
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val BSSHIGHRAM_START = 0u    // TODO
 | 
					    override val BSSHIGHRAM_START = 0u    // TODO address?
 | 
				
			||||||
    override val BSSHIGHRAM_END = 0u      // TODO
 | 
					    override val BSSHIGHRAM_END = 0u      // TODO address?
 | 
				
			||||||
    override val BSSGOLDENRAM_START = 0u  // TODO
 | 
					    override val BSSGOLDENRAM_START = 0u  // TODO address?
 | 
				
			||||||
    override val BSSGOLDENRAM_END = 0u    // TODO
 | 
					    override val BSSGOLDENRAM_END = 0u    // TODO address?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override lateinit var zeropage: Zeropage
 | 
					    override lateinit var zeropage: Zeropage
 | 
				
			||||||
    override lateinit var golden: GoldenRam
 | 
					    override lateinit var golden: GoldenRam
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,10 @@ import java.io.IOException
 | 
				
			|||||||
import java.nio.file.Path
 | 
					import java.nio.file.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class C64Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
					class C64Target: ICompilationTarget,
 | 
				
			||||||
 | 
					    IStringEncoding by Encoder(true),
 | 
				
			||||||
 | 
					    IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val name = NAME
 | 
					    override val name = NAME
 | 
				
			||||||
    override val defaultEncoding = Encoding.PETSCII
 | 
					    override val defaultEncoding = Encoding.PETSCII
 | 
				
			||||||
    override val libraryPath = null
 | 
					    override val libraryPath = null
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -37,7 +37,7 @@ class ConfigFileTarget(
 | 
				
			|||||||
    val zpFullsafe: List<UIntRange>,
 | 
					    val zpFullsafe: List<UIntRange>,
 | 
				
			||||||
    val zpKernalsafe: List<UIntRange>,
 | 
					    val zpKernalsafe: List<UIntRange>,
 | 
				
			||||||
    val zpBasicsafe: List<UIntRange>
 | 
					    val zpBasicsafe: List<UIntRange>
 | 
				
			||||||
): ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(8) {
 | 
					): ICompilationTarget, IStringEncoding by Encoder(true), IMemSizer by NormalMemSizer(8) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    companion object {
 | 
					    companion object {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,10 @@ import prog8.code.target.zp.CX16Zeropage
 | 
				
			|||||||
import java.nio.file.Path
 | 
					import java.nio.file.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class Cx16Target: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
					class Cx16Target: ICompilationTarget,
 | 
				
			||||||
 | 
					    IStringEncoding by Encoder(true),
 | 
				
			||||||
 | 
					    IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val name = NAME
 | 
					    override val name = NAME
 | 
				
			||||||
    override val defaultEncoding = Encoding.PETSCII
 | 
					    override val defaultEncoding = Encoding.PETSCII
 | 
				
			||||||
    override val libraryPath = null
 | 
					    override val libraryPath = null
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,7 +6,10 @@ import prog8.code.target.zp.PETZeropage
 | 
				
			|||||||
import java.nio.file.Path
 | 
					import java.nio.file.Path
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class PETTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
					class PETTarget: ICompilationTarget,
 | 
				
			||||||
 | 
					    IStringEncoding by Encoder(true),
 | 
				
			||||||
 | 
					    IMemSizer by NormalMemSizer(Mflpt5.Companion.FLOAT_MEM_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val name = NAME
 | 
					    override val name = NAME
 | 
				
			||||||
    override val defaultEncoding = Encoding.PETSCII
 | 
					    override val defaultEncoding = Encoding.PETSCII
 | 
				
			||||||
    override val libraryPath = null
 | 
					    override val libraryPath = null
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -7,7 +7,10 @@ import kotlin.io.path.isReadable
 | 
				
			|||||||
import kotlin.io.path.name
 | 
					import kotlin.io.path.name
 | 
				
			||||||
import kotlin.io.path.readText
 | 
					import kotlin.io.path.readText
 | 
				
			||||||
 | 
					
 | 
				
			||||||
class VMTarget: ICompilationTarget, IStringEncoding by Encoder, IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
 | 
					class VMTarget: ICompilationTarget,
 | 
				
			||||||
 | 
					    IStringEncoding by Encoder(false),
 | 
				
			||||||
 | 
					    IMemSizer by NormalMemSizer(FLOAT_MEM_SIZE) {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override val name = NAME
 | 
					    override val name = NAME
 | 
				
			||||||
    override val defaultEncoding = Encoding.ISO
 | 
					    override val defaultEncoding = Encoding.ISO
 | 
				
			||||||
    override val libraryPath = null
 | 
					    override val libraryPath = null
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -197,6 +197,7 @@ object AtasciiEncoding {
 | 
				
			|||||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
					    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
				
			||||||
        val mapped = str.map { chr ->
 | 
					        val mapped = str.map { chr ->
 | 
				
			||||||
            when (chr) {
 | 
					            when (chr) {
 | 
				
			||||||
 | 
					                '\r' -> 0x9bu
 | 
				
			||||||
                '\u0000' -> 0u
 | 
					                '\u0000' -> 0u
 | 
				
			||||||
                in '\u8000'..'\u80ff' -> {
 | 
					                in '\u8000'..'\u80ff' -> {
 | 
				
			||||||
                    // special case: take the lower 8 bit hex value directly
 | 
					                    // special case: take the lower 8 bit hex value directly
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -285,6 +285,7 @@ object C64osEncoding {
 | 
				
			|||||||
            val screencode = encodingC64os[chr]
 | 
					            val screencode = encodingC64os[chr]
 | 
				
			||||||
            return screencode?.toUByte() ?: when (chr) {
 | 
					            return screencode?.toUByte() ?: when (chr) {
 | 
				
			||||||
                '\u0000' -> 0u
 | 
					                '\u0000' -> 0u
 | 
				
			||||||
 | 
					                '\n' -> 13u
 | 
				
			||||||
                in '\u8000'..'\u80ff' -> {
 | 
					                in '\u8000'..'\u80ff' -> {
 | 
				
			||||||
                    // special case: take the lower 8 bit hex value directly
 | 
					                    // special case: take the lower 8 bit hex value directly
 | 
				
			||||||
                    (chr.code - 0x8000).toUByte()
 | 
					                    (chr.code - 0x8000).toUByte()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -5,19 +5,19 @@ import prog8.code.core.Encoding
 | 
				
			|||||||
import prog8.code.core.IStringEncoding
 | 
					import prog8.code.core.IStringEncoding
 | 
				
			||||||
import prog8.code.core.InternalCompilerException
 | 
					import prog8.code.core.InternalCompilerException
 | 
				
			||||||
 | 
					
 | 
				
			||||||
object Encoder: IStringEncoding {
 | 
					class Encoder(val newlineToCarriageReturn: Boolean): IStringEncoding {
 | 
				
			||||||
    override val defaultEncoding: Encoding = Encoding.ISO
 | 
					    override val defaultEncoding: Encoding = Encoding.ISO
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun encodeString(str: String, encoding: Encoding): List<UByte> {
 | 
					    override fun encodeString(str: String, encoding: Encoding): List<UByte> {
 | 
				
			||||||
        val coded = when(encoding) {
 | 
					        val coded = when(encoding) {
 | 
				
			||||||
            Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
 | 
					            Encoding.PETSCII -> PetsciiEncoding.encodePetscii(str, true)
 | 
				
			||||||
            Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
 | 
					            Encoding.SCREENCODES -> PetsciiEncoding.encodeScreencode(str, true)
 | 
				
			||||||
            Encoding.ISO -> IsoEncoding.encode(str)
 | 
					            Encoding.ISO -> IsoEncoding.encode(str, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ATASCII -> AtasciiEncoding.encode(str)
 | 
					            Encoding.ISO5 -> IsoCyrillicEncoding.encode(str, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ISO5 -> IsoCyrillicEncoding.encode(str)
 | 
					            Encoding.ISO16 -> IsoEasternEncoding.encode(str, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ISO16 -> IsoEasternEncoding.encode(str)
 | 
					 | 
				
			||||||
            Encoding.CP437 -> Cp437Encoding.encode(str)
 | 
					            Encoding.CP437 -> Cp437Encoding.encode(str)
 | 
				
			||||||
            Encoding.KATAKANA -> KatakanaEncoding.encode(str)
 | 
					            Encoding.KATAKANA -> KatakanaEncoding.encode(str, newlineToCarriageReturn)
 | 
				
			||||||
 | 
					            Encoding.ATASCII -> AtasciiEncoding.encode(str)
 | 
				
			||||||
            Encoding.C64OS -> C64osEncoding.encode(str)
 | 
					            Encoding.C64OS -> C64osEncoding.encode(str)
 | 
				
			||||||
            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
					            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
@@ -30,12 +30,12 @@ object Encoder: IStringEncoding {
 | 
				
			|||||||
        val decoded = when(encoding) {
 | 
					        val decoded = when(encoding) {
 | 
				
			||||||
            Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
 | 
					            Encoding.PETSCII -> PetsciiEncoding.decodePetscii(bytes, true)
 | 
				
			||||||
            Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
 | 
					            Encoding.SCREENCODES -> PetsciiEncoding.decodeScreencode(bytes, true)
 | 
				
			||||||
            Encoding.ISO -> IsoEncoding.decode(bytes)
 | 
					            Encoding.ISO -> IsoEncoding.decode(bytes, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
 | 
					            Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ISO5 -> IsoCyrillicEncoding.decode(bytes)
 | 
					            Encoding.ISO16 -> IsoEasternEncoding.decode(bytes, newlineToCarriageReturn)
 | 
				
			||||||
            Encoding.ISO16 -> IsoEasternEncoding.decode(bytes)
 | 
					 | 
				
			||||||
            Encoding.CP437 -> Cp437Encoding.decode(bytes)
 | 
					            Encoding.CP437 -> Cp437Encoding.decode(bytes)
 | 
				
			||||||
            Encoding.KATAKANA -> KatakanaEncoding.decode(bytes)
 | 
					            Encoding.KATAKANA -> KatakanaEncoding.decode(bytes, newlineToCarriageReturn)
 | 
				
			||||||
 | 
					            Encoding.ATASCII -> AtasciiEncoding.decode(bytes)
 | 
				
			||||||
            Encoding.C64OS -> C64osEncoding.decode(bytes)
 | 
					            Encoding.C64OS -> C64osEncoding.decode(bytes)
 | 
				
			||||||
            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
					            else -> throw InternalCompilerException("unsupported encoding $encoding")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -6,14 +6,16 @@ import com.github.michaelbull.result.Result
 | 
				
			|||||||
import java.io.CharConversionException
 | 
					import java.io.CharConversionException
 | 
				
			||||||
import java.nio.charset.Charset
 | 
					import java.nio.charset.Charset
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
open class IsoEncodingBase(charsetName: String) {
 | 
					open class IsoEncodingBase(charsetName: String) {
 | 
				
			||||||
    val charset: Charset = Charset.forName(charsetName)
 | 
					    val charset: Charset = Charset.forName(charsetName)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
					    fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
 | 
				
			||||||
        return try {
 | 
					        return try {
 | 
				
			||||||
            val mapped = str.map { chr ->
 | 
					            val mapped = str.map { chr ->
 | 
				
			||||||
                when (chr) {
 | 
					                when (chr) {
 | 
				
			||||||
                    '\u0000' -> 0u
 | 
					                    '\u0000' -> 0u
 | 
				
			||||||
 | 
					                    '\n' -> if(newlineToCarriageReturn) 13u else 10u
 | 
				
			||||||
                    in '\u8000'..'\u80ff' -> {
 | 
					                    in '\u8000'..'\u80ff' -> {
 | 
				
			||||||
                        // special case: take the lower 8 bit hex value directly
 | 
					                        // special case: take the lower 8 bit hex value directly
 | 
				
			||||||
                        (chr.code - 0x8000).toUByte()
 | 
					                        (chr.code - 0x8000).toUByte()
 | 
				
			||||||
@@ -27,9 +29,14 @@ open class IsoEncodingBase(charsetName: String) {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
 | 
					    fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
 | 
				
			||||||
        return try {
 | 
					        return try {
 | 
				
			||||||
            Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
 | 
					            Ok(String(bytes.map {
 | 
				
			||||||
 | 
					                when(it) {
 | 
				
			||||||
 | 
					                    13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
 | 
				
			||||||
 | 
					                    else -> it.toByte()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.toByteArray(), charset))
 | 
				
			||||||
        } catch (ce: CharConversionException) {
 | 
					        } catch (ce: CharConversionException) {
 | 
				
			||||||
            Err(ce)
 | 
					            Err(ce)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,10 +64,11 @@ object JapaneseCharacterConverter {
 | 
				
			|||||||
object KatakanaEncoding {
 | 
					object KatakanaEncoding {
 | 
				
			||||||
    val charset: Charset = Charset.forName("JIS_X0201")
 | 
					    val charset: Charset = Charset.forName("JIS_X0201")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun encode(str: String): Result<List<UByte>, CharConversionException> {
 | 
					    fun encode(str: String, newlineToCarriageReturn: Boolean): Result<List<UByte>, CharConversionException> {
 | 
				
			||||||
        return try {
 | 
					        return try {
 | 
				
			||||||
            val mapped = str.map { chr ->
 | 
					            val mapped = str.map { chr ->
 | 
				
			||||||
                when (chr) {
 | 
					                when (chr) {
 | 
				
			||||||
 | 
					                    '\n' -> if(newlineToCarriageReturn) 13u else 10u
 | 
				
			||||||
 | 
					
 | 
				
			||||||
                    '\u0000' -> 0u
 | 
					                    '\u0000' -> 0u
 | 
				
			||||||
                    '\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
 | 
					                    '\u00a0' -> 0xa0u // $a0 isn't technically a part of JIS X 0201 spec, and so we need to handle this ourselves
 | 
				
			||||||
@@ -112,9 +113,14 @@ object KatakanaEncoding {
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun decode(bytes: Iterable<UByte>): Result<String, CharConversionException> {
 | 
					    fun decode(bytes: Iterable<UByte>, newlineToCarriageReturn: Boolean): Result<String, CharConversionException> {
 | 
				
			||||||
        return try {
 | 
					        return try {
 | 
				
			||||||
            Ok(String(bytes.map { it.toByte() }.toByteArray(), charset))
 | 
					            Ok(String(bytes.map {
 | 
				
			||||||
 | 
					                when(it) {
 | 
				
			||||||
 | 
					                    13u.toUByte() -> if(newlineToCarriageReturn) 10 else 13
 | 
				
			||||||
 | 
					                    else -> it.toByte()
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }.toByteArray(), charset))
 | 
				
			||||||
        } catch (ce: CharConversionException) {
 | 
					        } catch (ce: CharConversionException) {
 | 
				
			||||||
            Err(ce)
 | 
					            Err(ce)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -21,7 +21,7 @@ object PetsciiEncoding {
 | 
				
			|||||||
        '\ufffe',    //       0x07 -> UNDEFINED
 | 
					        '\ufffe',    //       0x07 -> UNDEFINED
 | 
				
			||||||
        '\uf118',    //       0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
 | 
					        '\uf118',    //       0x08 -> DISABLE CHARACTER SET SWITCHING (CUS)
 | 
				
			||||||
        '\uf119',    //       0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
 | 
					        '\uf119',    //       0x09 -> ENABLE CHARACTER SET SWITCHING (CUS)
 | 
				
			||||||
        '\ufffe',    //       0x0A -> UNDEFINED
 | 
					        '\n',        //       0x0A -> LINE FEED (RETURN)
 | 
				
			||||||
        '\ufffe',    //       0x0B -> UNDEFINED
 | 
					        '\ufffe',    //       0x0B -> UNDEFINED
 | 
				
			||||||
        '\ufffe',    //       0x0C -> UNDEFINED
 | 
					        '\ufffe',    //       0x0C -> UNDEFINED
 | 
				
			||||||
        '\n'    ,    //       0x0D -> LINE FEED (RETURN)
 | 
					        '\n'    ,    //       0x0D -> LINE FEED (RETURN)
 | 
				
			||||||
@@ -1117,6 +1117,8 @@ object PetsciiEncoding {
 | 
				
			|||||||
            val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
 | 
					            val screencode = if(lowercase) encodingScreencodeLowercase[chr] else encodingScreencodeUppercase[chr]
 | 
				
			||||||
            return screencode?.toUByte() ?: when (chr) {
 | 
					            return screencode?.toUByte() ?: when (chr) {
 | 
				
			||||||
                '\u0000' -> 0u
 | 
					                '\u0000' -> 0u
 | 
				
			||||||
 | 
					                '\n' -> 141u
 | 
				
			||||||
 | 
					                '\r' -> 141u
 | 
				
			||||||
                in '\u8000'..'\u80ff' -> {
 | 
					                in '\u8000'..'\u80ff' -> {
 | 
				
			||||||
                    // special case: take the lower 8 bit hex value directly
 | 
					                    // special case: take the lower 8 bit hex value directly
 | 
				
			||||||
                    (chr.code - 0x8000).toUByte()
 | 
					                    (chr.code - 0x8000).toUByte()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1034,6 +1034,7 @@ $repeatLabel""")
 | 
				
			|||||||
            val target = getJumpTarget(jump)
 | 
					            val target = getJumpTarget(jump)
 | 
				
			||||||
            require(!target.needsExpressionEvaluation)
 | 
					            require(!target.needsExpressionEvaluation)
 | 
				
			||||||
            if(target.indirect) {
 | 
					            if(target.indirect) {
 | 
				
			||||||
 | 
					                require(!target.indexedX)
 | 
				
			||||||
                val complementedInstruction = branchInstruction(stmt.condition, true)
 | 
					                val complementedInstruction = branchInstruction(stmt.condition, true)
 | 
				
			||||||
                out("""
 | 
					                out("""
 | 
				
			||||||
                    $complementedInstruction +
 | 
					                    $complementedInstruction +
 | 
				
			||||||
@@ -1085,19 +1086,25 @@ $repeatLabel""")
 | 
				
			|||||||
        else {
 | 
					        else {
 | 
				
			||||||
            if(evaluateAddressExpression) {
 | 
					            if(evaluateAddressExpression) {
 | 
				
			||||||
                val arrayIdx = jump.target as? PtArrayIndexer
 | 
					                val arrayIdx = jump.target as? PtArrayIndexer
 | 
				
			||||||
                if (isTargetCpu(CpuType.CPU65C02) && arrayIdx!=null) {
 | 
					                if (arrayIdx!=null) {
 | 
				
			||||||
 | 
					                    if (isTargetCpu(CpuType.CPU65C02)) {
 | 
				
			||||||
                        if (!arrayIdx.splitWords) {
 | 
					                        if (!arrayIdx.splitWords) {
 | 
				
			||||||
                            // if the jump target is an address in a non-split array (like a jump table of only pointers),
 | 
					                            // if the jump target is an address in a non-split array (like a jump table of only pointers),
 | 
				
			||||||
                        // on the 65c02, more optimal assembly can be generated using JMP address,X
 | 
					                            // on the 65c02, more optimal assembly can be generated using JMP (address,X)
 | 
				
			||||||
                            assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
 | 
					                            assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
 | 
				
			||||||
                            out("  asl  a |  tax")
 | 
					                            out("  asl  a |  tax")
 | 
				
			||||||
                            return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
 | 
					                            return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
 | 
				
			||||||
                        } else {
 | 
					                        } else {
 | 
				
			||||||
                        // print a message when more optimal code is possible
 | 
					                            // print a message when more optimal code is possible for 65C02 cpu
 | 
				
			||||||
                            val variable = symbolTable.lookup(arrayIdx.variable.name)!!
 | 
					                            val variable = symbolTable.lookup(arrayIdx.variable.name)!!
 | 
				
			||||||
                            if(variable is StStaticVariable && variable.length!!<=128u)
 | 
					                            if(variable is StStaticVariable && variable.length!!<=128u)
 | 
				
			||||||
                                errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
 | 
					                                errors.info("the jump address array is @split, but @nosplit would create more efficient code here", jump.position)
 | 
				
			||||||
                        }
 | 
					                        }
 | 
				
			||||||
 | 
					                    } else {
 | 
				
			||||||
 | 
					                        // print a message when more optimal code is possible for 6502 cpu
 | 
				
			||||||
 | 
					                        if(!arrayIdx.splitWords)
 | 
				
			||||||
 | 
					                            errors.info("the jump address array is @nosplit, but @split would create more efficient code here", jump.position)
 | 
				
			||||||
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
                // we can do the address evaluation right now and just use a temporary pointer variable
 | 
					                // we can do the address evaluation right now and just use a temporary pointer variable
 | 
				
			||||||
                assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
 | 
					                assignExpressionToVariable(jump.target, "P8ZP_SCRATCH_W1", DataType.UWORD)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -475,7 +475,7 @@ private fun getAddressArg(line: String, symbolTable: SymbolTable): UInt? {
 | 
				
			|||||||
                val identifier = identMatch.value
 | 
					                val identifier = identMatch.value
 | 
				
			||||||
                when (val symbol = symbolTable.flat[identifier]) {
 | 
					                when (val symbol = symbolTable.flat[identifier]) {
 | 
				
			||||||
                    is StConstant -> symbol.value.toUInt()
 | 
					                    is StConstant -> symbol.value.toUInt()
 | 
				
			||||||
                    is StMemVar -> symbol.address.toUInt()
 | 
					                    is StMemVar -> symbol.address
 | 
				
			||||||
                    else -> null
 | 
					                    else -> null
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            } else null
 | 
					            } else null
 | 
				
			||||||
@@ -512,6 +512,7 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
 | 
				
			|||||||
    // rts + jmp -> remove jmp
 | 
					    // rts + jmp -> remove jmp
 | 
				
			||||||
    // rts + bxx -> remove bxx
 | 
					    // rts + bxx -> remove bxx
 | 
				
			||||||
    // lda  + cmp #0 -> remove cmp,  same for cpy and cpx.
 | 
					    // lda  + cmp #0 -> remove cmp,  same for cpy and cpx.
 | 
				
			||||||
 | 
					    // bra/jmp + bra/jmp -> remove second bra/jmp
 | 
				
			||||||
    // and some other optimizations.
 | 
					    // and some other optimizations.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val mods = mutableListOf<Modification>()
 | 
					    val mods = mutableListOf<Modification>()
 | 
				
			||||||
@@ -567,6 +568,12 @@ private fun optimizeJsrRtsAndOtherCombinations(linesByFour: Sequence<List<Indexe
 | 
				
			|||||||
                    }
 | 
					                    }
 | 
				
			||||||
                }
 | 
					                }
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					            if(" bra" in first || "\tbra" in first || " jmp" in first || "\tjmp" in first ) {
 | 
				
			||||||
 | 
					                if(" bra" in second || "\tbra" in second || " jmp" in second || "\tjmp" in second ) {
 | 
				
			||||||
 | 
					                    mods.add(Modification(lines[1].index, true, null))
 | 
				
			||||||
 | 
					                }
 | 
				
			||||||
 | 
					            }
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
        /*
 | 
					        /*
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -86,6 +86,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                if(testForBitSet) {
 | 
					                if(testForBitSet) {
 | 
				
			||||||
                    if(jumpAfterIf!=null) {
 | 
					                    if(jumpAfterIf!=null) {
 | 
				
			||||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        branch("bmi", target)
 | 
					                        branch("bmi", target)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
@@ -93,6 +94,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    if(jumpAfterIf!=null) {
 | 
					                    if(jumpAfterIf!=null) {
 | 
				
			||||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        branch("bpl", target)
 | 
					                        branch("bpl", target)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
@@ -106,6 +108,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                if(testForBitSet) {
 | 
					                if(testForBitSet) {
 | 
				
			||||||
                    if(jumpAfterIf!=null) {
 | 
					                    if(jumpAfterIf!=null) {
 | 
				
			||||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        branch("bvs", target)
 | 
					                        branch("bvs", target)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
@@ -113,6 +116,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                } else {
 | 
					                } else {
 | 
				
			||||||
                    if(jumpAfterIf!=null) {
 | 
					                    if(jumpAfterIf!=null) {
 | 
				
			||||||
                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                        val target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        branch("bvc", target)
 | 
					                        branch("bvc", target)
 | 
				
			||||||
                    }
 | 
					                    }
 | 
				
			||||||
                    else
 | 
					                    else
 | 
				
			||||||
@@ -171,9 +175,8 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
            asmgen.out("  $falseBranch  +")
 | 
					            asmgen.out("  $falseBranch  +")
 | 
				
			||||||
            if(target.needsExpressionEvaluation)
 | 
					            if(target.needsExpressionEvaluation)
 | 
				
			||||||
                target = asmgen.getJumpTarget(jump)
 | 
					                target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
            asmgen.out("""
 | 
					            asmgen.jmp(target.asmLabel, target.indirect, target.indexedX)
 | 
				
			||||||
                jmp  (${target.asmLabel})
 | 
					            asmgen.out("+")
 | 
				
			||||||
+""")
 | 
					 | 
				
			||||||
        } else {
 | 
					        } else {
 | 
				
			||||||
            require(!target.needsExpressionEvaluation)
 | 
					            require(!target.needsExpressionEvaluation)
 | 
				
			||||||
            asmgen.out("  $branchInstr  ${target.asmLabel}")
 | 
					            asmgen.out("  $branchInstr  ${target.asmLabel}")
 | 
				
			||||||
@@ -286,6 +289,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                            asmgen.out("  bmi  + |  beq +")
 | 
					                            asmgen.out("  bmi  + |  beq +")
 | 
				
			||||||
                            if(target.needsExpressionEvaluation)
 | 
					                            if(target.needsExpressionEvaluation)
 | 
				
			||||||
                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                            require(!target.indexedX)
 | 
				
			||||||
                            asmgen.out("""
 | 
					                            asmgen.out("""
 | 
				
			||||||
                                jmp  (${target.asmLabel})
 | 
					                                jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -353,6 +357,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                            asmgen.out("  bmi  + |  bne  ++")
 | 
					                            asmgen.out("  bmi  + |  bne  ++")
 | 
				
			||||||
                            if(target.needsExpressionEvaluation)
 | 
					                            if(target.needsExpressionEvaluation)
 | 
				
			||||||
                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                                target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                            require(!target.indexedX)
 | 
				
			||||||
                            asmgen.out("""
 | 
					                            asmgen.out("""
 | 
				
			||||||
+                               jmp  (${target.asmLabel})
 | 
					+                               jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -434,6 +439,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                    asmgen.out("  bcc  + |  beq  +")
 | 
					                    asmgen.out("  bcc  + |  beq  +")
 | 
				
			||||||
                    if(target.needsExpressionEvaluation)
 | 
					                    if(target.needsExpressionEvaluation)
 | 
				
			||||||
                        target = asmgen.getJumpTarget(jumpAfterIf)
 | 
					                        target = asmgen.getJumpTarget(jumpAfterIf)
 | 
				
			||||||
 | 
					                    require(!target.indexedX)
 | 
				
			||||||
                    asmgen.out("""
 | 
					                    asmgen.out("""
 | 
				
			||||||
                        jmp  (${target.asmLabel})
 | 
					                        jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -535,6 +541,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
+                           bpl  +""")
 | 
					+                           bpl  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
                            jmp  (${target.asmLabel})
 | 
					                            jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -590,6 +597,7 @@ internal class IfElseAsmGen(private val program: PtProgram,
 | 
				
			|||||||
                            bcs  +""")
 | 
					                            bcs  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
_jump                       jmp  (${target.asmLabel})
 | 
					_jump                       jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -667,6 +675,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
+                           bpl  +""")
 | 
					+                           bpl  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
                           jmp  (${target.asmLabel})
 | 
					                           jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -721,6 +730,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            bcc  +""")
 | 
					                            bcc  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
                            jmp  (${target.asmLabel})
 | 
					                            jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -829,6 +839,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            bne  ++""")
 | 
					                            bne  ++""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
+                           jmp  (${target.asmLabel})
 | 
					+                           jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -908,6 +919,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                        bne  ++""")
 | 
					                        bne  ++""")
 | 
				
			||||||
                    if(target.needsExpressionEvaluation)
 | 
					                    if(target.needsExpressionEvaluation)
 | 
				
			||||||
                        target = asmgen.getJumpTarget(jump)
 | 
					                        target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                    require(!target.indexedX)
 | 
				
			||||||
                    asmgen.out("""
 | 
					                    asmgen.out("""
 | 
				
			||||||
+                       jmp  (${target.asmLabel})
 | 
					+                       jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -973,6 +985,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            beq  ++""")
 | 
					                            beq  ++""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
+                           jmp  (${target.asmLabel})
 | 
					+                           jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1053,6 +1066,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                        beq  ++""")
 | 
					                        beq  ++""")
 | 
				
			||||||
                    if(target.needsExpressionEvaluation)
 | 
					                    if(target.needsExpressionEvaluation)
 | 
				
			||||||
                        target = asmgen.getJumpTarget(jump)
 | 
					                        target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                    require(!target.indexedX)
 | 
				
			||||||
                    asmgen.out("""
 | 
					                    asmgen.out("""
 | 
				
			||||||
+                       jmp  (${target.asmLabel})
 | 
					+                       jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1196,6 +1210,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                        beq  ++""")
 | 
					                        beq  ++""")
 | 
				
			||||||
                    if(target.needsExpressionEvaluation)
 | 
					                    if(target.needsExpressionEvaluation)
 | 
				
			||||||
                        target = asmgen.getJumpTarget(jump)
 | 
					                        target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                    require(!target.indexedX)
 | 
				
			||||||
                    asmgen.out("""
 | 
					                    asmgen.out("""
 | 
				
			||||||
+                       jmp  (${target.asmLabel})
 | 
					+                       jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1248,6 +1263,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                        bne  +""")
 | 
					                        bne  +""")
 | 
				
			||||||
                    if(target.needsExpressionEvaluation)
 | 
					                    if(target.needsExpressionEvaluation)
 | 
				
			||||||
                        target = asmgen.getJumpTarget(jump)
 | 
					                        target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                    require(!target.indexedX)
 | 
				
			||||||
                    asmgen.out("""
 | 
					                    asmgen.out("""
 | 
				
			||||||
                        jmp  (${target.asmLabel})
 | 
					                        jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1302,6 +1318,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            beq  ++""")
 | 
					                            beq  ++""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
+                           jmp  (${target.asmLabel})
 | 
					+                           jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1361,6 +1378,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            bne  +""")
 | 
					                            bne  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
                            jmp  (${target.asmLabel})
 | 
					                            jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1423,6 +1441,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            beq  ++""")
 | 
					                            beq  ++""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
+                           jmp  (${target.asmLabel})
 | 
					+                           jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
@@ -1481,6 +1500,7 @@ _jump                       jmp  (${target.asmLabel})
 | 
				
			|||||||
                            bne  +""")
 | 
					                            bne  +""")
 | 
				
			||||||
                        if(target.needsExpressionEvaluation)
 | 
					                        if(target.needsExpressionEvaluation)
 | 
				
			||||||
                            target = asmgen.getJumpTarget(jump)
 | 
					                            target = asmgen.getJumpTarget(jump)
 | 
				
			||||||
 | 
					                        require(!target.indexedX)
 | 
				
			||||||
                        asmgen.out("""
 | 
					                        asmgen.out("""
 | 
				
			||||||
                            jmp  (${target.asmLabel})
 | 
					                            jmp  (${target.asmLabel})
 | 
				
			||||||
+""")
 | 
					+""")
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -258,8 +258,8 @@ class IRCodeGen(
 | 
				
			|||||||
            is PtBool,
 | 
					            is PtBool,
 | 
				
			||||||
            is PtArray,
 | 
					            is PtArray,
 | 
				
			||||||
            is PtBlock,
 | 
					            is PtBlock,
 | 
				
			||||||
            is PtDefer -> throw AssemblyError("should have been transformed")
 | 
					            is PtDefer -> throw AssemblyError("defer should have been transformed")
 | 
				
			||||||
            is PtString -> throw AssemblyError("should not occur as separate statement node ${node.position}")
 | 
					            is PtString -> throw AssemblyError("string should not occur as separate statement node ${node.position}")
 | 
				
			||||||
            is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
 | 
					            is PtSub -> throw AssemblyError("nested subroutines should have been flattened ${node.position}")
 | 
				
			||||||
            is PtStructDecl -> emptyList()
 | 
					            is PtStructDecl -> emptyList()
 | 
				
			||||||
            is PtSubSignature -> emptyList()
 | 
					            is PtSubSignature -> emptyList()
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -424,7 +424,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
 | 
				
			|||||||
    errors.report()
 | 
					    errors.report()
 | 
				
			||||||
    program.reorderStatements(errors)
 | 
					    program.reorderStatements(errors)
 | 
				
			||||||
    errors.report()
 | 
					    errors.report()
 | 
				
			||||||
    program.desugaring(errors)
 | 
					    program.desugaring(errors, compilerOptions)
 | 
				
			||||||
    errors.report()
 | 
					    errors.report()
 | 
				
			||||||
    program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
 | 
					    program.changeNotExpressionAndIfComparisonExpr(errors, compilerOptions.compTarget)
 | 
				
			||||||
    errors.report()
 | 
					    errors.report()
 | 
				
			||||||
@@ -486,7 +486,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
 | 
					private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
 | 
				
			||||||
    program.desugaring(errors)
 | 
					    program.desugaring(errors, compilerOptions)
 | 
				
			||||||
    program.addTypecasts(errors, compilerOptions)
 | 
					    program.addTypecasts(errors, compilerOptions)
 | 
				
			||||||
    errors.report()
 | 
					    errors.report()
 | 
				
			||||||
    program.variousCleanups(errors, compilerOptions)
 | 
					    program.variousCleanups(errors, compilerOptions)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -2356,6 +2356,12 @@ internal class AstChecker(private val program: Program,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        return false
 | 
					        return false
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun visit(onGoto: OnGoto) {
 | 
				
			||||||
 | 
					        if(!onGoto.index.inferType(program).getOrUndef().isUnsignedByte) {
 | 
				
			||||||
 | 
					            errors.err("on..goto index must be an unsigned byte", onGoto.index.position)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
 | 
					internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -103,8 +103,8 @@ internal fun Program.addTypecasts(errors: IErrorReporter, options: CompilationOp
 | 
				
			|||||||
    caster.applyModifications()
 | 
					    caster.applyModifications()
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
fun Program.desugaring(errors: IErrorReporter) {
 | 
					fun Program.desugaring(errors: IErrorReporter, options: CompilationOptions) {
 | 
				
			||||||
    val desugar = CodeDesugarer(this, errors)
 | 
					    val desugar = CodeDesugarer(this, options.compTarget, errors)
 | 
				
			||||||
    desugar.visit(this)
 | 
					    desugar.visit(this)
 | 
				
			||||||
    while(errors.noErrors() && desugar.applyModifications()>0)
 | 
					    while(errors.noErrors() && desugar.applyModifications()>0)
 | 
				
			||||||
        desugar.visit(this)
 | 
					        desugar.visit(this)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -8,7 +8,7 @@ import prog8.ast.walk.IAstModification
 | 
				
			|||||||
import prog8.code.core.*
 | 
					import prog8.code.core.*
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
internal class CodeDesugarer(val program: Program, private val errors: IErrorReporter) : AstWalker() {
 | 
					internal class CodeDesugarer(val program: Program, private val target: ICompilationTarget, private val errors: IErrorReporter) : AstWalker() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    // Some more code shuffling to simplify the Ast that the codegenerator has to process.
 | 
					    // Some more code shuffling to simplify the Ast that the codegenerator has to process.
 | 
				
			||||||
    // Several changes have already been done by the StatementReorderer !
 | 
					    // Several changes have already been done by the StatementReorderer !
 | 
				
			||||||
@@ -24,6 +24,7 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
 | 
				
			|||||||
    // - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
 | 
					    // - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
 | 
				
			||||||
    // - flatten chained assignments
 | 
					    // - flatten chained assignments
 | 
				
			||||||
    // - remove alias nodes
 | 
					    // - remove alias nodes
 | 
				
			||||||
 | 
					    // - convert on..goto/call to jumpaddr array and separate goto/call
 | 
				
			||||||
    // - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
 | 
					    // - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
 | 
					    override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
 | 
				
			||||||
@@ -394,4 +395,60 @@ _after:
 | 
				
			|||||||
        }
 | 
					        }
 | 
				
			||||||
        return noModifications
 | 
					        return noModifications
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> {
 | 
				
			||||||
 | 
					        val indexDt = ongoto.index.inferType(program).getOrUndef()
 | 
				
			||||||
 | 
					        if(!indexDt.isUnsignedByte)
 | 
				
			||||||
 | 
					            return noModifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val numlabels = ongoto.labels.size
 | 
				
			||||||
 | 
					        val split = if(ongoto.isCall)
 | 
				
			||||||
 | 
					            true    // for calls (indirect JSR), split array is always the optimal choice
 | 
				
			||||||
 | 
					        else
 | 
				
			||||||
 | 
					            target.cpu==CpuType.CPU6502    // for goto (indirect JMP), split array is optimal for 6502, but NOT for the 65C02 (it has a different JMP addressing mode available)
 | 
				
			||||||
 | 
					        val arrayDt = DataType.arrayFor(BaseDataType.UWORD, split)
 | 
				
			||||||
 | 
					        val labelArray = ArrayLiteral(InferredTypes.knownFor(arrayDt), ongoto.labels.toTypedArray(), ongoto.position)
 | 
				
			||||||
 | 
					        val jumplistArray = VarDecl.createAutoOptionalSplit(labelArray)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val indexValue: Expression
 | 
				
			||||||
 | 
					        val conditionVar: VarDecl?
 | 
				
			||||||
 | 
					        val assignIndex: Assignment?
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        // put condition in temp var, if it is not simple; to avoid evaluating expression multiple times
 | 
				
			||||||
 | 
					        if (ongoto.index.isSimple) {
 | 
				
			||||||
 | 
					            indexValue = ongoto.index
 | 
				
			||||||
 | 
					            assignIndex = null
 | 
				
			||||||
 | 
					            conditionVar = null
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            conditionVar = VarDecl.createAuto(indexDt)
 | 
				
			||||||
 | 
					            indexValue = IdentifierReference(listOf(conditionVar.name), conditionVar.position)
 | 
				
			||||||
 | 
					            val varTarget = AssignTarget(indexValue, null, null, null, false, position=conditionVar.position)
 | 
				
			||||||
 | 
					            assignIndex = Assignment(varTarget, ongoto.index, AssignmentOrigin.USERCODE, ongoto.position)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val callTarget = ArrayIndexedExpression(IdentifierReference(listOf(jumplistArray.name), jumplistArray.position), ArrayIndex(indexValue.copy(), indexValue.position), ongoto.position)
 | 
				
			||||||
 | 
					        val callIndexed = AnonymousScope(mutableListOf(), ongoto.position)
 | 
				
			||||||
 | 
					        if(ongoto.isCall) {
 | 
				
			||||||
 | 
					            callIndexed.statements.add(FunctionCallStatement(IdentifierReference(listOf("call"), ongoto.position), mutableListOf(callTarget), true, ongoto.position))
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            callIndexed.statements.add(Jump(callTarget, ongoto.position))
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val ifSt = if(ongoto.elsepart==null || ongoto.elsepart!!.isEmpty()) {
 | 
				
			||||||
 | 
					            // if index<numlabels call(labels[index])
 | 
				
			||||||
 | 
					            val compare = BinaryExpression(indexValue.copy(), "<", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
 | 
				
			||||||
 | 
					            IfElse(compare, callIndexed, AnonymousScope(mutableListOf(), ongoto.position), ongoto.position)
 | 
				
			||||||
 | 
					        } else {
 | 
				
			||||||
 | 
					            // if index>=numlabels elselabel() else call(labels[index])
 | 
				
			||||||
 | 
					            val compare = BinaryExpression(indexValue.copy(), ">=", NumericLiteral.optimalInteger(numlabels, ongoto.position), ongoto.position)
 | 
				
			||||||
 | 
					            IfElse(compare, ongoto.elsepart!!, callIndexed, ongoto.position)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        val replacementScope = AnonymousScope(if(conditionVar==null)
 | 
				
			||||||
 | 
					                mutableListOf(ifSt, jumplistArray)
 | 
				
			||||||
 | 
					            else
 | 
				
			||||||
 | 
					                mutableListOf(conditionVar, assignIndex!!, ifSt, jumplistArray)
 | 
				
			||||||
 | 
					            , ongoto.position)
 | 
				
			||||||
 | 
					        return listOf(IAstModification.ReplaceNode(ongoto, replacementScope, parent))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -77,6 +77,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
 | 
				
			|||||||
            is StructDecl -> transform(statement)
 | 
					            is StructDecl -> transform(statement)
 | 
				
			||||||
            is When -> transform(statement)
 | 
					            is When -> transform(statement)
 | 
				
			||||||
            is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
 | 
					            is WhileLoop -> throw FatalAstException("while loops must have been converted to jumps")
 | 
				
			||||||
 | 
					            is OnGoto -> throw FatalAstException("ongoto must have been converted to array and separate call/goto")
 | 
				
			||||||
            is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
 | 
					            is StructFieldRef -> throw FatalAstException("should not occur as part of the actual AST")
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
@@ -950,7 +951,6 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
 | 
				
			|||||||
        return cast
 | 
					        return cast
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					 | 
				
			||||||
    private fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
 | 
					    private fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
 | 
				
			||||||
        return if (SourceCode.isLibraryResource(filename)) {
 | 
					        return if (SourceCode.isLibraryResource(filename)) {
 | 
				
			||||||
            return com.github.michaelbull.result.runCatching {
 | 
					            return com.github.michaelbull.result.runCatching {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -19,11 +19,7 @@ import prog8.code.core.Position
 | 
				
			|||||||
import prog8.code.core.unescape
 | 
					import prog8.code.core.unescape
 | 
				
			||||||
import prog8.code.target.C64Target
 | 
					import prog8.code.target.C64Target
 | 
				
			||||||
import prog8.code.target.Cx16Target
 | 
					import prog8.code.target.Cx16Target
 | 
				
			||||||
import prog8.code.target.encodings.Encoder
 | 
					import prog8.code.target.encodings.*
 | 
				
			||||||
import prog8.code.target.encodings.AtasciiEncoding
 | 
					 | 
				
			||||||
import prog8.code.target.encodings.C64osEncoding
 | 
					 | 
				
			||||||
import prog8.code.target.encodings.IsoEncoding
 | 
					 | 
				
			||||||
import prog8.code.target.encodings.PetsciiEncoding
 | 
					 | 
				
			||||||
import prog8tests.helpers.ErrorReporterForTests
 | 
					import prog8tests.helpers.ErrorReporterForTests
 | 
				
			||||||
import prog8tests.helpers.compileText
 | 
					import prog8tests.helpers.compileText
 | 
				
			||||||
import java.io.CharConversionException
 | 
					import java.io.CharConversionException
 | 
				
			||||||
@@ -227,7 +223,7 @@ class TestStringEncodings: FunSpec({
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
    context("iso") {
 | 
					    context("iso") {
 | 
				
			||||||
        test("iso accepts iso-characters") {
 | 
					        test("iso accepts iso-characters") {
 | 
				
			||||||
            val result = IsoEncoding.encode("a_~ëç")
 | 
					            val result = IsoEncoding.encode("a_~ëç", false)
 | 
				
			||||||
            result.getOrElse { throw it }.map {it.toInt()} shouldBe listOf(97, 95, 126, 235, 231)
 | 
					            result.getOrElse { throw it }.map {it.toInt()} shouldBe listOf(97, 95, 126, 235, 231)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -276,13 +272,13 @@ class TestStringEncodings: FunSpec({
 | 
				
			|||||||
        passthrough[1] shouldBe '\u801b'
 | 
					        passthrough[1] shouldBe '\u801b'
 | 
				
			||||||
        passthrough[2] shouldBe '\u8099'
 | 
					        passthrough[2] shouldBe '\u8099'
 | 
				
			||||||
        passthrough[3] shouldBe '\u80ff'
 | 
					        passthrough[3] shouldBe '\u80ff'
 | 
				
			||||||
        var encoded = Encoder.encodeString(passthrough, Encoding.PETSCII)
 | 
					        var encoded = Encoder(false).encodeString(passthrough, Encoding.PETSCII)
 | 
				
			||||||
        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
					        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
				
			||||||
        encoded = Encoder.encodeString(passthrough, Encoding.ATASCII)
 | 
					        encoded = Encoder(false).encodeString(passthrough, Encoding.ATASCII)
 | 
				
			||||||
        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
					        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
				
			||||||
        encoded = Encoder.encodeString(passthrough, Encoding.SCREENCODES)
 | 
					        encoded = Encoder(false).encodeString(passthrough, Encoding.SCREENCODES)
 | 
				
			||||||
        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
					        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
				
			||||||
        encoded = Encoder.encodeString(passthrough, Encoding.ISO)
 | 
					        encoded = Encoder(false).encodeString(passthrough, Encoding.ISO)
 | 
				
			||||||
        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
					        encoded shouldBe listOf<UByte>(0u, 0x1bu, 0x99u, 0xffu)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -413,5 +409,39 @@ class TestStringEncodings: FunSpec({
 | 
				
			|||||||
        val char2 = (main.statements[5] as Assignment).value as NumericLiteral
 | 
					        val char2 = (main.statements[5] as Assignment).value as NumericLiteral
 | 
				
			||||||
        char2.number shouldBe 80.0
 | 
					        char2.number shouldBe 80.0
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test("with newline conversion") {
 | 
				
			||||||
 | 
					        val encoder = Encoder(true)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 10)
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test("without newline conversion") {
 | 
				
			||||||
 | 
					        val encoder = Encoder(false)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.PETSCII) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.SCREENCODES) shouldBe listOf<UByte>(141u, 141u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ATASCII) shouldBe listOf<UByte>(155u, 155u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO5) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.ISO16) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.CP437) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.KATAKANA) shouldBe listOf<UByte>(10u, 13u)
 | 
				
			||||||
 | 
					        encoder.encodeString("\n\r", Encoding.C64OS) shouldBe listOf<UByte>(13u, 13u)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.PETSCII).map { it.code } shouldBe listOf(10, 10)
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.ISO).map { it.code } shouldBe listOf(10, 13)
 | 
				
			||||||
 | 
					        encoder.decodeString(listOf<UByte>(10u, 13u), Encoding.CP437).map { it.code } shouldBe listOf(10, 13)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1079,6 +1079,44 @@ main {
 | 
				
			|||||||
        compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
					        compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
				
			||||||
        compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
					        compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    test("on..goto") {
 | 
				
			||||||
 | 
					        val src="""
 | 
				
			||||||
 | 
					main {
 | 
				
			||||||
 | 
					    sub start() {
 | 
				
			||||||
 | 
					        cx16.r13L = 1
 | 
				
			||||||
 | 
					        cx16.r12L = 0
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        on cx16.r12L+1 call (
 | 
				
			||||||
 | 
					            thing.func1,
 | 
				
			||||||
 | 
					            thing.func2,
 | 
				
			||||||
 | 
					            thing.func3)
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            ; not jumped
 | 
				
			||||||
 | 
					            cx16.r0++
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        on cx16.r13L+1 goto (thing.func1, thing.func2, thing.func3)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					thing {
 | 
				
			||||||
 | 
					    sub func1() {
 | 
				
			||||||
 | 
					        cx16.r10 += 1
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    sub func2() {
 | 
				
			||||||
 | 
					        cx16.r10 += 2
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					    sub func3() {
 | 
				
			||||||
 | 
					        cx16.r10 += 3
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}"""
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        compileText(VMTarget(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
				
			||||||
 | 
					        compileText(C64Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
				
			||||||
 | 
					        compileText(Cx16Target(), optimize=false, src, outputDir, writeAssembly=true) shouldNotBe null
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
})
 | 
					})
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -532,6 +532,22 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
 | 
				
			|||||||
        output("alias ${alias.alias} = ${alias.target.nameInSource.joinToString(".")}")
 | 
					        output("alias ${alias.alias} = ${alias.target.nameInSource.joinToString(".")}")
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun visit(onGoto: OnGoto) {
 | 
				
			||||||
 | 
					        output(if(onGoto.isCall) "selectcall " else "selectgoto ")
 | 
				
			||||||
 | 
					        onGoto.index.accept(this)
 | 
				
			||||||
 | 
					        output(" from (")
 | 
				
			||||||
 | 
					        onGoto.labels.forEachIndexed { idx, label ->
 | 
				
			||||||
 | 
					            label.accept(this)
 | 
				
			||||||
 | 
					            if(idx!=onGoto.labels.lastIndex)
 | 
				
			||||||
 | 
					                output(", ")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					        outputln(")")
 | 
				
			||||||
 | 
					        if(onGoto.elsepart!=null && onGoto.elsepart.isNotEmpty()) {
 | 
				
			||||||
 | 
					            output(" else ")
 | 
				
			||||||
 | 
					            onGoto.elsepart.accept(this)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    override fun visit(deref: PtrDereference) {
 | 
					    override fun visit(deref: PtrDereference) {
 | 
				
			||||||
        output("${deref.identifier.nameInSource.joinToString(".")}^^")
 | 
					        output("${deref.identifier.nameInSource.joinToString(".")}^^")
 | 
				
			||||||
        deref.chain.forEach {
 | 
					        deref.chain.forEach {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -169,6 +169,9 @@ private fun StatementContext.toAst() : Statement {
 | 
				
			|||||||
    val aliasstmt = alias()?.toAst()
 | 
					    val aliasstmt = alias()?.toAst()
 | 
				
			||||||
    if(aliasstmt!=null) return aliasstmt
 | 
					    if(aliasstmt!=null) return aliasstmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    val ongotostmt = ongoto()?.toAst()
 | 
				
			||||||
 | 
					    if(ongotostmt!=null) return ongotostmt
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val structdecl = structdeclaration()?.toAst()
 | 
					    val structdecl = structdeclaration()?.toAst()
 | 
				
			||||||
    if(structdecl!=null) return structdecl
 | 
					    if(structdecl!=null) return structdecl
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -176,7 +179,7 @@ private fun StatementContext.toAst() : Statement {
 | 
				
			|||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun AsmsubroutineContext.toAst(): Subroutine {
 | 
					private fun AsmsubroutineContext.toAst(): Subroutine {
 | 
				
			||||||
    val inline = this.inline()!=null
 | 
					    val inline = this.INLINE()!=null
 | 
				
			||||||
    val subdecl = asmsub_decl().toAst()
 | 
					    val subdecl = asmsub_decl().toAst()
 | 
				
			||||||
    val statements = statement_block()?.toAst() ?: mutableListOf()
 | 
					    val statements = statement_block()?.toAst() ?: mutableListOf()
 | 
				
			||||||
    return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
 | 
					    return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes.toMutableList(),
 | 
				
			||||||
@@ -250,13 +253,17 @@ private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter> = asmsub_
 | 
				
			|||||||
    val identifiers = vardecl.identifierlist()?.identifier() ?: emptyList()
 | 
					    val identifiers = vardecl.identifierlist()?.identifier() ?: emptyList()
 | 
				
			||||||
    if(identifiers.size>1)
 | 
					    if(identifiers.size>1)
 | 
				
			||||||
        throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
 | 
					        throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
 | 
				
			||||||
    val identifiername = identifiers.single().name()
 | 
					    val identifiername = getname(identifiers.single())
 | 
				
			||||||
    AsmSubroutineParameter(identifiername, datatype, registerorpair, statusregister, toPosition())
 | 
					    AsmSubroutineParameter(identifiername, datatype, registerorpair, statusregister, toPosition())
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun IdentifierContext.name(): String {
 | 
					
 | 
				
			||||||
    return (this.UNICODEDNAME().text ?: this.UNDERSCORENAME()?.text)!!
 | 
					private fun getname(identifier: IdentifierContext): String = identifier.children[0].text
 | 
				
			||||||
}
 | 
					//    return if (identifier == null) null
 | 
				
			||||||
 | 
					//    else identifier.children[0].text
 | 
				
			||||||
 | 
					//    //else (identifier.NAME() ?: identifier.UNDERSCORENAME() ?: identifier.ON() ?: identifier.CALL()).text
 | 
				
			||||||
 | 
					//}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
 | 
					private fun parseParamRegister(registerTok: Token?, pos: Position): Pair<RegisterOrPair?, Statusflag?> {
 | 
				
			||||||
    if(registerTok==null)
 | 
					    if(registerTok==null)
 | 
				
			||||||
@@ -354,7 +361,7 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
 | 
				
			|||||||
            val identifiers = decl.identifierlist()?.identifier() ?: emptyList()
 | 
					            val identifiers = decl.identifierlist()?.identifier() ?: emptyList()
 | 
				
			||||||
            if(identifiers.size>1)
 | 
					            if(identifiers.size>1)
 | 
				
			||||||
                throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
 | 
					                throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
 | 
				
			||||||
            val identifiername = identifiers.single().name()
 | 
					            val identifiername = getname(identifiers.single())
 | 
				
			||||||
 | 
					
 | 
				
			||||||
            val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
 | 
					            val (registerorpair, statusregister) = parseParamRegister(it.register, it.toPosition())
 | 
				
			||||||
            if(statusregister!=null) {
 | 
					            if(statusregister!=null) {
 | 
				
			||||||
@@ -401,7 +408,7 @@ private fun Assign_targetContext.toAst() : AssignTarget {
 | 
				
			|||||||
            AssignTarget(null, arrayindexed, null, null, false, position = toPosition())
 | 
					            AssignTarget(null, arrayindexed, null, null, false, position = toPosition())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        is VoidTargetContext -> {
 | 
					        is VoidTargetContext -> {
 | 
				
			||||||
            AssignTarget(null, null, null, null, true, position = void_().toPosition())
 | 
					            AssignTarget(null, null, null, null, true, position = voidtarget().toPosition())
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
        is PointerDereferenceTargetContext -> {
 | 
					        is PointerDereferenceTargetContext -> {
 | 
				
			||||||
            val deref = this.pointerdereference().toAst()
 | 
					            val deref = this.pointerdereference().toAst()
 | 
				
			||||||
@@ -844,7 +851,7 @@ private fun When_choiceContext.toAst(): WhenChoice {
 | 
				
			|||||||
private fun StructfielddeclContext.toAst(): Pair<DataType, List<String>> {
 | 
					private fun StructfielddeclContext.toAst(): Pair<DataType, List<String>> {
 | 
				
			||||||
    val identifiers = identifierlist()?.identifier() ?: emptyList()
 | 
					    val identifiers = identifierlist()?.identifier() ?: emptyList()
 | 
				
			||||||
    val dt = datatype().toAst()
 | 
					    val dt = datatype().toAst()
 | 
				
			||||||
    return dt to identifiers.map { it.name() }
 | 
					    return dt to identifiers.map { getname(it) }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
 | 
					private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
 | 
				
			||||||
@@ -856,7 +863,7 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
 | 
				
			|||||||
    }
 | 
					    }
 | 
				
			||||||
    val zp = getZpOption(tags)
 | 
					    val zp = getZpOption(tags)
 | 
				
			||||||
    val split = getSplitOption(tags)
 | 
					    val split = getSplitOption(tags)
 | 
				
			||||||
    val identifiers = identifierlist().identifier().map { it.name() }
 | 
					    val identifiers = identifierlist().identifier().map { getname(it) }
 | 
				
			||||||
    val name = if(identifiers.size==1) identifiers[0] else "<multiple>"
 | 
					    val name = if(identifiers.size==1) identifiers[0] else "<multiple>"
 | 
				
			||||||
    val isArray = ARRAYSIG() != null || arrayindex() != null
 | 
					    val isArray = ARRAYSIG() != null || arrayindex() != null
 | 
				
			||||||
    val alignword = "@alignword" in tags
 | 
					    val alignword = "@alignword" in tags
 | 
				
			||||||
@@ -894,3 +901,12 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
 | 
				
			|||||||
            toPosition()
 | 
					            toPosition()
 | 
				
			||||||
    )
 | 
					    )
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					private fun OngotoContext.toAst(): Statement {
 | 
				
			||||||
 | 
					    val elseStatements = this.else_part()?.toAst()
 | 
				
			||||||
 | 
					    val elseScope = if(elseStatements==null) null else AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
 | 
				
			||||||
 | 
					    val isCall = this.kind.text == "call"
 | 
				
			||||||
 | 
					    val index = this.expression().toAst()
 | 
				
			||||||
 | 
					    val labels = directivenamelist().scoped_identifier().map { it.toAst() }
 | 
				
			||||||
 | 
					    return OnGoto(isCall, index, labels, elseScope, toPosition())
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -284,7 +284,7 @@ class VarDecl(val type: VarDeclType,
 | 
				
			|||||||
            val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
 | 
					            val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
 | 
				
			||||||
            var arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
 | 
					            var arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
 | 
				
			||||||
            if(arrayDt.isSplitWordArray) {
 | 
					            if(arrayDt.isSplitWordArray) {
 | 
				
			||||||
                // autovars for array literals are NOT stored as a split word array!
 | 
					                // autovars for array literals are NEVER stored as a split word array!
 | 
				
			||||||
                when(arrayDt.sub) {
 | 
					                when(arrayDt.sub) {
 | 
				
			||||||
                    BaseDataType.WORD -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false)
 | 
					                    BaseDataType.WORD -> arrayDt = DataType.arrayFor(BaseDataType.WORD, false)
 | 
				
			||||||
                    BaseDataType.UWORD -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false)
 | 
					                    BaseDataType.UWORD -> arrayDt = DataType.arrayFor(BaseDataType.UWORD, false)
 | 
				
			||||||
@@ -296,6 +296,24 @@ class VarDecl(val type: VarDeclType,
 | 
				
			|||||||
                SplitWish.NOSPLIT, arraysize, autoVarName, emptyList(), array,
 | 
					                SplitWish.NOSPLIT, arraysize, autoVarName, emptyList(), array,
 | 
				
			||||||
                    sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
 | 
					                    sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
 | 
				
			||||||
        }
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun createAutoOptionalSplit(array: ArrayLiteral): VarDecl {
 | 
				
			||||||
 | 
					            val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
 | 
				
			||||||
 | 
					            val arrayDt = array.type.getOrElse { throw FatalAstException("unknown dt") }
 | 
				
			||||||
 | 
					            val split = if(arrayDt.isSplitWordArray) SplitWish.SPLIT else if(arrayDt.isWordArray) SplitWish.NOSPLIT else SplitWish.DONTCARE
 | 
				
			||||||
 | 
					            val arraysize = ArrayIndex.forArray(array)
 | 
				
			||||||
 | 
					            return VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, arrayDt, ZeropageWish.NOT_IN_ZEROPAGE,
 | 
				
			||||||
 | 
					                split, arraysize, autoVarName, emptyList(), array,
 | 
				
			||||||
 | 
					                sharedWithAsm = false, alignment = 0u, dirty = false, position = array.position)
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					        fun createAuto(dt: DataType): VarDecl {
 | 
				
			||||||
 | 
					            val autoVarName = "auto_heap_value_${++autoHeapValueSequenceNumber}"
 | 
				
			||||||
 | 
					            val vardecl = VarDecl(VarDeclType.VAR, VarDeclOrigin.USERCODE, dt, ZeropageWish.NOT_IN_ZEROPAGE,
 | 
				
			||||||
 | 
					                SplitWish.DONTCARE, null, autoVarName, emptyList(), null,
 | 
				
			||||||
 | 
					                sharedWithAsm = false, alignment = 0u, dirty = false, position = Position.DUMMY)
 | 
				
			||||||
 | 
					            return vardecl
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    val isArray: Boolean
 | 
					    val isArray: Boolean
 | 
				
			||||||
@@ -1304,3 +1322,29 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
 | 
				
			|||||||
    override fun copy() = DirectMemoryWrite(addressExpression.copy(), position)
 | 
					    override fun copy() = DirectMemoryWrite(addressExpression.copy(), position)
 | 
				
			||||||
    override fun referencesIdentifier(nameInSource: List<String>): Boolean = addressExpression.referencesIdentifier(nameInSource)
 | 
					    override fun referencesIdentifier(nameInSource: List<String>): Boolean = addressExpression.referencesIdentifier(nameInSource)
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class OnGoto(
 | 
				
			||||||
 | 
					    val isCall: Boolean,
 | 
				
			||||||
 | 
					    val index: Expression,
 | 
				
			||||||
 | 
					    val labels: List<IdentifierReference>,
 | 
				
			||||||
 | 
					    val elsepart: AnonymousScope?,
 | 
				
			||||||
 | 
					    override val position: Position
 | 
				
			||||||
 | 
					) : Statement() {
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override lateinit var parent: Node
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun linkParents(parent: Node) {
 | 
				
			||||||
 | 
					        this.parent = parent
 | 
				
			||||||
 | 
					        index.linkParents(this)
 | 
				
			||||||
 | 
					        labels.forEach { it.linkParents(this) }
 | 
				
			||||||
 | 
					        elsepart?.linkParents(this)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    override fun copy(): OnGoto = OnGoto(isCall, index.copy(), labels.map { it.copy() }, elsepart?.copy(), position)
 | 
				
			||||||
 | 
					    override fun referencesIdentifier(nameInSource: List<String>) = index.referencesIdentifier(nameInSource) || labels.any { it.referencesIdentifier(nameInSource) } || elsepart?.referencesIdentifier(nameInSource) == true
 | 
				
			||||||
 | 
					    override fun accept(visitor: IAstVisitor) = visitor.visit(this)
 | 
				
			||||||
 | 
					    override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
 | 
				
			||||||
 | 
					    override fun replaceChildNode(node: Node, replacement: Node) {
 | 
				
			||||||
 | 
					        throw FatalAstException("can't replace")
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -141,6 +141,7 @@ abstract class AstWalker {
 | 
				
			|||||||
    open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
    open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun before(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
    open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
 | 
					    open fun before(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
    open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun after(array: ArrayLiteral, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
@@ -190,6 +191,7 @@ abstract class AstWalker {
 | 
				
			|||||||
    open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
    open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun after(whenStmt: When, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
    open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
 | 
					    open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
 | 
					    open fun after(ongoto: OnGoto, parent: Node): Iterable<IAstModification> = noModifications
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
 | 
					    protected val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -523,6 +525,14 @@ abstract class AstWalker {
 | 
				
			|||||||
        track(after(chainedAssignment, parent), chainedAssignment, parent)
 | 
					        track(after(chainedAssignment, parent), chainedAssignment, parent)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun visit(ongoto: OnGoto, parent: Node) {
 | 
				
			||||||
 | 
					        track(before(ongoto, parent), ongoto, parent)
 | 
				
			||||||
 | 
					        ongoto.index.accept(this, ongoto)
 | 
				
			||||||
 | 
					        ongoto.labels.forEach { it.accept(this, ongoto) }
 | 
				
			||||||
 | 
					        ongoto.elsepart?.accept(this, ongoto)
 | 
				
			||||||
 | 
					        track(after(ongoto, parent), ongoto, parent)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun visit(deref: PtrDereference, parent: Node) {
 | 
					    fun visit(deref: PtrDereference, parent: Node) {
 | 
				
			||||||
        track(before(deref, parent), deref, parent)
 | 
					        track(before(deref, parent), deref, parent)
 | 
				
			||||||
        deref.identifier.accept(this, deref)
 | 
					        deref.identifier.accept(this, deref)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -208,6 +208,12 @@ interface IAstVisitor {
 | 
				
			|||||||
        chainedAssignment.nested.accept(this)
 | 
					        chainedAssignment.nested.accept(this)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun visit(onGoto: OnGoto) {
 | 
				
			||||||
 | 
					        onGoto.index.accept(this)
 | 
				
			||||||
 | 
					        onGoto.labels.forEach { it.accept(this) }
 | 
				
			||||||
 | 
					        onGoto.elsepart?.accept(this)
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    fun visit(deref: PtrDereference) {
 | 
					    fun visit(deref: PtrDereference) {
 | 
				
			||||||
        deref.identifier.accept(this)
 | 
					        deref.identifier.accept(this)
 | 
				
			||||||
    }
 | 
					    }
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -101,6 +101,7 @@ Features
 | 
				
			|||||||
- Programs can be configured to execute in ROM
 | 
					- Programs can be configured to execute in ROM
 | 
				
			||||||
- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
 | 
					- Conditional branches for status flags that map 1:1 to processor branch instructions for optimal efficiency
 | 
				
			||||||
- ``when`` statement to avoid if-else chains
 | 
					- ``when`` statement to avoid if-else chains
 | 
				
			||||||
 | 
					- ``on .. goto`` statement for fast jump tables
 | 
				
			||||||
- ``in`` expression for concise and efficient multi-value/containment test
 | 
					- ``in`` expression for concise and efficient multi-value/containment test
 | 
				
			||||||
- ``defer`` statement to help write concise and robust subroutine cleanup logic
 | 
					- ``defer`` statement to help write concise and robust subroutine cleanup logic
 | 
				
			||||||
- Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
 | 
					- Several specialized built-in functions, such as ``lsb``, ``msb``, ``min``, ``max``, ``rol``, ``ror``
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -744,9 +744,30 @@ An example, to select the number of cards to use depending on what game is playe
 | 
				
			|||||||
        numcards = 52
 | 
					        numcards = 52
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					on .. goto / on .. call statement (jump table)
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
when statement ('jump table')
 | 
					The ``on goto / call`` statement is suitable to create a fast call of a subroutine from a list based on an index value.
 | 
				
			||||||
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
 | 
					it selects a function to jump to in O(1) whereas a similar when-statement, runs in O(n) because that one checks each index value.
 | 
				
			||||||
 | 
					The ``on goto / call`` instead simply gets the correct entry from an array of function pointers and jumps to it directly.
 | 
				
			||||||
 | 
					The index value that is used is 0-based; 0 will jump to the first entry in the list, 1 to the second, and so on.
 | 
				
			||||||
 | 
					If the value is too large (i.e. outside the list of functions), no call is performed, and execution continues.
 | 
				
			||||||
 | 
					In this case you can optionally add an ``else`` block that is then executed instead. Here's an example::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    on math.randrange(5) call (
 | 
				
			||||||
 | 
					            routines.func1,
 | 
				
			||||||
 | 
					            routines.func2,
 | 
				
			||||||
 | 
					            routines.func3 )
 | 
				
			||||||
 | 
					        else {
 | 
				
			||||||
 | 
					            txt.print("no call was done")
 | 
				
			||||||
 | 
					        }
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    on math.randrange(5) goto (routines.func1, routines.func2, routines.func3)
 | 
				
			||||||
 | 
					    txt.print("no jump was taken")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					when statement
 | 
				
			||||||
 | 
					^^^^^^^^^^^^^^
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Instead of writing a bunch of sequential if-elseif statements, it is more readable to
 | 
					Instead of writing a bunch of sequential if-elseif statements, it is more readable to
 | 
				
			||||||
use a ``when`` statement. (It will also result in greatly improved assembly code generation)
 | 
					use a ``when`` statement. (It will also result in greatly improved assembly code generation)
 | 
				
			||||||
@@ -767,7 +788,8 @@ datatype as the when-value, otherwise no efficient comparison can be done.
 | 
				
			|||||||
You can explicitly put a list of numbers that all should result in the same case,
 | 
					You can explicitly put a list of numbers that all should result in the same case,
 | 
				
			||||||
or even use any *range expression* as long as it denotes a constant list of numbers.
 | 
					or even use any *range expression* as long as it denotes a constant list of numbers.
 | 
				
			||||||
Be aware that every number is compared individually so using long lists of numbers and/or
 | 
					Be aware that every number is compared individually so using long lists of numbers and/or
 | 
				
			||||||
many choice cases will result in poor performance.
 | 
					many choice cases will result in poor performance. If you need to call a certain function
 | 
				
			||||||
 | 
					based on some sequential index number, look at the ``on .. goto / call`` statement instead.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
Choices can result in a single statement or a block of multiple statements in which
 | 
					Choices can result in a single statement or a block of multiple statements in which
 | 
				
			||||||
case you have to use { } to enclose them.
 | 
					case you have to use { } to enclose them.
 | 
				
			||||||
@@ -1231,7 +1253,7 @@ just before exiting of the current subroutine. That can be via a return statemen
 | 
				
			|||||||
or just the normal ending of the subroutine. This is often useful to "not forget" to clean up stuff,
 | 
					or just the normal ending of the subroutine. This is often useful to "not forget" to clean up stuff,
 | 
				
			||||||
and if the subroutine has multiple ways or places where it can exit, it saves you from repeating
 | 
					and if the subroutine has multiple ways or places where it can exit, it saves you from repeating
 | 
				
			||||||
the cleanup code at every exit spot. Multiple defers can be scheduled in a single subroutine (up to a maximum of 8).
 | 
					the cleanup code at every exit spot. Multiple defers can be scheduled in a single subroutine (up to a maximum of 8).
 | 
				
			||||||
They are handled in reversed order. Return values are evaluated before any deferred code is executed.
 | 
					The defers are handled in reversed (LIFO) order. Return values are evaluated before any deferred code is executed.
 | 
				
			||||||
You write defers like so::
 | 
					You write defers like so::
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    sub example() -> bool {
 | 
					    sub example() -> bool {
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -54,6 +54,17 @@ Various things:
 | 
				
			|||||||
    A Prog8 library module that provides Commander X16 style RAM banking on a C64 with an REU.
 | 
					    A Prog8 library module that provides Commander X16 style RAM banking on a C64 with an REU.
 | 
				
			||||||
    This module provides cx16.rambank(), x16jsrfar() and extsub @bank functionality on a C64.
 | 
					    This module provides cx16.rambank(), x16jsrfar() and extsub @bank functionality on a C64.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`Library blob link example <https://github.com/FearLabsAudio/Prog8_blobLink_example/>`_
 | 
				
			||||||
 | 
					    An example of a simple utility that can link symbols in a main Prog8 program
 | 
				
			||||||
 | 
					    so that they are accessable from an externally loaded library blob.
 | 
				
			||||||
 | 
					    It pre-processes the debug symbols list file at compile time,
 | 
				
			||||||
 | 
					    and substitutes references in a template module file.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					`XLink: an alternative library blob link example <https://github.com/gillham/X16/tree/main/xlink>`_
 | 
				
			||||||
 | 
					    This is another approach to access routines from a banked loaded library,
 | 
				
			||||||
 | 
					    and it does it at run time. In this demo a jump table is not only created in the library,
 | 
				
			||||||
 | 
					    but also in the main program and copied into the library for its use.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. image:: _static/curious.png
 | 
					.. image:: _static/curious.png
 | 
				
			||||||
    :align: center
 | 
					    :align: center
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -64,6 +64,7 @@ Future Things and Ideas
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
- fix the line, cols in Position, sometimes they count from 0 sometimes from 1
 | 
					- fix the line, cols in Position, sometimes they count from 0 sometimes from 1
 | 
				
			||||||
- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
 | 
					- is "checkAssignmentCompatible" redundant (gets called just 1 time!) when we also have "checkValueTypeAndRange" ?
 | 
				
			||||||
 | 
					- enums?
 | 
				
			||||||
- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
 | 
					- romable: should we have a way to explicitly set the memory address for the BSS area (instead of only the highram bank number on X16, allow a memory address too for the -varshigh option?)
 | 
				
			||||||
- Kotlin: can we use inline value classes in certain spots?
 | 
					- Kotlin: can we use inline value classes in certain spots?
 | 
				
			||||||
- add float support to the configurable compiler targets
 | 
					- add float support to the configurable compiler targets
 | 
				
			||||||
@@ -100,6 +101,7 @@ IR/VM
 | 
				
			|||||||
     cx16.r0sL = cx16.r1s as byte }
 | 
					     cx16.r0sL = cx16.r1s as byte }
 | 
				
			||||||
- do something with the 'split' tag on split word arrays
 | 
					- do something with the 'split' tag on split word arrays
 | 
				
			||||||
- add more optimizations in IRPeepholeOptimizer
 | 
					- add more optimizations in IRPeepholeOptimizer
 | 
				
			||||||
 | 
					- apparently for SSA form, the IRCodeChunk is not a proper "basic block" yet because the last operation should be a branch or return, and no other branches
 | 
				
			||||||
- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
 | 
					- reduce register usage via linear-scan algorithm (based on live intervals) https://anoopsarkar.github.io/compilers-class/assets/lectures/opt3-regalloc-linearscan.pdf
 | 
				
			||||||
  don't forget to take into account the data type of the register when it's going to be reused!
 | 
					  don't forget to take into account the data type of the register when it's going to be reused!
 | 
				
			||||||
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
 | 
					- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
 | 
				
			||||||
@@ -123,11 +125,11 @@ Libraries
 | 
				
			|||||||
Optimizations
 | 
					Optimizations
 | 
				
			||||||
-------------
 | 
					-------------
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					- when choices that are only a goto -> avoid the double branch, can branch to the label directly
 | 
				
			||||||
- Sorting module gnomesort_uw could be optimized more by fully rewriting it in asm? Shellshort seems consistently faster even if most of the words are already sorted.
 | 
					- Sorting module gnomesort_uw could be optimized more by fully rewriting it in asm? Shellshort seems consistently faster even if most of the words are already sorted.
 | 
				
			||||||
- can the for loop temp var be replaced by the same logic that createRepeatCounterVar() uses for repeat loops? Take care of nested for/repeat loops to not use the same var
 | 
					- can the for loop temp var be replaced by the same logic that createRepeatCounterVar() uses for repeat loops? Take care of nested for/repeat loops to not use the same var
 | 
				
			||||||
- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)
 | 
					- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)
 | 
				
			||||||
- Optimize the IfExpression code generation to be more like regular if-else code.  (both 6502 and IR) search for "TODO don't store condition as expression"
 | 
					- Optimize the IfExpression code generation to be more like regular if-else code.  (both 6502 and IR) search for "TODO don't store condition as expression"
 | 
				
			||||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
 | 
					- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
 | 
				
			||||||
  for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
 | 
					  for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
 | 
				
			||||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME.  Once 6502-codegen is done from IR code,
 | 
					- various optimizers skip stuff if compTarget.name==VMTarget.NAME.  Once 6502-codegen is done from IR code, those checks should probably be removed, or be made permanent
 | 
				
			||||||
  those checks should probably be removed, or be made permanent
 | 
					 | 
				
			||||||
 
 | 
				
			|||||||
@@ -412,7 +412,7 @@ change it for the whole file at once. Here are examples of the possible encoding
 | 
				
			|||||||
    - ``iso5:"Хозяин и Работник"``      string in iso-8859-5 encoding (Cyrillic)
 | 
					    - ``iso5:"Хозяин и Работник"``      string in iso-8859-5 encoding (Cyrillic)
 | 
				
			||||||
    - ``iso16:"zażółć gęślą jaźń"``     string in iso-8859-16 encoding (Eastern Europe)
 | 
					    - ``iso16:"zażółć gęślą jaźń"``     string in iso-8859-16 encoding (Eastern Europe)
 | 
				
			||||||
    - ``atascii:"I am Atari!"``         string in "atascii" encoding (Atari 8-bit)
 | 
					    - ``atascii:"I am Atari!"``         string in "atascii" encoding (Atari 8-bit)
 | 
				
			||||||
    - ``cp437:"≈ IBM Pc ≈ ♂♀♪☺¶"``     string in "cp437" encoding (IBM PC codepage 437)
 | 
					    - ``cp437:"≈ IBM Pc ≈ ♂♀♪☺¶"``     string in "cp437" encoding (IBM PC codepage 437) See note below!
 | 
				
			||||||
    - ``kata:"アノ ニホンジン ワ ガイコクジン。 # が # ガ"``  string in "kata" encoding (Katakana)
 | 
					    - ``kata:"アノ ニホンジン ワ ガイコクジン。 # が # ガ"``  string in "kata" encoding (Katakana)
 | 
				
			||||||
    - ``c64os:"^Hello_World! \\ ~{_}~"`` string in "c64os" encoding (C64 OS)
 | 
					    - ``c64os:"^Hello_World! \\ ~{_}~"`` string in "c64os" encoding (C64 OS)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -490,6 +490,13 @@ for a character in such strings (one that stops at the first 0 byte)
 | 
				
			|||||||
    of such a string without destroying other occurrences (as long as you stay within
 | 
					    of such a string without destroying other occurrences (as long as you stay within
 | 
				
			||||||
    the size of the allocated string!)
 | 
					    the size of the allocated string!)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					.. note:: printing **cp437** encoded strings
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    To print strings in the **cp437** encoding, you will probably need ``txt.print_lit(message)`` to properly print
 | 
				
			||||||
 | 
					    them to the screen. This is because this encoding has symbols in place of where normally ASCII
 | 
				
			||||||
 | 
					    control characters such as Line feed would be. A regular ``txt.print(message)`` will likely get confused
 | 
				
			||||||
 | 
					    by such symbols and print them as control characters, messing up the output.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
.. _range-expression:
 | 
					.. _range-expression:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -192,7 +192,7 @@ class IRProgram(val name: String,
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
        blocks.forEach { block ->
 | 
					        blocks.forEach { block ->
 | 
				
			||||||
            block.children.forEachIndexed { index, child ->
 | 
					            block.children.forEachIndexed { index, child ->
 | 
				
			||||||
                val next = if(index<block.children.size-1) block.children[index+1] as? IRCodeChunkBase else null
 | 
					                val next = if(index<block.children.lastIndex) block.children[index+1] as? IRCodeChunkBase else null
 | 
				
			||||||
                when (child) {
 | 
					                when (child) {
 | 
				
			||||||
                    is IRAsmSubroutine -> child.asmChunk.next = next
 | 
					                    is IRAsmSubroutine -> child.asmChunk.next = next
 | 
				
			||||||
                    is IRCodeChunk -> child.next = next
 | 
					                    is IRCodeChunk -> child.next = next
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -24,6 +24,12 @@ BLOCK_COMMENT : '/*' ( BLOCK_COMMENT | . )*? '*/'  -> skip ;
 | 
				
			|||||||
WS :  [ \t] -> skip ;
 | 
					WS :  [ \t] -> skip ;
 | 
				
			||||||
// WS2 : '\\' EOL -> skip;
 | 
					// WS2 : '\\' EOL -> skip;
 | 
				
			||||||
VOID: 'void';
 | 
					VOID: 'void';
 | 
				
			||||||
 | 
					ON: 'on';
 | 
				
			||||||
 | 
					GOTO: 'goto';
 | 
				
			||||||
 | 
					CALL: 'call';
 | 
				
			||||||
 | 
					INLINE: 'inline';
 | 
				
			||||||
 | 
					ELSE: 'else';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
UNICODEDNAME :  [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ;           // match unicode properties
 | 
					UNICODEDNAME :  [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ;           // match unicode properties
 | 
				
			||||||
UNDERSCORENAME :  '_' UNICODEDNAME ;           // match unicode properties
 | 
					UNDERSCORENAME :  '_' UNICODEDNAME ;           // match unicode properties
 | 
				
			||||||
DEC_INTEGER :  DEC_DIGIT (DEC_DIGIT | '_')* ;
 | 
					DEC_INTEGER :  DEC_DIGIT (DEC_DIGIT | '_')* ;
 | 
				
			||||||
@@ -109,6 +115,7 @@ statement :
 | 
				
			|||||||
    | breakstmt
 | 
					    | breakstmt
 | 
				
			||||||
    | continuestmt
 | 
					    | continuestmt
 | 
				
			||||||
    | labeldef
 | 
					    | labeldef
 | 
				
			||||||
 | 
					    | ongoto
 | 
				
			||||||
    | defer
 | 
					    | defer
 | 
				
			||||||
    | alias
 | 
					    | alias
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
@@ -141,7 +148,7 @@ defer: 'defer' (statement | statement_block) ;
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
labeldef :  identifier ':'  ;
 | 
					labeldef :  identifier ':'  ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
unconditionaljump :  'goto'  expression ;
 | 
					unconditionaljump :  GOTO  expression ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
directive : directivename (directivenamelist | (directivearg? | directivearg (',' directivearg)*))   ;
 | 
					directive : directivename (directivenamelist | (directivearg? | directivearg (',' directivearg)*))   ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -181,9 +188,11 @@ assign_target:
 | 
				
			|||||||
    | directmemory                  #MemoryTarget
 | 
					    | directmemory                  #MemoryTarget
 | 
				
			||||||
    | pointerdereference            #PointerDereferenceTarget
 | 
					    | pointerdereference            #PointerDereferenceTarget
 | 
				
			||||||
    | pointerindexedderef           #PointerIndexedDerefTarget
 | 
					    | pointerindexedderef           #PointerIndexedDerefTarget
 | 
				
			||||||
    | void                          #VoidTarget
 | 
					    | voidtarget                    #VoidTarget
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					voidtarget : VOID ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
multi_assign_target:
 | 
					multi_assign_target:
 | 
				
			||||||
    assign_target (',' assign_target)+ ;
 | 
					    assign_target (',' assign_target)+ ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -225,8 +234,6 @@ arrayindexed:
 | 
				
			|||||||
    ;
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					
 | 
				
			||||||
void : VOID ;
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
typecast : 'as' datatype;
 | 
					typecast : 'as' datatype;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
directmemory : '@' '(' expression ')';
 | 
					directmemory : '@' '(' expression ')';
 | 
				
			||||||
@@ -249,7 +256,7 @@ breakstmt : 'break';
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
continuestmt: 'continue';
 | 
					continuestmt: 'continue';
 | 
				
			||||||
 | 
					
 | 
				
			||||||
identifier :  UNICODEDNAME | UNDERSCORENAME ;
 | 
					identifier :  UNICODEDNAME | UNDERSCORENAME | ON | CALL | INLINE ;              // due to the way antlr creates tokens, need to list the tokens here explicitly that we want to allow as identifiers too
 | 
				
			||||||
 | 
					
 | 
				
			||||||
scoped_identifier :  identifier ('.' identifier)* ;
 | 
					scoped_identifier :  identifier ('.' identifier)* ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -277,8 +284,6 @@ literalvalue :
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
inlineasm :  directivename EOL? INLINEASMBLOCK;         // directive name should be '%asm' or '%ir'
 | 
					inlineasm :  directivename EOL? INLINEASMBLOCK;         // directive name should be '%asm' or '%ir'
 | 
				
			||||||
 | 
					
 | 
				
			||||||
inline: 'inline';
 | 
					 | 
				
			||||||
 | 
					 | 
				
			||||||
subroutine :
 | 
					subroutine :
 | 
				
			||||||
    'sub' identifier '(' sub_params? ')' sub_return_part? EOL? (statement_block EOL?)
 | 
					    'sub' identifier '(' sub_params? ')' sub_return_part? EOL? (statement_block EOL?)
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
@@ -297,7 +302,7 @@ sub_params :  sub_param (',' EOL? sub_param)* ;
 | 
				
			|||||||
sub_param: vardecl ('@' register=UNICODEDNAME)? ;
 | 
					sub_param: vardecl ('@' register=UNICODEDNAME)? ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
asmsubroutine :
 | 
					asmsubroutine :
 | 
				
			||||||
    inline? 'asmsub' asmsub_decl EOL? (statement_block EOL?)
 | 
					    INLINE? 'asmsub' asmsub_decl EOL? (statement_block EOL?)
 | 
				
			||||||
    ;
 | 
					    ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
extsubroutine :
 | 
					extsubroutine :
 | 
				
			||||||
@@ -321,9 +326,9 @@ asmsub_return :  datatype '@' register=UNICODEDNAME ;     // A,X,Y,AX,AY,XY,Pc,P
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
if_stmt :  'if' expression EOL? (statement | statement_block) EOL? else_part?  ; // statement is constrained later
 | 
					if_stmt :  'if' expression EOL? (statement | statement_block) EOL? else_part?  ; // statement is constrained later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
else_part :  'else' EOL? (statement | statement_block) ;   // statement is constrained later
 | 
					else_part :  ELSE EOL? (statement | statement_block) ;   // statement is constrained later
 | 
				
			||||||
 | 
					
 | 
				
			||||||
if_expression :  'if' expression EOL? expression EOL? 'else' EOL? expression ;
 | 
					if_expression :  'if' expression EOL? expression EOL? ELSE EOL? expression ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
// This is a cursed mix of IdentifierReference (scoped identifiers) and binary expressions with '.' dereference operators.
 | 
					// This is a cursed mix of IdentifierReference (scoped identifiers) and binary expressions with '.' dereference operators.
 | 
				
			||||||
// but it is needed for now to not have to rewrite all of Prog8's dependence on how the IdentifierReference now works (fully qualified identifier string inside)
 | 
					// but it is needed for now to not have to rewrite all of Prog8's dependence on how the IdentifierReference now works (fully qualified identifier string inside)
 | 
				
			||||||
@@ -356,4 +361,6 @@ unrollloop:  'unroll' expression EOL? (statement | statement_block) ;      // no
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
whenstmt: 'when' expression EOL? '{' EOL? (when_choice | EOL) * '}' EOL? ;
 | 
					whenstmt: 'when' expression EOL? '{' EOL? (when_choice | EOL) * '}' EOL? ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
when_choice:  (expression_list | 'else' ) '->' (statement | statement_block ) ;
 | 
					when_choice:  (expression_list | ELSE ) '->' (statement | statement_block ) ;
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					ongoto: ON expression kind=(GOTO | CALL) directivenamelist EOL? else_part? ;
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -79,9 +79,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
 | 
				
			|||||||
                else
 | 
					                else
 | 
				
			||||||
                    "%asm {{ ...${node.assembly.length} characters... }}"
 | 
					                    "%asm {{ ...${node.assembly.length} characters... }}"
 | 
				
			||||||
            }
 | 
					            }
 | 
				
			||||||
            is PtJump -> {
 | 
					            is PtJump -> "goto"
 | 
				
			||||||
                "goto ${txt(node.target)}"
 | 
					 | 
				
			||||||
            }
 | 
					 | 
				
			||||||
            is PtAsmSub -> {
 | 
					            is PtAsmSub -> {
 | 
				
			||||||
                val params = node.parameters.joinToString(", ") {
 | 
					                val params = node.parameters.joinToString(", ") {
 | 
				
			||||||
                    val register = it.first.registerOrPair
 | 
					                    val register = it.first.registerOrPair
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user