mirror of
https://github.com/irmen/prog8.git
synced 2025-11-01 22:16:16 +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,18 +1086,24 @@ $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 (!arrayIdx.splitWords) {
|
if (isTargetCpu(CpuType.CPU65C02)) {
|
||||||
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
if (!arrayIdx.splitWords) {
|
||||||
// on the 65c02, more optimal assembly can be generated using JMP address,X
|
// if the jump target is an address in a non-split array (like a jump table of only pointers),
|
||||||
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
// on the 65c02, more optimal assembly can be generated using JMP (address,X)
|
||||||
out(" asl a | tax")
|
assignExpressionToRegister(arrayIdx.index, RegisterOrPair.A)
|
||||||
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
out(" asl a | tax")
|
||||||
|
return JumpTarget(asmSymbolName(arrayIdx.variable), true, true, false)
|
||||||
|
} else {
|
||||||
|
// print a message when more optimal code is possible for 65C02 cpu
|
||||||
|
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
||||||
|
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)
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
// print a message when more optimal code is possible
|
// print a message when more optimal code is possible for 6502 cpu
|
||||||
val variable = symbolTable.lookup(arrayIdx.variable.name)!!
|
if(!arrayIdx.splitWords)
|
||||||
if(variable is StStaticVariable && variable.length!!<=128u)
|
errors.info("the jump address array is @nosplit, but @split 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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// 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
|
||||||
|
|||||||
@@ -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