mirror of
https://github.com/irmen/prog8.git
synced 2025-06-16 09:23:43 +00:00
Compare commits
83 Commits
version7.1
...
v7.2
Author | SHA1 | Date | |
---|---|---|---|
793596614e | |||
136280100c | |||
29f1e4d2c9 | |||
72a7e61fd0 | |||
381cfca67f | |||
f40620aa25 | |||
57a9fed42b | |||
18d820da94 | |||
26e66f046f | |||
4270c04856 | |||
74456d1135 | |||
62dc824bc0 | |||
1605791f1b | |||
37a46aa2cf | |||
1d2d217b94 | |||
23961f695d | |||
730b208617 | |||
f09c04eeac | |||
be73739c62 | |||
eea3fb48a8 | |||
b4fa72c058 | |||
b0a865b0f1 | |||
7f49731618 | |||
3410aea788 | |||
bc0a133bb1 | |||
7e287a5359 | |||
1110bd0851 | |||
1b576f826d | |||
fe17566370 | |||
e3c00669c1 | |||
33d17afc32 | |||
2388359a99 | |||
2df0c9503c | |||
61fa3bc77c | |||
03ac9b6956 | |||
dfbef8495d | |||
7b17c49d8f | |||
4b3f31c2ee | |||
9ccc65bf8f | |||
f9e22add03 | |||
846951cda7 | |||
97836e18b2 | |||
7b69df4db2 | |||
3767b4bbe7 | |||
d7d2eefa4f | |||
6737f28d1e | |||
3da9404c2d | |||
4d5bd0fa32 | |||
1137da37c3 | |||
495a18805c | |||
a226b82d0b | |||
0b5ddcdc9b | |||
82da8f4946 | |||
5ff481ce3c | |||
f21dcaa6fb | |||
2c940de598 | |||
ce75b776bb | |||
7d22b9b9f9 | |||
6cb8b3b5cd | |||
2bf4017f2b | |||
08d2f8568b | |||
ac5f45d2d4 | |||
3cc7ad7d20 | |||
d4513364fb | |||
9684f4e42a | |||
f4186981fd | |||
141689e697 | |||
743c8b44a2 | |||
5e1459564a | |||
69a8813a3d | |||
17175df835 | |||
6b32535cb6 | |||
2815a14bb5 | |||
f4dfa60790 | |||
35e88dd529 | |||
4d5094a517 | |||
dd5abae721 | |||
8f2fb20934 | |||
74555a32ed | |||
1a111b706e | |||
4668932bac | |||
e6c41eac93 | |||
14aad2358f |
6
.idea/kotlinc.xml
generated
6
.idea/kotlinc.xml
generated
@ -1,6 +0,0 @@
|
|||||||
<?xml version="1.0" encoding="UTF-8"?>
|
|
||||||
<project version="4">
|
|
||||||
<component name="Kotlin2JvmCompilerArguments">
|
|
||||||
<option name="jvmTarget" value="11" />
|
|
||||||
</component>
|
|
||||||
</project>
|
|
22
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
22
.idea/libraries/github_hypfvieh_dbus_java.xml
generated
@ -1,22 +1,22 @@
|
|||||||
<component name="libraryTable">
|
<component name="libraryTable">
|
||||||
<library name="github.hypfvieh.dbus.java" type="repository">
|
<library name="github.hypfvieh.dbus.java" type="repository">
|
||||||
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.0" />
|
<properties maven-id="com.github.hypfvieh:dbus-java:3.3.1" />
|
||||||
<CLASSES>
|
<CLASSES>
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.0/dbus-java-3.3.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/hypfvieh/dbus-java/3.3.1/dbus-java-3.3.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.5/jnr-unixsocket-0.38.5.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-unixsocket/0.38.6/jnr-unixsocket-0.38.6.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.1/jnr-ffi-2.2.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-ffi/2.2.2/jnr-ffi-2.2.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jffi/1.3.1/jffi-1.3.1-native.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.0/asm-9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm/9.1/asm-9.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.0/asm-commons-9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-commons/9.1/asm-commons-9.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.0/asm-analysis-9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-analysis/9.1/asm-analysis-9.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.0/asm-tree-9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-tree/9.1/asm-tree-9.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.0/asm-util-9.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/ow2/asm/asm-util/9.1/asm-util-9.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-a64asm/1.0.0/jnr-a64asm-1.0.0.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-x86asm/1.0.2/jnr-x86asm-1.0.2.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-constants/0.10.1/jnr-constants-0.10.1.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.3/jnr-enxio-0.32.3.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-enxio/0.32.4/jnr-enxio-0.32.4.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.4/jnr-posix-3.1.4.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/com/github/jnr/jnr-posix/3.1.5/jnr-posix-3.1.5.jar!/" />
|
||||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.30/slf4j-api-1.7.30.jar!/" />
|
||||||
</CLASSES>
|
</CLASSES>
|
||||||
<JAVADOC />
|
<JAVADOC />
|
||||||
|
3
.idea/modules.xml
generated
3
.idea/modules.xml
generated
@ -2,8 +2,11 @@
|
|||||||
<project version="4">
|
<project version="4">
|
||||||
<component name="ProjectModuleManager">
|
<component name="ProjectModuleManager">
|
||||||
<modules>
|
<modules>
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeGeneration/codeGeneration.iml" filepath="$PROJECT_DIR$/codeGeneration/codeGeneration.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" filepath="$PROJECT_DIR$/codeOptimizers/codeOptimizers.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
<module fileurl="file://$PROJECT_DIR$/compilerAst/compilerAst.iml" filepath="$PROJECT_DIR$/compilerAst/compilerAst.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" filepath="$PROJECT_DIR$/compilerInterfaces/compilerInterfaces.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
<module fileurl="file://$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" filepath="$PROJECT_DIR$/dbusCompilerService/dbusCompilerService.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
|
29
.readthedocs.yaml
Normal file
29
.readthedocs.yaml
Normal file
@ -0,0 +1,29 @@
|
|||||||
|
# .readthedocs.yaml
|
||||||
|
# Read the Docs configuration file
|
||||||
|
# See https://docs.readthedocs.io/en/stable/config-file/v2.html for details
|
||||||
|
|
||||||
|
# Required
|
||||||
|
version: 2
|
||||||
|
|
||||||
|
# Set the version of Python and other tools you might need
|
||||||
|
build:
|
||||||
|
os: ubuntu-20.04
|
||||||
|
tools:
|
||||||
|
python: "3.9"
|
||||||
|
# You can also specify other tool versions:
|
||||||
|
# nodejs: "16"
|
||||||
|
# rust: "1.55"
|
||||||
|
# golang: "1.17"
|
||||||
|
|
||||||
|
# Build documentation in the docs/ directory with Sphinx
|
||||||
|
sphinx:
|
||||||
|
configuration: docs/source/conf.py
|
||||||
|
|
||||||
|
# If using Sphinx, optionally build your docs in additional formats such as PDF
|
||||||
|
formats:
|
||||||
|
- pdf
|
||||||
|
|
||||||
|
# Optionally declare the Python requirements required to build your docs
|
||||||
|
python:
|
||||||
|
install:
|
||||||
|
- requirements: docs/requirements.txt
|
@ -1,3 +1,10 @@
|
|||||||
plugins {
|
plugins {
|
||||||
id "org.jetbrains.kotlin.jvm" version "1.5.30" apply false
|
id "org.jetbrains.kotlin.jvm" version "$kotlinVersion" apply false
|
||||||
|
}
|
||||||
|
|
||||||
|
allprojects {
|
||||||
|
repositories {
|
||||||
|
mavenLocal()
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
56
codeGeneration/build.gradle
Normal file
56
codeGeneration/build.gradle
Normal file
@ -0,0 +1,56 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':compilerInterfaces')
|
||||||
|
implementation project(':compilerAst')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||||
|
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
|
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/test"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
19
codeGeneration/codeGeneration.iml
Normal file
19
codeGeneration/codeGeneration.iml
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||||
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
||||||
|
<orderEntry type="library" name="junit.jupiter" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -0,0 +1,3 @@
|
|||||||
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
class AssemblyError(msg: String) : RuntimeException(msg)
|
35
codeGeneration/src/prog8/compiler/target/C64Target.kt
Normal file
35
codeGeneration/src/prog8/compiler/target/C64Target.kt
Normal file
@ -0,0 +1,35 @@
|
|||||||
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
|
import prog8.ast.base.ByteDatatypes
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.PassByReferenceDatatypes
|
||||||
|
import prog8.ast.base.WordDatatypes
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
|
import prog8.compiler.target.cbm.Petscii
|
||||||
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
|
object C64Target: ICompilationTarget {
|
||||||
|
override val name = "c64"
|
||||||
|
override val machine = C64MachineDefinition
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
val coded = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
return coded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
|
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
return when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes -> 2
|
||||||
|
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||||
|
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
||||||
|
else -> -9999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
36
codeGeneration/src/prog8/compiler/target/Cx16Target.kt
Normal file
36
codeGeneration/src/prog8/compiler/target/Cx16Target.kt
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import com.github.michaelbull.result.fold
|
||||||
|
import prog8.ast.base.ByteDatatypes
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.PassByReferenceDatatypes
|
||||||
|
import prog8.ast.base.WordDatatypes
|
||||||
|
import prog8.compiler.target.cbm.Petscii
|
||||||
|
import prog8.compiler.target.cx16.CX16MachineDefinition
|
||||||
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
|
||||||
|
|
||||||
|
object Cx16Target: ICompilationTarget {
|
||||||
|
override val name = "cx16"
|
||||||
|
override val machine = CX16MachineDefinition
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
val coded= if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
||||||
|
return coded.fold(
|
||||||
|
failure = { throw it },
|
||||||
|
success = { it }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
||||||
|
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
return when(dt) {
|
||||||
|
in ByteDatatypes -> 1
|
||||||
|
in WordDatatypes -> 2
|
||||||
|
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
||||||
|
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
||||||
|
else -> -9999999
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,16 +1,13 @@
|
|||||||
package prog8.compiler.target.c64
|
package prog8.compiler.target.c64
|
||||||
|
|
||||||
import prog8.compiler.*
|
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
|
||||||
import prog8.compiler.target.IMachineFloat
|
|
||||||
import prog8.compiler.target.cbm.viceMonListPostfix
|
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||||
|
import prog8.compilerinterface.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
internal object C64MachineDefinition: IMachineDefinition {
|
object C64MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
override val cpu = CpuType.CPU6502
|
override val cpu = CpuType.CPU6502
|
||||||
|
|
||||||
@ -30,7 +27,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
override fun getFloat(num: Number) = Mflpt5.fromNumber(num)
|
||||||
|
|
||||||
override fun importLibs(compilerOptions: CompilationOptions,compilationTargetName: String): List<String> {
|
override fun importLibs(compilerOptions: CompilationOptions, compilationTargetName: String): List<String> {
|
||||||
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
return if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG)
|
||||||
listOf("syslib")
|
listOf("syslib")
|
||||||
else
|
else
|
||||||
@ -77,7 +74,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
|
||||||
|
|
||||||
|
|
||||||
internal class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class C64Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
override val SCRATCH_B1 = 0x02 // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x03 // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x03 // temp storage for a register, must be B1+1
|
||||||
@ -87,12 +84,11 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
if (options.floats && options.zeropage !in arrayOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
if (options.zeropage == ZeropageType.FULL) {
|
if (options.zeropage == ZeropageType.FULL) {
|
||||||
free.addAll(0x04..0xf9)
|
free.addAll(0x04..0xf9)
|
||||||
free.add(0xff)
|
free.add(0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
free.removeAll(listOf(0xa0, 0xa1, 0xa2, 0x91, 0xc0, 0xc5, 0xcb, 0xf5, 0xf6)) // these are updated by IRQ
|
||||||
} else {
|
} else {
|
||||||
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
if (options.zeropage == ZeropageType.KERNALSAFE || options.zeropage == ZeropageType.FLOATSAFE) {
|
||||||
@ -125,7 +121,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
))
|
))
|
||||||
}
|
}
|
||||||
|
|
||||||
if(options.zeropage!=ZeropageType.DONTUSE) {
|
if(options.zeropage!= ZeropageType.DONTUSE) {
|
||||||
// add the free Zp addresses
|
// add the free Zp addresses
|
||||||
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
// these are valid for the C-64 but allow BASIC to keep running fully *as long as you don't use tape I/O*
|
||||||
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
|
||||||
@ -136,17 +132,13 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
free.clear()
|
free.clear()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
require(SCRATCH_B1 !in free)
|
|
||||||
require(SCRATCH_REG !in free)
|
|
||||||
require(SCRATCH_W1 !in free)
|
|
||||||
require(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
removeReservedFromFreePool()
|
||||||
reserve(reserved)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short): IMachineFloat {
|
data class Mflpt5(val b0: Short, val b1: Short, val b2: Short, val b3: Short, val b4: Short):
|
||||||
|
IMachineFloat {
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
val zero = Mflpt5(0, 0, 0, 0, 0)
|
val zero = Mflpt5(0, 0, 0, 0, 0)
|
||||||
@ -157,7 +149,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
val flt = num.toDouble()
|
val flt = num.toDouble()
|
||||||
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
if (flt < FLOAT_MAX_NEGATIVE || flt > FLOAT_MAX_POSITIVE)
|
||||||
throw CompilerException("floating point number out of 5-byte mflpt range: $this")
|
throw InternalCompilerException("floating point number out of 5-byte mflpt range: $this")
|
||||||
if (flt == 0.0)
|
if (flt == 0.0)
|
||||||
return zero
|
return zero
|
||||||
|
|
||||||
@ -178,7 +170,7 @@ internal object C64MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
return when {
|
return when {
|
||||||
exponent < 0 -> zero // underflow, use zero instead
|
exponent < 0 -> zero // underflow, use zero instead
|
||||||
exponent > 255 -> throw CompilerException("floating point overflow: $this")
|
exponent > 255 -> throw InternalCompilerException("floating point overflow: $this")
|
||||||
exponent == 0 -> zero
|
exponent == 0 -> zero
|
||||||
else -> {
|
else -> {
|
||||||
val mantLong = mantissa.toLong()
|
val mantLong = mantissa.toLong()
|
@ -1,10 +1,17 @@
|
|||||||
package prog8.compiler.target.cbm
|
package prog8.compiler.target.cbm
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
import com.github.michaelbull.result.Ok
|
||||||
import prog8.compiler.OutputType
|
import com.github.michaelbull.result.Result
|
||||||
import prog8.compiler.target.IAssemblyProgram
|
import com.github.michaelbull.result.mapError
|
||||||
import prog8.compiler.target.generatedLabelPrefix
|
import prog8.compilerinterface.CompilationOptions
|
||||||
|
import prog8.compilerinterface.IAssemblyProgram
|
||||||
|
import prog8.compilerinterface.OutputType
|
||||||
|
import prog8.compilerinterface.generatedLabelPrefix
|
||||||
|
import prog8.parser.SourceCode
|
||||||
|
import java.io.File
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
import kotlin.io.path.Path
|
||||||
|
import kotlin.io.path.isRegularFile
|
||||||
|
|
||||||
|
|
||||||
internal const val viceMonListPostfix = "vice-mon-list"
|
internal const val viceMonListPostfix = "vice-mon-list"
|
||||||
@ -20,12 +27,15 @@ class AssemblyProgram(
|
|||||||
private val binFile = outputDir.resolve("$name.bin")
|
private val binFile = outputDir.resolve("$name.bin")
|
||||||
private val viceMonListFile = outputDir.resolve("$name.$viceMonListPostfix")
|
private val viceMonListFile = outputDir.resolve("$name.$viceMonListPostfix")
|
||||||
|
|
||||||
override fun assemble(options: CompilationOptions): Int {
|
override fun assemble(quiet: Boolean, options: CompilationOptions): Int {
|
||||||
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps (default = do this silently)
|
||||||
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
|
||||||
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
"-Wall", "-Wno-strict-bool", "-Wno-shadow", // "-Werror",
|
||||||
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
"--dump-labels", "--vice-labels", "-l", viceMonListFile.toString(), "--no-monitor")
|
||||||
|
|
||||||
|
if(quiet)
|
||||||
|
command.add("--quiet")
|
||||||
|
|
||||||
val outFile = when (options.output) {
|
val outFile = when (options.output) {
|
||||||
OutputType.PRG -> {
|
OutputType.PRG -> {
|
||||||
command.add("--cbm-prg")
|
command.add("--cbm-prg")
|
||||||
@ -76,3 +86,18 @@ class AssemblyProgram(
|
|||||||
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
viceMonListFile.toFile().appendText(breakpoints.joinToString("\n") + "\n")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
||||||
|
return if (filename.startsWith(SourceCode.libraryFilePrefix)) {
|
||||||
|
return com.github.michaelbull.result.runCatching {
|
||||||
|
SourceCode.Resource("/prog8lib/${filename.substring(SourceCode.libraryFilePrefix.length)}").readText()
|
||||||
|
}.mapError { NoSuchFileException(File(filename)) }
|
||||||
|
} else {
|
||||||
|
val sib = Path(source.origin).resolveSibling(filename)
|
||||||
|
if (sib.isRegularFile())
|
||||||
|
Ok(SourceCode.File(sib).readText())
|
||||||
|
else
|
||||||
|
Ok(SourceCode.File(Path(filename)).readText())
|
||||||
|
}
|
||||||
|
}
|
@ -11,7 +11,7 @@ object Petscii {
|
|||||||
// decoding: from Petscii/Screencodes (0-255) to unicode
|
// decoding: from Petscii/Screencodes (0-255) to unicode
|
||||||
// character tables used from https://github.com/dj51d/cbmcodecs
|
// character tables used from https://github.com/dj51d/cbmcodecs
|
||||||
|
|
||||||
private val decodingPetsciiLowercase = arrayOf(
|
private val decodingPetsciiLowercase = charArrayOf(
|
||||||
'\u0000', // 0x00 -> \u0000
|
'\u0000', // 0x00 -> \u0000
|
||||||
'\ufffe', // 0x01 -> UNDEFINED
|
'\ufffe', // 0x01 -> UNDEFINED
|
||||||
'\ufffe', // 0x02 -> UNDEFINED
|
'\ufffe', // 0x02 -> UNDEFINED
|
||||||
@ -270,7 +270,7 @@ object Petscii {
|
|||||||
'\u2592' // ▒ 0xFF -> MEDIUM SHADE
|
'\u2592' // ▒ 0xFF -> MEDIUM SHADE
|
||||||
)
|
)
|
||||||
|
|
||||||
private val decodingPetsciiUppercase = arrayOf(
|
private val decodingPetsciiUppercase = charArrayOf(
|
||||||
'\u0000', // 0x00 -> \u0000
|
'\u0000', // 0x00 -> \u0000
|
||||||
'\ufffe', // 0x01 -> UNDEFINED
|
'\ufffe', // 0x01 -> UNDEFINED
|
||||||
'\ufffe', // 0x02 -> UNDEFINED
|
'\ufffe', // 0x02 -> UNDEFINED
|
||||||
@ -369,13 +369,13 @@ object Petscii {
|
|||||||
'\u2190', // ← 0x5F -> LEFTWARDS ARROW
|
'\u2190', // ← 0x5F -> LEFTWARDS ARROW
|
||||||
'\u2500', // ─ 0x60 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\u2500', // ─ 0x60 -> BOX DRAWINGS LIGHT HORIZONTAL
|
||||||
'\u2660', // ♠ 0x61 -> BLACK SPADE SUIT
|
'\u2660', // ♠ 0x61 -> BLACK SPADE SUIT
|
||||||
'\u2502', // │ 0x62 -> BOX DRAWINGS LIGHT VERTICAL
|
'\uf13c', // │ 0x62 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH LEFT (CUS)
|
||||||
'\u2500', // ─ 0x63 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\uf13b', // ─ 0x63 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH UP (CUS)
|
||||||
'\uf122', // 0x64 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER UP (CUS)
|
'\uf122', // 0x64 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTHS UP (CUS)
|
||||||
'\uf123', // 0x65 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS UP (CUS)
|
'\uf123', // 0x65 -> BOX DRAWINGS LIGHT HORIZONTAL THREE EIGHTHS UP (CUS)
|
||||||
'\uf124', // 0x66 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER DOWN (CUS)
|
'\uf124', // 0x66 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH DOWN (CUS)
|
||||||
'\uf126', // 0x67 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER LEFT (CUS)
|
'\uf126', // 0x67 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTHS LEFT (CUS)
|
||||||
'\uf128', // 0x68 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER RIGHT (CUS)
|
'\uf128', // 0x68 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH RIGHT (CUS)
|
||||||
'\u256e', // ╮ 0x69 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
'\u256e', // ╮ 0x69 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
||||||
'\u2570', // ╰ 0x6A -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
'\u2570', // ╰ 0x6A -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
||||||
'\u256f', // ╯ 0x6B -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
'\u256f', // ╯ 0x6B -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
||||||
@ -385,14 +385,14 @@ object Petscii {
|
|||||||
'\uf12b', // 0x6F -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
'\uf12b', // 0x6F -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
||||||
'\uf12c', // 0x70 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
'\uf12c', // 0x70 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
||||||
'\u25cf', // ● 0x71 -> BLACK CIRCLE
|
'\u25cf', // ● 0x71 -> BLACK CIRCLE
|
||||||
'\uf125', // 0x72 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS DOWN (CUS)
|
'\uf125', // 0x72 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTHS DOWN (CUS)
|
||||||
'\u2665', // ♥ 0x73 -> BLACK HEART SUIT
|
'\u2665', // ♥ 0x73 -> BLACK HEART SUIT
|
||||||
'\uf127', // 0x74 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS LEFT (CUS)
|
'\uf127', // 0x74 -> BOX DRAWINGS LIGHT VERTICAL THREE EIGHTHS LEFT (CUS)
|
||||||
'\u256d', // ╭ 0x75 -> BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
|
'\u256d', // ╭ 0x75 -> BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
|
||||||
'\u2573', // ╳ 0x76 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
'\u2573', // ╳ 0x76 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
||||||
'\u25cb', // ○ 0x77 -> WHITE CIRCLE
|
'\u25cb', // ○ 0x77 -> WHITE CIRCLE
|
||||||
'\u2663', // ♣ 0x78 -> BLACK CLUB SUIT
|
'\u2663', // ♣ 0x78 -> BLACK CLUB SUIT
|
||||||
'\uf129', // 0x79 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS RIGHT (CUS)
|
'\uf129', // 0x79 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTS RIGHT (CUS)
|
||||||
'\u2666', // ♦ 0x7A -> BLACK DIAMOND SUIT
|
'\u2666', // ♦ 0x7A -> BLACK DIAMOND SUIT
|
||||||
'\u253c', // ┼ 0x7B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
'\u253c', // ┼ 0x7B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
||||||
'\uf12e', // 0x7C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
'\uf12e', // 0x7C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
||||||
@ -465,13 +465,13 @@ object Petscii {
|
|||||||
'\u259a', // ▚ 0xBF -> QUADRANT UPPER LEFT AND LOWER RIGHT
|
'\u259a', // ▚ 0xBF -> QUADRANT UPPER LEFT AND LOWER RIGHT
|
||||||
'\u2500', // ─ 0xC0 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\u2500', // ─ 0xC0 -> BOX DRAWINGS LIGHT HORIZONTAL
|
||||||
'\u2660', // ♠ 0xC1 -> BLACK SPADE SUIT
|
'\u2660', // ♠ 0xC1 -> BLACK SPADE SUIT
|
||||||
'\u2502', // │ 0xC2 -> BOX DRAWINGS LIGHT VERTICAL
|
'\uf13c', // │ 0xC2 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH LEFT (CUS)
|
||||||
'\u2500', // ─ 0xC3 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\uf13b', // ─ 0xC3 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH UP (CUS)
|
||||||
'\uf122', // 0xC4 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER UP (CUS)
|
'\uf122', // 0xC4 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTHS UP (CUS)
|
||||||
'\uf123', // 0xC5 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS UP (CUS)
|
'\uf123', // 0xC5 -> BOX DRAWINGS LIGHT HORIZONTAL THREE EIGHTHS UP (CUS)
|
||||||
'\uf124', // 0xC6 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER DOWN (CUS)
|
'\uf124', // 0xC6 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH DOWN (CUS)
|
||||||
'\uf126', // 0xC7 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER LEFT (CUS)
|
'\uf126', // 0xC7 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTHS LEFT (CUS)
|
||||||
'\uf128', // 0xC8 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER RIGHT (CUS)
|
'\uf128', // 0xC8 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH RIGHT (CUS)
|
||||||
'\u256e', // ╮ 0xC9 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
'\u256e', // ╮ 0xC9 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
||||||
'\u2570', // ╰ 0xCA -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
'\u2570', // ╰ 0xCA -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
||||||
'\u256f', // ╯ 0xCB -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
'\u256f', // ╯ 0xCB -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
||||||
@ -481,14 +481,14 @@ object Petscii {
|
|||||||
'\uf12b', // 0xCF -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
'\uf12b', // 0xCF -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
||||||
'\uf12c', // 0xD0 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
'\uf12c', // 0xD0 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
||||||
'\u25cf', // ● 0xD1 -> BLACK CIRCLE
|
'\u25cf', // ● 0xD1 -> BLACK CIRCLE
|
||||||
'\uf125', // 0xD2 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS DOWN (CUS)
|
'\uf125', // 0xD2 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTS DOWN (CUS)
|
||||||
'\u2665', // ♥ 0xD3 -> BLACK HEART SUIT
|
'\u2665', // ♥ 0xD3 -> BLACK HEART SUIT
|
||||||
'\uf127', // 0xD4 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS LEFT (CUS)
|
'\uf127', // 0xD4 -> BOX DRAWINGS LIGHT VERTICAL THREE EIGHTS LEFT (CUS)
|
||||||
'\u256d', // ╭ 0xD5 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
'\u256d', // ╭ 0xD5 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
||||||
'\u2573', // ╳ 0xD6 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
'\u2573', // ╳ 0xD6 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
||||||
'\u25cb', // ○ 0xD7 -> WHITE CIRCLE
|
'\u25cb', // ○ 0xD7 -> WHITE CIRCLE
|
||||||
'\u2663', // ♣ 0xD8 -> BLACK CLUB SUIT
|
'\u2663', // ♣ 0xD8 -> BLACK CLUB SUIT
|
||||||
'\uf129', // 0xD9 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS RIGHT (CUS)
|
'\uf129', // 0xD9 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTS RIGHT (CUS)
|
||||||
'\u2666', // ♦ 0xDA -> BLACK DIAMOND SUIT
|
'\u2666', // ♦ 0xDA -> BLACK DIAMOND SUIT
|
||||||
'\u253c', // ┼ 0xDB -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
'\u253c', // ┼ 0xDB -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
||||||
'\uf12e', // 0xDC -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
'\uf12e', // 0xDC -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
||||||
@ -529,7 +529,7 @@ object Petscii {
|
|||||||
'\u03c0' // π 0xFF -> GREEK SMALL LETTER PI
|
'\u03c0' // π 0xFF -> GREEK SMALL LETTER PI
|
||||||
)
|
)
|
||||||
|
|
||||||
private val decodingScreencodeLowercase = arrayOf(
|
private val decodingScreencodeLowercase = charArrayOf(
|
||||||
'@' , // @ 0x00 -> COMMERCIAL AT
|
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||||
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
'a' , // a 0x01 -> LATIN SMALL LETTER A
|
||||||
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
'b' , // b 0x02 -> LATIN SMALL LETTER B
|
||||||
@ -788,7 +788,7 @@ object Petscii {
|
|||||||
'\ufffe' // 0xFF -> UNDEFINED
|
'\ufffe' // 0xFF -> UNDEFINED
|
||||||
)
|
)
|
||||||
|
|
||||||
private val decodingScreencodeUppercase = arrayOf(
|
private val decodingScreencodeUppercase = charArrayOf(
|
||||||
'@' , // @ 0x00 -> COMMERCIAL AT
|
'@' , // @ 0x00 -> COMMERCIAL AT
|
||||||
'A' , // A 0x01 -> LATIN CAPITAL LETTER A
|
'A' , // A 0x01 -> LATIN CAPITAL LETTER A
|
||||||
'B' , // B 0x02 -> LATIN CAPITAL LETTER B
|
'B' , // B 0x02 -> LATIN CAPITAL LETTER B
|
||||||
@ -855,13 +855,13 @@ object Petscii {
|
|||||||
'?' , // ? 0x3F -> QUESTION MARK
|
'?' , // ? 0x3F -> QUESTION MARK
|
||||||
'\u2500', // ─ 0x40 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\u2500', // ─ 0x40 -> BOX DRAWINGS LIGHT HORIZONTAL
|
||||||
'\u2660', // ♠ 0x41 -> BLACK SPADE SUIT
|
'\u2660', // ♠ 0x41 -> BLACK SPADE SUIT
|
||||||
'\u2502', // │ 0x42 -> BOX DRAWINGS LIGHT VERTICAL
|
'\uf13c', // │ 0x42 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH LEFT (CUS)
|
||||||
'\u2500', // ─ 0x43 -> BOX DRAWINGS LIGHT HORIZONTAL
|
'\uf13b', // ─ 0x43 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH UP (CUS)
|
||||||
'\uf122', // 0x44 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER UP (CUS)
|
'\uf122', // 0x44 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTHS UP (CUS)
|
||||||
'\uf123', // 0x45 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS UP (CUS)
|
'\uf123', // 0x45 -> BOX DRAWINGS LIGHT HORIZONTAL THREE EIGHTHS UP (CUS
|
||||||
'\uf124', // 0x46 -> BOX DRAWINGS LIGHT HORIZONTAL ONE QUARTER DOWN (CUS)
|
'\uf124', // 0x46 -> BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH DOWN (CUS)
|
||||||
'\uf126', // 0x47 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER LEFT (CUS)
|
'\uf126', // 0x47 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTHS LEFT (CUS)
|
||||||
'\uf128', // 0x48 -> BOX DRAWINGS LIGHT VERTICAL ONE QUARTER RIGHT (CUS)
|
'\uf128', // 0x48 -> BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH RIGHT (CUS)
|
||||||
'\u256e', // ╮ 0x49 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
'\u256e', // ╮ 0x49 -> BOX DRAWINGS LIGHT ARC DOWN AND LEFT
|
||||||
'\u2570', // ╰ 0x4A -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
'\u2570', // ╰ 0x4A -> BOX DRAWINGS LIGHT ARC UP AND RIGHT
|
||||||
'\u256f', // ╯ 0x4B -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
'\u256f', // ╯ 0x4B -> BOX DRAWINGS LIGHT ARC UP AND LEFT
|
||||||
@ -871,14 +871,14 @@ object Petscii {
|
|||||||
'\uf12b', // 0x4F -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
'\uf12b', // 0x4F -> ONE EIGHTH BLOCK DOWN AND RIGHT (CUS)
|
||||||
'\uf12c', // 0x50 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
'\uf12c', // 0x50 -> ONE EIGHTH BLOCK DOWN AND LEFT (CUS)
|
||||||
'\u25cf', // ● 0x51 -> BLACK CIRCLE
|
'\u25cf', // ● 0x51 -> BLACK CIRCLE
|
||||||
'\uf125', // 0x52 -> BOX DRAWINGS LIGHT HORIZONTAL TWO QUARTERS DOWN (CUS)
|
'\uf125', // 0x52 -> BOX DRAWINGS LIGHT HORIZONTAL TWO EIGHTS DOWN (CUS)
|
||||||
'\u2665', // ♥ 0x53 -> BLACK HEART SUIT
|
'\u2665', // ♥ 0x53 -> BLACK HEART SUIT
|
||||||
'\uf127', // 0x54 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS LEFT (CUS)
|
'\uf127', // 0x54 -> BOX DRAWINGS LIGHT VERTICAL THREE EIGHTS LEFT (CUS)
|
||||||
'\u256d', // ╭ 0x55 -> BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
|
'\u256d', // ╭ 0x55 -> BOX DRAWINGS LIGHT ARC DOWN AND RIGHT
|
||||||
'\u2573', // ╳ 0x56 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
'\u2573', // ╳ 0x56 -> BOX DRAWINGS LIGHT DIAGONAL CROSS
|
||||||
'\u25cb', // ○ 0x57 -> WHITE CIRCLE
|
'\u25cb', // ○ 0x57 -> WHITE CIRCLE
|
||||||
'\u2663', // ♣ 0x58 -> BLACK CLUB SUIT
|
'\u2663', // ♣ 0x58 -> BLACK CLUB SUIT
|
||||||
'\uf129', // 0x59 -> BOX DRAWINGS LIGHT VERTICAL TWO QUARTERS RIGHT (CUS)
|
'\uf129', // 0x59 -> BOX DRAWINGS LIGHT VERTICAL TWO EIGHTS RIGHT (CUS)
|
||||||
'\u2666', // ♦ 0x5A -> BLACK DIAMOND SUIT
|
'\u2666', // ♦ 0x5A -> BLACK DIAMOND SUIT
|
||||||
'\u253c', // ┼ 0x5B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
'\u253c', // ┼ 0x5B -> BOX DRAWINGS LIGHT VERTICAL AND HORIZONTAL
|
||||||
'\uf12e', // 0x5C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
'\uf12e', // 0x5C -> LEFT HALF BLOCK MEDIUM SHADE (CUS)
|
||||||
@ -1098,12 +1098,9 @@ object Petscii {
|
|||||||
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodePetscii(petscii: Iterable<Short>, lowercase: Boolean = false): String {
|
||||||
return petscii.map {
|
return petscii.map {
|
||||||
val code = it.toInt()
|
val code = it.toInt()
|
||||||
try {
|
if(code<0 || code>= decodingPetsciiLowercase.size)
|
||||||
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
throw CharConversionException("petscii $code out of range 0..${decodingPetsciiLowercase.size-1}")
|
||||||
} catch(x: CharConversionException) {
|
if(lowercase) decodingPetsciiLowercase[code] else decodingPetsciiUppercase[code]
|
||||||
// TODO this CharConversionException can never occur?? also clean up ICompilationTarget.decodeString?
|
|
||||||
if(lowercase) decodingPetsciiUppercase[code] else decodingPetsciiLowercase[code]
|
|
||||||
}
|
|
||||||
}.joinToString("")
|
}.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1140,17 +1137,15 @@ object Petscii {
|
|||||||
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
fun decodeScreencode(screencode: Iterable<Short>, lowercase: Boolean = false): String {
|
||||||
return screencode.map {
|
return screencode.map {
|
||||||
val code = it.toInt()
|
val code = it.toInt()
|
||||||
try {
|
if(code<0 || code>= decodingScreencodeLowercase.size)
|
||||||
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
throw CharConversionException("screencode $code out of range 0..${decodingScreencodeLowercase.size-1}")
|
||||||
} catch (x: CharConversionException) {
|
if (lowercase) decodingScreencodeLowercase[code] else decodingScreencodeUppercase[code]
|
||||||
// TODO this CharConversionException can never occur?? also clean up ICompilationTarget.decodeString?
|
|
||||||
if (lowercase) decodingScreencodeUppercase[code] else decodingScreencodeLowercase[code]
|
|
||||||
}
|
|
||||||
}.joinToString("")
|
}.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Result<Short, CharConversionException> {
|
fun petscii2scr(petscii_code: Short, inverseVideo: Boolean): Result<Short, CharConversionException> {
|
||||||
val code = when {
|
val code = when {
|
||||||
|
petscii_code < 0 -> return Err(CharConversionException("petscii code out of range"))
|
||||||
petscii_code <= 0x1f -> petscii_code + 128
|
petscii_code <= 0x1f -> petscii_code + 128
|
||||||
petscii_code <= 0x3f -> petscii_code.toInt()
|
petscii_code <= 0x3f -> petscii_code.toInt()
|
||||||
petscii_code <= 0x5f -> petscii_code - 64
|
petscii_code <= 0x5f -> petscii_code - 64
|
||||||
@ -1168,6 +1163,7 @@ object Petscii {
|
|||||||
|
|
||||||
fun scr2petscii(screencode: Short): Result<Short, CharConversionException> {
|
fun scr2petscii(screencode: Short): Result<Short, CharConversionException> {
|
||||||
val petscii = when {
|
val petscii = when {
|
||||||
|
screencode < 0 -> return Err(CharConversionException("screencode out of range"))
|
||||||
screencode <= 0x1f -> screencode + 64
|
screencode <= 0x1f -> screencode + 64
|
||||||
screencode <= 0x3f -> screencode.toInt()
|
screencode <= 0x3f -> screencode.toInt()
|
||||||
screencode <= 0x5d -> screencode +123
|
screencode <= 0x5d -> screencode +123
|
@ -1,30 +1,32 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen
|
package prog8.compiler.target.cpu6502.codegen
|
||||||
|
|
||||||
import com.github.michaelbull.result.*
|
import com.github.michaelbull.result.fold
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.*
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.functions.FSignature
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.*
|
|
||||||
import prog8.compiler.target.cbm.AssemblyProgram
|
import prog8.compiler.target.cbm.AssemblyProgram
|
||||||
|
import prog8.compiler.target.cbm.loadAsmIncludeFile
|
||||||
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||||
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
|
import prog8.compiler.target.cpu6502.codegen.assignment.AssignmentAsmGen
|
||||||
import prog8.optimizer.CallGraph
|
import prog8.compiler.target.cpu6502.codegen.assignment.SourceStorageKind
|
||||||
|
import prog8.compilerinterface.*
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.absolute
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
internal class AsmGen(private val program: Program,
|
class AsmGen(private val program: Program,
|
||||||
val errors: IErrorReporter,
|
val errors: IErrorReporter,
|
||||||
val zeropage: Zeropage,
|
val zeropage: Zeropage,
|
||||||
val options: CompilationOptions,
|
val options: CompilationOptions,
|
||||||
@ -47,9 +49,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen)
|
private val assignmentAsmGen = AssignmentAsmGen(program, this, expressionsAsmGen)
|
||||||
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
private val builtinFunctionsAsmGen = BuiltinFunctionsAsmGen(program, this, assignmentAsmGen)
|
||||||
internal val loopEndLabels = ArrayDeque<String>()
|
internal val loopEndLabels = ArrayDeque<String>()
|
||||||
private val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
|
|
||||||
internal val slabs = mutableMapOf<String, Int>()
|
internal val slabs = mutableMapOf<String, Int>()
|
||||||
internal val removals = mutableListOf<Pair<Statement, INameScope>>()
|
internal val removals = mutableListOf<Pair<Statement, IStatementContainer>>()
|
||||||
|
private val blockVariableInitializers = program.allBlocks.associateWith { it.statements.filterIsInstance<Assignment>() }
|
||||||
|
|
||||||
override fun compileToAssembly(): IAssemblyProgram {
|
override fun compileToAssembly(): IAssemblyProgram {
|
||||||
assemblyLines.clear()
|
assemblyLines.clear()
|
||||||
@ -61,8 +63,11 @@ internal class AsmGen(private val program: Program,
|
|||||||
val allBlocks = program.allBlocks
|
val allBlocks = program.allBlocks
|
||||||
if(allBlocks.first().name != "main")
|
if(allBlocks.first().name != "main")
|
||||||
throw AssemblyError("first block should be 'main'")
|
throw AssemblyError("first block should be 'main'")
|
||||||
for(b in program.allBlocks)
|
|
||||||
|
for(b in program.allBlocks) {
|
||||||
|
b.statements.removeAll(blockVariableInitializers.getValue(b)) // no longer output the initialization assignments as regular statements in the block!
|
||||||
block2asm(b)
|
block2asm(b)
|
||||||
|
}
|
||||||
|
|
||||||
for(removal in removals.toList()) {
|
for(removal in removals.toList()) {
|
||||||
removal.second.remove(removal.first)
|
removal.second.remove(removal.first)
|
||||||
@ -219,26 +224,17 @@ internal class AsmGen(private val program: Program,
|
|||||||
stmts.forEach { translate(it) }
|
stmts.forEach { translate(it) }
|
||||||
subroutine.forEach { translateSubroutine(it as Subroutine) }
|
subroutine.forEach { translateSubroutine(it as Subroutine) }
|
||||||
|
|
||||||
// if any global vars need to be initialized, generate a subroutine that does this
|
// generate subroutine to initialize block-level variables
|
||||||
// it will be called from program init.
|
val initializers = blockVariableInitializers.getValue(block)
|
||||||
if(block in blockLevelVarInits) {
|
if(initializers.isNotEmpty()) {
|
||||||
out("prog8_init_vars\t.proc\n")
|
out("prog8_init_vars\t.proc\n")
|
||||||
blockLevelVarInits.getValue(block).forEach { decl ->
|
initializers.forEach { assign -> translate(assign) }
|
||||||
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
|
||||||
require(scopedFullName.first()==block.name)
|
|
||||||
assignInitialValueToVar(decl, scopedFullName.drop(1))
|
|
||||||
}
|
|
||||||
out(" rts\n .pend")
|
out(" rts\n .pend")
|
||||||
}
|
}
|
||||||
|
|
||||||
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignInitialValueToVar(decl: VarDecl, variableName: List<String>) {
|
|
||||||
val asmName = asmVariableName(variableName)
|
|
||||||
assignmentAsmGen.assignExpressionToVariable(decl.value!!, asmName, decl.datatype, decl.definingSubroutine)
|
|
||||||
}
|
|
||||||
|
|
||||||
private var generatedLabelSequenceNumber: Int = 0
|
private var generatedLabelSequenceNumber: Int = 0
|
||||||
|
|
||||||
internal fun makeLabel(postfix: String): String {
|
internal fun makeLabel(postfix: String): String {
|
||||||
@ -275,7 +271,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
&& variable.datatype != DataType.FLOAT
|
&& variable.datatype != DataType.FLOAT
|
||||||
&& options.zeropage != ZeropageType.DONTUSE) {
|
&& options.zeropage != ZeropageType.DONTUSE) {
|
||||||
try {
|
try {
|
||||||
val errors = ErrorReporter()
|
|
||||||
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
||||||
@ -500,7 +495,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
return newName
|
return newName
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun asmSymbolName(identifier: IdentifierReference): String {
|
fun asmSymbolName(identifier: IdentifierReference): String {
|
||||||
if(identifier.nameInSource.size==2 && identifier.nameInSource[0]=="prog8_slabs")
|
if(identifier.nameInSource.size==2 && identifier.nameInSource[0]=="prog8_slabs")
|
||||||
return identifier.nameInSource.joinToString(".")
|
return identifier.nameInSource.joinToString(".")
|
||||||
|
|
||||||
@ -531,7 +526,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun asmVariableName(identifier: IdentifierReference) =
|
fun asmVariableName(identifier: IdentifierReference) =
|
||||||
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
fixNameSymbols(identifier.nameInSource.joinToString("."))
|
||||||
|
|
||||||
private fun getScopedSymbolNameForTarget(actualName: String, target: Statement): MutableList<String> {
|
private fun getScopedSymbolNameForTarget(actualName: String, target: Statement): MutableList<String> {
|
||||||
@ -837,12 +832,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||||
internal fun translateExpression(expression: Expression) =
|
internal fun translateExpression(expression: Expression) =
|
||||||
expressionsAsmGen.translateExpression(expression)
|
expressionsAsmGen.translateExpression(expression)
|
||||||
|
|
||||||
internal fun translateExpression(indexer: ArrayIndex) =
|
|
||||||
expressionsAsmGen.translateExpression(indexer)
|
|
||||||
|
|
||||||
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
internal fun translateBuiltinFunctionCallExpression(functionCall: FunctionCall, signature: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack, resultRegister)
|
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature, resultToStack, resultRegister)
|
||||||
|
|
||||||
@ -858,14 +851,53 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
internal fun translateNormalAssignment(assign: AsmAssignment) =
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
|
|
||||||
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) =
|
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean=false) =
|
||||||
assignmentAsmGen.assignExpressionToRegister(expr, register)
|
assignmentAsmGen.assignExpressionToRegister(expr, register, signed)
|
||||||
|
|
||||||
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) =
|
internal fun assignExpressionToVariable(expr: Expression, asmVarName: String, dt: DataType, scope: Subroutine?) =
|
||||||
assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope)
|
assignmentAsmGen.assignExpressionToVariable(expr, asmVarName, dt, scope)
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) =
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean=false) =
|
||||||
assignmentAsmGen.assignVariableToRegister(asmVarName, register)
|
assignmentAsmGen.assignVariableToRegister(asmVarName, register, signed)
|
||||||
|
|
||||||
|
internal fun assignRegister(reg: RegisterOrPair, target: AsmAssignTarget) {
|
||||||
|
when(reg) {
|
||||||
|
RegisterOrPair.A,
|
||||||
|
RegisterOrPair.X,
|
||||||
|
RegisterOrPair.Y -> assignmentAsmGen.assignRegisterByte(target, reg.asCpuRegister())
|
||||||
|
RegisterOrPair.AX,
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
RegisterOrPair.XY,
|
||||||
|
in Cx16VirtualRegisters -> assignmentAsmGen.assignRegisterpairWord(target, reg)
|
||||||
|
RegisterOrPair.FAC1 -> assignmentAsmGen.assignFAC1float(target)
|
||||||
|
RegisterOrPair.FAC2 -> TODO("no support yet to assign FAC2 directly to something")
|
||||||
|
else -> throw AssemblyError("invalid register")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun assignExpressionTo(value: Expression, target: AsmAssignTarget) {
|
||||||
|
// don't use asmgen.translateExpression() to avoid evalstack
|
||||||
|
when (target.datatype) {
|
||||||
|
in ByteDatatypes -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.A)
|
||||||
|
assignRegister(RegisterOrPair.A, target)
|
||||||
|
}
|
||||||
|
in WordDatatypes, in PassByReferenceDatatypes -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.AY)
|
||||||
|
translateNormalAssignment(
|
||||||
|
AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.REGISTER, program, this, target.datatype, register=RegisterOrPair.AY),
|
||||||
|
target, false, program.memsizer, value.position
|
||||||
|
)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
||||||
|
assignRegister(RegisterOrPair.FAC1, target)
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird dt ${target.datatype}")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun translateSubroutine(sub: Subroutine) {
|
private fun translateSubroutine(sub: Subroutine) {
|
||||||
@ -909,9 +941,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
if(sub.name=="start" && sub.definingBlock.name=="main") {
|
if(sub.name=="start" && sub.definingBlock.name=="main") {
|
||||||
out("; program startup initialization")
|
out("; program startup initialization")
|
||||||
out(" cld")
|
out(" cld")
|
||||||
program.allBlocks.forEach {
|
blockVariableInitializers.forEach {
|
||||||
if(it.statements.filterIsInstance<VarDecl>().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes})
|
if(it.value.isNotEmpty())
|
||||||
out(" jsr ${it.name}.prog8_init_vars")
|
out(" jsr ${it.key.name}.prog8_init_vars")
|
||||||
}
|
}
|
||||||
out("""
|
out("""
|
||||||
tsx
|
tsx
|
||||||
@ -984,15 +1016,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: IfStatement) {
|
private fun translate(stmt: IfStatement) {
|
||||||
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
|
requireComparisonExpression(stmt.condition) // IfStatement: condition must be of form 'x <comparison> <value>'
|
||||||
val booleanCondition = stmt.condition as BinaryExpression
|
val booleanCondition = stmt.condition as BinaryExpression
|
||||||
|
|
||||||
// DISABLED FOR NOW:
|
// DISABLED FOR NOW:
|
||||||
// if(!booleanCondition.left.isSimple || !booleanCondition.right.isSimple)
|
// if(!booleanCondition.left.isSimple || !booleanCondition.right.isSimple)
|
||||||
// throw AssemblyError("both operands for if comparison expression should have been simplified")
|
// throw AssemblyError("both operands for if comparison expression should have been simplified")
|
||||||
|
|
||||||
if (stmt.elsepart.containsNoCodeNorVars) {
|
if (stmt.elsepart.isEmpty()) {
|
||||||
// empty else
|
|
||||||
val endLabel = makeLabel("if_end")
|
val endLabel = makeLabel("if_end")
|
||||||
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
|
expressionsAsmGen.translateComparisonExpressionWithJumpIfFalse(booleanCondition, endLabel)
|
||||||
translate(stmt.truepart)
|
translate(stmt.truepart)
|
||||||
@ -1011,9 +1042,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkBooleanExpression(condition: Expression) {
|
private fun requireComparisonExpression(condition: Expression) {
|
||||||
if(condition !is BinaryExpression || condition.operator !in comparisonOperators)
|
if(condition !is BinaryExpression || condition.operator !in comparisonOperators)
|
||||||
throw AssemblyError("expected boolean expression $condition")
|
throw AssemblyError("expected boolean comparison expression $condition")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: RepeatLoop) {
|
private fun translate(stmt: RepeatLoop) {
|
||||||
@ -1164,7 +1195,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: WhileLoop) {
|
private fun translate(stmt: WhileLoop) {
|
||||||
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
|
requireComparisonExpression(stmt.condition) // WhileLoop: condition must be of form 'x <comparison> <value>'
|
||||||
val booleanCondition = stmt.condition as BinaryExpression
|
val booleanCondition = stmt.condition as BinaryExpression
|
||||||
val whileLabel = makeLabel("while")
|
val whileLabel = makeLabel("while")
|
||||||
val endLabel = makeLabel("whileend")
|
val endLabel = makeLabel("whileend")
|
||||||
@ -1178,7 +1209,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: UntilLoop) {
|
private fun translate(stmt: UntilLoop) {
|
||||||
checkBooleanExpression(stmt.condition) // we require the condition to be of the form 'x <comparison> <value>'
|
requireComparisonExpression(stmt.condition) // UntilLoop: condition must be of form 'x <comparison> <value>'
|
||||||
val booleanCondition = stmt.condition as BinaryExpression
|
val booleanCondition = stmt.condition as BinaryExpression
|
||||||
val repeatLabel = makeLabel("repeat")
|
val repeatLabel = makeLabel("repeat")
|
||||||
val endLabel = makeLabel("repeatend")
|
val endLabel = makeLabel("repeatend")
|
||||||
@ -1245,7 +1276,7 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: BranchStatement) {
|
private fun translate(stmt: BranchStatement) {
|
||||||
if(stmt.truepart.containsNoCodeNorVars && stmt.elsepart.containsCodeOrVars)
|
if(stmt.truepart.isEmpty() && stmt.elsepart.isNotEmpty())
|
||||||
throw AssemblyError("only else part contains code, shoud have been switched already")
|
throw AssemblyError("only else part contains code, shoud have been switched already")
|
||||||
|
|
||||||
val jump = stmt.truepart.statements.first() as? Jump
|
val jump = stmt.truepart.statements.first() as? Jump
|
||||||
@ -1257,7 +1288,7 @@ $repeatLabel lda $counterVar
|
|||||||
} else {
|
} else {
|
||||||
val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break
|
val truePartIsJustBreak = stmt.truepart.statements.firstOrNull() is Break
|
||||||
val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break
|
val elsePartIsJustBreak = stmt.elsepart.statements.firstOrNull() is Break
|
||||||
if(stmt.elsepart.containsNoCodeNorVars) {
|
if(stmt.elsepart.isEmpty()) {
|
||||||
if(truePartIsJustBreak) {
|
if(truePartIsJustBreak) {
|
||||||
// branch with just a break (jump out of loop)
|
// branch with just a break (jump out of loop)
|
||||||
val instruction = branchInstruction(stmt.condition, false)
|
val instruction = branchInstruction(stmt.condition, false)
|
||||||
@ -1297,34 +1328,15 @@ $repeatLabel lda $counterVar
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: VarDecl) {
|
private fun translate(decl: VarDecl) {
|
||||||
if(stmt.value!=null && stmt.type==VarDeclType.VAR && stmt.datatype in NumericDatatypes) {
|
if(decl.type==VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
||||||
// generate an assignment statement to (re)initialize the variable's value.
|
throw AssemblyError("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||||
// if the vardecl is not in a subroutine however, we have to initialize it globally.
|
// at this time, nothing has to be done here anymore code-wise
|
||||||
if(stmt.definingSubroutine ==null) {
|
|
||||||
val block = stmt.definingBlock
|
|
||||||
var inits = blockLevelVarInits[block]
|
|
||||||
if(inits==null) {
|
|
||||||
inits = mutableSetOf()
|
|
||||||
blockLevelVarInits[block] = inits
|
|
||||||
}
|
|
||||||
inits.add(stmt)
|
|
||||||
} else {
|
|
||||||
val next = (stmt.parent as INameScope).nextSibling(stmt)
|
|
||||||
if (next !is ForLoop || next.loopVar.nameInSource.single() != stmt.name) {
|
|
||||||
assignInitialValueToVar(stmt, listOf(stmt.name))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: %asminclude and %asmbinary should be done earlier than code gen (-> put content into AST) ... (describe why?)
|
|
||||||
*/
|
|
||||||
private fun translate(stmt: Directive) {
|
private fun translate(stmt: Directive) {
|
||||||
when(stmt.directive) {
|
when(stmt.directive) {
|
||||||
"%asminclude" -> {
|
"%asminclude" -> {
|
||||||
// TODO: handle %asminclude with SourceCode
|
|
||||||
val includedName = stmt.args[0].str!!
|
val includedName = stmt.args[0].str!!
|
||||||
if(stmt.definingModule.source is SourceCode.Generated)
|
if(stmt.definingModule.source is SourceCode.Generated)
|
||||||
TODO("%asminclude inside non-library, non-filesystem module")
|
TODO("%asminclude inside non-library, non-filesystem module")
|
||||||
@ -1387,7 +1399,7 @@ $label nop""")
|
|||||||
else -> {
|
else -> {
|
||||||
// all else take its address and assign that also to AY register pair
|
// all else take its address and assign that also to AY register pair
|
||||||
val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position)
|
val addrofValue = AddressOf(returnvalue as IdentifierReference, returnvalue.position)
|
||||||
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!)
|
assignmentAsmGen.assignExpressionToRegister(addrofValue, returnReg.registerOrPair!!, false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -180,7 +180,7 @@ private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<Stri
|
|||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can OFTEN be eliminated
|
||||||
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM)
|
// TODO this is not true if X is not a regular RAM memory address (but instead mapped I/O or ROM) but how does this code know?
|
||||||
val mods = mutableListOf<Modification>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByFour) {
|
for (pair in linesByFour) {
|
||||||
val first = pair[0].value.trimStart()
|
val first = pair[0].value.trimStart()
|
@ -10,12 +10,12 @@ import prog8.ast.statements.DirectMemoryWrite
|
|||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.functions.FSignature
|
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.*
|
import prog8.compiler.target.cpu6502.codegen.assignment.*
|
||||||
import prog8.compiler.target.subroutineFloatEvalResultVar2
|
import prog8.compilerinterface.CpuType
|
||||||
|
import prog8.compilerinterface.FSignature
|
||||||
|
import prog8.compilerinterface.subroutineFloatEvalResultVar2
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
|
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen, private val assignAsmGen: AssignmentAsmGen) {
|
||||||
|
|
||||||
@ -261,7 +261,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
if(resultToStack)
|
if(resultToStack)
|
||||||
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, null)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, null, program, asmgen)
|
AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
val assign = AsmAssignment(src, target, false, program.memsizer, fcall.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
asmgen.slabs[name] = size
|
asmgen.slabs[name] = size
|
||||||
@ -273,7 +273,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
asmgen.out(" jsr prog8_lib.func_sqrt16_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
asmgen.out(" jsr prog8_lib.func_sqrt16_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -285,11 +285,11 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when(func.name) {
|
when(func.name) {
|
||||||
"sin8", "sin8u", "cos8", "cos8u" -> {
|
"sin8", "sin8u", "cos8", "cos8u" -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
|
asmgen.out(" jsr prog8_lib.func_${func.name}_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
"sin16", "sin16u", "cos16", "cos16u" -> {
|
"sin16", "sin16u", "cos16", "cos16u" -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${func.name}_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -578,7 +578,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr floats.func_${func.name}_stack")
|
asmgen.out(" jsr floats.func_${func.name}_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr floats.func_${func.name}_fac1")
|
asmgen.out(" jsr floats.func_${func.name}_fac1")
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +603,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
DataType.FLOAT -> asmgen.out(" jsr floats.func_sign_f_into_A")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -624,7 +624,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
DataType.ARRAY_F -> asmgen.out(" jsr floats.func_${function.name}_f_into_A")
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -644,23 +644,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
DataType.ARRAY_UB, DataType.STR -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_ub_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_b_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_${function.name}_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
asmgen.out(" jsr floats.func_${function.name}_f_fac1")
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
@ -683,23 +683,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt.getOr(DataType.UNDEFINED)) {
|
when (dt.getOr(DataType.UNDEFINED)) {
|
||||||
DataType.ARRAY_UB, DataType.STR -> {
|
DataType.ARRAY_UB, DataType.STR -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_ub_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_b_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_uw_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
asmgen.out(" jsr prog8_lib.func_sum_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
asmgen.out(" jsr floats.func_sum_f_fac1")
|
asmgen.out(" jsr floats.func_sum_f_fac1")
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type $dt")
|
else -> throw AssemblyError("weird type $dt")
|
||||||
}
|
}
|
||||||
@ -785,7 +785,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
&& (firstOffset is NumericLiteralValue || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
&& (firstOffset is NumericLiteralValue || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
||||||
&& (secondOffset is NumericLiteralValue || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
&& (secondOffset is NumericLiteralValue || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
||||||
) {
|
) {
|
||||||
val pointerVar = firstExpr.left as IdentifierReference
|
|
||||||
if(firstOffset is NumericLiteralValue && secondOffset is NumericLiteralValue) {
|
if(firstOffset is NumericLiteralValue && secondOffset is NumericLiteralValue) {
|
||||||
if(firstOffset!=secondOffset) {
|
if(firstOffset!=secondOffset) {
|
||||||
swapArrayValues(
|
swapArrayValues(
|
||||||
@ -876,21 +875,23 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.translateNormalAssignment(assignSecond)
|
asmgen.translateNormalAssignment(assignSecond)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
// via evaluation stack
|
// via temp variable and FAC1
|
||||||
asmgen.translateExpression(first)
|
asmgen.assignExpressionTo(first, AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.FLOAT, first.definingSubroutine, "floats.tempvar_swap_float"))
|
||||||
asmgen.translateExpression(second)
|
asmgen.assignExpressionTo(second, AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, null, register=RegisterOrPair.FAC1))
|
||||||
val assignFirst = AsmAssignment(
|
asmgen.translateNormalAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, datatype, register = RegisterOrPair.FAC1),
|
||||||
targetFromExpr(first, datatype),
|
targetFromExpr(first, datatype),
|
||||||
false, program.memsizer, first.position
|
false, program.memsizer, first.position
|
||||||
|
)
|
||||||
)
|
)
|
||||||
val assignSecond = AsmAssignment(
|
asmgen.translateNormalAssignment(
|
||||||
AsmAssignSource(SourceStorageKind.STACK, program, asmgen, DataType.FLOAT),
|
AsmAssignment(
|
||||||
|
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, "floats.tempvar_swap_float"),
|
||||||
targetFromExpr(second, datatype),
|
targetFromExpr(second, datatype),
|
||||||
false, program.memsizer, second.position
|
false, program.memsizer, second.position
|
||||||
|
)
|
||||||
)
|
)
|
||||||
asmgen.translateNormalAssignment(assignFirst)
|
|
||||||
asmgen.translateNormalAssignment(assignSecond)
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird swap dt")
|
else -> throw AssemblyError("weird swap dt")
|
||||||
}
|
}
|
||||||
@ -1140,15 +1141,15 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
asmgen.out(" jsr prog8_lib.abs_b_into_A")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
asmgen.out(" jsr prog8_lib.abs_w_into_AY")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
asmgen.out(" jsr floats.abs_f_fac1")
|
asmgen.out(" jsr floats.abs_f_fac1")
|
||||||
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, scope, program, asmgen))
|
assignAsmGen.assignFAC1float(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.FAC1, true, scope, program, asmgen))
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -1162,7 +1163,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
asmgen.out(" jsr prog8_lib.func_rnd_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr math.randbyte")
|
asmgen.out(" jsr math.randbyte")
|
||||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, scope, program, asmgen), CpuRegister.A)
|
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.A, false, scope, program, asmgen), CpuRegister.A)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"rndw" -> {
|
"rndw" -> {
|
||||||
@ -1170,7 +1171,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
asmgen.out(" jsr prog8_lib.func_rndw_stack")
|
asmgen.out(" jsr prog8_lib.func_rndw_stack")
|
||||||
else {
|
else {
|
||||||
asmgen.out(" jsr math.randword")
|
asmgen.out(" jsr math.randword")
|
||||||
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, scope, program, asmgen), RegisterOrPair.AY)
|
assignAsmGen.assignRegisterpairWord(AsmAssignTarget.fromRegisters(resultRegister ?: RegisterOrPair.AY, false, scope, program, asmgen), RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("wrong func")
|
else -> throw AssemblyError("wrong func")
|
||||||
@ -1533,7 +1534,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
AsmAssignSource.fromAstSource(value, program, asmgen)
|
AsmAssignSource.fromAstSource(value, program, asmgen)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
val tgt = AsmAssignTarget.fromRegisters(conv.reg, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(conv.reg!!, false, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, value.position)
|
||||||
asmgen.translateNormalAssignment(assign)
|
asmgen.translateNormalAssignment(assign)
|
||||||
}
|
}
|
@ -3,19 +3,27 @@ package prog8.compiler.target.cpu6502.codegen
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.ArrayIndex
|
|
||||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compiler.target.CpuType
|
import prog8.compilerinterface.CpuType
|
||||||
import prog8.compiler.target.subroutineFloatEvalResultVar1
|
import prog8.compilerinterface.subroutineFloatEvalResultVar1
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ExpressionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
internal fun translateExpression(expression: Expression) {
|
@Deprecated("avoid calling this as it generates slow evalstack based code")
|
||||||
|
internal fun translateExpression(expression:Expression) {
|
||||||
|
if (this.asmgen.options.slowCodegenWarnings) {
|
||||||
|
asmgen.errors.warn("slow stack evaluation used for expression $expression", expression.position)
|
||||||
|
}
|
||||||
|
translateExpressionInternal(expression)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun translateExpressionInternal(expression: Expression) {
|
||||||
|
|
||||||
when(expression) {
|
when(expression) {
|
||||||
is PrefixExpression -> translateExpression(expression)
|
is PrefixExpression -> translateExpression(expression)
|
||||||
is BinaryExpression -> translateExpression(expression)
|
is BinaryExpression -> translateExpression(expression)
|
||||||
@ -28,17 +36,21 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
is FunctionCall -> translateFunctionCallResultOntoStack(expression)
|
is FunctionCall -> translateFunctionCallResultOntoStack(expression)
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
|
is CharLiteral -> throw AssemblyError("charliteral should have been replaced by ubyte using certain encoding")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateComparisonExpressionWithJumpIfFalse(expr: BinaryExpression, jumpIfFalseLabel: String) {
|
internal fun translateComparisonExpressionWithJumpIfFalse(expr: BinaryExpression, jumpIfFalseLabel: String) {
|
||||||
// This is a helper routine called from while, do-util, and if expressions to generate optimized conditional branching code.
|
// This is a helper routine called from while, do-util, and if expressions to generate optimized conditional branching code.
|
||||||
// First, if it is of the form: <constvalue> <comparison> X , then flip the expression so the constant is always the right operand.
|
// First, if it is of the form: <constvalue> <comparison> X , then flip the expression so the constant is always the right operand.
|
||||||
|
|
||||||
var left = expr.left
|
var left = expr.left
|
||||||
var right = expr.right
|
var right = expr.right
|
||||||
var operator = expr.operator
|
var operator = expr.operator
|
||||||
var leftConstVal = left.constValue(program)
|
var leftConstVal = left.constValue(program)
|
||||||
var rightConstVal = right.constValue(program)
|
var rightConstVal = right.constValue(program)
|
||||||
|
|
||||||
|
// make sure the constant value is on the right of the comparison expression
|
||||||
if(leftConstVal!=null) {
|
if(leftConstVal!=null) {
|
||||||
val tmp = left
|
val tmp = left
|
||||||
left = right
|
left = right
|
||||||
@ -54,32 +66,113 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val idt = left.inferType(program)
|
if (rightConstVal!=null && rightConstVal.number.toDouble() == 0.0)
|
||||||
if(!idt.isKnown)
|
jumpIfZeroOrNot(left, operator, jumpIfFalseLabel)
|
||||||
throw AssemblyError("unknown dt")
|
else
|
||||||
val dt = idt.getOr(DataType.UNDEFINED)
|
jumpIfComparison(left, operator, right, jumpIfFalseLabel, leftConstVal, rightConstVal)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jumpIfZeroOrNot(
|
||||||
|
left: Expression,
|
||||||
|
operator: String,
|
||||||
|
jumpIfFalseLabel: String
|
||||||
|
) {
|
||||||
|
when(val dt = left.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
|
DataType.UBYTE, DataType.UWORD -> {
|
||||||
|
if(operator=="<") {
|
||||||
|
asmgen.out(" jmp $jumpIfFalseLabel")
|
||||||
|
return
|
||||||
|
} else if(operator==">=") {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
if(dt==DataType.UBYTE) {
|
||||||
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.A, false)
|
||||||
|
if (left is FunctionCall && !left.isSimple)
|
||||||
|
asmgen.out(" cmp #0")
|
||||||
|
} else {
|
||||||
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, false)
|
||||||
|
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
|
||||||
|
}
|
||||||
|
when (operator) {
|
||||||
|
"==" -> asmgen.out(" bne $jumpIfFalseLabel")
|
||||||
|
"!=" -> asmgen.out(" beq $jumpIfFalseLabel")
|
||||||
|
">" -> asmgen.out(" beq $jumpIfFalseLabel")
|
||||||
|
"<=" -> asmgen.out(" bne $jumpIfFalseLabel")
|
||||||
|
else -> throw AssemblyError("invalid comparison operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.A, true)
|
||||||
|
if (left is FunctionCall && !left.isSimple)
|
||||||
|
asmgen.out(" cmp #0")
|
||||||
|
when (operator) {
|
||||||
|
"==" -> asmgen.out(" bne $jumpIfFalseLabel")
|
||||||
|
"!=" -> asmgen.out(" beq $jumpIfFalseLabel")
|
||||||
|
">" -> asmgen.out(" beq $jumpIfFalseLabel | bmi $jumpIfFalseLabel")
|
||||||
|
"<" -> asmgen.out(" bpl $jumpIfFalseLabel")
|
||||||
|
">=" -> asmgen.out(" bmi $jumpIfFalseLabel")
|
||||||
|
"<=" -> asmgen.out("""
|
||||||
|
beq +
|
||||||
|
bpl $jumpIfFalseLabel
|
||||||
|
+ """)
|
||||||
|
else -> throw AssemblyError("invalid comparison operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY, true)
|
||||||
|
when (operator) {
|
||||||
|
"==" -> asmgen.out(" bne $jumpIfFalseLabel | cpy #0 | bne $jumpIfFalseLabel")
|
||||||
|
"!=" -> asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1 | beq $jumpIfFalseLabel")
|
||||||
|
">" -> asmgen.out("""
|
||||||
|
cpy #0
|
||||||
|
bmi $jumpIfFalseLabel
|
||||||
|
bne +
|
||||||
|
cmp #0
|
||||||
|
beq $jumpIfFalseLabel
|
||||||
|
+ """)
|
||||||
|
"<" -> asmgen.out(" cpy #0 | bpl $jumpIfFalseLabel")
|
||||||
|
">=" -> asmgen.out(" cpy #0 | bmi $jumpIfFalseLabel")
|
||||||
|
"<=" -> asmgen.out("""
|
||||||
|
cpy #0
|
||||||
|
bmi +
|
||||||
|
bne $jumpIfFalseLabel
|
||||||
|
cmp #0
|
||||||
|
bne $jumpIfFalseLabel
|
||||||
|
+ """)
|
||||||
|
else -> throw AssemblyError("invalid comparison operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.FLOAT -> {
|
||||||
|
asmgen.assignExpressionToRegister(left, RegisterOrPair.FAC1)
|
||||||
|
asmgen.out(" jsr floats.SIGN") // SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||||
|
when (operator) {
|
||||||
|
"==" -> asmgen.out(" bne $jumpIfFalseLabel")
|
||||||
|
"!=" -> asmgen.out(" beq $jumpIfFalseLabel")
|
||||||
|
">" -> asmgen.out(" bmi $jumpIfFalseLabel | beq $jumpIfFalseLabel")
|
||||||
|
"<" -> asmgen.out(" bpl $jumpIfFalseLabel")
|
||||||
|
">=" -> asmgen.out(" bmi $jumpIfFalseLabel")
|
||||||
|
"<=" -> asmgen.out(" cmp #1 | beq $jumpIfFalseLabel")
|
||||||
|
else -> throw AssemblyError("invalid comparison operator $operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
throw AssemblyError("invalid dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun jumpIfComparison(
|
||||||
|
left: Expression,
|
||||||
|
operator: String,
|
||||||
|
right: Expression,
|
||||||
|
jumpIfFalseLabel: String,
|
||||||
|
leftConstVal: NumericLiteralValue?,
|
||||||
|
rightConstVal: NumericLiteralValue?
|
||||||
|
) {
|
||||||
|
val dt = left.inferType(program).getOrElse { throw AssemblyError("unknown dt") }
|
||||||
|
|
||||||
when (operator) {
|
when (operator) {
|
||||||
"==" -> {
|
"==" -> {
|
||||||
// if the left operand is an expression, and the right is 0, we can just evaluate that expression,
|
|
||||||
// and use the result value directly to determine the boolean result. Shortcut only for integers.
|
|
||||||
if(rightConstVal?.number?.toDouble() == 0.0) {
|
|
||||||
if(dt in ByteDatatypes) {
|
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.A)
|
|
||||||
if(left is FunctionCall && !left.isSimple)
|
|
||||||
asmgen.out(" cmp #0")
|
|
||||||
asmgen.out(" bne $jumpIfFalseLabel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else if(dt in WordDatatypes) {
|
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
|
|
||||||
asmgen.out("""
|
|
||||||
sty P8ZP_SCRATCH_B1
|
|
||||||
ora P8ZP_SCRATCH_B1
|
|
||||||
bne $jumpIfFalseLabel""")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> translateByteEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in ByteDatatypes -> translateByteEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
in WordDatatypes -> translateWordEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in WordDatatypes -> translateWordEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
@ -89,26 +182,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
"!=" -> {
|
"!=" -> {
|
||||||
// if the left operand is an expression, and the right is 0, we can just evaluate that expression,
|
|
||||||
// and use the result value directly to determine the boolean result. Shortcut only for integers.
|
|
||||||
if(rightConstVal?.number?.toDouble() == 0.0) {
|
|
||||||
if(dt in ByteDatatypes) {
|
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.A)
|
|
||||||
if(left is FunctionCall && !left.isSimple)
|
|
||||||
asmgen.out(" cmp #0")
|
|
||||||
asmgen.out(" beq $jumpIfFalseLabel")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
else if(dt in WordDatatypes) {
|
|
||||||
asmgen.assignExpressionToRegister(left, RegisterOrPair.AY)
|
|
||||||
asmgen.out("""
|
|
||||||
sty P8ZP_SCRATCH_B1
|
|
||||||
ora P8ZP_SCRATCH_B1
|
|
||||||
beq $jumpIfFalseLabel""")
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
when (dt) {
|
when (dt) {
|
||||||
in ByteDatatypes -> translateByteNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in ByteDatatypes -> translateByteNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
in WordDatatypes -> translateWordNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
in WordDatatypes -> translateWordNotEqualsJump(left, right, leftConstVal, rightConstVal, jumpIfFalseLabel)
|
||||||
@ -1625,7 +1698,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(typecast: TypecastExpression) {
|
private fun translateExpression(typecast: TypecastExpression) {
|
||||||
translateExpression(typecast.expression)
|
translateExpressionInternal(typecast.expression)
|
||||||
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
when(typecast.expression.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when(typecast.type) {
|
when(typecast.type) {
|
||||||
@ -1775,7 +1848,8 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: BinaryExpression) {
|
private fun translateExpression(expr: BinaryExpression) {
|
||||||
// TODO needs to use optimized assembly generation like the assignment instructions. But avoid code duplication.... rewrite all expressions into assignment form?
|
// Uses evalstack to evaluate the given expression.
|
||||||
|
// TODO we're slowly reducing the number of places where this is called and instead replace that by more efficient assignment-form code (using temp var or register for instance).
|
||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
val rightIDt = expr.right.inferType(program)
|
val rightIDt = expr.right.inferType(program)
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
@ -1784,14 +1858,13 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
val leftDt = leftIDt.getOr(DataType.UNDEFINED)
|
||||||
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
|
val rightDt = rightIDt.getOr(DataType.UNDEFINED)
|
||||||
// see if we can apply some optimized routines
|
// see if we can apply some optimized routines
|
||||||
// TODO avoid using evaluation on stack everywhere
|
|
||||||
when(expr.operator) {
|
when(expr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
val leftVal = expr.left.constValue(program)?.number?.toInt()
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
if (leftVal!=null && leftVal in -4..4) {
|
if (leftVal!=null && leftVal in -4..4) {
|
||||||
translateExpression(expr.right)
|
translateExpressionInternal(expr.right)
|
||||||
if(rightDt in ByteDatatypes) {
|
if(rightDt in ByteDatatypes) {
|
||||||
val incdec = if(leftVal<0) "dec" else "inc"
|
val incdec = if(leftVal<0) "dec" else "inc"
|
||||||
repeat(leftVal.absoluteValue) {
|
repeat(leftVal.absoluteValue) {
|
||||||
@ -1821,7 +1894,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
else if (rightVal!=null && rightVal in -4..4)
|
else if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "dec" else "inc"
|
val incdec = if(rightVal<0) "dec" else "inc"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -1856,7 +1929,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
if (rightVal!=null && rightVal in -4..4)
|
if (rightVal!=null && rightVal in -4..4)
|
||||||
{
|
{
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
val incdec = if(rightVal<0) "inc" else "dec"
|
val incdec = if(rightVal<0) "inc" else "dec"
|
||||||
repeat(rightVal.absoluteValue) {
|
repeat(rightVal.absoluteValue) {
|
||||||
@ -1889,7 +1962,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
">>" -> {
|
">>" -> {
|
||||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
@ -1960,7 +2033,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
"<<" -> {
|
"<<" -> {
|
||||||
val amount = expr.right.constValue(program)?.number?.toInt()
|
val amount = expr.right.constValue(program)?.number?.toInt()
|
||||||
if(amount!=null) {
|
if(amount!=null) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
if (leftDt in ByteDatatypes) {
|
if (leftDt in ByteDatatypes) {
|
||||||
if (amount <= 2)
|
if (amount <= 2)
|
||||||
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
repeat(amount) { asmgen.out(" asl P8ESTACK_LO+1,x") }
|
||||||
@ -1997,7 +2070,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
if(amount==2) {
|
if(amount==2) {
|
||||||
// optimize x*2 common case
|
// optimize x*2 common case
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
if(leftDt in ByteDatatypes) {
|
if(leftDt in ByteDatatypes) {
|
||||||
asmgen.out(" asl P8ESTACK_LO+1,x")
|
asmgen.out(" asl P8ESTACK_LO+1,x")
|
||||||
} else {
|
} else {
|
||||||
@ -2008,38 +2081,38 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
when(rightDt) {
|
when(rightDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
if(amount in asmgen.optimizedByteMultiplications) {
|
if(amount in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
asmgen.out(" jsr math.stack_mul_byte_$amount")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedByteMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_b | jsr math.stack_mul_byte_${amount.absoluteValue}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
if(amount in asmgen.optimizedWordMultiplications) {
|
if(amount in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr math.stack_mul_word_$amount")
|
asmgen.out(" jsr math.stack_mul_word_$amount")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
if(amount.absoluteValue in asmgen.optimizedWordMultiplications) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
asmgen.out(" jsr prog8_lib.neg_w | jsr math.stack_mul_word_${amount.absoluteValue}")
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
@ -2053,7 +2126,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
if(leftDt in IntegerDatatypes && rightDt in IntegerDatatypes) {
|
||||||
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
val rightVal = expr.right.constValue(program)?.number?.toInt()
|
||||||
if(rightVal!=null && rightVal==2) {
|
if(rightVal!=null && rightVal==2) {
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
when(leftDt) {
|
when(leftDt) {
|
||||||
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
DataType.UBYTE -> asmgen.out(" lsr P8ESTACK_LO+1,x")
|
||||||
DataType.BYTE -> asmgen.out(" asl P8ESTACK_LO+1,x | ror P8ESTACK_LO+1,x")
|
DataType.BYTE -> asmgen.out(" asl P8ESTACK_LO+1,x | ror P8ESTACK_LO+1,x")
|
||||||
@ -2075,9 +2148,9 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateCompareStrings(expr.left, expr.operator, expr.right)
|
translateCompareStrings(expr.left, expr.operator, expr.right)
|
||||||
}
|
}
|
||||||
else {
|
else {
|
||||||
// the general, non-optimized cases TODO optimize more cases....
|
// the general, non-optimized cases TODO optimize more cases.... (or one day just don't use the evalstack at all anymore)
|
||||||
translateExpression(expr.left)
|
translateExpressionInternal(expr.left)
|
||||||
translateExpression(expr.right)
|
translateExpressionInternal(expr.right)
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
in ByteDatatypes -> translateBinaryOperatorBytes(expr.operator, leftDt)
|
||||||
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
in WordDatatypes -> translateBinaryOperatorWords(expr.operator, leftDt)
|
||||||
@ -2104,7 +2177,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translateExpression(expr: PrefixExpression) {
|
private fun translateExpression(expr: PrefixExpression) {
|
||||||
translateExpression(expr.expression)
|
translateExpressionInternal(expr.expression)
|
||||||
val itype = expr.inferType(program)
|
val itype = expr.inferType(program)
|
||||||
if(!itype.isKnown)
|
if(!itype.isKnown)
|
||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
@ -2189,8 +2262,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun translateExpression(indexer: ArrayIndex) = asmgen.translateExpression(indexer.indexExpr)
|
|
||||||
|
|
||||||
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
private fun translateBinaryOperatorBytes(operator: String, types: DataType) {
|
||||||
when(operator) {
|
when(operator) {
|
||||||
"**" -> throw AssemblyError("** operator requires floats")
|
"**" -> throw AssemblyError("** operator requires floats")
|
@ -8,8 +8,8 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.RangeExpr
|
import prog8.ast.expressions.RangeExpr
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.astprocessing.toConstantIntegerRange
|
import prog8.compilerinterface.toConstantIntegerRange
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class ForLoopsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -20,7 +20,7 @@ internal class ForLoopsAsmGen(private val program: Program, private val asmgen:
|
|||||||
throw AssemblyError("unknown dt")
|
throw AssemblyError("unknown dt")
|
||||||
when(stmt.iterable) {
|
when(stmt.iterable) {
|
||||||
is RangeExpr -> {
|
is RangeExpr -> {
|
||||||
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange(asmgen.options.compTarget)
|
val range = (stmt.iterable as RangeExpr).toConstantIntegerRange()
|
||||||
if(range==null) {
|
if(range==null) {
|
||||||
translateForOverNonconstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
translateForOverNonconstRange(stmt, iterableDt.getOr(DataType.UNDEFINED), stmt.iterable as RangeExpr)
|
||||||
} else {
|
} else {
|
@ -9,12 +9,12 @@ import prog8.ast.statements.InlineAssembly
|
|||||||
import prog8.ast.statements.RegisterOrStatusflag
|
import prog8.ast.statements.RegisterOrStatusflag
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignSource
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignTarget
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
import prog8.compiler.target.cpu6502.codegen.assignment.AsmAssignment
|
||||||
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
|
import prog8.compiler.target.cpu6502.codegen.assignment.TargetStorageKind
|
||||||
|
import prog8.compilerinterface.CpuType
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -64,7 +64,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
argumentViaVariable(sub, arg.first, arg.second)
|
argumentViaVariable(sub, arg.first, arg.second)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// via registers
|
require(sub.isAsmSubroutine)
|
||||||
if(sub.parameters.size==1) {
|
if(sub.parameters.size==1) {
|
||||||
// just a single parameter, no risk of clobbering registers
|
// just a single parameter, no risk of clobbering registers
|
||||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
argumentViaRegister(sub, IndexedValue(0, sub.parameters.single()), stmt.args[0])
|
||||||
@ -135,14 +135,25 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
private fun registerArgsViaStackEvaluation(stmt: IFunctionCall, sub: Subroutine) {
|
||||||
// this is called when one or more of the arguments are 'complex' and
|
// this is called when one or more of the arguments are 'complex' and
|
||||||
// cannot be assigned to a register easily or risk clobbering other registers.
|
// cannot be assigned to a register easily or risk clobbering other registers.
|
||||||
|
// TODO find another way to prepare the arguments, without using the eval stack
|
||||||
|
|
||||||
if(sub.parameters.isEmpty())
|
if(sub.parameters.isEmpty())
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
// 1. load all arguments reversed onto the stack: first arg goes last (is on top).
|
||||||
|
|
||||||
for (arg in stmt.args.reversed())
|
for (arg in stmt.args.reversed())
|
||||||
asmgen.translateExpression(arg)
|
asmgen.translateExpression(arg)
|
||||||
|
|
||||||
|
// TODO here's an alternative to the above, but for now generates bigger code due to intermediate register steps:
|
||||||
|
// for (arg in stmt.args.reversed()) {
|
||||||
|
// // note this stuff below is needed to (eventually) avoid calling asmgen.translateExpression()
|
||||||
|
// // TODO also This STILL requires the translateNormalAssignment() to be fixed to avoid stack eval for expressions...
|
||||||
|
// val dt = arg.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
|
// asmgen.assignExpressionTo(arg, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, dt, sub))
|
||||||
|
// }
|
||||||
|
|
||||||
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
var argForCarry: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
var argForXregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
var argForAregister: IndexedValue<Pair<Expression, RegisterOrStatusflag>>? = null
|
||||||
@ -325,7 +336,7 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
|||||||
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
if(parameter.value.type in ByteDatatypes && (register==RegisterOrPair.AX || register == RegisterOrPair.AY || register==RegisterOrPair.XY || register in Cx16VirtualRegisters))
|
||||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
|
AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, parameter.value.type, sub, register = register)
|
||||||
else
|
else
|
||||||
AsmAssignTarget.fromRegisters(register, sub, program, asmgen)
|
AsmAssignTarget.fromRegisters(register, false, sub, program, asmgen)
|
||||||
val src = if(valueDt in PassByReferenceDatatypes) {
|
val src = if(valueDt in PassByReferenceDatatypes) {
|
||||||
if(value is IdentifierReference) {
|
if(value is IdentifierReference) {
|
||||||
val addr = AddressOf(value, Position.DUMMY)
|
val addr = AddressOf(value, Position.DUMMY)
|
@ -6,7 +6,7 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
|
|
||||||
|
|
||||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
@ -1,11 +1,11 @@
|
|||||||
package prog8.compiler.target.cpu6502.codegen.assignment
|
package prog8.compiler.target.cpu6502.codegen.assignment
|
||||||
|
|
||||||
import prog8.ast.IMemSizer
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compilerinterface.IMemSizer
|
||||||
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
|
|
||||||
|
|
||||||
@ -41,11 +41,12 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
{
|
{
|
||||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toInt() ?: 0}
|
||||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
val constArrayIndexValue by lazy { array?.indexer?.constIndex() }
|
||||||
val asmVarname: String
|
val asmVarname: String by lazy {
|
||||||
get() = if(array==null)
|
if (array == null)
|
||||||
variableAsmName!!
|
variableAsmName!!
|
||||||
else
|
else
|
||||||
asmgen.asmVariableName(array.arrayvar)
|
asmgen.asmVariableName(array.arrayvar)
|
||||||
|
}
|
||||||
|
|
||||||
lateinit var origAssign: AsmAssignment
|
lateinit var origAssign: AsmAssignment
|
||||||
|
|
||||||
@ -68,14 +69,14 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun fromRegisters(registers: RegisterOrPair, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
fun fromRegisters(registers: RegisterOrPair, signed: Boolean, scope: Subroutine?, program: Program, asmgen: AsmGen): AsmAssignTarget =
|
||||||
when(registers) {
|
when(registers) {
|
||||||
RegisterOrPair.A,
|
RegisterOrPair.A,
|
||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UBYTE, scope, register = registers)
|
RegisterOrPair.Y -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.BYTE else DataType.UBYTE, scope, register = registers)
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
RegisterOrPair.XY -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||||
RegisterOrPair.FAC1,
|
RegisterOrPair.FAC1,
|
||||||
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
RegisterOrPair.FAC2 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, scope, register = registers)
|
||||||
RegisterOrPair.R0,
|
RegisterOrPair.R0,
|
||||||
@ -93,7 +94,7 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
|||||||
RegisterOrPair.R12,
|
RegisterOrPair.R12,
|
||||||
RegisterOrPair.R13,
|
RegisterOrPair.R13,
|
||||||
RegisterOrPair.R14,
|
RegisterOrPair.R14,
|
||||||
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.UWORD, scope, register = registers)
|
RegisterOrPair.R15 -> AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, if(signed) DataType.WORD else DataType.UWORD, scope, register = registers)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -5,12 +5,12 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
|
||||||
import prog8.compiler.functions.builtinFunctionReturnType
|
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
||||||
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
|
import prog8.compilerinterface.CpuType
|
||||||
|
import prog8.compilerinterface.builtinFunctionReturnType
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen,
|
||||||
@ -33,6 +33,11 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun translateNormalAssignment(assign: AsmAssignment) {
|
fun translateNormalAssignment(assign: AsmAssignment) {
|
||||||
|
if(assign.isAugmentable) {
|
||||||
|
augmentableAsmGen.translate(assign)
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
when(assign.source.kind) {
|
when(assign.source.kind) {
|
||||||
SourceStorageKind.LITERALNUMBER -> {
|
SourceStorageKind.LITERALNUMBER -> {
|
||||||
// simple case: assign a constant number
|
// simple case: assign a constant number
|
||||||
@ -171,13 +176,13 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
// copy the actual string result into the target string variable
|
// copy the actual string result into the target string variable
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
pha
|
pha
|
||||||
lda #<${assign.target.asmVarname}
|
lda #<${assign.target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
lda #>${assign.target.asmVarname}
|
lda #>${assign.target.asmVarname}
|
||||||
sta P8ZP_SCRATCH_W1+1
|
sta P8ZP_SCRATCH_W1+1
|
||||||
pla
|
pla
|
||||||
jsr prog8_lib.strcpy""")
|
jsr prog8_lib.strcpy""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target dt")
|
else -> throw AssemblyError("weird target dt")
|
||||||
}
|
}
|
||||||
@ -249,30 +254,40 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
is PrefixExpression -> {
|
||||||
|
// first assign the value to the target then apply the operator in place on the target.
|
||||||
|
translateNormalAssignment(AsmAssignment(
|
||||||
|
AsmAssignSource.fromAstSource(value.expression, program, asmgen),
|
||||||
|
assign.target,
|
||||||
|
false, program.memsizer, assign.position
|
||||||
|
))
|
||||||
|
when(value.operator) {
|
||||||
|
"+" -> {}
|
||||||
|
"-" -> augmentableAsmGen.inplaceNegate(assign.target, assign.target.datatype)
|
||||||
|
"~" -> augmentableAsmGen.inplaceInvert(assign.target, assign.target.datatype)
|
||||||
|
"not" -> augmentableAsmGen.inplaceBooleanNot(assign.target, assign.target.datatype)
|
||||||
|
else -> throw AssemblyError("invalid prefix operator")
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// Everything else just evaluate via the stack.
|
// Everything else just evaluate via the stack.
|
||||||
// (we can't use the assignment helper functions to do it via registers here,
|
// (we can't use the assignment helper functions (assignExpressionTo...) to do it via registers here,
|
||||||
// because the code here is the implementation of exactly that...)
|
// because the code here is the implementation of exactly that...)
|
||||||
if (value.parent is Return) {
|
// TODO FIX THIS... by using a temp var? so that it becomes augmentable assignment expression?
|
||||||
if (this.asmgen.options.slowCodegenWarnings)
|
asmgen.translateExpression(value)
|
||||||
println("warning: slow stack evaluation used for return: $value target=${assign.target.kind} at ${value.position}")
|
|
||||||
}
|
|
||||||
exprAsmgen.translateExpression(value)
|
|
||||||
if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
if (assign.target.datatype in WordDatatypes && assign.source.datatype in ByteDatatypes)
|
||||||
asmgen.signExtendStackLsb(assign.source.datatype)
|
asmgen.signExtendStackLsb(assign.source.datatype)
|
||||||
assignStackValue(assign.target)
|
if(assign.target.kind!=TargetStorageKind.STACK || assign.target.datatype != assign.source.datatype)
|
||||||
|
assignStackValue(assign.target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
SourceStorageKind.REGISTER -> {
|
SourceStorageKind.REGISTER -> {
|
||||||
when(assign.source.datatype) {
|
asmgen.assignRegister(assign.source.register!!, assign.target)
|
||||||
DataType.UBYTE -> assignRegisterByte(assign.target, assign.source.register!!.asCpuRegister())
|
|
||||||
DataType.UWORD -> assignRegisterpairWord(assign.target, assign.source.register!!)
|
|
||||||
else -> throw AssemblyError("invalid register dt")
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
SourceStorageKind.STACK -> {
|
SourceStorageKind.STACK -> {
|
||||||
assignStackValue(assign.target)
|
if(assign.target.kind!=TargetStorageKind.STACK || assign.target.datatype != assign.source.datatype)
|
||||||
|
assignStackValue(assign.target)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -364,16 +379,16 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
|
|
||||||
when (valueDt) {
|
when (valueDt) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.A)
|
assignExpressionToRegister(value, RegisterOrPair.A, valueDt==DataType.BYTE)
|
||||||
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
|
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.A, valueDt)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.AY)
|
assignExpressionToRegister(value, RegisterOrPair.AY, valueDt==DataType.WORD)
|
||||||
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, valueDt)
|
assignTypeCastedRegisters(target.asmVarname, targetDt, RegisterOrPair.AY, valueDt)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
assignExpressionToRegister(value, RegisterOrPair.FAC1)
|
assignExpressionToRegister(value, RegisterOrPair.FAC1, true)
|
||||||
assignTypecastedFloatFAC1(target.asmVarname, targetDt)
|
assignTypeCastedFloatFAC1(target.asmVarname, targetDt)
|
||||||
}
|
}
|
||||||
in PassByReferenceDatatypes -> {
|
in PassByReferenceDatatypes -> {
|
||||||
// str/array value cast (most likely to UWORD, take address-of)
|
// str/array value cast (most likely to UWORD, take address-of)
|
||||||
@ -399,14 +414,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.X,
|
RegisterOrPair.X,
|
||||||
RegisterOrPair.Y -> {
|
RegisterOrPair.Y -> {
|
||||||
// 'cast' an ubyte value to a byte register; no cast needed at all
|
// 'cast' an ubyte value to a byte register; no cast needed at all
|
||||||
return assignExpressionToRegister(value, target.register)
|
return assignExpressionToRegister(value, target.register, false)
|
||||||
}
|
}
|
||||||
RegisterOrPair.AX,
|
RegisterOrPair.AX,
|
||||||
RegisterOrPair.AY,
|
RegisterOrPair.AY,
|
||||||
RegisterOrPair.XY,
|
RegisterOrPair.XY,
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
// cast an ubyte value to a 16 bits register, just assign it and make use of the value extension
|
// cast an ubyte value to a 16 bits register, just assign it and make use of the value extension
|
||||||
return assignExpressionToRegister(value, target.register!!)
|
return assignExpressionToRegister(value, target.register!!, false)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
@ -424,19 +439,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
RegisterOrPair.XY,
|
RegisterOrPair.XY,
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
// 'cast' uword into a 16 bits register, just assign it
|
// 'cast' uword into a 16 bits register, just assign it
|
||||||
return assignExpressionToRegister(value, target.register!!)
|
return assignExpressionToRegister(value, target.register!!, false)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// give up, do it via eval stack
|
// No more special optmized cases yet. Do the rest via more complex evaluation
|
||||||
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
// note: cannot use assignTypeCastedValue because that is ourselves :P
|
||||||
// TODO optimize typecasts for more special cases?
|
asmgen.assignExpressionTo(origTypeCastExpression, target)
|
||||||
if(this.asmgen.options.slowCodegenWarnings)
|
|
||||||
println("warning: slow stack evaluation used for typecast: $value into $targetDt (target=${target.kind} at ${value.position}")
|
|
||||||
asmgen.translateExpression(origTypeCastExpression) // this performs the actual type cast in translateExpression(Typecast)
|
|
||||||
assignStackValue(target)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {
|
private fun assignCastViaLsbFunc(value: Expression, target: AsmAssignTarget) {
|
||||||
@ -447,7 +458,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun assignTypecastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
private fun assignTypeCastedFloatFAC1(targetAsmVarName: String, targetDt: DataType) {
|
||||||
|
|
||||||
if(targetDt==DataType.FLOAT)
|
if(targetDt==DataType.FLOAT)
|
||||||
throw AssemblyError("typecast to identical type")
|
throw AssemblyError("typecast to identical type")
|
||||||
@ -954,7 +965,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
lda #<$sourceName
|
lda #<$sourceName
|
||||||
ldy #>$sourceName+1
|
ldy #>$sourceName+1
|
||||||
sta P8ESTACK_LO,x
|
sta P8ESTACK_LO,x
|
||||||
sty P8ESTACK_HI,x
|
tya
|
||||||
|
sta P8ESTACK_HI,x
|
||||||
dex""")
|
dex""")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("string-assign to weird target")
|
else -> throw AssemblyError("string-assign to weird target")
|
||||||
@ -1363,7 +1375,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
// these will be correctly typecasted from a byte to a word value
|
// these will be correctly typecasted from a byte to a word value
|
||||||
if(target.register !in Cx16VirtualRegisters &&
|
if(target.register !in Cx16VirtualRegisters &&
|
||||||
target.register!=RegisterOrPair.AX && target.register!=RegisterOrPair.AY && target.register!=RegisterOrPair.XY) {
|
target.register!=RegisterOrPair.AX && target.register!=RegisterOrPair.AY && target.register!=RegisterOrPair.XY) {
|
||||||
if(target.kind==TargetStorageKind.VARIABLE) {
|
if(target.kind== TargetStorageKind.VARIABLE) {
|
||||||
val parts = target.asmVarname.split('.')
|
val parts = target.asmVarname.split('.')
|
||||||
if (parts.size != 2 || parts[0] != "cx16")
|
if (parts.size != 2 || parts[0] != "cx16")
|
||||||
require(target.datatype in ByteDatatypes)
|
require(target.datatype in ByteDatatypes)
|
||||||
@ -1459,7 +1471,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
internal fun assignRegisterpairWord(target: AsmAssignTarget, regs: RegisterOrPair) {
|
||||||
require(target.datatype in NumericDatatypes)
|
require(target.datatype in NumericDatatypes || target.datatype in PassByReferenceDatatypes)
|
||||||
if(target.datatype==DataType.FLOAT)
|
if(target.datatype==DataType.FLOAT)
|
||||||
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
|
throw AssemblyError("float value should be from FAC1 not from registerpair memory pointer")
|
||||||
|
|
||||||
@ -1586,7 +1598,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
TargetStorageKind.STACK -> {
|
TargetStorageKind.STACK -> {
|
||||||
when(regs) {
|
when(regs) {
|
||||||
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | sty P8ESTACK_HI,x | dex")
|
RegisterOrPair.AY -> asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||||
RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't use X here")
|
RegisterOrPair.AX, RegisterOrPair.XY -> throw AssemblyError("can't use X here")
|
||||||
in Cx16VirtualRegisters -> {
|
in Cx16VirtualRegisters -> {
|
||||||
val srcReg = asmgen.asmSymbolName(regs)
|
val srcReg = asmgen.asmSymbolName(regs)
|
||||||
@ -2157,9 +2169,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair) {
|
internal fun assignExpressionToRegister(expr: Expression, register: RegisterOrPair, signed: Boolean) {
|
||||||
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
val src = AsmAssignSource.fromAstSource(expr, program, asmgen)
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, program, asmgen)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, expr.position)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
@ -2171,8 +2183,8 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair) {
|
internal fun assignVariableToRegister(asmVarName: String, register: RegisterOrPair, signed: Boolean) {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(register, null, program, asmgen)
|
val tgt = AsmAssignTarget.fromRegisters(register, signed, null, program, asmgen)
|
||||||
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
val src = AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, tgt.datatype, variableAsmName = asmVarName)
|
||||||
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
|
val assign = AsmAssignment(src, tgt, false, program.memsizer, Position.DUMMY)
|
||||||
translateNormalAssignment(assign)
|
translateNormalAssignment(assign)
|
@ -5,10 +5,10 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.target.AssemblyError
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
import prog8.compiler.target.cpu6502.codegen.ExpressionsAsmGen
|
||||||
|
import prog8.compilerinterface.CpuType
|
||||||
|
|
||||||
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
internal class AugmentableAssignmentAsmGen(private val program: Program,
|
||||||
private val assignmentAsmGen: AssignmentAsmGen,
|
private val assignmentAsmGen: AssignmentAsmGen,
|
||||||
@ -181,8 +181,9 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(memory.addressExpression)
|
// TODO OTHER EVALUATION HERE, don't use the estack
|
||||||
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1")
|
asmgen.assignExpressionTo(memory.addressExpression, AsmAssignTarget(TargetStorageKind.STACK, program, asmgen, DataType.UWORD, memory.definingSubroutine))
|
||||||
|
asmgen.out(" jsr prog8_lib.read_byte_from_address_on_stack | sta P8ZP_SCRATCH_B1") // TODO don't use estack to transfer the address to read from
|
||||||
when {
|
when {
|
||||||
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
valueLv != null -> inplaceModification_byte_litval_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, valueLv.toInt())
|
||||||
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
ident != null -> inplaceModification_byte_variable_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, ident)
|
||||||
@ -193,7 +194,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
else -> inplaceModification_byte_value_to_variable("P8ZP_SCRATCH_B1", DataType.UBYTE, operator, value)
|
||||||
}
|
}
|
||||||
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx")
|
asmgen.out(" lda P8ZP_SCRATCH_B1 | jsr prog8_lib.write_byte_to_address_on_stack | inx") // TODO don't use estack to transfer the address to read from
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -246,19 +247,37 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
indexVar!=null -> {
|
indexVar!=null -> {
|
||||||
when (target.datatype) {
|
when (target.datatype) {
|
||||||
in ByteDatatypes -> {
|
in ByteDatatypes -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.A, null, program, asmgen)
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.A,
|
||||||
|
target.datatype == DataType.BYTE, null,
|
||||||
|
program,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
assignmentAsmGen.assignRegisterByte(target, CpuRegister.A)
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
in WordDatatypes -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.AY, null, program, asmgen)
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.AY,
|
||||||
|
target.datatype == DataType.WORD, null,
|
||||||
|
program,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
assignmentAsmGen.assignRegisterpairWord(target, RegisterOrPair.AY)
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
val tgt = AsmAssignTarget.fromRegisters(RegisterOrPair.FAC1, null, program, asmgen)
|
val tgt =
|
||||||
|
AsmAssignTarget.fromRegisters(
|
||||||
|
RegisterOrPair.FAC1,
|
||||||
|
true, null,
|
||||||
|
program,
|
||||||
|
asmgen
|
||||||
|
)
|
||||||
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
val assign = AsmAssignment(target.origAssign.source, tgt, false, program.memsizer, value.position)
|
||||||
assignmentAsmGen.translateNormalAssignment(assign)
|
assignmentAsmGen.translateNormalAssignment(assign)
|
||||||
assignmentAsmGen.assignFAC1float(target)
|
assignmentAsmGen.assignFAC1float(target)
|
||||||
@ -1705,7 +1724,7 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceBooleanNot(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
@ -1749,9 +1768,30 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of ubyte array")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
when(target.register!!) {
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
RegisterOrPair.A -> asmgen.out("""
|
||||||
|
cmp #0
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+ eor #1""")
|
||||||
|
RegisterOrPair.X -> asmgen.out("""
|
||||||
|
txa
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+ eor #1
|
||||||
|
tax""")
|
||||||
|
RegisterOrPair.Y -> asmgen.out("""
|
||||||
|
tya
|
||||||
|
beq +
|
||||||
|
lda #1
|
||||||
|
+ eor #1
|
||||||
|
tay""")
|
||||||
|
else -> throw AssemblyError("invalid reg dt for byte not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for byte stack not")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place not of ubyte ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1767,17 +1807,56 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
lsr a
|
lsr a
|
||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory not")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place not of uword array")
|
when(target.register!!) {
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg not")
|
RegisterOrPair.AX -> {
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack not")
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
ora P8ZP_SCRATCH_REG
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
tax
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
RegisterOrPair.AY -> {
|
||||||
|
asmgen.out("""
|
||||||
|
sty P8ZP_SCRATCH_REG
|
||||||
|
ora P8ZP_SCRATCH_REG
|
||||||
|
beq +
|
||||||
|
lda #0
|
||||||
|
tay
|
||||||
|
beq ++
|
||||||
|
+ lda #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
RegisterOrPair.XY -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
tya
|
||||||
|
ora P8ZP_SCRATCH_REG
|
||||||
|
beq +
|
||||||
|
ldy #0
|
||||||
|
ldx #0
|
||||||
|
beq ++
|
||||||
|
+ ldx #1
|
||||||
|
+""")
|
||||||
|
}
|
||||||
|
in Cx16VirtualRegisters -> TODO()
|
||||||
|
else -> throw AssemblyError("invalid reg dt for word not")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> TODO("no asm gen for uword-memory not")
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for word stack not")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place not of uword for ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("boolean-not of invalid type")
|
else -> throw AssemblyError("boolean-not of invalid type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceInvert(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
@ -1812,9 +1891,16 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert ubyte array")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
when(target.register!!) {
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
RegisterOrPair.A -> asmgen.out(" eor #255")
|
||||||
|
RegisterOrPair.X -> asmgen.out(" txa | eor #255 | tax")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" tya | eor #255 | tay")
|
||||||
|
else -> throw AssemblyError("invalid reg dt for byte invert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for byte stack invert")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place invert ubyte for ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
DataType.UWORD -> {
|
||||||
@ -1828,17 +1914,27 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
eor #255
|
eor #255
|
||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for uword-memory invert")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place invert uword array")
|
when(target.register!!) {
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg invert")
|
RegisterOrPair.AX -> asmgen.out(" pha | txa | eor #255 | tax | pla | eor #255")
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack invert")
|
RegisterOrPair.AY -> asmgen.out(" pha | tya | eor #255 | tay | pla | eor #255")
|
||||||
|
RegisterOrPair.XY -> asmgen.out(" txa | eor #255 | tax | tya | eor #255 | tay")
|
||||||
|
in Cx16VirtualRegisters -> {
|
||||||
|
TODO("codegen for cx16 word register invert")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid reg dt for word invert")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> TODO("no asm gen for uword-memory invert")
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for word stack invert")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place invert uword for ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("invert of invalid type")
|
else -> throw AssemblyError("invert of invalid type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun inplaceNegate(target: AsmAssignTarget, dt: DataType) {
|
internal fun inplaceNegate(target: AsmAssignTarget, dt: DataType) {
|
||||||
when (dt) {
|
when (dt) {
|
||||||
DataType.BYTE -> {
|
DataType.BYTE -> {
|
||||||
when (target.kind) {
|
when (target.kind) {
|
||||||
@ -1849,10 +1945,17 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc ${target.asmVarname}
|
sbc ${target.asmVarname}
|
||||||
sta ${target.asmVarname}""")
|
sta ${target.asmVarname}""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("can't in-place negate memory ubyte")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate byte array")
|
when(target.register!!) {
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
RegisterOrPair.A -> asmgen.out(" sta P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1")
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
RegisterOrPair.X -> asmgen.out(" stx P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1 | tax")
|
||||||
|
RegisterOrPair.Y -> asmgen.out(" sty P8ZP_SCRATCH_B1 | lda #0 | sec | sbc P8ZP_SCRATCH_B1 | tay")
|
||||||
|
else -> throw AssemblyError("invalid reg dt for byte negate")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> TODO("can't in-place negate memory ubyte")
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for byte stack negate")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place negate byte array")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.WORD -> {
|
DataType.WORD -> {
|
||||||
@ -1867,10 +1970,55 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sbc ${target.asmVarname}+1
|
sbc ${target.asmVarname}+1
|
||||||
sta ${target.asmVarname}+1""")
|
sta ${target.asmVarname}+1""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate word array")
|
TargetStorageKind.REGISTER -> {
|
||||||
TargetStorageKind.MEMORY -> throw AssemblyError("no asm gen for word memory negate")
|
when(target.register!!) { //P8ZP_SCRATCH_REG
|
||||||
TargetStorageKind.REGISTER -> throw AssemblyError("missing codegen for reg negate")
|
RegisterOrPair.AX -> {
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack negate")
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
stx P8ZP_SCRATCH_REG+1
|
||||||
|
lda #0
|
||||||
|
sec
|
||||||
|
sbc P8ZP_SCRATCH_REG
|
||||||
|
pha
|
||||||
|
lda #0
|
||||||
|
sbc P8ZP_SCRATCH_REG+1
|
||||||
|
tax
|
||||||
|
pla""")
|
||||||
|
}
|
||||||
|
RegisterOrPair.AY -> {
|
||||||
|
asmgen.out("""
|
||||||
|
sta P8ZP_SCRATCH_REG
|
||||||
|
sty P8ZP_SCRATCH_REG+1
|
||||||
|
lda #0
|
||||||
|
sec
|
||||||
|
sbc P8ZP_SCRATCH_REG
|
||||||
|
pha
|
||||||
|
lda #0
|
||||||
|
sbc P8ZP_SCRATCH_REG+1
|
||||||
|
tay
|
||||||
|
pla""")
|
||||||
|
}
|
||||||
|
RegisterOrPair.XY -> {
|
||||||
|
asmgen.out("""
|
||||||
|
stx P8ZP_SCRATCH_REG
|
||||||
|
sty P8ZP_SCRATCH_REG+1
|
||||||
|
lda #0
|
||||||
|
sec
|
||||||
|
sbc P8ZP_SCRATCH_REG
|
||||||
|
tax
|
||||||
|
lda #0
|
||||||
|
sbc P8ZP_SCRATCH_REG+1
|
||||||
|
tay""")
|
||||||
|
}
|
||||||
|
in Cx16VirtualRegisters -> {
|
||||||
|
TODO("codegen for cx16 word register negate")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("invalid reg dt for word neg")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TargetStorageKind.MEMORY -> TODO("no asm gen for word memory negate")
|
||||||
|
TargetStorageKind.STACK -> TODO("missing codegen for word stack negate")
|
||||||
|
else -> throw AssemblyError("missing codegen for in-place negate word array")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
@ -1883,9 +2031,10 @@ internal class AugmentableAssignmentAsmGen(private val program: Program,
|
|||||||
sta ${target.asmVarname}+1
|
sta ${target.asmVarname}+1
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
TargetStorageKind.ARRAY -> throw AssemblyError("missing codegen for in-place negate float array")
|
TargetStorageKind.REGISTER -> TODO("missing codegen for float reg negate")
|
||||||
TargetStorageKind.STACK -> throw AssemblyError("missing codegen for stack float negate")
|
TargetStorageKind.MEMORY -> TODO("missing codegen for float memory negate")
|
||||||
else -> throw AssemblyError("weird target kind for float")
|
TargetStorageKind.STACK -> TODO("missing codegen for stack float negate")
|
||||||
|
else -> throw AssemblyError("weird target kind for inplace negate float ${target.kind}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("negate of invalid type")
|
else -> throw AssemblyError("negate of invalid type")
|
@ -1,14 +1,13 @@
|
|||||||
package prog8.compiler.target.cx16
|
package prog8.compiler.target.cx16
|
||||||
|
|
||||||
import prog8.compiler.*
|
|
||||||
import prog8.compiler.target.CpuType
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.cbm.viceMonListPostfix
|
import prog8.compiler.target.cbm.viceMonListPostfix
|
||||||
|
import prog8.compilerinterface.*
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
internal object CX16MachineDefinition: IMachineDefinition {
|
|
||||||
|
object CX16MachineDefinition: IMachineDefinition {
|
||||||
|
|
||||||
override val cpu = CpuType.CPU65c02
|
override val cpu = CpuType.CPU65c02
|
||||||
|
|
||||||
@ -88,7 +87,7 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
"rmb", "smb", "stp", "wai")
|
"rmb", "smb", "stp", "wai")
|
||||||
|
|
||||||
|
|
||||||
internal class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
class CX16Zeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
|
||||||
override val SCRATCH_B1 = 0x7a // temp storage for a single byte
|
override val SCRATCH_B1 = 0x7a // temp storage for a single byte
|
||||||
override val SCRATCH_REG = 0x7b // temp storage for a register, must be B1+1
|
override val SCRATCH_REG = 0x7b // temp storage for a register, must be B1+1
|
||||||
@ -98,37 +97,28 @@ internal object CX16MachineDefinition: IMachineDefinition {
|
|||||||
|
|
||||||
init {
|
init {
|
||||||
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
if (options.floats && options.zeropage !in arrayOf(ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
|
||||||
throw CompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
|
throw InternalCompilerException("when floats are enabled, zero page type should be 'basicsafe' or 'dontuse'")
|
||||||
|
|
||||||
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
// the addresses 0x02 to 0x21 (inclusive) are taken for sixteen virtual 16-bit api registers.
|
||||||
|
|
||||||
when (options.zeropage) {
|
when (options.zeropage) {
|
||||||
ZeropageType.FULL -> {
|
ZeropageType.FULL -> {
|
||||||
free.addAll(0x22..0xff)
|
free.addAll(0x22..0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.KERNALSAFE -> {
|
ZeropageType.KERNALSAFE -> {
|
||||||
free.addAll(0x22..0x7f)
|
free.addAll(0x22..0x7f)
|
||||||
free.addAll(0xa9..0xff)
|
free.addAll(0xa9..0xff)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.BASICSAFE -> {
|
ZeropageType.BASICSAFE -> {
|
||||||
free.addAll(0x22..0x7f)
|
free.addAll(0x22..0x7f)
|
||||||
free.removeAll(listOf(SCRATCH_B1, SCRATCH_REG, SCRATCH_W1, SCRATCH_W1 + 1, SCRATCH_W2, SCRATCH_W2 + 1))
|
|
||||||
}
|
}
|
||||||
ZeropageType.DONTUSE -> {
|
ZeropageType.DONTUSE -> {
|
||||||
free.clear() // don't use zeropage at all
|
free.clear() // don't use zeropage at all
|
||||||
}
|
}
|
||||||
else -> throw CompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
else -> throw InternalCompilerException("for this machine target, zero page type 'floatsafe' is not available. ${options.zeropage}")
|
||||||
}
|
}
|
||||||
|
|
||||||
require(SCRATCH_B1 !in free)
|
removeReservedFromFreePool()
|
||||||
require(SCRATCH_REG !in free)
|
|
||||||
require(SCRATCH_W1 !in free)
|
|
||||||
require(SCRATCH_W2 !in free)
|
|
||||||
|
|
||||||
for (reserved in options.zpReserved)
|
|
||||||
reserve(reserved)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -1,4 +1,4 @@
|
|||||||
package prog8tests
|
package prog8tests.asmgen
|
||||||
|
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
@ -6,18 +6,23 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.RegisterOrPair
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.AddressOf
|
import prog8.ast.expressions.AddressOf
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.*
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
|
import prog8.compilerinterface.*
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.DummyFunctions
|
import prog8tests.asmgen.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.asmgen.helpers.DummyMemsizer
|
||||||
|
import prog8tests.asmgen.helpers.DummyStringEncoder
|
||||||
|
import prog8tests.asmgen.helpers.ErrorReporterForTests
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
@ -63,20 +68,20 @@ locallabel:
|
|||||||
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val assign8 = Assignment(tgt, AddressOf(IdentifierReference(listOf("main","label_outside"), Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
|
|
||||||
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
val statements = mutableListOf(varInSub, var2InSub, labelInSub, assign1, assign2, assign3, assign4, assign5, assign6, assign7, assign8)
|
||||||
val subroutine = Subroutine("start", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements, Position.DUMMY)
|
val subroutine = Subroutine("start", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, statements, Position.DUMMY)
|
||||||
val labelInBlock = Label("label_outside", Position.DUMMY)
|
val labelInBlock = Label("label_outside", Position.DUMMY)
|
||||||
val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
|
val varInBlock = VarDecl(VarDeclType.VAR, DataType.UWORD, ZeropageWish.DONTCARE, null, "var_outside", null, false, false, false, Position.DUMMY)
|
||||||
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
val block = Block("main", null, mutableListOf(labelInBlock, varInBlock, subroutine), false, Position.DUMMY)
|
||||||
|
|
||||||
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(block), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
return program
|
return program
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun createTestAsmGen(program: Program): AsmGen {
|
private fun createTestAsmGen(program: Program): AsmGen {
|
||||||
val errors = ErrorReporter()
|
val errors = ErrorReporterForTests()
|
||||||
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target)
|
val options = CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, true, C64Target)
|
||||||
val zp = C64MachineDefinition.C64Zeropage(options)
|
val zp = C64MachineDefinition.C64Zeropage(options)
|
||||||
val asmgen = AsmGen(program, errors, zp, options, C64Target, Path.of(""))
|
val asmgen = AsmGen(program, errors, zp, options, C64Target, Path.of(""))
|
37
codeGeneration/test/helpers/Dummies.kt
Normal file
37
codeGeneration/test/helpers/Dummies.kt
Normal file
@ -0,0 +1,37 @@
|
|||||||
|
package prog8tests.asmgen.helpers
|
||||||
|
|
||||||
|
import prog8.ast.IBuiltinFunctions
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.InferredTypes
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.compilerinterface.IMemSizer
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.compilerinterface.IStringEncoding
|
||||||
|
|
||||||
|
|
||||||
|
internal val DummyFunctions = object : IBuiltinFunctions {
|
||||||
|
override val names: Set<String> = emptySet()
|
||||||
|
override val purefunctionNames: Set<String> = emptySet()
|
||||||
|
override fun constValue(
|
||||||
|
name: String,
|
||||||
|
args: List<Expression>,
|
||||||
|
position: Position,
|
||||||
|
): NumericLiteralValue? = null
|
||||||
|
|
||||||
|
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val DummyMemsizer = object : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType): Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val DummyStringEncoder = object : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
30
codeGeneration/test/helpers/ErrorReporterForTests.kt
Normal file
30
codeGeneration/test/helpers/ErrorReporterForTests.kt
Normal file
@ -0,0 +1,30 @@
|
|||||||
|
package prog8tests.asmgen.helpers
|
||||||
|
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true): IErrorReporter {
|
||||||
|
|
||||||
|
|
||||||
|
val errors = mutableListOf<String>()
|
||||||
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
|
override fun err(msg: String, position: Position) {
|
||||||
|
errors.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun warn(msg: String, position: Position) {
|
||||||
|
warnings.add("${position.toClickableStr()} $msg")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
|
||||||
|
override fun report() {
|
||||||
|
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||||
|
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||||
|
if(throwExceptionAtReportIfErrors)
|
||||||
|
finalizeNumErrors(errors.size, warnings.size)
|
||||||
|
errors.clear()
|
||||||
|
warnings.clear()
|
||||||
|
}
|
||||||
|
}
|
54
codeOptimizers/build.gradle
Normal file
54
codeOptimizers/build.gradle
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
|
||||||
|
plugins {
|
||||||
|
id 'java'
|
||||||
|
id 'application'
|
||||||
|
id "org.jetbrains.kotlin.jvm"
|
||||||
|
}
|
||||||
|
|
||||||
|
java {
|
||||||
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation project(':compilerInterfaces')
|
||||||
|
implementation project(':compilerAst')
|
||||||
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
|
|
||||||
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
|
testImplementation 'org.junit.jupiter:junit-jupiter-api:5.7.2'
|
||||||
|
testImplementation 'org.hamcrest:hamcrest:2.2'
|
||||||
|
testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.7.2'
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets {
|
||||||
|
main {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/src"]
|
||||||
|
}
|
||||||
|
resources {
|
||||||
|
srcDirs = ["${project.projectDir}/res"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
test {
|
||||||
|
java {
|
||||||
|
srcDirs = ["${project.projectDir}/test"]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
test {
|
||||||
|
// Enable JUnit 5 (Gradle 4.6+).
|
||||||
|
useJUnitPlatform()
|
||||||
|
|
||||||
|
// Always run tests, even when nothing changed.
|
||||||
|
dependsOn 'cleanTest'
|
||||||
|
|
||||||
|
// Show test results.
|
||||||
|
testLogging {
|
||||||
|
events "skipped", "failed"
|
||||||
|
}
|
||||||
|
}
|
18
codeOptimizers/codeOptimizers.iml
Normal file
18
codeOptimizers/codeOptimizers.iml
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/test" isTestSource="true" />
|
||||||
|
<excludeFolder url="file://$MODULE_DIR$/build" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="jdk" jdkName="openjdk-11" jdkType="JavaSDK" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||||
|
<orderEntry type="module" module-name="compilerAst" />
|
||||||
|
<orderEntry type="library" scope="TEST" name="hamcrest" level="project" />
|
||||||
|
<orderEntry type="library" name="junit.jupiter" level="project" />
|
||||||
|
</component>
|
||||||
|
</module>
|
@ -1,22 +1,24 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.expressions.BinaryExpression
|
import prog8.ast.expressions.BinaryExpression
|
||||||
import prog8.ast.expressions.augmentAssignmentOperators
|
import prog8.ast.expressions.augmentAssignmentOperators
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.astprocessing.isInRegularRAMof
|
import prog8.compilerinterface.CompilationOptions
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
import prog8.compilerinterface.isInRegularRAMof
|
||||||
|
|
||||||
|
|
||||||
internal class BinExprSplitter(private val program: Program, private val compTarget: ICompilationTarget) : AstWalker() {
|
class BinExprSplitter(private val program: Program, private val options: CompilationOptions, private val compTarget: ICompilationTarget) : AstWalker() {
|
||||||
|
|
||||||
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
// override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...:
|
// TODO somehow if we do this, the resulting code for some programs (cube3d.p8) gets hundreds of bytes larger...: [ IS THIS STILL TRUE AFTER ALL CHANGES? ]
|
||||||
// if(decl.type==VarDeclType.VAR ) {
|
// if(decl.type==VarDeclType.VAR ) {
|
||||||
// val binExpr = decl.value as? BinaryExpression
|
// val binExpr = decl.value as? BinaryExpression
|
||||||
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
// if (binExpr != null && binExpr.operator in augmentAssignmentOperators) {
|
||||||
@ -38,6 +40,11 @@ internal class BinExprSplitter(private val program: Program, private val compTar
|
|||||||
|
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
if (binExpr != null) {
|
if (binExpr != null) {
|
||||||
|
|
||||||
|
if(binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
|
|
||||||
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
Reduce the complexity of a (binary) expression that has to be evaluated on the eval stack,
|
||||||
@ -65,7 +72,7 @@ X = BinExpr X = LeftExpr
|
|||||||
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
val augExpr = BinaryExpression(targetExpr, binExpr.operator, binExpr.right, binExpr.right.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
IAstModification.ReplaceNode(binExpr, augExpr, assignment),
|
||||||
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as INameScope)
|
IAstModification.InsertBefore(assignment, firstAssign, assignment.parent as IStatementContainer)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
@ -12,7 +12,7 @@ import prog8.ast.walk.IAstModification
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
|
||||||
internal class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||||
|
|
||||||
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
override fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// @( &thing ) --> thing
|
// @( &thing ) --> thing
|
@ -1,6 +1,5 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -8,16 +7,20 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compiler.astprocessing.size
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.compiler.astprocessing.toConstantIntegerRange
|
import prog8.compilerinterface.size
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.toConstantIntegerRange
|
||||||
|
|
||||||
// Fix up the literal value's type to match that of the vardecl
|
// Fix up the literal value's type to match that of the vardecl
|
||||||
// (also check range literal operands types before they get expanded into arrays for instance)
|
// (also check range literal operands types before they get expanded into arrays for instance)
|
||||||
internal class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
class VarConstantValueTypeAdjuster(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
if(decl.parent is AnonymousScope)
|
||||||
|
throw FatalAstException("vardecl may no longer occur in anonymousscope")
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val declConstValue = decl.value?.constValue(program)
|
val declConstValue = decl.value?.constValue(program)
|
||||||
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
if(declConstValue!=null && (decl.type==VarDeclType.VAR || decl.type==VarDeclType.CONST)
|
||||||
@ -31,28 +34,6 @@ internal class VarConstantValueTypeAdjuster(private val program: Program, privat
|
|||||||
errors.err(x.message, x.position)
|
errors.err(x.message, x.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
// move vardecl to the containing subroutine and add initialization assignment in its place if needed
|
|
||||||
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
val subroutine = decl.definingSubroutine as? INameScope
|
|
||||||
if(subroutine!=null && subroutine!==parent) {
|
|
||||||
val declValue = decl.value
|
|
||||||
decl.value = null
|
|
||||||
decl.allowInitializeWithZero = false
|
|
||||||
return if (declValue == null) {
|
|
||||||
listOf(
|
|
||||||
IAstModification.Remove(decl, parent as INameScope),
|
|
||||||
IAstModification.InsertFirst(decl, subroutine)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
val assign = Assignment(target, declValue, decl.position)
|
|
||||||
listOf(
|
|
||||||
IAstModification.ReplaceNode(decl, assign, parent),
|
|
||||||
IAstModification.InsertFirst(decl, subroutine)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -160,9 +141,9 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array
|
// convert the initializer range expression to an actual array
|
||||||
val declArraySize = decl.arraysize?.constIndex()
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size(compTarget))
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange(compTarget)
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
val eltType = rangeExpr.inferType(program).getOr(DataType.UBYTE)
|
||||||
val newValue = if(eltType in ByteDatatypes) {
|
val newValue = if(eltType in ByteDatatypes) {
|
||||||
@ -214,9 +195,9 @@ internal class ConstantIdentifierReplacer(private val program: Program, private
|
|||||||
if(rangeExpr!=null) {
|
if(rangeExpr!=null) {
|
||||||
// convert the initializer range expression to an actual array of floats
|
// convert the initializer range expression to an actual array of floats
|
||||||
val declArraySize = decl.arraysize?.constIndex()
|
val declArraySize = decl.arraysize?.constIndex()
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size(compTarget))
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.err("range expression size (${rangeExpr.size(compTarget)}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
errors.err("range expression size (${rangeExpr.size()}) doesn't match declared array size ($declArraySize)", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange(compTarget)
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
val newValue = ArrayLiteralValue(InferredTypes.InferredType.known(DataType.ARRAY_F),
|
||||||
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
constRange.map { NumericLiteralValue(DataType.FLOAT, it.toDouble(), decl.value!!.position) }.toTypedArray(),
|
@ -15,14 +15,25 @@ import kotlin.math.log2
|
|||||||
import kotlin.math.pow
|
import kotlin.math.pow
|
||||||
|
|
||||||
/*
|
/*
|
||||||
todo add more expression optimizations
|
todo add more peephole expression optimizations
|
||||||
|
|
||||||
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||||
|
|
||||||
|
*(&X) => X
|
||||||
|
X % 1 => 0
|
||||||
|
X / 1 => X
|
||||||
|
X ^ -1 => ~x
|
||||||
|
X >= 1 => X > 0
|
||||||
|
X < 1 => X <= 0
|
||||||
|
X + С1 == C2 => X == C2 - C1
|
||||||
|
((X + C1) + C2) => (X + (C1 + C2))
|
||||||
|
((X + C1) + (Y + C2)) => ((X + Y) + (C1 + C2))
|
||||||
|
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
|
|
@ -2,11 +2,12 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.CompilationOptions
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
||||||
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
val valuetypefixer = VarConstantValueTypeAdjuster(this, errors)
|
||||||
valuetypefixer.visit(this)
|
valuetypefixer.visit(this)
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
@ -40,9 +41,10 @@ internal fun Program.constantFold(errors: IErrorReporter, compTarget: ICompilati
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.optimizeStatements(errors: IErrorReporter,
|
fun Program.optimizeStatements(errors: IErrorReporter,
|
||||||
functions: IBuiltinFunctions,
|
functions: IBuiltinFunctions,
|
||||||
compTarget: ICompilationTarget): Int {
|
compTarget: ICompilationTarget
|
||||||
|
): Int {
|
||||||
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
val optimizer = StatementOptimizer(this, errors, functions, compTarget)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
val optimizationCount = optimizer.applyModifications()
|
val optimizationCount = optimizer.applyModifications()
|
||||||
@ -52,14 +54,14 @@ internal fun Program.optimizeStatements(errors: IErrorReporter,
|
|||||||
return optimizationCount
|
return optimizationCount
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyExpressions() : Int {
|
fun Program.simplifyExpressions() : Int {
|
||||||
val opti = ExpressionSimplifier(this)
|
val opti = ExpressionSimplifier(this)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.splitBinaryExpressions(compTarget: ICompilationTarget) : Int {
|
fun Program.splitBinaryExpressions(options: CompilationOptions, compTarget: ICompilationTarget) : Int {
|
||||||
val opti = BinExprSplitter(this, compTarget)
|
val opti = BinExprSplitter(this, options, compTarget)
|
||||||
opti.visit(this)
|
opti.visit(this)
|
||||||
return opti.applyModifications()
|
return opti.applyModifications()
|
||||||
}
|
}
|
@ -1,7 +1,7 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -10,20 +10,21 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compiler.astprocessing.size
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.size
|
||||||
import kotlin.math.floor
|
import kotlin.math.floor
|
||||||
|
|
||||||
internal const val retvarName = "prog8_retval"
|
internal const val retvarName = "prog8_retval"
|
||||||
|
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program,
|
class StatementOptimizer(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val functions: IBuiltinFunctions,
|
private val functions: IBuiltinFunctions,
|
||||||
private val compTarget: ICompilationTarget) : AstWalker() {
|
private val compTarget: ICompilationTarget
|
||||||
|
) : AstWalker() {
|
||||||
|
|
||||||
private val subsThatNeedReturnVariable = mutableSetOf<Triple<INameScope, DataType, Position>>()
|
private val subsThatNeedReturnVariable = mutableSetOf<Triple<IStatementContainer, DataType, Position>>()
|
||||||
|
|
||||||
|
|
||||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
@ -79,7 +80,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in functions.purefunctionNames) {
|
if (functionName in functions.purefunctionNames) {
|
||||||
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope))
|
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -117,7 +118,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
functionCallStatement.void, pos
|
functionCallStatement.void, pos
|
||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as INameScope),
|
IAstModification.InsertBefore(functionCallStatement, chrout1, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
IAstModification.ReplaceNode(functionCallStatement, chrout2, parent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -130,7 +131,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
if(subroutine!=null) {
|
if(subroutine!=null) {
|
||||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||||
if(first is Return)
|
if(first is Return)
|
||||||
return listOf(IAstModification.Remove(functionCallStatement, functionCallStatement.definingScope))
|
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -152,11 +153,11 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
// remove empty if statements
|
// remove empty if statements
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars && ifStatement.elsepart.containsNoCodeNorVars)
|
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isEmpty())
|
||||||
return listOf(IAstModification.Remove(ifStatement, ifStatement.definingScope))
|
return listOf(IAstModification.Remove(ifStatement, parent as IStatementContainer))
|
||||||
|
|
||||||
// empty true part? switch with the else part
|
// empty true part? switch with the else part
|
||||||
if(ifStatement.truepart.containsNoCodeNorVars && ifStatement.elsepart.containsCodeOrVars) {
|
if(ifStatement.truepart.isEmpty() && ifStatement.elsepart.isNotEmpty()) {
|
||||||
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
val invertedCondition = PrefixExpression("not", ifStatement.condition, ifStatement.condition.position)
|
||||||
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
val emptyscope = AnonymousScope(mutableListOf(), ifStatement.elsepart.position)
|
||||||
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
val truepart = AnonymousScope(ifStatement.elsepart.statements, ifStatement.truepart.position)
|
||||||
@ -184,20 +185,20 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> {
|
||||||
if(forLoop.body.containsNoCodeNorVars) {
|
if(forLoop.body.isEmpty()) {
|
||||||
errors.warn("removing empty for loop", forLoop.position)
|
errors.warn("removing empty for loop", forLoop.position)
|
||||||
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope))
|
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||||
} else if(forLoop.body.statements.size==1) {
|
} else if(forLoop.body.statements.size==1) {
|
||||||
val loopvar = forLoop.body.statements[0] as? VarDecl
|
val loopvar = forLoop.body.statements[0] as? VarDecl
|
||||||
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
if(loopvar!=null && loopvar.name==forLoop.loopVar.nameInSource.singleOrNull()) {
|
||||||
// remove empty for loop (only loopvar decl in it)
|
// remove empty for loop (only loopvar decl in it)
|
||||||
return listOf(IAstModification.Remove(forLoop, forLoop.definingScope))
|
return listOf(IAstModification.Remove(forLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val range = forLoop.iterable as? RangeExpr
|
val range = forLoop.iterable as? RangeExpr
|
||||||
if(range!=null) {
|
if(range!=null) {
|
||||||
if (range.size(compTarget) == 1) {
|
if (range.size() == 1) {
|
||||||
// for loop over a (constant) range of just a single value-- optimize the loop away
|
// for loop over a (constant) range of just a single value-- optimize the loop away
|
||||||
// loopvar/reg = range value , follow by block
|
// loopvar/reg = range value , follow by block
|
||||||
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
val scope = AnonymousScope(mutableListOf(), forLoop.position)
|
||||||
@ -268,7 +269,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
// always false -> remove the while statement altogether
|
// always false -> remove the while statement altogether
|
||||||
errors.warn("condition is always false", whileLoop.condition.position)
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
listOf(IAstModification.Remove(whileLoop, whileLoop.definingScope))
|
listOf(IAstModification.Remove(whileLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
@ -277,14 +278,14 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
val iter = repeatLoop.iterations
|
val iter = repeatLoop.iterations
|
||||||
if(iter!=null) {
|
if(iter!=null) {
|
||||||
if(repeatLoop.body.containsNoCodeNorVars) {
|
if(repeatLoop.body.isEmpty()) {
|
||||||
errors.warn("empty loop removed", repeatLoop.position)
|
errors.warn("empty loop removed", repeatLoop.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope))
|
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
val iterations = iter.constValue(program)?.number?.toInt()
|
val iterations = iter.constValue(program)?.number?.toInt()
|
||||||
if (iterations == 0) {
|
if (iterations == 0) {
|
||||||
errors.warn("iterations is always 0, removed loop", iter.position)
|
errors.warn("iterations is always 0, removed loop", iter.position)
|
||||||
return listOf(IAstModification.Remove(repeatLoop, repeatLoop.definingScope))
|
return listOf(IAstModification.Remove(repeatLoop, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
if (iterations == 1) {
|
if (iterations == 1) {
|
||||||
errors.warn("iterations is always 1", iter.position)
|
errors.warn("iterations is always 1", iter.position)
|
||||||
@ -296,10 +297,10 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
|
|
||||||
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
override fun after(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
// if the jump is to the next statement, remove the jump
|
// if the jump is to the next statement, remove the jump
|
||||||
val scope = jump.definingScope
|
val scope = jump.parent as IStatementContainer
|
||||||
val label = jump.identifier?.targetStatement(program)
|
val label = jump.identifier?.targetStatement(program)
|
||||||
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
if(label!=null && scope.statements.indexOf(label) == scope.statements.indexOf(jump)+1)
|
||||||
return listOf(IAstModification.Remove(jump, jump.definingScope))
|
return listOf(IAstModification.Remove(jump, scope))
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
@ -332,7 +333,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, addConstant, assignment.definingScope))
|
IAstModification.InsertAfter(assignment, addConstant, parent as IStatementContainer))
|
||||||
} else if (op2 == "-") {
|
} else if (op2 == "-") {
|
||||||
// A = A +/- B - N
|
// A = A +/- B - N
|
||||||
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
val expr2 = BinaryExpression(binExpr.left, binExpr.operator, rExpr.left, binExpr.position)
|
||||||
@ -343,7 +344,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
)
|
)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
IAstModification.ReplaceNode(binExpr, expr2, binExpr.parent),
|
||||||
IAstModification.InsertAfter(assignment, subConstant, assignment.definingScope))
|
IAstModification.InsertAfter(assignment, subConstant, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -365,7 +366,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
if(assignment.target isSameAs assignment.value) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
// remove assignment to self
|
// remove assignment to self
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
val targetIDt = assignment.target.inferType(program)
|
val targetIDt = assignment.target.inferType(program)
|
||||||
@ -385,7 +386,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
when (bexpr.operator) {
|
when (bexpr.operator) {
|
||||||
"+" -> {
|
"+" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
// replace by several INCs if it's not a memory address (inc on a memory mapped register doesn't work very well)
|
||||||
@ -399,7 +400,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
"-" -> {
|
"-" -> {
|
||||||
if (rightCv == 0.0) {
|
if (rightCv == 0.0) {
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
} else if (targetDt in IntegerDatatypes && floor(rightCv) == rightCv) {
|
||||||
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
if (vardeclDt != VarDeclType.MEMORY && rightCv in 1.0..4.0) {
|
||||||
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
// replace by several DECs if it's not a memory address (dec on a memory mapped register doesn't work very well)
|
||||||
@ -411,18 +412,18 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
"*" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
"/" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
"**" -> if (rightCv == 1.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
"|" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
"^" -> if (rightCv == 0.0) return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
"<<" -> {
|
"<<" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
">>" -> {
|
">>" -> {
|
||||||
if (rightCv == 0.0)
|
if (rightCv == 0.0)
|
||||||
return listOf(IAstModification.Remove(assignment, assignment.definingScope))
|
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -445,7 +446,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
val assign = Assignment(tgt, value, returnStmt.position)
|
val assign = Assignment(tgt, value, returnStmt.position)
|
||||||
val returnReplacement = Return(returnValueIntermediary2, returnStmt.position)
|
val returnReplacement = Return(returnValueIntermediary2, returnStmt.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(returnStmt, assign, parent as INameScope),
|
IAstModification.InsertBefore(returnStmt, assign, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
IAstModification.ReplaceNode(returnStmt, returnReplacement, parent)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
@ -469,7 +470,7 @@ internal class StatementOptimizer(private val program: Program,
|
|||||||
return super.after(returnStmt, parent)
|
return super.after(returnStmt, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasBreak(scope: INameScope): Boolean {
|
private fun hasBreak(scope: IStatementContainer): Boolean {
|
||||||
|
|
||||||
class Searcher: IAstVisitor
|
class Searcher: IAstVisitor
|
||||||
{
|
{
|
@ -9,47 +9,49 @@ import prog8.ast.expressions.TypecastExpression
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.CallGraph
|
||||||
import prog8.compiler.astprocessing.isInRegularRAMof
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
import prog8.compilerinterface.isInRegularRAMof
|
||||||
|
|
||||||
|
|
||||||
internal class UnusedCodeRemover(private val program: Program,
|
class UnusedCodeRemover(private val program: Program,
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val compTarget: ICompilationTarget): AstWalker() {
|
private val compTarget: ICompilationTarget
|
||||||
|
): AstWalker() {
|
||||||
|
|
||||||
private val callgraph = CallGraph(program)
|
private val callgraph = CallGraph(program)
|
||||||
|
|
||||||
override fun before(module: Module, parent: Node): Iterable<IAstModification> {
|
override fun before(module: Module, parent: Node): Iterable<IAstModification> {
|
||||||
return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module)))
|
return if (!module.isLibrary && (module.containsNoCodeNorVars || callgraph.unused(module)))
|
||||||
listOf(IAstModification.Remove(module, module.definingScope))
|
listOf(IAstModification.Remove(module, parent as IStatementContainer))
|
||||||
else
|
else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(breakStmt, parent as INameScope)
|
reportUnreachable(breakStmt, parent as IStatementContainer)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(jump, parent as INameScope)
|
reportUnreachable(jump, parent as IStatementContainer)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
reportUnreachable(returnStmt, parent as INameScope)
|
reportUnreachable(returnStmt, parent as IStatementContainer)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||||
reportUnreachable(functionCallStatement, parent as INameScope)
|
reportUnreachable(functionCallStatement, parent as IStatementContainer)
|
||||||
return emptyList()
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reportUnreachable(stmt: Statement, parent: INameScope) {
|
private fun reportUnreachable(stmt: Statement, parent: IStatementContainer) {
|
||||||
when(val next = parent.nextSibling(stmt)) {
|
when(val next = stmt.nextSibling()) {
|
||||||
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
null, is Label, is Directive, is VarDecl, is InlineAssembly, is Subroutine -> {}
|
||||||
else -> errors.warn("unreachable code", next.position)
|
else -> errors.warn("unreachable code", next.position)
|
||||||
}
|
}
|
||||||
@ -65,11 +67,11 @@ internal class UnusedCodeRemover(private val program: Program,
|
|||||||
if (block.containsNoCodeNorVars) {
|
if (block.containsNoCodeNorVars) {
|
||||||
if(block.name != internedStringsModuleName)
|
if(block.name != internedStringsModuleName)
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
if(callgraph.unused(block)) {
|
if(callgraph.unused(block)) {
|
||||||
errors.warn("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return listOf(IAstModification.Remove(block, parent as INameScope))
|
return listOf(IAstModification.Remove(block, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,16 +86,16 @@ internal class UnusedCodeRemover(private val program: Program,
|
|||||||
if(subroutine.containsNoCodeNorVars) {
|
if(subroutine.containsNoCodeNorVars) {
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary)
|
||||||
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
val removals = mutableListOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
val removals = mutableListOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||||
callgraph.calledBy[subroutine]?.let {
|
callgraph.calledBy[subroutine]?.let {
|
||||||
for(node in it)
|
for(node in it)
|
||||||
removals.add(IAstModification.Remove(node, node.definingScope))
|
removals.add(IAstModification.Remove(node, node.parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
return removals
|
return removals
|
||||||
}
|
}
|
||||||
if(!subroutine.definingModule.isLibrary)
|
if(!subroutine.definingModule.isLibrary)
|
||||||
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
return listOf(IAstModification.Remove(subroutine, subroutine.definingScope))
|
return listOf(IAstModification.Remove(subroutine, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -107,7 +109,7 @@ internal class UnusedCodeRemover(private val program: Program,
|
|||||||
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
|
if (!forceOutput && !decl.autogeneratedDontRemove && !decl.sharedWithAsm && !decl.definingBlock.isInLibrary) {
|
||||||
if (callgraph.unused(decl)) {
|
if (callgraph.unused(decl)) {
|
||||||
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
errors.warn("removing unused variable '${decl.name}'", decl.position)
|
||||||
return listOf(IAstModification.Remove(decl, decl.definingScope))
|
return listOf(IAstModification.Remove(decl, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
2
codeOptimizers/test/readme.txt
Normal file
2
codeOptimizers/test/readme.txt
Normal file
@ -0,0 +1,2 @@
|
|||||||
|
Unittests for things in this module are located in the Compiler module instead,
|
||||||
|
for convenience sake - and to not spread the test cases around too much.
|
@ -5,20 +5,19 @@ plugins {
|
|||||||
id 'com.github.johnrengelman.shadow' version '7.1.0'
|
id 'com.github.johnrengelman.shadow' version '7.1.0'
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCompatibility = 11
|
java {
|
||||||
sourceCompatibility = 11
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
repositories {
|
|
||||||
mavenLocal()
|
|
||||||
mavenCentral()
|
|
||||||
maven { url "https://kotlin.bintray.com/kotlinx" }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
|
implementation project(':compilerInterfaces')
|
||||||
|
implementation project(':codeOptimizers')
|
||||||
implementation project(':compilerAst')
|
implementation project(':compilerAst')
|
||||||
|
implementation project(':codeGeneration')
|
||||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.3'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.3'
|
||||||
@ -44,22 +43,6 @@ configurations {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
useIR = true
|
|
||||||
// verbose = true
|
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
useIR = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
@ -84,11 +67,6 @@ application {
|
|||||||
applicationName = 'p8compile'
|
applicationName = 'p8compile'
|
||||||
}
|
}
|
||||||
|
|
||||||
artifacts {
|
|
||||||
archives shadowJar
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
shadowJar {
|
shadowJar {
|
||||||
archiveBaseName = 'prog8compiler'
|
archiveBaseName = 'prog8compiler'
|
||||||
archiveVersion = prog8version
|
archiveVersion = prog8version
|
||||||
@ -108,3 +86,5 @@ test {
|
|||||||
events "skipped", "failed"
|
events "skipped", "failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
build.finalizedBy installDist, installShadowDist
|
||||||
|
@ -22,5 +22,8 @@
|
|||||||
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
<orderEntry type="library" name="jetbrains.kotlinx.cli.jvm" level="project" />
|
||||||
<orderEntry type="library" name="junit.jupiter" level="project" />
|
<orderEntry type="library" name="junit.jupiter" level="project" />
|
||||||
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
<orderEntry type="library" name="michael.bull.kotlin.result.jvm" level="project" />
|
||||||
|
<orderEntry type="module" module-name="codeOptimizers" />
|
||||||
|
<orderEntry type="module" module-name="compilerInterfaces" />
|
||||||
|
<orderEntry type="module" module-name="codeGeneration" />
|
||||||
</component>
|
</component>
|
||||||
</module>
|
</module>
|
@ -12,6 +12,7 @@ floats {
|
|||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
|
ubyte[5] tempvar_swap_float ; used for some swap() operations
|
||||||
|
|
||||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||||
|
|
||||||
|
@ -10,10 +10,11 @@ floats {
|
|||||||
; ---- this block contains C-64 compatible floating point related functions ----
|
; ---- this block contains C-64 compatible floating point related functions ----
|
||||||
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
; the addresses are from cx16 V39 emulator and roms! they won't work on older versions.
|
||||||
|
|
||||||
|
|
||||||
const float PI = 3.141592653589793
|
const float PI = 3.141592653589793
|
||||||
const float TWOPI = 6.283185307179586
|
const float TWOPI = 6.283185307179586
|
||||||
|
|
||||||
|
ubyte[5] tempvar_swap_float ; used for some swap() operations
|
||||||
|
|
||||||
|
|
||||||
; ---- ROM float functions ----
|
; ---- ROM float functions ----
|
||||||
|
|
||||||
|
@ -6,10 +6,12 @@ prog8_lib {
|
|||||||
%asminclude "library:prog8_lib.asm"
|
%asminclude "library:prog8_lib.asm"
|
||||||
%asminclude "library:prog8_funcs.asm"
|
%asminclude "library:prog8_funcs.asm"
|
||||||
|
|
||||||
|
; TODO these retval variables are no longer used???
|
||||||
uword @zp retval_interm_uw ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
uword @zp retval_interm_uw ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
word @zp retval_interm_w ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
word @zp retval_interm_w ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
ubyte @zp retval_interm_ub ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
ubyte @zp retval_interm_ub ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
byte @zp retval_interm_b ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
byte @zp retval_interm_b ; to store intermediary expression results for return values (hopefully allocated on ZP to reduce code size)
|
||||||
|
; NOTE: these variables are checked in the StatementReorderer (in fun after(decl: VarDecl)), for these exact names!
|
||||||
|
|
||||||
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
asmsub pattern_match(str string @AY, str pattern @R0) clobbers(Y) -> ubyte @A {
|
||||||
%asm {{
|
%asm {{
|
||||||
|
@ -1 +1 @@
|
|||||||
7.1
|
7.2
|
||||||
|
@ -6,7 +6,6 @@ import prog8.compiler.CompilationResult
|
|||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.parser.ParsingFailedError
|
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -36,10 +35,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
val outputDir by cli.option(ArgType.String, fullName = "out", description = "directory for output files instead of current directory").default(".")
|
||||||
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
val dontWriteAssembly by cli.option(ArgType.Boolean, fullName = "noasm", description="don't create assembly code")
|
||||||
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
val dontOptimize by cli.option(ArgType.Boolean, fullName = "noopt", description = "don't perform any optimizations")
|
||||||
|
val optimizeFloatExpressions by cli.option(ArgType.Boolean, fullName = "optfloatx", description = "optimize float expressions (warning: can increase program size)")
|
||||||
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
val watchMode by cli.option(ArgType.Boolean, fullName = "watch", description = "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||||
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
val slowCodegenWarnings by cli.option(ArgType.Boolean, fullName = "slowwarn", description="show debug warnings about slow/problematic assembly code generation")
|
||||||
|
val quietAssembler by cli.option(ArgType.Boolean, fullName = "quietasm", description = "don't print assembler output results")
|
||||||
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
|
||||||
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
val sourceDirs by cli.option(ArgType.String, fullName="srcdirs", description = "list of extra paths, separated with ${File.pathSeparator}, to search in for imported modules").multiple().delimiter(File.pathSeparator)
|
||||||
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
|
||||||
|
|
||||||
try {
|
try {
|
||||||
@ -74,7 +75,10 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val results = mutableListOf<CompilationResult>()
|
val results = mutableListOf<CompilationResult>()
|
||||||
for(filepathRaw in moduleFiles) {
|
for(filepathRaw in moduleFiles) {
|
||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, srcdirs, outputPath)
|
val compilationResult = compileProgram(filepath,
|
||||||
|
dontOptimize!=true, optimizeFloatExpressions==true,
|
||||||
|
dontWriteAssembly!=true, slowCodegenWarnings==true, quietAssembler==true,
|
||||||
|
compilationTarget, srcdirs, outputPath)
|
||||||
results.add(compilationResult)
|
results.add(compilationResult)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,11 +115,12 @@ private fun compileMain(args: Array<String>): Boolean {
|
|||||||
val filepath = pathFrom(filepathRaw).normalize()
|
val filepath = pathFrom(filepathRaw).normalize()
|
||||||
val compilationResult: CompilationResult
|
val compilationResult: CompilationResult
|
||||||
try {
|
try {
|
||||||
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, srcdirs, outputPath)
|
compilationResult = compileProgram(filepath,
|
||||||
|
dontOptimize!=true, optimizeFloatExpressions==true,
|
||||||
|
dontWriteAssembly!=true, slowCodegenWarnings==true, quietAssembler==true,
|
||||||
|
compilationTarget, srcdirs, outputPath)
|
||||||
if(!compilationResult.success)
|
if(!compilationResult.success)
|
||||||
return false
|
return false
|
||||||
} catch (x: ParsingFailedError) {
|
|
||||||
return false
|
|
||||||
} catch (x: AstException) {
|
} catch (x: AstException) {
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1,3 +0,0 @@
|
|||||||
package prog8.compiler
|
|
||||||
|
|
||||||
internal class AssemblyError(msg: String) : RuntimeException(msg)
|
|
@ -1,6 +1,7 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
@ -9,28 +10,18 @@ import prog8.ast.statements.*
|
|||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.astprocessing.isInRegularRAMof
|
import prog8.compiler.astprocessing.isSubroutineParameter
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.*
|
||||||
|
|
||||||
|
|
||||||
internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: IErrorReporter, private val compTarget: ICompilationTarget) : AstWalker() {
|
internal class BeforeAsmGenerationAstChanger(val program: Program, private val options: CompilationOptions,
|
||||||
|
private val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(decl.type==VarDeclType.VAR && decl.value != null && decl.datatype in NumericDatatypes)
|
||||||
|
throw FatalAstException("vardecls for variables, with initial numerical value, should have been rewritten as plain vardecl + assignment $decl")
|
||||||
|
|
||||||
subroutineVariables.add(decl.name to decl)
|
subroutineVariables.add(decl.name to decl)
|
||||||
if (decl.value == null && !decl.autogeneratedDontRemove && decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
|
||||||
// A numeric vardecl without an initial value is initialized with zero,
|
|
||||||
// unless there's already an assignment below, that initializes the value.
|
|
||||||
// This allows you to restart the program and have the same starting values of the variables
|
|
||||||
if(decl.allowInitializeWithZero)
|
|
||||||
{
|
|
||||||
val nextAssign = decl.definingScope.nextSibling(decl) as? Assignment
|
|
||||||
if (nextAssign != null && nextAssign.target isSameAs IdentifierReference(listOf(decl.name), Position.DUMMY))
|
|
||||||
decl.value = null
|
|
||||||
else {
|
|
||||||
decl.value = decl.zeroElementValue()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -40,8 +31,12 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
|
// But it can only be done if the target variable IS NOT OCCURRING AS AN OPERAND ITSELF.
|
||||||
if(!assignment.isAugmentable
|
if(!assignment.isAugmentable
|
||||||
&& assignment.target.identifier != null
|
&& assignment.target.identifier != null
|
||||||
&& assignment.target.isInRegularRAMof(compTarget.machine)) {
|
&& assignment.target.isInRegularRAMof(options.compTarget.machine)) {
|
||||||
val binExpr = assignment.value as? BinaryExpression
|
val binExpr = assignment.value as? BinaryExpression
|
||||||
|
|
||||||
|
if(binExpr!=null && binExpr.inferType(program).istype(DataType.FLOAT) && !options.optimizeFloatExpressions)
|
||||||
|
return noModifications
|
||||||
|
|
||||||
if (binExpr != null && binExpr.operator !in comparisonOperators) {
|
if (binExpr != null && binExpr.operator !in comparisonOperators) {
|
||||||
if (binExpr.left !is BinaryExpression) {
|
if (binExpr.left !is BinaryExpression) {
|
||||||
if (binExpr.right.referencesIdentifier(*assignment.target.identifier!!.nameInSource.toTypedArray())) {
|
if (binExpr.right.referencesIdentifier(*assignment.target.identifier!!.nameInSource.toTypedArray())) {
|
||||||
@ -52,14 +47,14 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
// use the other part of the expression to split.
|
// use the other part of the expression to split.
|
||||||
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
val assignRight = Assignment(assignment.target, binExpr.right, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignRight, assignment.definingScope),
|
IAstModification.InsertBefore(assignment, assignRight, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
IAstModification.ReplaceNode(binExpr.right, binExpr.left, binExpr),
|
||||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
val assignLeft = Assignment(assignment.target, binExpr.left, assignment.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.InsertBefore(assignment, assignLeft, assignment.definingScope),
|
IAstModification.InsertBefore(assignment, assignLeft, parent as IStatementContainer),
|
||||||
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
IAstModification.ReplaceNode(binExpr.left, assignment.target.toExpression(), binExpr))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -74,33 +69,35 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
override fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
subroutineVariables.clear()
|
subroutineVariables.clear()
|
||||||
addedIfConditionVars.clear()
|
addedIfConditionVars.clear()
|
||||||
|
|
||||||
|
if(!subroutine.isAsmSubroutine) {
|
||||||
|
// change 'str' parameters into 'uword' (just treat it as an address)
|
||||||
|
val stringParams = subroutine.parameters.filter { it.type==DataType.STR }
|
||||||
|
val parameterChanges = stringParams.map {
|
||||||
|
val uwordParam = SubroutineParameter(it.name, DataType.UWORD, it.position)
|
||||||
|
IAstModification.ReplaceNode(it, uwordParam, subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
val stringParamNames = stringParams.map { it.name }.toSet()
|
||||||
|
val varsChanges = subroutine.statements
|
||||||
|
.filterIsInstance<VarDecl>()
|
||||||
|
.filter { it.autogeneratedDontRemove && it.name in stringParamNames }
|
||||||
|
.map {
|
||||||
|
val newvar = VarDecl(it.type, DataType.UWORD, it.zeropage, null, it.name, null, false, true, it.sharedWithAsm, it.position)
|
||||||
|
IAstModification.ReplaceNode(it, newvar, subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
return parameterChanges + varsChanges
|
||||||
|
}
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(scope.statements.any { it is VarDecl || it is IStatementContainer })
|
||||||
|
throw FatalAstException("anonymousscope may no longer contain any vardecls or subscopes")
|
||||||
|
|
||||||
val decls = scope.statements.filterIsInstance<VarDecl>().filter { it.type == VarDeclType.VAR }
|
val decls = scope.statements.filterIsInstance<VarDecl>().filter { it.type == VarDeclType.VAR }
|
||||||
subroutineVariables.addAll(decls.map { it.name to it })
|
subroutineVariables.addAll(decls.map { it.name to it })
|
||||||
|
|
||||||
val sub = scope.definingSubroutine
|
|
||||||
if (sub != null) {
|
|
||||||
// move any remaining vardecls of the scope into the upper scope. Make sure the position remains the same!
|
|
||||||
val replacements = mutableListOf<IAstModification>()
|
|
||||||
val movements = mutableListOf<IAstModification.InsertFirst>()
|
|
||||||
|
|
||||||
for(decl in decls) {
|
|
||||||
if(decl.value!=null && decl.datatype in NumericDatatypes) {
|
|
||||||
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
|
||||||
val assign = Assignment(target, decl.value!!, decl.position)
|
|
||||||
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
|
||||||
decl.value = null
|
|
||||||
decl.allowInitializeWithZero = false
|
|
||||||
} else {
|
|
||||||
replacements.add(IAstModification.Remove(decl, scope))
|
|
||||||
}
|
|
||||||
movements.add(IAstModification.InsertFirst(decl, sub))
|
|
||||||
}
|
|
||||||
return replacements + movements
|
|
||||||
}
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -146,7 +143,8 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// see if we can remove superfluous typecasts (outside of expressions)
|
// see if we can remove superfluous typecasts (outside of expressions)
|
||||||
// such as casting byte<->ubyte, word<->uword
|
// such as casting byte<->ubyte, word<->uword
|
||||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of.
|
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,
|
||||||
|
// UNLESS it's a str parameter in the containing subroutine - then we remove the typecast altogether
|
||||||
val sourceDt = typecast.expression.inferType(program).getOr(DataType.UNDEFINED)
|
val sourceDt = typecast.expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||||
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
|| typecast.type in WordDatatypes && sourceDt in WordDatatypes) {
|
||||||
@ -155,22 +153,23 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// Note: for various reasons (most importantly, code simplicity), the code generator assumes/requires
|
|
||||||
// that the types of assignment values and their target are the same,
|
|
||||||
// and that the types of both operands of a binaryexpression node are the same.
|
|
||||||
// So, it is not easily possible to remove the typecasts that are there to make these conditions true.
|
|
||||||
// The only place for now where we can do this is for:
|
|
||||||
// asmsub register pair parameter.
|
|
||||||
|
|
||||||
if(sourceDt in PassByReferenceDatatypes) {
|
if(sourceDt in PassByReferenceDatatypes) {
|
||||||
if(typecast.type==DataType.UWORD) {
|
if(typecast.type==DataType.UWORD) {
|
||||||
if(typecast.expression is IdentifierReference) {
|
val identifier = typecast.expression as? IdentifierReference
|
||||||
return listOf(IAstModification.ReplaceNode(
|
if(identifier!=null) {
|
||||||
|
return if(identifier.isSubroutineParameter(program)) {
|
||||||
|
listOf(IAstModification.ReplaceNode(
|
||||||
typecast,
|
typecast,
|
||||||
AddressOf(typecast.expression as IdentifierReference, typecast.position),
|
typecast.expression,
|
||||||
parent
|
parent
|
||||||
))
|
))
|
||||||
|
} else {
|
||||||
|
listOf(IAstModification.ReplaceNode(
|
||||||
|
typecast,
|
||||||
|
AddressOf(identifier, typecast.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
} else if(typecast.expression is IFunctionCall) {
|
} else if(typecast.expression is IFunctionCall) {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
typecast,
|
typecast,
|
||||||
@ -186,7 +185,15 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DuplicatedCode")
|
||||||
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
override fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
val prefixExpr = ifStatement.condition as? PrefixExpression
|
||||||
|
if(prefixExpr!=null && prefixExpr.operator=="not") {
|
||||||
|
// if not x -> if x==0
|
||||||
|
val booleanExpr = BinaryExpression(prefixExpr.expression, "==", NumericLiteralValue.optimalInteger(0, ifStatement.condition.position), ifStatement.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(ifStatement.condition, booleanExpr, ifStatement))
|
||||||
|
}
|
||||||
|
|
||||||
val binExpr = ifStatement.condition as? BinaryExpression
|
val binExpr = ifStatement.condition as? BinaryExpression
|
||||||
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
// if x -> if x!=0, if x+5 -> if x+5 != 0
|
// if x -> if x!=0, if x+5 -> if x+5 != 0
|
||||||
@ -197,52 +204,20 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
if((binExpr.operator=="==" || binExpr.operator=="!=") &&
|
if((binExpr.operator=="==" || binExpr.operator=="!=") &&
|
||||||
(binExpr.left as? NumericLiteralValue)?.number==0 &&
|
(binExpr.left as? NumericLiteralValue)?.number==0 &&
|
||||||
(binExpr.right as? NumericLiteralValue)?.number!=0)
|
(binExpr.right as? NumericLiteralValue)?.number!=0)
|
||||||
throw CompilerException("if 0==X should have been swapped to if X==0")
|
throw InternalCompilerException("if 0==X should have been swapped to if X==0")
|
||||||
|
|
||||||
// split the conditional expression into separate variables if the operand(s) is not simple.
|
|
||||||
// DISABLED FOR NOW AS IT GENEREATES LARGER CODE IN THE SIMPLE CASES LIKE IF X {...} or IF NOT X {...}
|
|
||||||
// val modifications = mutableListOf<IAstModification>()
|
|
||||||
// if(!binExpr.left.isSimple) {
|
|
||||||
// val sub = binExpr.definingSubroutine()!!
|
|
||||||
// val (variable, isNew, assignment) = addIfOperandVar(sub, "left", binExpr.left)
|
|
||||||
// if(isNew)
|
|
||||||
// modifications.add(IAstModification.InsertFirst(variable, sub))
|
|
||||||
// modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope))
|
|
||||||
// modifications.add(IAstModification.ReplaceNode(binExpr.left, IdentifierReference(listOf(variable.name), binExpr.position), binExpr))
|
|
||||||
// addedIfConditionVars.add(Pair(sub, variable.name))
|
|
||||||
// }
|
|
||||||
// if(!binExpr.right.isSimple) {
|
|
||||||
// val sub = binExpr.definingSubroutine()!!
|
|
||||||
// val (variable, isNew, assignment) = addIfOperandVar(sub, "right", binExpr.right)
|
|
||||||
// if(isNew)
|
|
||||||
// modifications.add(IAstModification.InsertFirst(variable, sub))
|
|
||||||
// modifications.add(IAstModification.InsertBefore(ifStatement, assignment, parent as INameScope))
|
|
||||||
// modifications.add(IAstModification.ReplaceNode(binExpr.right, IdentifierReference(listOf(variable.name), binExpr.position), binExpr))
|
|
||||||
// addedIfConditionVars.add(Pair(sub, variable.name))
|
|
||||||
// }
|
|
||||||
// return modifications
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
// private fun addIfOperandVar(sub: Subroutine, side: String, operand: Expression): Triple<VarDecl, Boolean, Assignment> {
|
@Suppress("DuplicatedCode")
|
||||||
// val dt = operand.inferType(program).typeOrElse(DataType.UNDEFINED)
|
|
||||||
// val varname = "prog8_ifvar_${side}_${dt.name.toLowerCase()}"
|
|
||||||
// val tgt = AssignTarget(IdentifierReference(listOf(varname), operand.position), null, null, operand.position)
|
|
||||||
// val assign = Assignment(tgt, operand, operand.position)
|
|
||||||
// if(Pair(sub, varname) in addedIfConditionVars) {
|
|
||||||
// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position)
|
|
||||||
// return Triple(vardecl, false, assign)
|
|
||||||
// }
|
|
||||||
// val existing = sub.statements.firstOrNull { it is VarDecl && it.name == varname} as VarDecl?
|
|
||||||
// return if (existing == null) {
|
|
||||||
// val vardecl = VarDecl(VarDeclType.VAR, dt, ZeropageWish.DONTCARE, null, varname, null, null, false, true, operand.position)
|
|
||||||
// Triple(vardecl, true, assign)
|
|
||||||
// } else {
|
|
||||||
// Triple(existing, false, assign)
|
|
||||||
// }
|
|
||||||
// }
|
|
||||||
|
|
||||||
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val prefixExpr = untilLoop.condition as? PrefixExpression
|
||||||
|
if(prefixExpr!=null && prefixExpr.operator=="not") {
|
||||||
|
// until not x -> until x==0
|
||||||
|
val booleanExpr = BinaryExpression(prefixExpr.expression, "==", NumericLiteralValue.optimalInteger(0, untilLoop.condition.position), untilLoop.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(untilLoop.condition, booleanExpr, untilLoop))
|
||||||
|
}
|
||||||
|
|
||||||
val binExpr = untilLoop.condition as? BinaryExpression
|
val binExpr = untilLoop.condition as? BinaryExpression
|
||||||
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
// until x -> until x!=0, until x+5 -> until x+5 != 0
|
// until x -> until x!=0, until x+5 -> until x+5 != 0
|
||||||
@ -252,7 +227,15 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Suppress("DuplicatedCode")
|
||||||
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
override fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val prefixExpr = whileLoop.condition as? PrefixExpression
|
||||||
|
if(prefixExpr!=null && prefixExpr.operator=="not") {
|
||||||
|
// while not x -> while x==0
|
||||||
|
val booleanExpr = BinaryExpression(prefixExpr.expression, "==", NumericLiteralValue.optimalInteger(0, whileLoop.condition.position), whileLoop.condition.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(whileLoop.condition, booleanExpr, whileLoop))
|
||||||
|
}
|
||||||
|
|
||||||
val binExpr = whileLoop.condition as? BinaryExpression
|
val binExpr = whileLoop.condition as? BinaryExpression
|
||||||
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
if(binExpr==null || binExpr.operator !in comparisonOperators) {
|
||||||
// while x -> while x!=0, while x+5 -> while x+5 != 0
|
// while x -> while x!=0, while x+5 -> while x+5 != 0
|
||||||
@ -347,7 +330,7 @@ internal class BeforeAsmGenerationAstChanger(val program: Program, val errors: I
|
|||||||
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
// assign the indexing expression to the helper variable, but only if that hasn't been done already
|
||||||
val target = AssignTarget(IdentifierReference(listOf("cx16", register), expr.indexer.position), null, null, expr.indexer.position)
|
val target = AssignTarget(IdentifierReference(listOf("cx16", register), expr.indexer.position), null, null, expr.indexer.position)
|
||||||
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
|
val assign = Assignment(target, expr.indexer.indexExpr, expr.indexer.position)
|
||||||
modifications.add(IAstModification.InsertBefore(statement, assign, statement.definingScope))
|
modifications.add(IAstModification.InsertBefore(statement, assign, statement.parent as IStatementContainer))
|
||||||
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
|
modifications.add(IAstModification.ReplaceNode(expr.indexer.indexExpr, target.identifier!!.copy(), expr.indexer))
|
||||||
return modifications
|
return modifications
|
||||||
}
|
}
|
||||||
|
@ -1,9 +1,8 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import com.github.michaelbull.result.*
|
import com.github.michaelbull.result.*
|
||||||
import prog8.ast.AstToSourceCode
|
import prog8.ast.AstToSourceTextConverter
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.IMemSizer
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
@ -11,73 +10,39 @@ import prog8.ast.expressions.Expression
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.compiler.astprocessing.*
|
import prog8.compiler.astprocessing.*
|
||||||
import prog8.compiler.functions.*
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
||||||
import prog8.compiler.target.asmGeneratorFor
|
import prog8.compilerinterface.*
|
||||||
import prog8.optimizer.*
|
import prog8.optimizer.*
|
||||||
import prog8.parser.ParseError
|
import prog8.parser.ParseError
|
||||||
import prog8.parser.ParsingFailedError
|
|
||||||
import prog8.parser.SourceCode
|
|
||||||
import prog8.parser.SourceCode.Companion.libraryFilePrefix
|
|
||||||
import java.io.File
|
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.nameWithoutExtension
|
import kotlin.io.path.nameWithoutExtension
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
|
||||||
enum class OutputType {
|
|
||||||
RAW,
|
|
||||||
PRG
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class LauncherType {
|
|
||||||
BASIC,
|
|
||||||
NONE
|
|
||||||
}
|
|
||||||
|
|
||||||
enum class ZeropageType {
|
|
||||||
BASICSAFE,
|
|
||||||
FLOATSAFE,
|
|
||||||
KERNALSAFE,
|
|
||||||
FULL,
|
|
||||||
DONTUSE
|
|
||||||
}
|
|
||||||
|
|
||||||
data class CompilationOptions(val output: OutputType,
|
|
||||||
val launcher: LauncherType,
|
|
||||||
val zeropage: ZeropageType,
|
|
||||||
val zpReserved: List<IntRange>,
|
|
||||||
val floats: Boolean,
|
|
||||||
val noSysInit: Boolean,
|
|
||||||
val compTarget: ICompilationTarget) {
|
|
||||||
var slowCodegenWarnings = false
|
|
||||||
var optimize = false
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class CompilerException(message: String?) : Exception(message)
|
|
||||||
|
|
||||||
class CompilationResult(val success: Boolean,
|
class CompilationResult(val success: Boolean,
|
||||||
val programAst: Program,
|
val program: Program,
|
||||||
val programName: String,
|
val programName: String,
|
||||||
val compTarget: ICompilationTarget,
|
val compTarget: ICompilationTarget,
|
||||||
val importedFiles: List<Path>)
|
val importedFiles: List<Path>)
|
||||||
|
|
||||||
|
|
||||||
|
// TODO refactor the gigantic list of parameters
|
||||||
fun compileProgram(filepath: Path,
|
fun compileProgram(filepath: Path,
|
||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
|
optimizeFloatExpressions: Boolean,
|
||||||
writeAssembly: Boolean,
|
writeAssembly: Boolean,
|
||||||
slowCodegenWarnings: Boolean,
|
slowCodegenWarnings: Boolean,
|
||||||
|
quietAssembler: Boolean,
|
||||||
compilationTarget: String,
|
compilationTarget: String,
|
||||||
sourceDirs: List<String>,
|
sourceDirs: List<String>,
|
||||||
outputDir: Path): CompilationResult {
|
outputDir: Path,
|
||||||
|
errors: IErrorReporter = ErrorReporter()): CompilationResult {
|
||||||
var programName = ""
|
var programName = ""
|
||||||
lateinit var programAst: Program
|
lateinit var program: Program
|
||||||
lateinit var importedFiles: List<Path>
|
lateinit var importedFiles: List<Path>
|
||||||
val errors = ErrorReporter()
|
|
||||||
|
|
||||||
val compTarget =
|
val compTarget =
|
||||||
when(compilationTarget) {
|
when(compilationTarget) {
|
||||||
@ -89,31 +54,35 @@ fun compileProgram(filepath: Path,
|
|||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, sourceDirs)
|
val (programresult, compilationOptions, imported) = parseImports(filepath, errors, compTarget, sourceDirs)
|
||||||
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
|
with(compilationOptions) {
|
||||||
compilationOptions.optimize = optimize
|
this.slowCodegenWarnings = slowCodegenWarnings
|
||||||
programAst = ast
|
this.optimize = optimize
|
||||||
|
this.optimizeFloatExpressions = optimizeFloatExpressions
|
||||||
|
}
|
||||||
|
program = programresult
|
||||||
importedFiles = imported
|
importedFiles = imported
|
||||||
processAst(programAst, errors, compilationOptions)
|
processAst(program, errors, compilationOptions)
|
||||||
if (compilationOptions.optimize)
|
if (compilationOptions.optimize)
|
||||||
optimizeAst(
|
optimizeAst(
|
||||||
programAst,
|
program,
|
||||||
|
compilationOptions,
|
||||||
errors,
|
errors,
|
||||||
BuiltinFunctionsFacade(BuiltinFunctions),
|
BuiltinFunctionsFacade(BuiltinFunctions),
|
||||||
compTarget,
|
compTarget
|
||||||
compilationOptions
|
|
||||||
)
|
)
|
||||||
postprocessAst(programAst, errors, compilationOptions)
|
postprocessAst(program, errors, compilationOptions)
|
||||||
|
|
||||||
// printAst(programAst)
|
// println("*********** AST BEFORE ASSEMBLYGEN *************")
|
||||||
|
// printAst(program)
|
||||||
|
|
||||||
if (writeAssembly) {
|
if (writeAssembly) {
|
||||||
val result = writeAssembly(programAst, errors, outputDir, compilationOptions)
|
val result = writeAssembly(program, errors, outputDir, quietAssembler, compilationOptions)
|
||||||
when (result) {
|
when (result) {
|
||||||
is WriteAssemblyResult.Ok -> programName = result.filename
|
is WriteAssemblyResult.Ok -> programName = result.filename
|
||||||
is WriteAssemblyResult.Fail -> {
|
is WriteAssemblyResult.Fail -> {
|
||||||
System.err.println(result.error)
|
System.err.println(result.error)
|
||||||
return CompilationResult(false, programAst, programName, compTarget, importedFiles)
|
return CompilationResult(false, program, programName, compTarget, importedFiles)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -121,14 +90,20 @@ fun compileProgram(filepath: Path,
|
|||||||
System.out.flush()
|
System.out.flush()
|
||||||
System.err.flush()
|
System.err.flush()
|
||||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||||
return CompilationResult(true, programAst, programName, compTarget, importedFiles)
|
return CompilationResult(true, program, programName, compTarget, importedFiles)
|
||||||
} catch (px: ParseError) {
|
} catch (px: ParseError) {
|
||||||
System.err.print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
System.err.println("${px.position.toClickableStr()} parse error: ${px.message}".trim())
|
||||||
System.err.print("\u001b[0m") // reset
|
System.err.print("\u001b[0m") // reset
|
||||||
} catch (pfx: ParsingFailedError) {
|
} catch (ac: AbortCompilation) {
|
||||||
|
if(!ac.message.isNullOrEmpty()) {
|
||||||
|
System.err.print("\u001b[91m") // bright red
|
||||||
|
System.err.println(ac.message)
|
||||||
|
System.err.print("\u001b[0m") // reset
|
||||||
|
}
|
||||||
|
} catch (nsf: NoSuchFileException) {
|
||||||
System.err.print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
System.err.println(pfx.message)
|
System.err.println("File not found: ${nsf.message}")
|
||||||
System.err.print("\u001b[0m") // reset
|
System.err.print("\u001b[0m") // reset
|
||||||
} catch (ax: AstException) {
|
} catch (ax: AstException) {
|
||||||
System.err.print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
@ -148,7 +123,7 @@ fun compileProgram(filepath: Path,
|
|||||||
throw x
|
throw x
|
||||||
}
|
}
|
||||||
|
|
||||||
val failedProgram = Program("failed", BuiltinFunctionsFacade(BuiltinFunctions), compTarget)
|
val failedProgram = Program("failed", BuiltinFunctionsFacade(BuiltinFunctions), compTarget, compTarget)
|
||||||
return CompilationResult(false, failedProgram, programName, compTarget, emptyList())
|
return CompilationResult(false, failedProgram, programName, compTarget, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -158,13 +133,13 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
|
|||||||
override val names = functions.keys
|
override val names = functions.keys
|
||||||
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
|
override val purefunctionNames = functions.filter { it.value.pure }.map { it.key }.toSet()
|
||||||
|
|
||||||
override fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue? {
|
override fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue? {
|
||||||
val func = BuiltinFunctions[name]
|
val func = BuiltinFunctions[name]
|
||||||
if(func!=null) {
|
if(func!=null) {
|
||||||
val exprfunc = func.constExpressionFunc
|
val exprfunc = func.constExpressionFunc
|
||||||
if(exprfunc!=null) {
|
if(exprfunc!=null) {
|
||||||
return try {
|
return try {
|
||||||
exprfunc(args, position, program, memsizer)
|
exprfunc(args, position, program)
|
||||||
} catch(x: NotConstArgumentException) {
|
} catch(x: NotConstArgumentException) {
|
||||||
// const-evaluating the builtin function call failed.
|
// const-evaluating the builtin function call failed.
|
||||||
null
|
null
|
||||||
@ -188,21 +163,18 @@ fun parseImports(filepath: Path,
|
|||||||
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
sourceDirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
println("Compiler target: ${compTarget.name}. Parsing...")
|
println("Compiler target: ${compTarget.name}. Parsing...")
|
||||||
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
|
||||||
val programAst = Program(filepath.nameWithoutExtension, bf, compTarget)
|
val program = Program(filepath.nameWithoutExtension, bf, compTarget, compTarget)
|
||||||
bf.program = programAst
|
bf.program = program
|
||||||
|
|
||||||
val importer = ModuleImporter(programAst, compTarget.name, errors, sourceDirs)
|
val importer = ModuleImporter(program, compTarget.name, errors, sourceDirs)
|
||||||
val importedModuleResult = importer.importModule(filepath)
|
val importedModuleResult = importer.importModule(filepath)
|
||||||
importedModuleResult.onFailure { throw it }
|
importedModuleResult.onFailure { throw it }
|
||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
val importedFiles = programAst.modules.map { it.source }
|
val importedFiles = program.modules.map { it.source }
|
||||||
.filter { it.isFromFilesystem }
|
.filter { it.isFromFilesystem }
|
||||||
.map { Path(it.origin) }
|
.map { Path(it.origin) }
|
||||||
val compilerOptions = determineCompilationOptions(programAst, compTarget)
|
val compilerOptions = determineCompilationOptions(program, compTarget)
|
||||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
|
||||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
|
||||||
|
|
||||||
// depending on the machine and compiler options we may have to include some libraries
|
// depending on the machine and compiler options we may have to include some libraries
|
||||||
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
for(lib in compTarget.machine.importLibs(compilerOptions, compTarget.name))
|
||||||
importer.importLibraryModule(lib)
|
importer.importLibraryModule(lib)
|
||||||
@ -210,8 +182,12 @@ fun parseImports(filepath: Path,
|
|||||||
// always import prog8_lib and math
|
// always import prog8_lib and math
|
||||||
importer.importLibraryModule("math")
|
importer.importLibraryModule("math")
|
||||||
importer.importLibraryModule("prog8_lib")
|
importer.importLibraryModule("prog8_lib")
|
||||||
|
|
||||||
|
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||||
|
errors.err("BASIC launcher requires output type PRG", program.toplevelModule.position)
|
||||||
errors.report()
|
errors.report()
|
||||||
return Triple(programAst, compilerOptions, importedFiles)
|
|
||||||
|
return Triple(program, compilerOptions, importedFiles)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
|
||||||
@ -274,44 +250,44 @@ fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
// perform initial syntax checks and processings
|
// perform initial syntax checks and processings
|
||||||
println("Processing for target ${compilerOptions.compTarget.name}...")
|
println("Processing for target ${compilerOptions.compTarget.name}...")
|
||||||
programAst.checkIdentifiers(errors, compilerOptions)
|
program.preprocessAst()
|
||||||
|
program.checkIdentifiers(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
|
// TODO: turning char literals into UBYTEs via an encoding should really happen in code gen - but for that we'd need DataType.CHAR
|
||||||
// NOTE: we will then lose the opportunity to do constant-folding on any expression containing a char literal, but how often will those occur?
|
// ...but what do we gain from this? We can leave it as it is now: where a char literal is no more than syntactic sugar for an UBYTE value.
|
||||||
// Also they might be optimized away eventually in codegen or by the assembler even
|
// By introduction a CHAR dt, we will also lose the opportunity to do constant-folding on any expression containing a char literal.
|
||||||
programAst.charLiteralsToUByteLiterals(errors, compilerOptions.compTarget)
|
program.charLiteralsToUByteLiterals(compilerOptions.compTarget)
|
||||||
|
program.constantFold(errors, compilerOptions.compTarget)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.constantFold(errors, compilerOptions.compTarget)
|
program.reorderStatements(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.reorderStatements(errors)
|
program.addTypecasts(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.addTypecasts(errors)
|
program.variousCleanups(program, errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.variousCleanups(programAst, errors)
|
program.checkValid(errors, compilerOptions)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
|
program.checkIdentifiers(errors, compilerOptions)
|
||||||
errors.report()
|
|
||||||
programAst.checkIdentifiers(errors, compilerOptions)
|
|
||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget, options: CompilationOptions) {
|
private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, errors: IErrorReporter, functions: IBuiltinFunctions, compTarget: ICompilationTarget) {
|
||||||
// optimize the parse tree
|
// optimize the parse tree
|
||||||
println("Optimizing...")
|
println("Optimizing...")
|
||||||
|
|
||||||
val remover = UnusedCodeRemover(programAst, errors, compTarget)
|
val remover = UnusedCodeRemover(program, errors, compTarget)
|
||||||
remover.visit(programAst)
|
remover.visit(program)
|
||||||
remover.applyModifications()
|
remover.applyModifications()
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
// keep optimizing expressions and statements until no more steps remain
|
// keep optimizing expressions and statements until no more steps remain
|
||||||
val optsDone1 = programAst.simplifyExpressions()
|
val optsDone1 = program.simplifyExpressions()
|
||||||
val optsDone2 = programAst.splitBinaryExpressions(compTarget)
|
val optsDone2 = program.splitBinaryExpressions(compilerOptions, compTarget)
|
||||||
val optsDone3 = programAst.optimizeStatements(errors, functions, compTarget)
|
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
||||||
programAst.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
program.constantFold(errors, compTarget) // because simplified statements and expressions can result in more constants that can be folded away
|
||||||
errors.report()
|
errors.report()
|
||||||
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
if (optsDone1 + optsDone2 + optsDone3 == 0)
|
||||||
break
|
break
|
||||||
@ -320,17 +296,17 @@ private fun optimizeAst(programAst: Program, errors: IErrorReporter, functions:
|
|||||||
errors.report()
|
errors.report()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun postprocessAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
private fun postprocessAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
programAst.addTypecasts(errors)
|
program.addTypecasts(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.variousCleanups(programAst, errors)
|
program.variousCleanups(program, errors)
|
||||||
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget) // check if final tree is still valid
|
program.checkValid(errors, compilerOptions) // check if final tree is still valid
|
||||||
errors.report()
|
errors.report()
|
||||||
val callGraph = CallGraph(programAst)
|
val callGraph = CallGraph(program)
|
||||||
callGraph.checkRecursiveCalls(errors)
|
callGraph.checkRecursiveCalls(errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
programAst.verifyFunctionArgTypes()
|
program.verifyFunctionArgTypes()
|
||||||
programAst.moveMainAndStartToFirst()
|
program.moveMainAndStartToFirst()
|
||||||
}
|
}
|
||||||
|
|
||||||
private sealed class WriteAssemblyResult {
|
private sealed class WriteAssemblyResult {
|
||||||
@ -338,58 +314,56 @@ private sealed class WriteAssemblyResult {
|
|||||||
class Fail(val error: String): WriteAssemblyResult()
|
class Fail(val error: String): WriteAssemblyResult()
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun writeAssembly(programAst: Program,
|
private fun writeAssembly(program: Program,
|
||||||
errors: IErrorReporter,
|
errors: IErrorReporter,
|
||||||
outputDir: Path,
|
outputDir: Path,
|
||||||
compilerOptions: CompilationOptions): WriteAssemblyResult {
|
quietAssembler: Boolean,
|
||||||
|
compilerOptions: CompilationOptions
|
||||||
|
): WriteAssemblyResult {
|
||||||
// asm generation directly from the Ast
|
// asm generation directly from the Ast
|
||||||
programAst.processAstBeforeAsmGeneration(errors, compilerOptions.compTarget)
|
program.processAstBeforeAsmGeneration(compilerOptions, errors)
|
||||||
errors.report()
|
errors.report()
|
||||||
|
|
||||||
// printAst(programAst)
|
// println("*********** AST RIGHT BEFORE ASM GENERATION *************")
|
||||||
|
// printAst(program)
|
||||||
|
|
||||||
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
compilerOptions.compTarget.machine.initializeZeropage(compilerOptions)
|
||||||
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
val assembly = asmGeneratorFor(compilerOptions.compTarget,
|
||||||
programAst,
|
program,
|
||||||
errors,
|
errors,
|
||||||
compilerOptions.compTarget.machine.zeropage,
|
compilerOptions.compTarget.machine.zeropage,
|
||||||
compilerOptions,
|
compilerOptions,
|
||||||
outputDir).compileToAssembly()
|
outputDir).compileToAssembly()
|
||||||
|
errors.report()
|
||||||
|
|
||||||
return if(assembly.valid && errors.noErrors()) {
|
return if(assembly.valid && errors.noErrors()) {
|
||||||
val assemblerReturnStatus = assembly.assemble(compilerOptions)
|
val assemblerReturnStatus = assembly.assemble(quietAssembler, compilerOptions)
|
||||||
if(assemblerReturnStatus!=0)
|
if(assemblerReturnStatus!=0)
|
||||||
WriteAssemblyResult.Fail("assembler step failed with return code $assemblerReturnStatus")
|
WriteAssemblyResult.Fail("assembler step failed with return code $assemblerReturnStatus")
|
||||||
else {
|
else {
|
||||||
errors.report()
|
|
||||||
WriteAssemblyResult.Ok(assembly.name)
|
WriteAssemblyResult.Ok(assembly.name)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
errors.report()
|
|
||||||
WriteAssemblyResult.Fail("compiler failed with errors")
|
WriteAssemblyResult.Fail("compiler failed with errors")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printAst(programAst: Program) {
|
fun printAst(program: Program) {
|
||||||
println()
|
println()
|
||||||
val printer = AstToSourceCode(::print, programAst)
|
val printer = AstToSourceTextConverter(::print, program)
|
||||||
printer.visit(programAst)
|
printer.visit(program)
|
||||||
println()
|
println()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun loadAsmIncludeFile(filename: String, source: SourceCode): Result<String, NoSuchFileException> {
|
internal fun asmGeneratorFor(
|
||||||
return if (filename.startsWith(libraryFilePrefix)) {
|
compTarget: ICompilationTarget,
|
||||||
return runCatching {
|
program: Program,
|
||||||
val stream = object {}.javaClass.getResourceAsStream("/prog8lib/${filename.substring(libraryFilePrefix.length)}") // TODO handle via SourceCode
|
errors: IErrorReporter,
|
||||||
stream!!.bufferedReader().use { r -> r.readText() }
|
zp: Zeropage,
|
||||||
}.mapError { NoSuchFileException(File(filename)) }
|
options: CompilationOptions,
|
||||||
} else {
|
outputDir: Path
|
||||||
// first try in the isSameAs folder as where the containing file was imported from
|
): IAssemblyGenerator
|
||||||
val sib = Path(source.origin).resolveSibling(filename)
|
{
|
||||||
|
// at the moment we only have one code generation backend (for 6502 and 65c02)
|
||||||
if (sib.toFile().isFile)
|
return AsmGen(program, errors, zp, options, compTarget, outputDir)
|
||||||
Ok(sib.toFile().readText())
|
|
||||||
else
|
|
||||||
Ok(File(filename).readText())
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,7 @@
|
|||||||
package prog8.compiler
|
package prog8.compiler
|
||||||
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
interface IErrorReporter {
|
|
||||||
fun err(msg: String, position: Position)
|
|
||||||
fun warn(msg: String, position: Position)
|
|
||||||
fun noErrors(): Boolean
|
|
||||||
fun report()
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal class ErrorReporter: IErrorReporter {
|
internal class ErrorReporter: IErrorReporter {
|
||||||
private enum class MessageSeverity {
|
private enum class MessageSeverity {
|
||||||
@ -33,24 +24,29 @@ internal class ErrorReporter: IErrorReporter {
|
|||||||
var numErrors = 0
|
var numErrors = 0
|
||||||
var numWarnings = 0
|
var numWarnings = 0
|
||||||
messages.forEach {
|
messages.forEach {
|
||||||
|
val printer = when(it.severity) {
|
||||||
|
MessageSeverity.WARNING -> System.out
|
||||||
|
MessageSeverity.ERROR -> System.err
|
||||||
|
}
|
||||||
when(it.severity) {
|
when(it.severity) {
|
||||||
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
MessageSeverity.ERROR -> printer.print("\u001b[91m") // bright red
|
||||||
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
MessageSeverity.WARNING -> printer.print("\u001b[93m") // bright yellow
|
||||||
}
|
}
|
||||||
val msg = "${it.position.toClickableStr()} ${it.severity} ${it.message}".trim()
|
val msg = "${it.position.toClickableStr()} ${it.severity} ${it.message}".trim()
|
||||||
if(msg !in alreadyReportedMessages) {
|
if(msg !in alreadyReportedMessages) {
|
||||||
System.err.println(msg)
|
printer.println(msg)
|
||||||
alreadyReportedMessages.add(msg)
|
alreadyReportedMessages.add(msg)
|
||||||
when(it.severity) {
|
when(it.severity) {
|
||||||
MessageSeverity.WARNING -> numWarnings++
|
MessageSeverity.WARNING -> numWarnings++
|
||||||
MessageSeverity.ERROR -> numErrors++
|
MessageSeverity.ERROR -> numErrors++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.err.print("\u001b[0m") // reset color
|
printer.print("\u001b[0m") // reset color
|
||||||
}
|
}
|
||||||
|
System.out.flush()
|
||||||
|
System.err.flush()
|
||||||
messages.clear()
|
messages.clear()
|
||||||
if(numErrors>0)
|
finalizeNumErrors(numErrors, numWarnings)
|
||||||
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
override fun noErrors() = messages.none { it.severity==MessageSeverity.ERROR }
|
@ -7,6 +7,7 @@ import prog8.ast.base.Position
|
|||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.statements.DirectiveArg
|
import prog8.ast.statements.DirectiveArg
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.parser.Prog8Parser
|
import prog8.parser.Prog8Parser
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@ -33,7 +34,7 @@ class ModuleImporter(private val program: Program,
|
|||||||
val srcPath = when (candidates.size) {
|
val srcPath = when (candidates.size) {
|
||||||
0 -> return Err(NoSuchFileException(
|
0 -> return Err(NoSuchFileException(
|
||||||
file = filePath.normalize().toFile(),
|
file = filePath.normalize().toFile(),
|
||||||
reason = "searched in $searchIn"))
|
reason = "Searched in $searchIn"))
|
||||||
1 -> candidates.first()
|
1 -> candidates.first()
|
||||||
else -> candidates.first() // when more candiates, pick the one from the first location
|
else -> candidates.first() // when more candiates, pick the one from the first location
|
||||||
}
|
}
|
||||||
@ -105,7 +106,7 @@ class ModuleImporter(private val program: Program,
|
|||||||
importModule(it)
|
importModule(it)
|
||||||
},
|
},
|
||||||
failure = {
|
failure = {
|
||||||
errors.err("no module found with name $moduleName", import.position)
|
errors.err("no module found with name $moduleName. Searched in: $sourcePaths (and internal libraries)", import.position)
|
||||||
return null
|
return null
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
@ -142,7 +143,6 @@ class ModuleImporter(private val program: Program,
|
|||||||
} else {
|
} else {
|
||||||
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
|
val dropCurDir = if(sourcePaths.isNotEmpty() && sourcePaths[0].name == ".") 1 else 0
|
||||||
sourcePaths.drop(dropCurDir) +
|
sourcePaths.drop(dropCurDir) +
|
||||||
// TODO: won't work until Prog8Parser is fixed s.t. it fully initializes the modules it returns. // hm, what won't work?)
|
|
||||||
listOf(Path(importingModule.position.file).parent ?: Path("")) +
|
listOf(Path(importingModule.position.file).parent ?: Path("")) +
|
||||||
listOf(Path(".", "prog8lib"))
|
listOf(Path(".", "prog8lib"))
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,22 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compilerinterface.*
|
||||||
import prog8.compiler.IErrorReporter
|
|
||||||
import prog8.compiler.ZeropageType
|
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
|
||||||
import prog8.compiler.functions.builtinFunctionReturnType
|
|
||||||
import prog8.compiler.target.ICompilationTarget
|
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.util.*
|
import java.util.*
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.Path
|
||||||
|
|
||||||
internal class AstChecker(private val program: Program,
|
internal class AstChecker(private val program: Program,
|
||||||
private val compilerOptions: CompilationOptions,
|
|
||||||
private val errors: IErrorReporter,
|
private val errors: IErrorReporter,
|
||||||
private val compTarget: ICompilationTarget
|
private val compilerOptions: CompilationOptions
|
||||||
) : IAstVisitor {
|
) : IAstVisitor {
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
override fun visit(program: Program) {
|
||||||
@ -31,7 +26,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(mainBlocks.size>1)
|
if(mainBlocks.size>1)
|
||||||
errors.err("more than one 'main' block", mainBlocks[0].position)
|
errors.err("more than one 'main' block", mainBlocks[0].position)
|
||||||
if(mainBlocks.isEmpty())
|
if(mainBlocks.isEmpty())
|
||||||
errors.err("there is no 'main' block", program.modules.firstOrNull()?.position ?: program.position)
|
errors.err("there is no 'main' block", program.modules.firstOrNull()?.position ?: Position.DUMMY)
|
||||||
|
|
||||||
for(mainBlock in mainBlocks) {
|
for(mainBlock in mainBlocks) {
|
||||||
val startSub = mainBlock.subScope("start") as? Subroutine
|
val startSub = mainBlock.subScope("start") as? Subroutine
|
||||||
@ -196,8 +191,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
is Label,
|
is Label,
|
||||||
is VarDecl,
|
is VarDecl,
|
||||||
is InlineAssembly,
|
is InlineAssembly,
|
||||||
is INameScope,
|
is IStatementContainer,
|
||||||
is NopStatement -> true
|
is NopStatement -> true
|
||||||
|
is Assignment -> {
|
||||||
|
val target = statement.target.identifier!!.targetStatement(program)
|
||||||
|
target === statement.previousSibling() // an initializer assignment is okay
|
||||||
|
}
|
||||||
else -> false
|
else -> false
|
||||||
}
|
}
|
||||||
if (!ok) {
|
if (!ok) {
|
||||||
@ -217,7 +216,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
super.visit(label)
|
super.visit(label)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun hasReturnOrJump(scope: INameScope): Boolean {
|
private fun hasReturnOrJump(scope: IStatementContainer): Boolean {
|
||||||
class Searcher: IAstVisitor
|
class Searcher: IAstVisitor
|
||||||
{
|
{
|
||||||
var count=0
|
var count=0
|
||||||
@ -245,8 +244,8 @@ internal class AstChecker(private val program: Program,
|
|||||||
if(subroutine.name in BuiltinFunctions)
|
if(subroutine.name in BuiltinFunctions)
|
||||||
err("cannot redefine a built-in function")
|
err("cannot redefine a built-in function")
|
||||||
|
|
||||||
if(subroutine.parameters.size>16)
|
if(subroutine.parameters.size>6 && !subroutine.isAsmSubroutine)
|
||||||
err("subroutines are limited to 16 parameters")
|
errors.warn("subroutine has a large number of parameters, this slows down code execution a lot", subroutine.position)
|
||||||
|
|
||||||
val uniqueNames = subroutine.parameters.asSequence().map { it.name }.toSet()
|
val uniqueNames = subroutine.parameters.asSequence().map { it.name }.toSet()
|
||||||
if(uniqueNames.size!=subroutine.parameters.size)
|
if(uniqueNames.size!=subroutine.parameters.size)
|
||||||
@ -290,7 +289,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
else if(param.second.registerOrPair in arrayOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||||
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
if (param.first.type != DataType.UWORD && param.first.type != DataType.WORD
|
||||||
&& param.first.type != DataType.STR && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT)
|
&& param.first.type != DataType.STR && param.first.type !in ArrayDatatypes && param.first.type != DataType.FLOAT)
|
||||||
err("parameter '${param.first.name}' should be (u)word/address")
|
err("parameter '${param.first.name}' should be (u)word (an address) or str")
|
||||||
}
|
}
|
||||||
else if(param.second.statusflag!=null) {
|
else if(param.second.statusflag!=null) {
|
||||||
if (param.first.type != DataType.UBYTE)
|
if (param.first.type != DataType.UBYTE)
|
||||||
@ -382,10 +381,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
err("can only use Carry as status flag parameter")
|
err("can only use Carry as status flag parameter")
|
||||||
|
|
||||||
} else {
|
} else {
|
||||||
// Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
// Non-string Pass-by-reference datatypes can not occur as parameters to a subroutine directly
|
||||||
// Instead, their reference (address) should be passed (as an UWORD).
|
// Instead, their reference (address) should be passed (as an UWORD).
|
||||||
if(subroutine.parameters.any{it.type in PassByReferenceDatatypes }) {
|
for(p in subroutine.parameters) {
|
||||||
err("Pass-by-reference types (str, array) cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
|
if(p.type in PassByReferenceDatatypes && p.type != DataType.STR) {
|
||||||
|
err("Non-string pass-by-reference types cannot occur as a parameter type directly. Instead, use an uword to receive their address, or access the variable from the outer scope directly.")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -453,7 +454,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val targetIdentifier = assignTarget.identifier
|
val targetIdentifier = assignTarget.identifier
|
||||||
if (targetIdentifier != null) {
|
if (targetIdentifier != null) {
|
||||||
val targetName = targetIdentifier.nameInSource
|
val targetName = targetIdentifier.nameInSource
|
||||||
when (val targetSymbol = program.namespace.lookup(targetName, assignment)) {
|
when (val targetSymbol = assignment.definingScope.lookup(targetName)) {
|
||||||
null -> {
|
null -> {
|
||||||
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
errors.err("undefined symbol: ${targetIdentifier.nameInSource.joinToString(".")}", targetIdentifier.position)
|
||||||
return
|
return
|
||||||
@ -505,7 +506,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
fun err(msg: String, position: Position?=null) = errors.err(msg, position ?: decl.position)
|
fun err(msg: String) = errors.err(msg, decl.position)
|
||||||
|
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true)
|
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.indexExpr?.referencesIdentifier(decl.name) == true)
|
||||||
@ -586,10 +587,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
val numvalue = decl.value as? NumericLiteralValue
|
val numvalue = decl.value as? NumericLiteralValue
|
||||||
if(numvalue!=null) {
|
if(numvalue!=null) {
|
||||||
if (numvalue.type !in IntegerDatatypes || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
|
if (numvalue.type !in IntegerDatatypes || numvalue.number.toInt() < 0 || numvalue.number.toInt() > 65535) {
|
||||||
err("memory address must be valid integer 0..\$ffff", decl.value?.position)
|
err("memory address must be valid integer 0..\$ffff")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
err("value of memory mapped variable can only be a fixed number, perhaps you meant to use an address pointer type instead?", decl.value?.position)
|
err("value of memory mapped variable can only be a fixed number, perhaps you meant to use an address pointer type instead?")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -597,7 +598,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
val declValue = decl.value
|
val declValue = decl.value
|
||||||
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
if(declValue!=null && decl.type==VarDeclType.VAR) {
|
||||||
if (declValue.inferType(program) isnot decl.datatype) {
|
if (declValue.inferType(program) isnot decl.datatype) {
|
||||||
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})", declValue.position)
|
err("initialisation value has incompatible type (${declValue.inferType(program)}) for the variable (${decl.datatype})")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -626,10 +627,13 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// string assignment is not supported in a vard
|
|
||||||
if(decl.datatype==DataType.STR) {
|
if(decl.datatype==DataType.STR) {
|
||||||
if(decl.value==null)
|
if(decl.value==null) {
|
||||||
err("string var must be initialized with a string literal")
|
// complain about uninitialized str, but only if it's a regular variable
|
||||||
|
val parameter = (decl.parent as? Subroutine)?.parameters?.singleOrNull{ it.name==decl.name }
|
||||||
|
if(parameter==null)
|
||||||
|
err("string var must be initialized with a string literal")
|
||||||
|
}
|
||||||
else if (decl.type==VarDeclType.VAR && decl.value !is StringLiteralValue)
|
else if (decl.type==VarDeclType.VAR && decl.value !is StringLiteralValue)
|
||||||
err("string var can only be initialized with a string literal")
|
err("string var can only be initialized with a string literal")
|
||||||
}
|
}
|
||||||
@ -768,7 +772,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
override fun visit(char: CharLiteral) {
|
override fun visit(char: CharLiteral) {
|
||||||
try { // just *try* if it can be encoded, don't actually do it
|
try { // just *try* if it can be encoded, don't actually do it
|
||||||
compTarget.encodeString(char.value.toString(), char.altEncoding)
|
compilerOptions.compTarget.encodeString(char.value.toString(), char.altEncoding)
|
||||||
} catch (cx: CharConversionException) {
|
} catch (cx: CharConversionException) {
|
||||||
errors.err(cx.message ?: "can't encode character", char.position)
|
errors.err(cx.message ?: "can't encode character", char.position)
|
||||||
}
|
}
|
||||||
@ -780,7 +784,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
checkValueTypeAndRangeString(DataType.STR, string)
|
checkValueTypeAndRangeString(DataType.STR, string)
|
||||||
|
|
||||||
try { // just *try* if it can be encoded, don't actually do it
|
try { // just *try* if it can be encoded, don't actually do it
|
||||||
compTarget.encodeString(string.value, string.altEncoding)
|
compilerOptions.compTarget.encodeString(string.value, string.altEncoding)
|
||||||
} catch (cx: CharConversionException) {
|
} catch (cx: CharConversionException) {
|
||||||
errors.err(cx.message ?: "can't encode string", string.position)
|
errors.err(cx.message ?: "can't encode string", string.position)
|
||||||
}
|
}
|
||||||
@ -789,11 +793,10 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression) {
|
override fun visit(expr: PrefixExpression) {
|
||||||
val idt = expr.inferType(program)
|
val dt = expr.expression.inferType(program).getOr(DataType.UNDEFINED)
|
||||||
if(!idt.isKnown)
|
if(dt==DataType.UNDEFINED)
|
||||||
return // any error should be reported elsewhere
|
return // any error should be reported elsewhere
|
||||||
|
|
||||||
val dt = idt.getOr(DataType.UNDEFINED)
|
|
||||||
if(expr.operator=="-") {
|
if(expr.operator=="-") {
|
||||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||||
errors.err("can only take negative of a signed number type", expr.position)
|
errors.err("can only take negative of a signed number type", expr.position)
|
||||||
@ -1067,7 +1070,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||||
if(postIncrDecr.target.identifier != null) {
|
if(postIncrDecr.target.identifier != null) {
|
||||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||||
val target = program.namespace.lookup(targetName, postIncrDecr)
|
val target = postIncrDecr.definingScope.lookup(targetName)
|
||||||
if(target==null) {
|
if(target==null) {
|
||||||
val symbol = postIncrDecr.target.identifier!!
|
val symbol = postIncrDecr.target.identifier!!
|
||||||
errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
errors.err("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||||
@ -1254,7 +1257,7 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
// check if the floating point values are all within range
|
// check if the floating point values are all within range
|
||||||
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
val doubles = value.value.map {it.constValue(program)?.number!!.toDouble()}.toDoubleArray()
|
||||||
if(doubles.any { it < compTarget.machine.FLOAT_MAX_NEGATIVE || it > compTarget.machine.FLOAT_MAX_POSITIVE })
|
if(doubles.any { it < compilerOptions.compTarget.machine.FLOAT_MAX_NEGATIVE || it > compilerOptions.compTarget.machine.FLOAT_MAX_POSITIVE })
|
||||||
return err("floating point value overflow")
|
return err("floating point value overflow")
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
@ -3,74 +3,25 @@ package prog8.compiler.astprocessing
|
|||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.expressions.CharLiteral
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.BeforeAsmGenerationAstChanger
|
import prog8.compiler.BeforeAsmGenerationAstChanger
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compilerinterface.CompilationOptions
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.compiler.IStringEncoding
|
import prog8.compilerinterface.IStringEncoding
|
||||||
import prog8.compiler.target.ICompilationTarget
|
|
||||||
import prog8.compiler.target.IMachineDefinition
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
|
|
||||||
fun RangeExpr.size(encoding: IStringEncoding): Int? {
|
internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
val fromLv = (from as? NumericLiteralValue)
|
val checker = AstChecker(this, errors, compilerOptions)
|
||||||
val toLv = (to as? NumericLiteralValue)
|
|
||||||
if(fromLv==null || toLv==null)
|
|
||||||
return null
|
|
||||||
return toConstantIntegerRange(encoding)?.count()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun RangeExpr.toConstantIntegerRange(encoding: IStringEncoding): IntProgression? {
|
|
||||||
val fromVal: Int
|
|
||||||
val toVal: Int
|
|
||||||
val fromString = from as? StringLiteralValue
|
|
||||||
val toString = to as? StringLiteralValue
|
|
||||||
if(fromString!=null && toString!=null ) {
|
|
||||||
// string range -> int range over character values
|
|
||||||
fromVal = encoding.encodeString(fromString.value, fromString.altEncoding)[0].toInt()
|
|
||||||
toVal = encoding.encodeString(toString.value, fromString.altEncoding)[0].toInt()
|
|
||||||
} else {
|
|
||||||
val fromLv = from as? NumericLiteralValue
|
|
||||||
val toLv = to as? NumericLiteralValue
|
|
||||||
if(fromLv==null || toLv==null)
|
|
||||||
return null // non-constant range
|
|
||||||
// integer range
|
|
||||||
fromVal = fromLv.number.toInt()
|
|
||||||
toVal = toLv.number.toInt()
|
|
||||||
}
|
|
||||||
val stepVal = (step as? NumericLiteralValue)?.number?.toInt() ?: 1
|
|
||||||
return makeRange(fromVal, toVal, stepVal)
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun makeRange(fromVal: Int, toVal: Int, stepVal: Int): IntProgression {
|
|
||||||
return when {
|
|
||||||
fromVal <= toVal -> when {
|
|
||||||
stepVal <= 0 -> IntRange.EMPTY
|
|
||||||
stepVal == 1 -> fromVal..toVal
|
|
||||||
else -> fromVal..toVal step stepVal
|
|
||||||
}
|
|
||||||
else -> when {
|
|
||||||
stepVal >= 0 -> IntRange.EMPTY
|
|
||||||
stepVal == -1 -> fromVal downTo toVal
|
|
||||||
else -> fromVal downTo toVal step abs(stepVal)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: IErrorReporter, compTarget: ICompilationTarget) {
|
|
||||||
val checker = AstChecker(this, compilerOptions, errors, compTarget)
|
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.processAstBeforeAsmGeneration(errors: IErrorReporter, compTarget: ICompilationTarget) {
|
internal fun Program.processAstBeforeAsmGeneration(compilerOptions: CompilationOptions, errors: IErrorReporter) {
|
||||||
val fixer = BeforeAsmGenerationAstChanger(this, errors, compTarget)
|
val fixer = BeforeAsmGenerationAstChanger(this, compilerOptions, errors)
|
||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
while(errors.noErrors() && fixer.applyModifications()>0) {
|
while(errors.noErrors() && fixer.applyModifications()>0) {
|
||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
@ -88,7 +39,7 @@ internal fun Program.reorderStatements(errors: IErrorReporter) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.charLiteralsToUByteLiterals(errors: IErrorReporter, enc: IStringEncoding) {
|
internal fun Program.charLiteralsToUByteLiterals(enc: IStringEncoding) {
|
||||||
val walker = object : AstWalker() {
|
val walker = object : AstWalker() {
|
||||||
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
|
override fun after(char: CharLiteral, parent: Node): Iterable<IAstModification> {
|
||||||
return listOf(IAstModification.ReplaceNode(
|
return listOf(IAstModification.ReplaceNode(
|
||||||
@ -113,9 +64,17 @@ internal fun Program.verifyFunctionArgTypes() {
|
|||||||
fixer.visit(this)
|
fixer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.preprocessAst() {
|
||||||
|
val transforms = AstPreprocessor()
|
||||||
|
transforms.visit(this)
|
||||||
|
var mods = transforms.applyModifications()
|
||||||
|
while(mods>0)
|
||||||
|
mods = transforms.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
|
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
|
||||||
|
|
||||||
val checker2 = AstIdentifiersChecker(this, errors, options.compTarget)
|
val checker2 = AstIdentifiersChecker(errors, options.compTarget)
|
||||||
checker2.visit(this)
|
checker2.visit(this)
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
@ -135,7 +94,6 @@ internal fun Program.variousCleanups(program: Program, errors: IErrorReporter) {
|
|||||||
process.applyModifications()
|
process.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.moveMainAndStartToFirst() {
|
internal fun Program.moveMainAndStartToFirst() {
|
||||||
// the module containing the program entrypoint is moved to the first in the sequence.
|
// the module containing the program entrypoint is moved to the first in the sequence.
|
||||||
// the "main" block containing the entrypoint is moved to the top in there,
|
// the "main" block containing the entrypoint is moved to the top in there,
|
||||||
@ -166,46 +124,10 @@ internal fun Program.moveMainAndStartToFirst() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun AssignTarget.isInRegularRAMof(machine: IMachineDefinition): Boolean {
|
internal fun IdentifierReference.isSubroutineParameter(program: Program): Boolean {
|
||||||
val memAddr = memoryAddress
|
val vardecl = this.targetVarDecl(program)
|
||||||
val arrayIdx = arrayindexed
|
if(vardecl!=null && vardecl.autogeneratedDontRemove) {
|
||||||
val ident = identifier
|
return vardecl.definingSubroutine?.parameters?.any { it.name==vardecl.name } == true
|
||||||
when {
|
|
||||||
memAddr != null -> {
|
|
||||||
return when (memAddr.addressExpression) {
|
|
||||||
is NumericLiteralValue -> {
|
|
||||||
machine.isRegularRAMaddress((memAddr.addressExpression as NumericLiteralValue).number.toInt())
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val program = definingModule.program
|
|
||||||
val decl = (memAddr.addressExpression as IdentifierReference).targetVarDecl(program)
|
|
||||||
if ((decl?.type == VarDeclType.VAR || decl?.type == VarDeclType.CONST) && decl.value is NumericLiteralValue)
|
|
||||||
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
|
||||||
else
|
|
||||||
false
|
|
||||||
}
|
|
||||||
else -> false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
arrayIdx != null -> {
|
|
||||||
val program = definingModule.program
|
|
||||||
val targetStmt = arrayIdx.arrayvar.targetVarDecl(program)
|
|
||||||
return if (targetStmt?.type == VarDeclType.MEMORY) {
|
|
||||||
val addr = targetStmt.value as? NumericLiteralValue
|
|
||||||
if (addr != null)
|
|
||||||
machine.isRegularRAMaddress(addr.number.toInt())
|
|
||||||
else
|
|
||||||
false
|
|
||||||
} else true
|
|
||||||
}
|
|
||||||
ident != null -> {
|
|
||||||
val program = definingModule.program
|
|
||||||
val decl = ident.targetVarDecl(program)!!
|
|
||||||
return if (decl.type == VarDeclType.MEMORY && decl.value is NumericLiteralValue)
|
|
||||||
machine.isRegularRAMaddress((decl.value as NumericLiteralValue).number.toInt())
|
|
||||||
else
|
|
||||||
true
|
|
||||||
}
|
|
||||||
else -> return true
|
|
||||||
}
|
}
|
||||||
|
return false
|
||||||
}
|
}
|
||||||
|
@ -1,15 +1,14 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val program: Program, private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
internal class AstIdentifiersChecker(private val errors: IErrorReporter, private val compTarget: ICompilationTarget) : IAstVisitor {
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
@ -42,7 +41,7 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
if(decl.name in compTarget.machine.opcodeNames)
|
if(decl.name in compTarget.machine.opcodeNames)
|
||||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(decl.name), decl)
|
val existing = decl.definingScope.lookup(listOf(decl.name))
|
||||||
if (existing != null && existing !== decl)
|
if (existing != null && existing !== decl)
|
||||||
nameError(decl.name, decl.position, existing)
|
nameError(decl.name, decl.position, existing)
|
||||||
|
|
||||||
@ -65,22 +64,19 @@ internal class AstIdentifiersChecker(private val program: Program, private val e
|
|||||||
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
||||||
// checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
// checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
|
||||||
|
|
||||||
val existing = program.namespace.lookup(listOf(subroutine.name), subroutine)
|
val existing = subroutine.lookup(listOf(subroutine.name))
|
||||||
if (existing != null && existing !== subroutine)
|
if (existing != null && existing !== subroutine)
|
||||||
nameError(subroutine.name, subroutine.position, existing)
|
nameError(subroutine.name, subroutine.position, existing)
|
||||||
|
|
||||||
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters. Blocks are okay.
|
// check that there are no local symbols (variables, labels, subs) that redefine the subroutine's parameters.
|
||||||
val symbolsInSub = subroutine.allDefinedSymbols
|
val symbolsInSub = subroutine.allDefinedSymbols
|
||||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||||
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
val paramNames = subroutine.parameters.map { it.name }.toSet()
|
||||||
val paramsToCheck = paramNames.intersect(namesInSub)
|
val paramsToCheck = paramNames.intersect(namesInSub)
|
||||||
for(name in paramsToCheck) {
|
for(name in paramsToCheck) {
|
||||||
val labelOrVar = subroutine.getLabelOrVariable(name)
|
val symbol = subroutine.searchSymbol(name)
|
||||||
if(labelOrVar!=null && labelOrVar.position != subroutine.position)
|
if(symbol!=null && symbol.position != subroutine.position)
|
||||||
nameError(name, labelOrVar.position, subroutine)
|
nameError(name, symbol.position, subroutine)
|
||||||
val sub = subroutine.statements.firstOrNull { it is Subroutine && it.name==name}
|
|
||||||
if(sub!=null)
|
|
||||||
nameError(name, subroutine.position, sub)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||||
|
48
compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt
Normal file
48
compiler/src/prog8/compiler/astprocessing/AstPreprocessor.kt
Normal file
@ -0,0 +1,48 @@
|
|||||||
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.base.NumericDatatypes
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.AssignTarget
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.ast.walk.AstWalker
|
||||||
|
import prog8.ast.walk.IAstModification
|
||||||
|
|
||||||
|
|
||||||
|
class AstPreprocessor : AstWalker() {
|
||||||
|
|
||||||
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
|
||||||
|
// move vardecls in Anonymous scope up to the containing subroutine
|
||||||
|
// and add initialization assignment in its place if needed
|
||||||
|
val vars = scope.statements.filterIsInstance<VarDecl>()
|
||||||
|
val parentscope = scope.definingScope
|
||||||
|
if(vars.any() && parentscope !== parent) {
|
||||||
|
val movements = mutableListOf<IAstModification>()
|
||||||
|
val replacements = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
for(decl in vars) {
|
||||||
|
if(decl.type != VarDeclType.VAR) {
|
||||||
|
movements.add(IAstModification.InsertFirst(decl, parentscope))
|
||||||
|
replacements.add(IAstModification.Remove(decl, scope))
|
||||||
|
} else {
|
||||||
|
if(decl.value!=null && decl.datatype in NumericDatatypes) {
|
||||||
|
val target = AssignTarget(IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
val assign = Assignment(target, decl.value!!, decl.position)
|
||||||
|
replacements.add(IAstModification.ReplaceNode(decl, assign, scope))
|
||||||
|
decl.value = null
|
||||||
|
decl.allowInitializeWithZero = false
|
||||||
|
} else {
|
||||||
|
replacements.add(IAstModification.Remove(decl, scope))
|
||||||
|
}
|
||||||
|
movements.add(IAstModification.InsertFirst(decl, parentscope))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return movements + replacements
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
}
|
@ -67,7 +67,7 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
private fun concatString(expr: BinaryExpression): StringLiteralValue? {
|
||||||
@ -99,12 +99,12 @@ internal class AstVariousTransforms(private val program: Program) : AstWalker()
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal fun replacePointerVarIndexWithMemread(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
internal fun replacePointerVarIndexWithMemreadOrMemwrite(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||||
// rewrite pointervar[index] into @(pointervar+index)
|
// rewrite pointervar[index] into @(pointervar+index)
|
||||||
val indexer = arrayIndexedExpression.indexer
|
val indexer = arrayIndexedExpression.indexer
|
||||||
val add = BinaryExpression(arrayIndexedExpression.arrayvar, "+", indexer.indexExpr, arrayIndexedExpression.position)
|
val add = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||||
return if(parent is AssignTarget) {
|
return if(parent is AssignTarget) {
|
||||||
// we're part of the target of an assignment, we have to actually change the assign target itself
|
// we're part of the target of an assignment, we have to actually change the assign target itself
|
||||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
@ -40,11 +41,13 @@ internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
|
|||||||
// this array literal is part of an expression, turn it into an identifier reference
|
// this array literal is part of an expression, turn it into an identifier reference
|
||||||
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
val litval2 = array.cast(arrayDt.getOr(DataType.UNDEFINED))
|
||||||
if(litval2!=null) {
|
if(litval2!=null) {
|
||||||
|
if(array.parent !is IStatementContainer)
|
||||||
|
return noModifications
|
||||||
val vardecl2 = VarDecl.createAuto(litval2)
|
val vardecl2 = VarDecl.createAuto(litval2)
|
||||||
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
val identifier = IdentifierReference(listOf(vardecl2.name), vardecl2.position)
|
||||||
return listOf(
|
return listOf(
|
||||||
IAstModification.ReplaceNode(array, identifier, parent),
|
IAstModification.ReplaceNode(array, identifier, parent),
|
||||||
IAstModification.InsertFirst(vardecl2, array.definingScope)
|
IAstModification.InsertFirst(vardecl2, array.parent as IStatementContainer)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,17 +1,13 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.*
|
||||||
import prog8.ast.Module
|
import prog8.ast.base.*
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal class StatementReorderer(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
internal class StatementReorderer(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||||
@ -38,15 +34,62 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
module.statements.add(0, mainBlock)
|
module.statements.add(0, mainBlock)
|
||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(module.statements)
|
directivesToTheTop(module.statements)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun reorderVardeclsAndDirectives(statements: MutableList<Statement>) {
|
private val declsProcessedWithInitAssignment = mutableSetOf<VarDecl>()
|
||||||
val varDecls = statements.filterIsInstance<VarDecl>()
|
|
||||||
statements.removeAll(varDecls)
|
|
||||||
statements.addAll(0, varDecls)
|
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(decl.type == VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
if(decl !in declsProcessedWithInitAssignment) {
|
||||||
|
declsProcessedWithInitAssignment.add(decl)
|
||||||
|
if (decl.value == null) {
|
||||||
|
if (!decl.autogeneratedDontRemove && decl.allowInitializeWithZero) {
|
||||||
|
// A numeric vardecl without an initial value is initialized with zero,
|
||||||
|
// unless there's already an assignment below it, that initializes the value (or a for loop that uses it as loopvar).
|
||||||
|
// This allows you to restart the program and have the same starting values of the variables
|
||||||
|
// So basically consider 'ubyte xx' as a short form for 'ubyte xx; xx=0'
|
||||||
|
decl.value = null
|
||||||
|
if(decl.name.startsWith("retval_interm_") && decl.definingScope.name=="prog8_lib") {
|
||||||
|
// no need to zero out the special internal returnvalue intermediates.
|
||||||
|
// TODO these variables are no longer used???
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
val nextStmt = decl.nextSibling()
|
||||||
|
val nextAssign = nextStmt as? Assignment
|
||||||
|
val nextFor = nextStmt as? ForLoop
|
||||||
|
val hasNextForWithThisLoopvar = nextFor?.loopVar?.nameInSource==listOf(decl.name)
|
||||||
|
val hasNextAssignment = nextAssign!=null && nextAssign.target isSameAs IdentifierReference(listOf(decl.name), Position.DUMMY)
|
||||||
|
if (!hasNextAssignment && !hasNextForWithThisLoopvar) {
|
||||||
|
// Add assignment to initialize with zero
|
||||||
|
// Note: for block-level vars, this will introduce assignments in the block scope. These have to be dealt with correctly later.
|
||||||
|
val identifier = IdentifierReference(listOf(decl.name), decl.position)
|
||||||
|
val assignzero = Assignment(AssignTarget(identifier, null, null, decl.position), decl.zeroElementValue(), decl.position)
|
||||||
|
return listOf(IAstModification.InsertAfter(
|
||||||
|
decl, assignzero, parent as IStatementContainer
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Transform the vardecl with initvalue to a plain vardecl + assignment
|
||||||
|
// this allows for other optimizations to kick in.
|
||||||
|
// So basically consider 'ubyte xx=99' as a short form for 'ubyte xx; xx=99'
|
||||||
|
val pos = decl.value!!.position
|
||||||
|
val identifier = IdentifierReference(listOf(decl.name), pos)
|
||||||
|
val assign = Assignment(AssignTarget(identifier, null, null, pos), decl.value!!, pos)
|
||||||
|
decl.value = null
|
||||||
|
return listOf(IAstModification.InsertAfter(
|
||||||
|
decl, assign, parent as IStatementContainer
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun directivesToTheTop(statements: MutableList<Statement>) {
|
||||||
val directives = statements.filterIsInstance<Directive>().filter {it.directive in directivesToMove}
|
val directives = statements.filterIsInstance<Directive>().filter {it.directive in directivesToMove}
|
||||||
statements.removeAll(directives)
|
statements.removeAll(directives)
|
||||||
statements.addAll(0, directives)
|
statements.addAll(0, directives)
|
||||||
@ -61,7 +104,7 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
reorderVardeclsAndDirectives(block.statements)
|
directivesToTheTop(block.statements)
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -86,7 +129,12 @@ internal class StatementReorderer(val program: Program, val errors: IErrorReport
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||||
return replacePointerVarIndexWithMemread(program, arrayIndexedExpression, parent)
|
if(parent !is VarDecl) {
|
||||||
|
// don't replace the initializer value in a vardecl - this will be moved to a separate
|
||||||
|
// assignment statement soon in after(VarDecl)
|
||||||
|
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
@ -8,8 +8,8 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalker() {
|
||||||
@ -78,10 +78,10 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
|||||||
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||||
assignment))
|
assignment))
|
||||||
} else {
|
} else {
|
||||||
fun castLiteral(cvalue: NumericLiteralValue): List<IAstModification.ReplaceNode> {
|
fun castLiteral(cvalue2: NumericLiteralValue): List<IAstModification.ReplaceNode> {
|
||||||
val cast = cvalue.cast(targettype)
|
val cast = cvalue2.cast(targettype)
|
||||||
return if(cast.isValid)
|
return if(cast.isValid)
|
||||||
listOf(IAstModification.ReplaceNode(cvalue, cast.valueOrZero(), cvalue.parent))
|
listOf(IAstModification.ReplaceNode(assignment.value, cast.valueOrZero(), assignment))
|
||||||
else
|
else
|
||||||
emptyList()
|
emptyList()
|
||||||
}
|
}
|
||||||
@ -135,11 +135,13 @@ class TypecastsAdder(val program: Program, val errors: IErrorReporter) : AstWalk
|
|||||||
TypecastExpression(pair.second, requiredType, true, pair.second.position),
|
TypecastExpression(pair.second, requiredType, true, pair.second.position),
|
||||||
call as Node)
|
call as Node)
|
||||||
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
} else if(requiredType == DataType.UWORD && argtype in PassByReferenceDatatypes) {
|
||||||
// we allow STR/ARRAY values in place of UWORD parameters. Take their address instead.
|
// We allow STR/ARRAY values in place of UWORD parameters.
|
||||||
if(pair.second is IdentifierReference) {
|
// Take their address instead, UNLESS it's a str parameter in the containing subroutine
|
||||||
|
val identifier = pair.second as? IdentifierReference
|
||||||
|
if(identifier?.isSubroutineParameter(program)==false) {
|
||||||
modifications += IAstModification.ReplaceNode(
|
modifications += IAstModification.ReplaceNode(
|
||||||
call.args[index],
|
call.args[index],
|
||||||
AddressOf(pair.second as IdentifierReference, pair.second.position),
|
AddressOf(identifier, pair.second.position),
|
||||||
call as Node)
|
call as Node)
|
||||||
}
|
}
|
||||||
} else if(pair.second is NumericLiteralValue) {
|
} else if(pair.second is NumericLiteralValue) {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package prog8.compiler.astprocessing
|
package prog8.compiler.astprocessing
|
||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.IStatementContainer
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
@ -10,23 +10,23 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstModification
|
import prog8.ast.walk.IAstModification
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal class VariousCleanups(val program: Program, val errors: IErrorReporter): AstWalker() {
|
internal class VariousCleanups(val program: Program, val errors: IErrorReporter): AstWalker() {
|
||||||
|
|
||||||
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
override fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> {
|
||||||
return listOf(IAstModification.Remove(nopStatement, parent as INameScope))
|
return listOf(IAstModification.Remove(nopStatement, parent as IStatementContainer))
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
override fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
return if(parent is INameScope)
|
return if(parent is IStatementContainer)
|
||||||
listOf(ScopeFlatten(scope, parent as INameScope))
|
listOf(ScopeFlatten(scope, parent as IStatementContainer))
|
||||||
else
|
else
|
||||||
noModifications
|
noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
class ScopeFlatten(val scope: AnonymousScope, val into: INameScope) : IAstModification {
|
class ScopeFlatten(val scope: AnonymousScope, val into: IStatementContainer) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
val idx = into.statements.indexOf(scope)
|
val idx = into.statements.indexOf(scope)
|
||||||
if(idx>=0) {
|
if(idx>=0) {
|
||||||
|
@ -8,21 +8,21 @@ import prog8.ast.expressions.FunctionCall
|
|||||||
import prog8.ast.expressions.TypecastExpression
|
import prog8.ast.expressions.TypecastExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
import prog8.compiler.CompilerException
|
import prog8.compilerinterface.BuiltinFunctions
|
||||||
import prog8.compiler.functions.BuiltinFunctions
|
import prog8.compilerinterface.InternalCompilerException
|
||||||
|
|
||||||
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
class VerifyFunctionArgTypes(val program: Program) : IAstVisitor {
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall) {
|
override fun visit(functionCall: FunctionCall) {
|
||||||
val error = checkTypes(functionCall as IFunctionCall, program)
|
val error = checkTypes(functionCall as IFunctionCall, program)
|
||||||
if(error!=null)
|
if(error!=null)
|
||||||
throw CompilerException(error)
|
throw InternalCompilerException(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
val error = checkTypes(functionCallStatement as IFunctionCall, program)
|
val error = checkTypes(functionCallStatement as IFunctionCall, program)
|
||||||
if (error!=null)
|
if (error!=null)
|
||||||
throw CompilerException(error)
|
throw InternalCompilerException(error)
|
||||||
}
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
|
@ -1,17 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
|
|
||||||
internal interface IAssemblyGenerator {
|
|
||||||
fun compileToAssembly(): IAssemblyProgram
|
|
||||||
}
|
|
||||||
|
|
||||||
internal const val generatedLabelPrefix = "_prog8_label_"
|
|
||||||
internal const val subroutineFloatEvalResultVar1 = "_prog8_float_eval_result1"
|
|
||||||
internal const val subroutineFloatEvalResultVar2 = "_prog8_float_eval_result2"
|
|
||||||
|
|
||||||
internal interface IAssemblyProgram {
|
|
||||||
val valid: Boolean
|
|
||||||
val name: String
|
|
||||||
fun assemble(options: CompilationOptions): Int
|
|
||||||
}
|
|
@ -1,98 +0,0 @@
|
|||||||
package prog8.compiler.target
|
|
||||||
|
|
||||||
import com.github.michaelbull.result.fold
|
|
||||||
import prog8.ast.IMemSizer
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.IdentifierReference
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.statements.AssignTarget
|
|
||||||
import prog8.compiler.CompilationOptions
|
|
||||||
import prog8.compiler.IErrorReporter
|
|
||||||
import prog8.compiler.IStringEncoding
|
|
||||||
import prog8.compiler.Zeropage
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import prog8.compiler.target.cbm.Petscii
|
|
||||||
import prog8.compiler.target.cpu6502.codegen.AsmGen
|
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition
|
|
||||||
import java.io.CharConversionException
|
|
||||||
import java.nio.file.Path
|
|
||||||
|
|
||||||
|
|
||||||
interface ICompilationTarget: IStringEncoding, IMemSizer {
|
|
||||||
val name: String
|
|
||||||
val machine: IMachineDefinition
|
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal object C64Target: ICompilationTarget {
|
|
||||||
override val name = "c64"
|
|
||||||
override val machine = C64MachineDefinition
|
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
|
||||||
val coded = if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
return coded.fold(
|
|
||||||
failure = { throw it },
|
|
||||||
success = { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
|
||||||
try {
|
|
||||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
|
||||||
} catch (x: CharConversionException) {
|
|
||||||
throw CharConversionException("can't decode string: ${x.message}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypes -> 1
|
|
||||||
in WordDatatypes -> 2
|
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
|
||||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
|
||||||
else -> -9999999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
internal object Cx16Target: ICompilationTarget {
|
|
||||||
override val name = "cx16"
|
|
||||||
override val machine = CX16MachineDefinition
|
|
||||||
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
|
||||||
val coded= if (altEncoding) Petscii.encodeScreencode(str, true) else Petscii.encodePetscii(str, true)
|
|
||||||
return coded.fold(
|
|
||||||
failure = { throw it },
|
|
||||||
success = { it }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
override fun decodeString(bytes: List<Short>, altEncoding: Boolean) =
|
|
||||||
try {
|
|
||||||
if (altEncoding) Petscii.decodeScreencode(bytes, true) else Petscii.decodePetscii(bytes, true)
|
|
||||||
} catch (x: CharConversionException) {
|
|
||||||
throw CharConversionException("can't decode string: ${x.message}")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun memorySize(dt: DataType): Int {
|
|
||||||
return when(dt) {
|
|
||||||
in ByteDatatypes -> 1
|
|
||||||
in WordDatatypes -> 2
|
|
||||||
DataType.FLOAT -> machine.FLOAT_MEM_SIZE
|
|
||||||
in PassByReferenceDatatypes -> machine.POINTER_MEM_SIZE
|
|
||||||
else -> -9999999
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun asmGeneratorFor(
|
|
||||||
compTarget: ICompilationTarget,
|
|
||||||
program: Program,
|
|
||||||
errors: IErrorReporter,
|
|
||||||
zp: Zeropage,
|
|
||||||
options: CompilationOptions,
|
|
||||||
outputDir: Path
|
|
||||||
): IAssemblyGenerator
|
|
||||||
{
|
|
||||||
// at the moment we only have one code generation backend (for 6502 and 65c02)
|
|
||||||
return AsmGen(program, errors, zp, options, compTarget, outputDir)
|
|
||||||
}
|
|
@ -1,24 +1,28 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import kotlin.test.*
|
|
||||||
import com.github.michaelbull.result.getErrorOrElse
|
import com.github.michaelbull.result.getErrorOrElse
|
||||||
import com.github.michaelbull.result.getOrElse
|
import com.github.michaelbull.result.getOrElse
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.Matchers.*
|
import org.hamcrest.Matchers.equalTo
|
||||||
|
import org.hamcrest.Matchers.nullValue
|
||||||
import org.hamcrest.core.Is
|
import org.hamcrest.core.Is
|
||||||
import org.junit.jupiter.api.BeforeEach
|
import org.junit.jupiter.api.*
|
||||||
import org.junit.jupiter.api.Nested
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.TestInstance
|
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.internedStringsModuleName
|
import prog8.ast.internedStringsModuleName
|
||||||
import prog8.compiler.IErrorReporter
|
|
||||||
import prog8.compiler.ModuleImporter
|
import prog8.compiler.ModuleImporter
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
import prog8.parser.ParseError
|
import prog8.parser.ParseError
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.*
|
import prog8tests.ast.helpers.*
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
|
import prog8tests.helpers.DummyFunctions
|
||||||
|
import prog8tests.helpers.DummyMemsizer
|
||||||
|
import prog8tests.helpers.DummyStringEncoder
|
||||||
import kotlin.io.path.*
|
import kotlin.io.path.*
|
||||||
|
import kotlin.test.assertContains
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.fail
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@ -28,7 +32,7 @@ class TestModuleImporter {
|
|||||||
private lateinit var program: Program
|
private lateinit var program: Program
|
||||||
@BeforeEach
|
@BeforeEach
|
||||||
fun beforeEach() {
|
fun beforeEach() {
|
||||||
program = Program("foo", DummyFunctions, DummyMemsizer)
|
program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun makeImporter(errors: IErrorReporter?, vararg searchIn: String): ModuleImporter {
|
private fun makeImporter(errors: IErrorReporter?, vararg searchIn: String): ModuleImporter {
|
||||||
@ -36,7 +40,7 @@ class TestModuleImporter {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeImporter(errors: IErrorReporter? = null, searchIn: Iterable<String>) =
|
private fun makeImporter(errors: IErrorReporter? = null, searchIn: Iterable<String>) =
|
||||||
ModuleImporter(program, "blah", errors ?: ErrorReporterForTests(), searchIn.toList())
|
ModuleImporter(program, "blah", errors ?: ErrorReporterForTests(false), searchIn.toList())
|
||||||
|
|
||||||
@Nested
|
@Nested
|
||||||
inner class Constructor {
|
inner class Constructor {
|
||||||
@ -243,7 +247,7 @@ class TestModuleImporter {
|
|||||||
@Test
|
@Test
|
||||||
fun testWithNonExistingName() {
|
fun testWithNonExistingName() {
|
||||||
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
val searchIn = assumeDirectory("./", workingDir.relativize(fixturesDir))
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests(false)
|
||||||
val importer = makeImporter(errors, searchIn.invariantSeparatorsPathString)
|
val importer = makeImporter(errors, searchIn.invariantSeparatorsPathString)
|
||||||
val filenameNoExt = assumeNotExists(fixturesDir, "i_do_not_exist").name
|
val filenameNoExt = assumeNotExists(fixturesDir, "i_do_not_exist").name
|
||||||
val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name
|
val filenameWithExt = assumeNotExists(fixturesDir, "i_do_not_exist.p8").name
|
||||||
@ -252,14 +256,14 @@ class TestModuleImporter {
|
|||||||
val result = importer.importLibraryModule(filenameNoExt)
|
val result = importer.importLibraryModule(filenameNoExt)
|
||||||
assertThat(count[n] + " call / NO .p8 extension", result, Is(nullValue()))
|
assertThat(count[n] + " call / NO .p8 extension", result, Is(nullValue()))
|
||||||
assertFalse(errors.noErrors(), count[n] + " call / NO .p8 extension")
|
assertFalse(errors.noErrors(), count[n] + " call / NO .p8 extension")
|
||||||
assertEquals(errors.errors.single(), "no module found with name i_do_not_exist")
|
assertContains(errors.errors.single(), "0:0: no module found with name i_do_not_exist")
|
||||||
errors.report()
|
errors.report()
|
||||||
assertThat(program.modules.size, equalTo(1))
|
assertThat(program.modules.size, equalTo(1))
|
||||||
|
|
||||||
val result2 = importer.importLibraryModule(filenameWithExt)
|
val result2 = importer.importLibraryModule(filenameWithExt)
|
||||||
assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue()))
|
assertThat(count[n] + " call / with .p8 extension", result2, Is(nullValue()))
|
||||||
assertFalse(importer.errors.noErrors(), count[n] + " call / with .p8 extension")
|
assertFalse(importer.errors.noErrors(), count[n] + " call / with .p8 extension")
|
||||||
assertEquals(errors.errors.single(), "no module found with name i_do_not_exist.p8")
|
assertContains(errors.errors.single(), "0:0: no module found with name i_do_not_exist.p8")
|
||||||
errors.report()
|
errors.report()
|
||||||
assertThat(program.modules.size, equalTo(1))
|
assertThat(program.modules.size, equalTo(1))
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,7 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import prog8.ast.statements.Block
|
import prog8.ast.statements.Block
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.optimizer.CallGraph
|
import prog8.compilerinterface.CallGraph
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
@ -26,11 +26,11 @@ class TestCallgraph {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||||
val graph = CallGraph(result.programAst)
|
val graph = CallGraph(result.program)
|
||||||
|
|
||||||
assertEquals(1, graph.imports.size)
|
assertEquals(1, graph.imports.size)
|
||||||
assertEquals(1, graph.importedBy.size)
|
assertEquals(1, graph.importedBy.size)
|
||||||
val toplevelModule = result.programAst.toplevelModule
|
val toplevelModule = result.program.toplevelModule
|
||||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
assertEquals("string", importedModule.name)
|
assertEquals("string", importedModule.name)
|
||||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
@ -45,7 +45,7 @@ class TestCallgraph {
|
|||||||
assertFalse(sub in graph.calls)
|
assertFalse(sub in graph.calls)
|
||||||
assertFalse(sub in graph.calledBy)
|
assertFalse(sub in graph.calledBy)
|
||||||
|
|
||||||
if(sub === result.programAst.entrypoint)
|
if(sub === result.program.entrypoint)
|
||||||
assertFalse(graph.unused(sub), "start() should always be marked as used to avoid having it removed")
|
assertFalse(graph.unused(sub), "start() should always be marked as used to avoid having it removed")
|
||||||
else
|
else
|
||||||
assertTrue(graph.unused(sub))
|
assertTrue(graph.unused(sub))
|
||||||
@ -66,11 +66,11 @@ class TestCallgraph {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
val result = compileText(C64Target, false, sourcecode).assertSuccess()
|
||||||
val graph = CallGraph(result.programAst)
|
val graph = CallGraph(result.program)
|
||||||
|
|
||||||
assertEquals(1, graph.imports.size)
|
assertEquals(1, graph.imports.size)
|
||||||
assertEquals(1, graph.importedBy.size)
|
assertEquals(1, graph.importedBy.size)
|
||||||
val toplevelModule = result.programAst.toplevelModule
|
val toplevelModule = result.program.toplevelModule
|
||||||
val importedModule = graph.imports.getValue(toplevelModule).single()
|
val importedModule = graph.imports.getValue(toplevelModule).single()
|
||||||
assertEquals("string", importedModule.name)
|
assertEquals("string", importedModule.name)
|
||||||
val importedBy = graph.importedBy.getValue(importedModule).single()
|
val importedBy = graph.importedBy.getValue(importedModule).single()
|
||||||
|
@ -7,11 +7,13 @@ import prog8.ast.base.DataType
|
|||||||
import prog8.ast.base.VarDeclType
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertIs
|
import kotlin.test.assertIs
|
||||||
|
import kotlin.test.assertNull
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -34,7 +36,7 @@ class TestCompilerOnCharLit {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
@ -58,7 +60,7 @@ class TestCompilerOnCharLit {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
@ -73,9 +75,11 @@ class TestCompilerOnCharLit {
|
|||||||
// val initializerValue = decl.value as CharLiteral
|
// val initializerValue = decl.value as CharLiteral
|
||||||
// assertEquals('\n', (initializerValue as CharLiteral).value)
|
// assertEquals('\n', (initializerValue as CharLiteral).value)
|
||||||
|
|
||||||
assertIs<NumericLiteralValue>(decl.value,
|
assertNull(decl.value, "initializer value should have been moved to separate assignment")
|
||||||
"char literal should have been replaced by ubyte literal")
|
val assignInitialValue = decl.nextSibling() as Assignment
|
||||||
val initializerValue = decl.value as NumericLiteralValue
|
assertEquals(listOf("ch"), assignInitialValue.target.identifier!!.nameInSource)
|
||||||
|
assertIs<NumericLiteralValue>(assignInitialValue.value, "char literal should have been replaced by ubyte literal")
|
||||||
|
val initializerValue = assignInitialValue.value as NumericLiteralValue
|
||||||
assertEquals(DataType.UBYTE, initializerValue.type)
|
assertEquals(DataType.UBYTE, initializerValue.type)
|
||||||
assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort())
|
assertEquals(platform.encodeString("\n", false)[0], initializerValue.number.toShort())
|
||||||
}
|
}
|
||||||
@ -93,7 +97,7 @@ class TestCompilerOnCharLit {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
val funCall = startSub.statements.filterIsInstance<IFunctionCall>()[0]
|
||||||
|
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import org.junit.jupiter.api.Disabled
|
|
||||||
import org.junit.jupiter.api.DynamicTest
|
import org.junit.jupiter.api.DynamicTest
|
||||||
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
import org.junit.jupiter.api.DynamicTest.dynamicTest
|
||||||
import org.junit.jupiter.api.TestFactory
|
import org.junit.jupiter.api.TestFactory
|
||||||
@ -8,8 +7,12 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
import prog8tests.helpers.*
|
import prog8tests.ast.helpers.assumeDirectory
|
||||||
|
import prog8tests.ast.helpers.mapCombinations
|
||||||
|
import prog8tests.ast.helpers.outputDir
|
||||||
|
import prog8tests.ast.helpers.workingDir
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
import kotlin.io.path.absolute
|
import kotlin.io.path.absolute
|
||||||
import kotlin.io.path.exists
|
import kotlin.io.path.exists
|
||||||
|
|
||||||
@ -39,8 +42,10 @@ class TestCompilerOnExamples {
|
|||||||
compileProgram(
|
compileProgram(
|
||||||
filepath,
|
filepath,
|
||||||
optimize,
|
optimize,
|
||||||
|
optimizeFloatExpressions = false,
|
||||||
writeAssembly = true,
|
writeAssembly = true,
|
||||||
slowCodegenWarnings = false,
|
slowCodegenWarnings = false,
|
||||||
|
quietAssembler = true,
|
||||||
compilationTarget = platform.name,
|
compilationTarget = platform.name,
|
||||||
sourceDirs = listOf(),
|
sourceDirs = listOf(),
|
||||||
outputDir
|
outputDir
|
||||||
|
@ -8,7 +8,10 @@ import prog8.ast.expressions.StringLiteralValue
|
|||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.ast.statements.Label
|
import prog8.ast.statements.Label
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8tests.helpers.*
|
import prog8tests.ast.helpers.*
|
||||||
|
import prog8tests.helpers.assertFailure
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
|
import prog8tests.helpers.compileFile
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertNotEquals
|
import kotlin.test.assertNotEquals
|
||||||
@ -34,7 +37,7 @@ class TestCompilerOnImportsAndIncludes {
|
|||||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
||||||
.assertSuccess()
|
.assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val strLits = startSub.statements
|
val strLits = startSub.statements
|
||||||
.filterIsInstance<FunctionCallStatement>()
|
.filterIsInstance<FunctionCallStatement>()
|
||||||
@ -59,7 +62,7 @@ class TestCompilerOnImportsAndIncludes {
|
|||||||
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
val result = compileFile(platform, optimize = false, fixturesDir, filepath.name)
|
||||||
.assertSuccess()
|
.assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val args = startSub.statements
|
val args = startSub.statements
|
||||||
.filterIsInstance<FunctionCallStatement>()
|
.filterIsInstance<FunctionCallStatement>()
|
||||||
@ -105,8 +108,8 @@ class TestCompilerOnImportsAndIncludes {
|
|||||||
val (where, p8Str, binStr) = it
|
val (where, p8Str, binStr) = it
|
||||||
dynamicTest("%asmbinary from ${where}folder") {
|
dynamicTest("%asmbinary from ${where}folder") {
|
||||||
val p8Path = assumeReadableFile(fixturesDir, p8Str)
|
val p8Path = assumeReadableFile(fixturesDir, p8Str)
|
||||||
val binPath = assumeReadableFile(fixturesDir, binStr)
|
// val binPath = assumeReadableFile(fixturesDir, binStr)
|
||||||
assertNotEquals( // the bug we're testing for (#54) was hidden if outputDir == workinDir
|
assertNotEquals( // the bug we're testing for (#54) was hidden if outputDir == workingDir
|
||||||
workingDir.normalize().toAbsolutePath(),
|
workingDir.normalize().toAbsolutePath(),
|
||||||
outputDir.normalize().toAbsolutePath(),
|
outputDir.normalize().toAbsolutePath(),
|
||||||
"sanity check: workingDir and outputDir should not be the same folder"
|
"sanity check: workingDir and outputDir should not be the same folder"
|
||||||
|
@ -5,18 +5,21 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestFactory
|
import org.junit.jupiter.api.TestFactory
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.astprocessing.size
|
|
||||||
import prog8.compiler.astprocessing.toConstantIntegerRange
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
|
import prog8.compilerinterface.size
|
||||||
|
import prog8.compilerinterface.toConstantIntegerRange
|
||||||
|
import prog8tests.ast.helpers.mapCombinations
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.assertFailure
|
import prog8tests.helpers.assertFailure
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import prog8tests.helpers.mapCombinations
|
import kotlin.test.assertContains
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
|
|
||||||
|
|
||||||
@ -40,7 +43,7 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val decl = startSub
|
val decl = startSub
|
||||||
.statements.filterIsInstance<VarDecl>()[0]
|
.statements.filterIsInstance<VarDecl>()[0]
|
||||||
@ -69,7 +72,7 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val decl = startSub
|
val decl = startSub
|
||||||
.statements.filterIsInstance<VarDecl>()[0]
|
.statements.filterIsInstance<VarDecl>()[0]
|
||||||
@ -151,7 +154,7 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val iterable = startSub
|
val iterable = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -162,10 +165,10 @@ class TestCompilerOnRanges {
|
|||||||
val expectedEnd = platform.encodeString("f", false)[0].toInt()
|
val expectedEnd = platform.encodeString("f", false)[0].toInt()
|
||||||
val expectedStr = "$expectedStart .. $expectedEnd"
|
val expectedStr = "$expectedStart .. $expectedEnd"
|
||||||
|
|
||||||
val intProgression = rangeExpr.toConstantIntegerRange(platform)
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
||||||
val actualStr = "${intProgression?.first} .. ${intProgression?.last}"
|
val actualStr = "${intProgression?.first} .. ${intProgression?.last}"
|
||||||
assertEquals(expectedStr, actualStr,".first .. .last")
|
assertEquals(expectedStr, actualStr,".first .. .last")
|
||||||
assertEquals(expectedEnd - expectedStart + 1, rangeExpr.size(platform), "rangeExpr.size()")
|
assertEquals(expectedEnd - expectedStart + 1, rangeExpr.size(), "rangeExpr.size()")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -182,15 +185,15 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val rangeExpr = startSub
|
val rangeExpr = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
.map { it.iterable }
|
.map { it.iterable }
|
||||||
.filterIsInstance<RangeExpr>()[0]
|
.filterIsInstance<RangeExpr>()[0]
|
||||||
|
|
||||||
assertEquals(2, rangeExpr.size(platform))
|
assertEquals(2, rangeExpr.size())
|
||||||
val intProgression = rangeExpr.toConstantIntegerRange(platform)
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
||||||
assertEquals(0, intProgression?.first)
|
assertEquals(0, intProgression?.first)
|
||||||
assertEquals(1, intProgression?.last)
|
assertEquals(1, intProgression?.last)
|
||||||
}
|
}
|
||||||
@ -209,21 +212,22 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val rangeExpr = startSub
|
val rangeExpr = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
.map { it.iterable }
|
.map { it.iterable }
|
||||||
.filterIsInstance<RangeExpr>()[0]
|
.filterIsInstance<RangeExpr>()[0]
|
||||||
|
|
||||||
assertEquals(9, rangeExpr.size(platform))
|
assertEquals(9, rangeExpr.size())
|
||||||
val intProgression = rangeExpr.toConstantIntegerRange(platform)
|
val intProgression = rangeExpr.toConstantIntegerRange()
|
||||||
assertEquals(1, intProgression?.first)
|
assertEquals(1, intProgression?.first)
|
||||||
assertEquals(9, intProgression?.last)
|
assertEquals(9, intProgression?.last)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testForLoopWithRange_str_downto_str() {
|
fun testForLoopWithRange_str_downto_str() {
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
compileText(Cx16Target, true, """
|
compileText(Cx16Target, true, """
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
@ -233,8 +237,10 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
""").assertFailure()
|
""", errors, false).assertFailure()
|
||||||
//TODO("test exact compile error(s)")
|
assertEquals(2, errors.errors.size)
|
||||||
|
assertContains(errors.errors[0], ".p8:5:29: range expression from value must be integer")
|
||||||
|
assertContains(errors.errors[1], ".p8:5:44: range expression to value must be integer")
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -250,7 +256,7 @@ class TestCompilerOnRanges {
|
|||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
|
|
||||||
val program = result.programAst
|
val program = result.program
|
||||||
val startSub = program.entrypoint
|
val startSub = program.entrypoint
|
||||||
val iterable = startSub
|
val iterable = startSub
|
||||||
.statements.filterIsInstance<ForLoop>()
|
.statements.filterIsInstance<ForLoop>()
|
||||||
@ -260,5 +266,15 @@ class TestCompilerOnRanges {
|
|||||||
assertEquals(DataType.STR, iterable.inferType(program).getOr(DataType.UNDEFINED))
|
assertEquals(DataType.STR, iterable.inferType(program).getOr(DataType.UNDEFINED))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testRangeExprNumericSize() {
|
||||||
|
val expr = RangeExpr(
|
||||||
|
NumericLiteralValue.optimalInteger(10, Position.DUMMY),
|
||||||
|
NumericLiteralValue.optimalInteger(20, Position.DUMMY),
|
||||||
|
NumericLiteralValue.optimalInteger(2, Position.DUMMY),
|
||||||
|
Position.DUMMY)
|
||||||
|
assertEquals(6, expr.size())
|
||||||
|
expr.toConstantIntegerRange()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,7 +6,11 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8tests.helpers.*
|
import prog8tests.ast.helpers.assumeReadableFile
|
||||||
|
import prog8tests.ast.helpers.fixturesDir
|
||||||
|
import prog8tests.ast.helpers.outputDir
|
||||||
|
import prog8tests.ast.helpers.workingDir
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.absolute
|
import kotlin.io.path.absolute
|
||||||
import kotlin.io.path.createTempFile
|
import kotlin.io.path.createTempFile
|
||||||
@ -43,8 +47,10 @@ class TestCompilerOptionSourcedirs {
|
|||||||
compileProgram(
|
compileProgram(
|
||||||
filepath = filePath,
|
filepath = filePath,
|
||||||
optimize = false,
|
optimize = false,
|
||||||
|
optimizeFloatExpressions = false,
|
||||||
writeAssembly = true,
|
writeAssembly = true,
|
||||||
slowCodegenWarnings = false,
|
slowCodegenWarnings = false,
|
||||||
|
quietAssembler = true,
|
||||||
compilationTarget = Cx16Target.name,
|
compilationTarget = Cx16Target.name,
|
||||||
sourceDirs,
|
sourceDirs,
|
||||||
outputDir
|
outputDir
|
||||||
|
@ -3,14 +3,14 @@ package prog8tests
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.internedStringsModuleName
|
import prog8.ast.internedStringsModuleName
|
||||||
import prog8.compiler.ErrorReporter
|
|
||||||
import prog8.compiler.ZeropageType
|
|
||||||
import prog8.compiler.determineCompilationOptions
|
import prog8.compiler.determineCompilationOptions
|
||||||
import prog8.compiler.parseImports
|
import prog8.compiler.parseImports
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.compilerinterface.ZeropageType
|
||||||
|
import prog8tests.ast.helpers.outputDir
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import prog8tests.helpers.outputDir
|
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -30,9 +30,9 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||||
|
|
||||||
val moduleNames = result.programAst.modules.map { it.name }
|
val moduleNames = result.program.modules.map { it.name }
|
||||||
assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first")
|
assertTrue(moduleNames[0].startsWith("on_the_fly_test"), "main module must be first")
|
||||||
assertEquals(listOf(
|
assertEquals(listOf(
|
||||||
"prog8_interned_strings",
|
"prog8_interned_strings",
|
||||||
@ -44,7 +44,7 @@ main {
|
|||||||
"prog8_lib"
|
"prog8_lib"
|
||||||
), moduleNames.drop(1), "module order in parse tree")
|
), moduleNames.drop(1), "module order in parse tree")
|
||||||
|
|
||||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -61,8 +61,8 @@ main {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
""").assertSuccess()
|
""").assertSuccess()
|
||||||
assertTrue(result.programAst.toplevelModule.name.startsWith("on_the_fly_test"))
|
assertTrue(result.program.toplevelModule.name.startsWith("on_the_fly_test"))
|
||||||
val options = determineCompilationOptions(result.programAst, C64Target)
|
val options = determineCompilationOptions(result.program, C64Target)
|
||||||
assertTrue(options.floats)
|
assertTrue(options.floats)
|
||||||
assertEquals(ZeropageType.DONTUSE, options.zeropage)
|
assertEquals(ZeropageType.DONTUSE, options.zeropage)
|
||||||
assertTrue(options.noSysInit)
|
assertTrue(options.noSysInit)
|
||||||
@ -70,7 +70,7 @@ main {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testModuleOrderAndCompilationOptionsCorrectWithJustImports() {
|
fun testModuleOrderAndCompilationOptionsCorrectWithJustImports() {
|
||||||
val errors = ErrorReporter()
|
val errors = ErrorReporterForTests()
|
||||||
val sourceText = """
|
val sourceText = """
|
||||||
%import textio
|
%import textio
|
||||||
%import floats
|
%import floats
|
||||||
|
@ -12,11 +12,12 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.PrefixExpression
|
import prog8.ast.expressions.PrefixExpression
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.astprocessing.isInRegularRAMof
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8.compilerinterface.isInRegularRAMof
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.DummyFunctions
|
import prog8tests.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.helpers.DummyMemsizer
|
||||||
|
import prog8tests.helpers.DummyStringEncoder
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
@ -70,7 +71,7 @@ class TestMemory {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testInValidRamC64_memory_identifiers() {
|
fun testInValidRamC64_memory_identifiers() {
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR)
|
var target = createTestProgramForMemoryRefViaVar(program, 0x1000, VarDeclType.VAR)
|
||||||
|
|
||||||
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -89,7 +90,7 @@ class TestMemory {
|
|||||||
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
val memexpr = IdentifierReference(listOf("address"), Position.DUMMY)
|
||||||
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
val target = AssignTarget(null, null, DirectMemoryWrite(memexpr, Position.DUMMY), Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
return target
|
return target
|
||||||
@ -107,9 +108,9 @@ class TestMemory {
|
|||||||
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.VAR, DataType.BYTE, ZeropageWish.DONTCARE, null, "address", null, false, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -121,9 +122,9 @@ class TestMemory {
|
|||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -135,9 +136,9 @@ class TestMemory {
|
|||||||
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
val decl = VarDecl(VarDeclType.MEMORY, DataType.UBYTE, ZeropageWish.DONTCARE, null, "address", NumericLiteralValue.optimalInteger(address, Position.DUMMY), false, false, false, Position.DUMMY)
|
||||||
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
val target = AssignTarget(IdentifierReference(listOf("address"), Position.DUMMY), null, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertFalse(target.isInRegularRAMof(C64Target.machine))
|
assertFalse(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -149,9 +150,9 @@ class TestMemory {
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -164,9 +165,9 @@ class TestMemory {
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
assertTrue(target.isInRegularRAMof(C64Target.machine))
|
||||||
@ -179,9 +180,9 @@ class TestMemory {
|
|||||||
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
val arrayindexed = ArrayIndexedExpression(IdentifierReference(listOf("address"), Position.DUMMY), ArrayIndex(NumericLiteralValue.optimalInteger(1, Position.DUMMY), Position.DUMMY), Position.DUMMY)
|
||||||
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
val target = AssignTarget(null, arrayindexed, null, Position.DUMMY)
|
||||||
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
val assignment = Assignment(target, NumericLiteralValue.optimalInteger(0, Position.DUMMY), Position.DUMMY)
|
||||||
val subroutine = Subroutine("test", emptyList(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
val subroutine = Subroutine("test", mutableListOf(), emptyList(), emptyList(), emptyList(), emptySet(), null, false, false, mutableListOf(decl, assignment), Position.DUMMY)
|
||||||
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
val module = Module(mutableListOf(subroutine), Position.DUMMY, SourceCode.Generated("test"))
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
module.linkIntoProgram(program)
|
module.linkIntoProgram(program)
|
||||||
assertFalse(target.isInRegularRAMof(C64Target.machine))
|
assertFalse(target.isInRegularRAMof(C64Target.machine))
|
||||||
|
@ -6,10 +6,10 @@ import org.hamcrest.Matchers.equalTo
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.toHex
|
import prog8.ast.toHex
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||||
|
import prog8.compilerinterface.InternalCompilerException
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
@ -81,10 +81,10 @@ class TestNumbers {
|
|||||||
assertThat(Mflpt5.fromNumber(1.7e-39), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
assertThat(Mflpt5.fromNumber(1.7e-39), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
||||||
assertThat(Mflpt5.fromNumber(-1.7e-38), equalTo(Mflpt5(0x03, 0xb9, 0x1d, 0x15, 0x63)))
|
assertThat(Mflpt5.fromNumber(-1.7e-38), equalTo(Mflpt5(0x03, 0xb9, 0x1d, 0x15, 0x63)))
|
||||||
assertThat(Mflpt5.fromNumber(-1.7e-39), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
assertThat(Mflpt5.fromNumber(-1.7e-39), equalTo(Mflpt5(0x00, 0x00, 0x00, 0x00, 0x00)))
|
||||||
assertFailsWith<CompilerException> { Mflpt5.fromNumber(1.7014118346e+38) }
|
assertFailsWith<InternalCompilerException> { Mflpt5.fromNumber(1.7014118346e+38) }
|
||||||
assertFailsWith<CompilerException> { Mflpt5.fromNumber(-1.7014118346e+38) }
|
assertFailsWith<InternalCompilerException> { Mflpt5.fromNumber(-1.7014118346e+38) }
|
||||||
assertFailsWith<CompilerException> { Mflpt5.fromNumber(1.7014118347e+38) }
|
assertFailsWith<InternalCompilerException> { Mflpt5.fromNumber(1.7014118347e+38) }
|
||||||
assertFailsWith<CompilerException> { Mflpt5.fromNumber(-1.7014118347e+38) }
|
assertFailsWith<InternalCompilerException> { Mflpt5.fromNumber(-1.7014118347e+38) }
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
|
@ -1,18 +1,21 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import org.hamcrest.CoreMatchers.instanceOf
|
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.statements.Block
|
import prog8.ast.Program
|
||||||
import prog8.ast.statements.Return
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.base.ParentSentinel
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.expressions.TypecastExpression
|
||||||
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8tests.helpers.DummyFunctions
|
||||||
|
import prog8tests.helpers.DummyMemsizer
|
||||||
|
import prog8tests.helpers.DummyStringEncoder
|
||||||
import prog8tests.helpers.assertSuccess
|
import prog8tests.helpers.assertSuccess
|
||||||
import prog8tests.helpers.compileText
|
import prog8tests.helpers.compileText
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.*
|
||||||
import kotlin.test.assertSame
|
|
||||||
import kotlin.test.assertTrue
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestOptimization {
|
class TestOptimization {
|
||||||
@ -28,10 +31,10 @@ class TestOptimization {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||||
val toplevelModule = result.programAst.toplevelModule
|
val toplevelModule = result.program.toplevelModule
|
||||||
val mainBlock = toplevelModule.statements.single() as Block
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
val startSub = mainBlock.statements.single() as Subroutine
|
val startSub = mainBlock.statements.single() as Subroutine
|
||||||
assertSame(result.programAst.entrypoint, startSub)
|
assertSame(result.program.entrypoint, startSub)
|
||||||
assertEquals("start", startSub.name, "only start sub should remain")
|
assertEquals("start", startSub.name, "only start sub should remain")
|
||||||
assertTrue(startSub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
assertTrue(startSub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
||||||
}
|
}
|
||||||
@ -50,13 +53,71 @@ class TestOptimization {
|
|||||||
}
|
}
|
||||||
"""
|
"""
|
||||||
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||||
val toplevelModule = result.programAst.toplevelModule
|
val toplevelModule = result.program.toplevelModule
|
||||||
val mainBlock = toplevelModule.statements.single() as Block
|
val mainBlock = toplevelModule.statements.single() as Block
|
||||||
val startSub = mainBlock.statements[0] as Subroutine
|
val startSub = mainBlock.statements[0] as Subroutine
|
||||||
val emptySub = mainBlock.statements[1] as Subroutine
|
val emptySub = mainBlock.statements[1] as Subroutine
|
||||||
assertSame(result.programAst.entrypoint, startSub)
|
assertSame(result.program.entrypoint, startSub)
|
||||||
assertEquals("start", startSub.name)
|
assertEquals("start", startSub.name)
|
||||||
assertEquals("empty", emptySub.name)
|
assertEquals("empty", emptySub.name)
|
||||||
assertTrue(emptySub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
assertTrue(emptySub.statements.single() is Return, "compiler has inserted return in empty subroutines")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testGeneratedConstvalueInheritsProperParentLinkage()
|
||||||
|
{
|
||||||
|
val number = NumericLiteralValue(DataType.UBYTE, 11, Position.DUMMY)
|
||||||
|
val tc = TypecastExpression(number, DataType.BYTE, false, Position.DUMMY)
|
||||||
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
|
tc.linkParents(ParentSentinel)
|
||||||
|
assertNotNull(tc.parent)
|
||||||
|
assertNotNull(number.parent)
|
||||||
|
assertSame(tc, number.parent)
|
||||||
|
val constvalue = tc.constValue(program)!!
|
||||||
|
assertIs<NumericLiteralValue>(constvalue)
|
||||||
|
assertEquals(11, constvalue.number.toInt())
|
||||||
|
assertEquals(DataType.BYTE, constvalue.type)
|
||||||
|
assertSame(tc, constvalue.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testConstantFoldedAndSilentlyTypecastedForInitializerValues() {
|
||||||
|
val sourcecode = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const ubyte TEST = 10
|
||||||
|
byte x1 = TEST as byte + 1
|
||||||
|
byte x2 = 1 + TEST as byte
|
||||||
|
ubyte y1 = TEST + 1 as byte
|
||||||
|
ubyte y2 = 1 as byte + TEST
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, true, sourcecode).assertSuccess()
|
||||||
|
val mainsub = result.program.entrypoint
|
||||||
|
assertEquals(10, mainsub.statements.size)
|
||||||
|
val declTest = mainsub.statements[0] as VarDecl
|
||||||
|
val declX1 = mainsub.statements[1] as VarDecl
|
||||||
|
val initX1 = mainsub.statements[2] as Assignment
|
||||||
|
val declX2 = mainsub.statements[3] as VarDecl
|
||||||
|
val initX2 = mainsub.statements[4] as Assignment
|
||||||
|
val declY1 = mainsub.statements[5] as VarDecl
|
||||||
|
val initY1 = mainsub.statements[6] as Assignment
|
||||||
|
val declY2 = mainsub.statements[7] as VarDecl
|
||||||
|
val initY2 = mainsub.statements[8] as Assignment
|
||||||
|
assertIs<Return>(mainsub.statements[9])
|
||||||
|
assertEquals(10.0, (declTest.value as NumericLiteralValue).number.toDouble())
|
||||||
|
assertNull(declX1.value)
|
||||||
|
assertNull(declX2.value)
|
||||||
|
assertNull(declY1.value)
|
||||||
|
assertNull(declY2.value)
|
||||||
|
assertEquals(DataType.BYTE, (initX1.value as NumericLiteralValue).type)
|
||||||
|
assertEquals(11.0, (initX1.value as NumericLiteralValue).number.toDouble())
|
||||||
|
assertEquals(DataType.BYTE, (initX2.value as NumericLiteralValue).type)
|
||||||
|
assertEquals(11.0, (initX2.value as NumericLiteralValue).number.toDouble())
|
||||||
|
assertEquals(DataType.UBYTE, (initY1.value as NumericLiteralValue).type)
|
||||||
|
assertEquals(11.0, (initY1.value as NumericLiteralValue).number.toDouble())
|
||||||
|
assertEquals(DataType.UBYTE, (initY2.value as NumericLiteralValue).type)
|
||||||
|
assertEquals(11.0, (initY2.value as NumericLiteralValue).number.toDouble())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,16 +1,17 @@
|
|||||||
package prog8tests
|
package prog8tests
|
||||||
|
|
||||||
import com.github.michaelbull.result.Ok
|
import com.github.michaelbull.result.Ok
|
||||||
|
import com.github.michaelbull.result.expect
|
||||||
|
import com.github.michaelbull.result.expectError
|
||||||
|
import com.github.michaelbull.result.getOrElse
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.Matchers.equalTo
|
import org.hamcrest.Matchers.equalTo
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.Position
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
|
||||||
import prog8.compiler.target.cbm.Petscii
|
import prog8.compiler.target.cbm.Petscii
|
||||||
import kotlin.test.*
|
import java.io.CharConversionException
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFailsWith
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
@ -34,8 +35,6 @@ class TestPetscii {
|
|||||||
assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(Ok(listOf<Short>(0xd3))))
|
assertThat("expect lowercase error fallback", Petscii.encodePetscii("♥", true), equalTo(Ok(listOf<Short>(0xd3))))
|
||||||
|
|
||||||
assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A"))
|
assertThat(Petscii.decodePetscii(listOf(72, 0xd7, 0x5c, 0xfa, 0x12), true), equalTo("hW£✓\uF11A"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1), true) }
|
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(256), true) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -48,8 +47,6 @@ class TestPetscii {
|
|||||||
assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(Ok(listOf<Short>(250))))
|
assertThat("expecting fallback", Petscii.encodePetscii("✓"), equalTo(Ok(listOf<Short>(250))))
|
||||||
|
|
||||||
assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π"))
|
assertThat(Petscii.decodePetscii(listOf(72, 0x5c, 0xd3, 0xff)), equalTo("H£♥π"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(-1)) }
|
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodePetscii(listOf(256)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -62,8 +59,6 @@ class TestPetscii {
|
|||||||
assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(Ok(listOf<Short>(94))))
|
assertThat("expect fallback", Petscii.encodeScreencode("π", true), equalTo(Ok(listOf<Short>(94))))
|
||||||
|
|
||||||
assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓"))
|
assertThat(Petscii.decodeScreencode(listOf(0x08, 0x57, 0x1c, 0x7a), true), equalTo("hW£✓"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1), true) }
|
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(256), true) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -77,33 +72,129 @@ class TestPetscii {
|
|||||||
assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(Ok(listOf<Short>(122))))
|
assertThat("expecting fallback", Petscii.encodeScreencode("✓"), equalTo(Ok(listOf<Short>(122))))
|
||||||
|
|
||||||
assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π"))
|
assertThat(Petscii.decodeScreencode(listOf(0x17, 0x1c, 0x53, 0x5e)), equalTo("W£♥π"))
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(-1)) }
|
|
||||||
assertFailsWith<ArrayIndexOutOfBoundsException> { Petscii.decodeScreencode(listOf(256)) }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLiteralValueComparisons() {
|
fun testErrorCases() {
|
||||||
val ten = NumericLiteralValue(DataType.UWORD, 10, Position.DUMMY)
|
Petscii.encodePetscii("~", true).expectError { "shouldn't be able to encode tilde" }
|
||||||
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position.DUMMY)
|
Petscii.encodePetscii("~", false).expectError { "shouldn't be able to encode tilde" }
|
||||||
assertEquals(ten, ten)
|
Petscii.encodeScreencode("~", true).expectError { "shouldn't be able to encode tilde" }
|
||||||
assertNotEquals(ten, nine)
|
Petscii.encodeScreencode("~", false).expectError { "shouldn't be able to encode tilde" }
|
||||||
assertFalse(ten != ten)
|
|
||||||
assertTrue(ten != nine)
|
|
||||||
|
|
||||||
assertTrue(ten > nine)
|
assertFailsWith<CharConversionException> { Petscii.decodePetscii(listOf<Short>(-1), true) }
|
||||||
assertTrue(ten >= nine)
|
assertFailsWith<CharConversionException> { Petscii.decodePetscii(listOf<Short>(256), true) }
|
||||||
assertTrue(ten >= ten)
|
assertFailsWith<CharConversionException> { Petscii.decodePetscii(listOf<Short>(-1), false) }
|
||||||
assertFalse(ten > ten)
|
assertFailsWith<CharConversionException> { Petscii.decodePetscii(listOf<Short>(256), false) }
|
||||||
|
assertFailsWith<CharConversionException> { Petscii.decodeScreencode(listOf<Short>(-1), true) }
|
||||||
|
assertFailsWith<CharConversionException> { Petscii.decodeScreencode(listOf<Short>(256), true) }
|
||||||
|
assertFailsWith<CharConversionException> { Petscii.decodeScreencode(listOf<Short>(-1), false) }
|
||||||
|
assertFailsWith<CharConversionException> { Petscii.decodeScreencode(listOf<Short>(256), false) }
|
||||||
|
|
||||||
assertFalse(ten < nine)
|
Petscii.scr2petscii(-1).expectError { "-1 should error" }
|
||||||
assertFalse(ten <= nine)
|
Petscii.scr2petscii(256).expectError { "256 should error" }
|
||||||
assertTrue(ten <= ten)
|
Petscii.petscii2scr(-1, true).expectError { "-1 should error" }
|
||||||
assertFalse(ten < ten)
|
Petscii.petscii2scr(256, true).expectError { "256 should error" }
|
||||||
|
Petscii.petscii2scr(-1, false).expectError { "-1 should error" }
|
||||||
val abc = StringLiteralValue("abc", false, Position.DUMMY)
|
Petscii.petscii2scr(256, false).expectError { "256 should error" }
|
||||||
val abd = StringLiteralValue("abd", false, Position.DUMMY)
|
|
||||||
assertEquals(abc, abc)
|
|
||||||
assertTrue(abc!=abd)
|
|
||||||
assertFalse(abc!=abc)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testSpecialReplacements()
|
||||||
|
{
|
||||||
|
fun encodeP(c: Char, lower: Boolean) = Petscii.encodePetscii(c.toString(), lower).getOrElse { throw it }.single()
|
||||||
|
fun encodeS(c: Char, lower: Boolean) = Petscii.encodeScreencode(c.toString(), lower).getOrElse { throw it }.single()
|
||||||
|
|
||||||
|
Petscii.encodePetscii("`", false).expectError { "shouldn't have translation for backtick" }
|
||||||
|
Petscii.encodePetscii("`", true).expectError { "shouldn't have translation for backtick" }
|
||||||
|
Petscii.encodePetscii("~", false).expectError { "shouldn't have translation for tilde" }
|
||||||
|
Petscii.encodePetscii("~", true).expectError { "shouldn't have translation for tilde" }
|
||||||
|
|
||||||
|
assertEquals(94, encodeP('^', false))
|
||||||
|
assertEquals(94, encodeP('^', true))
|
||||||
|
assertEquals(30, encodeS('^', false))
|
||||||
|
assertEquals(30, encodeS('^', true))
|
||||||
|
assertEquals(228, encodeP('_', false))
|
||||||
|
assertEquals(228, encodeP('_', true))
|
||||||
|
assertEquals(100, encodeS('_', false))
|
||||||
|
assertEquals(100, encodeS('_', true))
|
||||||
|
assertEquals(243, encodeP('{', false))
|
||||||
|
assertEquals(243, encodeP('{', true))
|
||||||
|
assertEquals(115, encodeS('{', false))
|
||||||
|
assertEquals(115, encodeS('{', true))
|
||||||
|
assertEquals(235, encodeP('}', false))
|
||||||
|
assertEquals(235, encodeP('}', true))
|
||||||
|
assertEquals(107, encodeS('}', false))
|
||||||
|
assertEquals(107, encodeS('}', true))
|
||||||
|
assertEquals(221, encodeP('|', false))
|
||||||
|
assertEquals(221, encodeP('|', true))
|
||||||
|
assertEquals(93, encodeS('|', false))
|
||||||
|
assertEquals(93, encodeS('|', true))
|
||||||
|
assertEquals(205, encodeP('\\', false))
|
||||||
|
assertEquals(205, encodeP('\\', true))
|
||||||
|
assertEquals(77, encodeS('\\', false))
|
||||||
|
assertEquals(77, encodeS('\\', true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBoxDrawingCharsEncoding() {
|
||||||
|
fun encodeP(c: Char, lower: Boolean) = Petscii.encodePetscii(c.toString(), lower).getOrElse { throw it }.single()
|
||||||
|
fun encodeS(c: Char, lower: Boolean) = Petscii.encodeScreencode(c.toString(), lower).getOrElse { throw it }.single()
|
||||||
|
|
||||||
|
// pipe char
|
||||||
|
assertEquals(221, encodeP('|', false))
|
||||||
|
assertEquals(221, encodeP('|', true))
|
||||||
|
assertEquals(93, encodeS('|', false))
|
||||||
|
assertEquals(93, encodeS('|', true))
|
||||||
|
// ... same as '│', 0x7D -> BOX DRAWINGS LIGHT VERTICAL
|
||||||
|
assertEquals(221, encodeP('│', false))
|
||||||
|
assertEquals(221, encodeP('│', true))
|
||||||
|
assertEquals(93, encodeS('│', false))
|
||||||
|
assertEquals(93, encodeS('│', true))
|
||||||
|
|
||||||
|
// underscore
|
||||||
|
assertEquals(228, encodeP('_', false))
|
||||||
|
assertEquals(228, encodeP('_', true))
|
||||||
|
assertEquals(100, encodeS('_', false))
|
||||||
|
assertEquals(100, encodeS('_', true))
|
||||||
|
// ... same as '▁', 0xE4 LOWER ONE EIGHTH BLOCK
|
||||||
|
assertEquals(228, encodeP('▁', false))
|
||||||
|
assertEquals(228, encodeP('▁', true))
|
||||||
|
assertEquals(100, encodeS('▁', false))
|
||||||
|
assertEquals(100, encodeS('▁', true))
|
||||||
|
|
||||||
|
// ─ 0xC0 -> BOX DRAWINGS LIGHT HORIZONTAL
|
||||||
|
assertEquals(192, encodeP('─', false))
|
||||||
|
assertEquals(192, encodeP('─', true))
|
||||||
|
assertEquals(64, encodeS('─', false))
|
||||||
|
assertEquals(64, encodeS('─', true))
|
||||||
|
// │ 0x62 -> BOX DRAWINGS LIGHT VERTICAL
|
||||||
|
assertEquals(221, encodeP('│', false))
|
||||||
|
assertEquals(221, encodeP('│', true))
|
||||||
|
assertEquals(93, encodeS('│', false))
|
||||||
|
assertEquals(93, encodeS('│', true))
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testBoxDrawingCharsDecoding() {
|
||||||
|
// ─ 0xC0 -> BOX DRAWINGS LIGHT HORIZONTAL
|
||||||
|
assertEquals('\uf13b', Petscii.decodePetscii(listOf(195), false).single(), "BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH UP (CUS)")
|
||||||
|
assertEquals('C', Petscii.decodePetscii(listOf(195), true).single())
|
||||||
|
assertEquals('─', Petscii.decodePetscii(listOf(192), false).single())
|
||||||
|
assertEquals('─', Petscii.decodePetscii(listOf(192), true).single())
|
||||||
|
assertEquals('\uf13b', Petscii.decodeScreencode(listOf(67), false).single(), "BOX DRAWINGS LIGHT HORIZONTAL ONE EIGHTH UP (CUS)")
|
||||||
|
assertEquals('C', Petscii.decodeScreencode(listOf(67), true).single())
|
||||||
|
assertEquals('─', Petscii.decodeScreencode(listOf(64), false).single())
|
||||||
|
assertEquals('─', Petscii.decodeScreencode(listOf(64), true).single())
|
||||||
|
|
||||||
|
// │ 0x62 -> BOX DRAWINGS LIGHT VERTICAL
|
||||||
|
assertEquals('│', Petscii.decodePetscii(listOf(125), false).single())
|
||||||
|
assertEquals('│', Petscii.decodePetscii(listOf(125), true).single())
|
||||||
|
assertEquals('│', Petscii.decodePetscii(listOf(221), false).single())
|
||||||
|
assertEquals('│', Petscii.decodePetscii(listOf(221), true).single())
|
||||||
|
assertEquals('│', Petscii.decodeScreencode(listOf(93), false).single())
|
||||||
|
assertEquals('│', Petscii.decodeScreencode(listOf(93), true).single())
|
||||||
|
assertEquals('\uf13c', Petscii.decodeScreencode(listOf(66), false).single(), "BOX DRAWINGS LIGHT VERTICAL ONE EIGHTH LEFT (CUS)")
|
||||||
|
assertEquals('B', Petscii.decodeScreencode(listOf(66), true).single())
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
126
compiler/test/TestScoping.kt
Normal file
126
compiler/test/TestScoping.kt
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.GlobalNamespace
|
||||||
|
import prog8.ast.base.ParentSentinel
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
|
import prog8tests.helpers.compileText
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestScoping {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testModulesParentIsGlobalNamespace() {
|
||||||
|
val src = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
assertIs<GlobalNamespace>(module.parent)
|
||||||
|
assertSame(result.program, module.program)
|
||||||
|
assertIs<ParentSentinel>(module.parent.parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAnonScopeVarsMovedIntoSubroutineScope() {
|
||||||
|
val src = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
repeat {
|
||||||
|
ubyte xx = 99
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, src, writeAssembly = false).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
|
val repeatbody = start.statements.filterIsInstance<RepeatLoop>().single().body
|
||||||
|
assertFalse(mainBlock.statements.any { it is VarDecl }, "no vars moved to main block")
|
||||||
|
val subroutineVars = start.statements.filterIsInstance<VarDecl>()
|
||||||
|
assertEquals(1, subroutineVars.size, "var from repeat anonscope must be moved up to subroutine")
|
||||||
|
assertEquals("xx", subroutineVars[0].name)
|
||||||
|
assertFalse(repeatbody.statements.any { it is VarDecl }, "var should have been removed from repeat anonscope")
|
||||||
|
val initassign = repeatbody.statements[0] as? Assignment
|
||||||
|
assertEquals(listOf("xx"), initassign?.target?.identifier?.nameInSource, "vardecl in repeat should be replaced by init assignment")
|
||||||
|
assertEquals(99, (initassign?.value as? NumericLiteralValue)?.number?.toInt(), "vardecl in repeat should be replaced by init assignment")
|
||||||
|
assertTrue(repeatbody.statements[1] is PostIncrDecr)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLabelsWithAnonScopes() {
|
||||||
|
val src = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
uword addr
|
||||||
|
goto labeloutside
|
||||||
|
|
||||||
|
if true {
|
||||||
|
if true {
|
||||||
|
addr = &iflabel
|
||||||
|
addr = &labelinside
|
||||||
|
addr = &labeloutside
|
||||||
|
addr = &main.start.nested.nestedlabel
|
||||||
|
addr = &nested.nestedlabel
|
||||||
|
goto labeloutside
|
||||||
|
goto iflabel
|
||||||
|
goto main.start.nested.nestedlabel
|
||||||
|
goto nested.nestedlabel
|
||||||
|
}
|
||||||
|
iflabel:
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
addr = &iflabel
|
||||||
|
addr = &labelinside
|
||||||
|
addr = &labeloutside
|
||||||
|
addr = &main.start.nested.nestedlabel
|
||||||
|
addr = &nested.nestedlabel
|
||||||
|
goto iflabel
|
||||||
|
goto labelinside
|
||||||
|
goto main.start.nested.nestedlabel
|
||||||
|
goto nested.nestedlabel
|
||||||
|
labelinside:
|
||||||
|
}
|
||||||
|
|
||||||
|
sub nested () {
|
||||||
|
nestedlabel:
|
||||||
|
addr = &nestedlabel
|
||||||
|
goto nestedlabel
|
||||||
|
goto main.start.nested.nestedlabel
|
||||||
|
}
|
||||||
|
|
||||||
|
labeloutside:
|
||||||
|
addr = &iflabel
|
||||||
|
addr = &labelinside
|
||||||
|
addr = &labeloutside
|
||||||
|
addr = &main.start.nested.nestedlabel
|
||||||
|
addr = &nested.nestedlabel
|
||||||
|
goto main.start.nested.nestedlabel
|
||||||
|
goto nested.nestedlabel
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, src, writeAssembly = true).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
|
val labels = start.statements.filterIsInstance<Label>()
|
||||||
|
assertEquals(1, labels.size, "only one label in subroutine scope")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
255
compiler/test/TestSubroutines.kt
Normal file
255
compiler/test/TestSubroutines.kt
Normal file
@ -0,0 +1,255 @@
|
|||||||
|
package prog8tests
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Disabled
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
import prog8.compiler.target.C64Target
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
|
import prog8tests.helpers.assertFailure
|
||||||
|
import prog8tests.helpers.assertSuccess
|
||||||
|
import prog8tests.helpers.compileText
|
||||||
|
import kotlin.test.*
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestSubroutines {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stringParameter() {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
str text = "test"
|
||||||
|
|
||||||
|
asmfunc("text")
|
||||||
|
asmfunc(text)
|
||||||
|
asmfunc($2000)
|
||||||
|
func("text")
|
||||||
|
func(text)
|
||||||
|
func($2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub asmfunc(str thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(str thing) {
|
||||||
|
uword t2 = thing as uword
|
||||||
|
asmfunc(thing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
|
assertTrue(asmfunc.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.STR, asmfunc.parameters.single().type)
|
||||||
|
assertTrue(asmfunc.statements.isEmpty())
|
||||||
|
assertFalse(func.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.STR, func.parameters.single().type)
|
||||||
|
assertEquals(4, func.statements.size)
|
||||||
|
val paramvar = func.statements[0] as VarDecl
|
||||||
|
assertEquals("thing", paramvar.name)
|
||||||
|
assertEquals(DataType.STR, paramvar.datatype)
|
||||||
|
val assign = func.statements[2] as Assignment
|
||||||
|
assertEquals(listOf("t2"), assign.target.identifier!!.nameInSource)
|
||||||
|
assertTrue(assign.value is TypecastExpression, "str param in function body should not be transformed by normal compiler steps")
|
||||||
|
assertEquals(DataType.UWORD, (assign.value as TypecastExpression).type)
|
||||||
|
val call = func.statements[3] as FunctionCallStatement
|
||||||
|
assertEquals("asmfunc", call.target.nameInSource.single())
|
||||||
|
assertTrue(call.args.single() is IdentifierReference, "str param in function body should not be transformed by normal compiler steps")
|
||||||
|
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stringParameterAsmGen() {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
str text = "test"
|
||||||
|
|
||||||
|
asmfunc("text")
|
||||||
|
asmfunc(text)
|
||||||
|
asmfunc($2000)
|
||||||
|
func("text")
|
||||||
|
func(text)
|
||||||
|
func($2000)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub asmfunc(str thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(str thing) {
|
||||||
|
uword t2 = thing as uword
|
||||||
|
asmfunc(thing)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
|
assertTrue(asmfunc.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.STR, asmfunc.parameters.single().type)
|
||||||
|
assertTrue(asmfunc.statements.single() is Return)
|
||||||
|
assertFalse(func.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.UWORD, func.parameters.single().type, "asmgen should have changed str to uword type")
|
||||||
|
assertTrue(asmfunc.statements.last() is Return)
|
||||||
|
|
||||||
|
assertEquals(5, func.statements.size)
|
||||||
|
assertTrue(func.statements[4] is Return)
|
||||||
|
val paramvar = func.statements[0] as VarDecl
|
||||||
|
assertEquals("thing", paramvar.name)
|
||||||
|
assertEquals(DataType.UWORD, paramvar.datatype, "pre-asmgen should have changed str to uword type")
|
||||||
|
val assign = func.statements[2] as Assignment
|
||||||
|
assertEquals(listOf("t2"), assign.target.identifier!!.nameInSource)
|
||||||
|
assertTrue(assign.value is IdentifierReference, "str param in function body should be treated as plain uword before asmgen")
|
||||||
|
assertEquals("thing", (assign.value as IdentifierReference).nameInSource.single())
|
||||||
|
val call = func.statements[3] as FunctionCallStatement
|
||||||
|
assertEquals("asmfunc", call.target.nameInSource.single())
|
||||||
|
assertTrue(call.args.single() is IdentifierReference, "str param in function body should be treated as plain uword and not been transformed")
|
||||||
|
assertEquals("thing", (call.args.single() as IdentifierReference).nameInSource.single())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arrayParameterNotYetAllowed_ButShouldPerhapsBe() {
|
||||||
|
// note: the *parser* accepts this as it is valid *syntax*,
|
||||||
|
// however, it's not (yet) valid for the compiler
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub asmfunc(ubyte[] thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(ubyte[22] thing) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val errors = ErrorReporterForTests()
|
||||||
|
compileText(C64Target, false, text, errors, false).assertFailure("currently array dt in signature is invalid") // TODO should not be invalid?
|
||||||
|
assertEquals(0, errors.warnings.size)
|
||||||
|
assertContains(errors.errors.single(), ".p8:9:16: Non-string pass-by-reference types cannot occur as a parameter type directly")
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@Disabled("TODO: allow array parameter in signature") // TODO allow this?
|
||||||
|
fun arrayParameter() {
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
ubyte[] array = [1,2,3]
|
||||||
|
|
||||||
|
asmfunc(array)
|
||||||
|
asmfunc([4,5,6])
|
||||||
|
asmfunc($2000)
|
||||||
|
asmfunc(12.345)
|
||||||
|
func(array)
|
||||||
|
func([4,5,6])
|
||||||
|
func($2000)
|
||||||
|
func(12.345)
|
||||||
|
}
|
||||||
|
|
||||||
|
asmsub asmfunc(ubyte[] thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(ubyte[22] thing) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, text, writeAssembly = false).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
|
assertTrue(asmfunc.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.ARRAY_UB, asmfunc.parameters.single().type)
|
||||||
|
assertTrue(asmfunc.statements.isEmpty())
|
||||||
|
assertFalse(func.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.ARRAY_UB, func.parameters.single().type)
|
||||||
|
assertTrue(func.statements.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUwordParameterAndNormalVarIndexedAsArrayWorkAsDirectMemoryRead() {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub thing(uword rr) {
|
||||||
|
ubyte xx = rr[1] ; should still work as var initializer that will be rewritten
|
||||||
|
ubyte yy
|
||||||
|
yy = rr[2]
|
||||||
|
uword other
|
||||||
|
ubyte zz = other[3]
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubyte[] array=[1,2,3]
|
||||||
|
thing(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val block = module.statements.single() as Block
|
||||||
|
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
||||||
|
assertEquals("main", block.name)
|
||||||
|
assertEquals(10, thing.statements.size, "rr, xx, xx assign, yy, yy assign, other, other assign 0, zz, zz assign, return")
|
||||||
|
val xx = thing.statements[1] as VarDecl
|
||||||
|
assertNull(xx.value, "vardecl init values must have been moved to separate assignments")
|
||||||
|
val assignXX = thing.statements[2] as Assignment
|
||||||
|
val assignYY = thing.statements[4] as Assignment
|
||||||
|
val assignZZ = thing.statements[8] as Assignment
|
||||||
|
assertEquals(listOf("xx"), assignXX.target.identifier!!.nameInSource)
|
||||||
|
assertEquals(listOf("yy"), assignYY.target.identifier!!.nameInSource)
|
||||||
|
assertEquals(listOf("zz"), assignZZ.target.identifier!!.nameInSource)
|
||||||
|
val valueXXexpr = (assignXX.value as DirectMemoryRead).addressExpression as BinaryExpression
|
||||||
|
val valueYYexpr = (assignYY.value as DirectMemoryRead).addressExpression as BinaryExpression
|
||||||
|
val valueZZexpr = (assignZZ.value as DirectMemoryRead).addressExpression as BinaryExpression
|
||||||
|
assertEquals(listOf("rr"), (valueXXexpr.left as IdentifierReference).nameInSource)
|
||||||
|
assertEquals(listOf("rr"), (valueYYexpr.left as IdentifierReference).nameInSource)
|
||||||
|
assertEquals(listOf("other"), (valueZZexpr.left as IdentifierReference).nameInSource)
|
||||||
|
assertEquals(1, (valueXXexpr.right as NumericLiteralValue).number.toInt())
|
||||||
|
assertEquals(2, (valueYYexpr.right as NumericLiteralValue).number.toInt())
|
||||||
|
assertEquals(3, (valueZZexpr.right as NumericLiteralValue).number.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testUwordParameterAndNormalVarIndexedAsArrayWorkAsMemoryWrite() {
|
||||||
|
val text="""
|
||||||
|
main {
|
||||||
|
sub thing(uword rr) {
|
||||||
|
rr[10] = 42
|
||||||
|
}
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
ubyte[] array=[1,2,3]
|
||||||
|
thing(array)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
|
||||||
|
val result = compileText(C64Target, false, text, writeAssembly = true).assertSuccess()
|
||||||
|
val module = result.program.toplevelModule
|
||||||
|
val block = module.statements.single() as Block
|
||||||
|
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
||||||
|
assertEquals("main", block.name)
|
||||||
|
assertEquals(3, thing.statements.size, "rr, rr assign, return void")
|
||||||
|
val assignRR = thing.statements[1] as Assignment
|
||||||
|
assertEquals(42, (assignRR.value as NumericLiteralValue).number.toInt())
|
||||||
|
val memwrite = assignRR.target.memoryAddress
|
||||||
|
assertNotNull(memwrite, "memwrite to set byte array value")
|
||||||
|
val addressExpr = memwrite.addressExpression as BinaryExpression
|
||||||
|
assertEquals(listOf("rr"), (addressExpr.left as IdentifierReference).nameInSource)
|
||||||
|
assertEquals("+", addressExpr.operator)
|
||||||
|
assertEquals(10, (addressExpr.right as NumericLiteralValue).number.toInt())
|
||||||
|
}
|
||||||
|
}
|
@ -3,21 +3,77 @@ package prog8tests
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.compiler.*
|
|
||||||
import prog8.compiler.target.C64Target
|
import prog8.compiler.target.C64Target
|
||||||
import prog8.compiler.target.Cx16Target
|
import prog8.compiler.target.Cx16Target
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
import prog8.compiler.target.cx16.CX16MachineDefinition.CX16Zeropage
|
||||||
|
import prog8.compilerinterface.*
|
||||||
|
import prog8tests.helpers.ErrorReporterForTests
|
||||||
import kotlin.test.assertEquals
|
import kotlin.test.assertEquals
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
import kotlin.test.assertTrue
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestAbstractZeropage {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAbstractZeropage() {
|
||||||
|
val compTarget = DummyCompilationTarget()
|
||||||
|
val zp = DummyZeropage(
|
||||||
|
CompilationOptions(
|
||||||
|
OutputType.RAW,
|
||||||
|
LauncherType.NONE,
|
||||||
|
ZeropageType.FULL,
|
||||||
|
listOf((0x50..0x5f)),
|
||||||
|
false,
|
||||||
|
false,
|
||||||
|
compTarget
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(256-6-16, zp.free.size)
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyCompilationTarget: ICompilationTarget {
|
||||||
|
override val name: String = "dummy"
|
||||||
|
override val machine: IMachineDefinition
|
||||||
|
get() = throw NotImplementedError("dummy")
|
||||||
|
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
throw NotImplementedError("dummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||||
|
throw NotImplementedError("dummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun memorySize(dt: DataType): Int {
|
||||||
|
throw NotImplementedError("dummy")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
class DummyZeropage(options: CompilationOptions) : Zeropage(options) {
|
||||||
|
override val SCRATCH_B1: Int = 0x10
|
||||||
|
override val SCRATCH_REG: Int = 0x11
|
||||||
|
override val SCRATCH_W1: Int= 0x20
|
||||||
|
override val SCRATCH_W2: Int = 0x30
|
||||||
|
|
||||||
|
init {
|
||||||
|
free.addAll(0..255)
|
||||||
|
|
||||||
|
removeReservedFromFreePool()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestC64Zeropage {
|
class TestC64Zeropage {
|
||||||
|
|
||||||
private val errors = ErrorReporter()
|
private val errors = ErrorReporterForTests()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
@ -35,11 +91,11 @@ class TestC64Zeropage {
|
|||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<InternalCompilerException> {
|
||||||
zp.allocate("", DataType.FLOAT, null, errors)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<InternalCompilerException> {
|
||||||
zp2.allocate("", DataType.FLOAT, null, errors)
|
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||||
@ -54,10 +110,10 @@ class TestC64Zeropage {
|
|||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<InternalCompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
|
||||||
}
|
}
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<InternalCompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
|
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -67,7 +123,7 @@ class TestC64Zeropage {
|
|||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
assertEquals(0, zp.availableBytes())
|
assertEquals(0, zp.availableBytes())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<InternalCompilerException> {
|
||||||
zp.allocate("", DataType.BYTE, null, errors)
|
zp.allocate("", DataType.BYTE, null, errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -216,7 +272,7 @@ class TestC64Zeropage {
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestCx16Zeropage {
|
class TestCx16Zeropage {
|
||||||
private val errors = ErrorReporter()
|
private val errors = ErrorReporterForTests()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testReservedLocations() {
|
fun testReservedLocations() {
|
||||||
|
@ -1,21 +1,36 @@
|
|||||||
package prog8tests.helpers
|
package prog8tests.helpers
|
||||||
|
|
||||||
import prog8.ast.IBuiltinFunctions
|
import prog8.ast.IBuiltinFunctions
|
||||||
import prog8.ast.IMemSizer
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.InferredTypes
|
import prog8.ast.expressions.InferredTypes
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.compilerinterface.IMemSizer
|
||||||
|
import prog8.compilerinterface.IStringEncoding
|
||||||
|
|
||||||
val DummyFunctions = object : IBuiltinFunctions {
|
internal val DummyFunctions = object : IBuiltinFunctions {
|
||||||
override val names: Set<String> = emptySet()
|
override val names: Set<String> = emptySet()
|
||||||
override val purefunctionNames: Set<String> = emptySet()
|
override val purefunctionNames: Set<String> = emptySet()
|
||||||
override fun constValue(
|
override fun constValue(
|
||||||
name: String,
|
name: String,
|
||||||
args: List<Expression>,
|
args: List<Expression>,
|
||||||
position: Position,
|
position: Position,
|
||||||
memsizer: IMemSizer
|
|
||||||
): NumericLiteralValue? = null
|
): NumericLiteralValue? = null
|
||||||
|
|
||||||
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal val DummyMemsizer = object : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType): Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val DummyStringEncoder = object : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
@ -1,25 +1,29 @@
|
|||||||
package prog8tests.helpers
|
package prog8tests.helpers
|
||||||
|
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.compiler.IErrorReporter
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
|
||||||
class ErrorReporterForTests: IErrorReporter {
|
internal class ErrorReporterForTests(private val throwExceptionAtReportIfErrors: Boolean=true): IErrorReporter {
|
||||||
|
|
||||||
|
|
||||||
val errors = mutableListOf<String>()
|
val errors = mutableListOf<String>()
|
||||||
val warnings = mutableListOf<String>()
|
val warnings = mutableListOf<String>()
|
||||||
|
|
||||||
override fun err(msg: String, position: Position) {
|
override fun err(msg: String, position: Position) {
|
||||||
errors.add(msg)
|
errors.add("${position.toClickableStr()} $msg")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun warn(msg: String, position: Position) {
|
override fun warn(msg: String, position: Position) {
|
||||||
warnings.add(msg)
|
warnings.add("${position.toClickableStr()} $msg")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun noErrors(): Boolean = errors.isEmpty()
|
override fun noErrors(): Boolean = errors.isEmpty()
|
||||||
|
|
||||||
override fun report() {
|
override fun report() {
|
||||||
|
warnings.forEach { println("UNITTEST COMPILATION REPORT: WARNING: $it") }
|
||||||
|
errors.forEach { println("UNITTEST COMPILATION REPORT: ERROR: $it") }
|
||||||
|
if(throwExceptionAtReportIfErrors)
|
||||||
|
finalizeNumErrors(errors.size, warnings.size)
|
||||||
errors.clear()
|
errors.clear()
|
||||||
warnings.clear()
|
warnings.clear()
|
||||||
}
|
}
|
||||||
|
@ -2,7 +2,10 @@ package prog8tests.helpers
|
|||||||
|
|
||||||
import prog8.compiler.CompilationResult
|
import prog8.compiler.CompilationResult
|
||||||
import prog8.compiler.compileProgram
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.ICompilationTarget
|
import prog8.compilerinterface.ICompilationTarget
|
||||||
|
import prog8.compilerinterface.IErrorReporter
|
||||||
|
import prog8tests.ast.helpers.assumeReadableFile
|
||||||
|
import prog8tests.ast.helpers.outputDir
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
import kotlin.test.assertFalse
|
import kotlin.test.assertFalse
|
||||||
@ -28,18 +31,23 @@ internal fun compileFile(
|
|||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
fileDir: Path,
|
fileDir: Path,
|
||||||
fileName: String,
|
fileName: String,
|
||||||
outputDir: Path = prog8tests.helpers.outputDir
|
outputDir: Path = prog8tests.ast.helpers.outputDir,
|
||||||
|
errors: IErrorReporter? = null,
|
||||||
|
writeAssembly: Boolean = true
|
||||||
) : CompilationResult {
|
) : CompilationResult {
|
||||||
val filepath = fileDir.resolve(fileName)
|
val filepath = fileDir.resolve(fileName)
|
||||||
assumeReadableFile(filepath)
|
assumeReadableFile(filepath)
|
||||||
return compileProgram(
|
return compileProgram(
|
||||||
filepath,
|
filepath,
|
||||||
optimize,
|
optimize,
|
||||||
writeAssembly = true,
|
optimizeFloatExpressions = false,
|
||||||
|
writeAssembly = writeAssembly,
|
||||||
slowCodegenWarnings = false,
|
slowCodegenWarnings = false,
|
||||||
|
quietAssembler = true,
|
||||||
platform.name,
|
platform.name,
|
||||||
sourceDirs = listOf(),
|
sourceDirs = listOf(),
|
||||||
outputDir
|
outputDir,
|
||||||
|
errors = errors ?: ErrorReporterForTests()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -51,10 +59,12 @@ internal fun compileFile(
|
|||||||
internal fun compileText(
|
internal fun compileText(
|
||||||
platform: ICompilationTarget,
|
platform: ICompilationTarget,
|
||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
sourceText: String
|
sourceText: String,
|
||||||
|
errors: IErrorReporter? = null,
|
||||||
|
writeAssembly: Boolean = true
|
||||||
) : CompilationResult {
|
) : CompilationResult {
|
||||||
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
|
val filePath = outputDir.resolve("on_the_fly_test_" + sourceText.hashCode().toUInt().toString(16) + ".p8")
|
||||||
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
|
// we don't assumeNotExists(filePath) - should be ok to just overwrite it
|
||||||
filePath.toFile().writeText(sourceText)
|
filePath.toFile().writeText(sourceText)
|
||||||
return compileFile(platform, optimize, filePath.parent, filePath.name)
|
return compileFile(platform, optimize, filePath.parent, filePath.name, errors=errors, writeAssembly=writeAssembly)
|
||||||
}
|
}
|
||||||
|
@ -3,14 +3,13 @@ plugins {
|
|||||||
id "org.jetbrains.kotlin.jvm"
|
id "org.jetbrains.kotlin.jvm"
|
||||||
}
|
}
|
||||||
|
|
||||||
targetCompatibility = 11
|
|
||||||
sourceCompatibility = 11
|
|
||||||
|
|
||||||
repositories {
|
java {
|
||||||
mavenCentral()
|
toolchain {
|
||||||
|
languageVersion = JavaLanguageVersion.of(javaVersion)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
dependencies {
|
dependencies {
|
||||||
implementation 'org.antlr:antlr4-runtime:4.9.2'
|
implementation 'org.antlr:antlr4-runtime:4.9.2'
|
||||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.12"
|
||||||
@ -26,22 +25,6 @@ configurations.all {
|
|||||||
exclude group: 'com.ibm.icu', module: 'icu4j'
|
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||||
}
|
}
|
||||||
|
|
||||||
compileKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
useIR = true
|
|
||||||
// verbose = true
|
|
||||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
compileTestKotlin {
|
|
||||||
kotlinOptions {
|
|
||||||
jvmTarget = "11"
|
|
||||||
useIR = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
sourceSets {
|
sourceSets {
|
||||||
main {
|
main {
|
||||||
java {
|
java {
|
||||||
|
@ -12,9 +12,8 @@ import prog8.ast.walk.IAstVisitor
|
|||||||
/**
|
/**
|
||||||
* Produces Prog8 source text from a [Program] (AST node),
|
* Produces Prog8 source text from a [Program] (AST node),
|
||||||
* passing it as a String to the specified receiver function.
|
* passing it as a String to the specified receiver function.
|
||||||
* TODO: rename/refactor to make proper sense in the presence of class [prog8.parser.SourceCode]
|
|
||||||
*/
|
*/
|
||||||
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
class AstToSourceTextConverter(val output: (text: String) -> Unit, val program: Program): IAstVisitor {
|
||||||
private var scopelevel = 0
|
private var scopelevel = 0
|
||||||
|
|
||||||
private fun indent(s: String) = " ".repeat(scopelevel) + s
|
private fun indent(s: String) = " ".repeat(scopelevel) + s
|
||||||
@ -98,7 +97,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
|
|
||||||
// if the vardecl is a parameter of a subroutine, don't output it again
|
// if the vardecl is a parameter of a subroutine, don't output it again
|
||||||
val paramNames = (decl.definingScope as? Subroutine)?.parameters?.map { it.name }
|
val paramNames = decl.definingSubroutine?.parameters?.map { it.name }
|
||||||
if(paramNames!=null && decl.name in paramNames)
|
if(paramNames!=null && decl.name in paramNames)
|
||||||
return
|
return
|
||||||
|
|
@ -3,7 +3,6 @@ package prog8.ast
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
@ -21,117 +20,12 @@ interface IFunctionCall {
|
|||||||
var args: MutableList<Expression>
|
var args: MutableList<Expression>
|
||||||
}
|
}
|
||||||
|
|
||||||
interface INameScope {
|
interface IStatementContainer {
|
||||||
val name: String
|
|
||||||
val position: Position
|
val position: Position
|
||||||
val statements: MutableList<Statement>
|
|
||||||
val parent: Node
|
val parent: Node
|
||||||
|
val statements: MutableList<Statement>
|
||||||
fun linkParents(parent: Node)
|
fun linkParents(parent: Node)
|
||||||
|
|
||||||
fun subScope(name: String): INameScope? {
|
|
||||||
for(stmt in statements) {
|
|
||||||
when(stmt) {
|
|
||||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
|
||||||
is ForLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is UntilLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is WhileLoop -> if(stmt.body.name==name) return stmt.body
|
|
||||||
is BranchStatement -> {
|
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
|
||||||
if(stmt.elsepart.containsCodeOrVars && stmt.elsepart.name==name) return stmt.elsepart
|
|
||||||
}
|
|
||||||
is IfStatement -> {
|
|
||||||
if(stmt.truepart.name==name) return stmt.truepart
|
|
||||||
if(stmt.elsepart.containsCodeOrVars && stmt.elsepart.name==name) return stmt.elsepart
|
|
||||||
}
|
|
||||||
is WhenStatement -> {
|
|
||||||
val scope = stmt.choices.firstOrNull { it.statements.name==name }
|
|
||||||
if(scope!=null)
|
|
||||||
return scope.statements
|
|
||||||
}
|
|
||||||
is INameScope -> if(stmt.name==name) return stmt
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getLabelOrVariable(name: String): Statement? {
|
|
||||||
// this is called A LOT and could perhaps be optimized a bit more,
|
|
||||||
// but adding a memoization cache didn't make much of a practical runtime difference
|
|
||||||
for (stmt in statements) {
|
|
||||||
if (stmt is VarDecl && stmt.name==name) return stmt
|
|
||||||
if (stmt is Label && stmt.name==name) return stmt
|
|
||||||
if (stmt is AnonymousScope) {
|
|
||||||
val sub = stmt.getLabelOrVariable(name)
|
|
||||||
if(sub!=null)
|
|
||||||
return sub
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
|
|
||||||
val allDefinedSymbols: List<Pair<String, Statement>>
|
|
||||||
get() {
|
|
||||||
return statements.mapNotNull {
|
|
||||||
when (it) {
|
|
||||||
is Label -> it.name to it
|
|
||||||
is VarDecl -> it.name to it
|
|
||||||
is Subroutine -> it.name to it
|
|
||||||
is Block -> it.name to it
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
|
||||||
if(scopedName.size>1) {
|
|
||||||
// a scoped name refers to a name in another module.
|
|
||||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
|
||||||
for(module in localContext.definingModule.program.modules) {
|
|
||||||
var scope: INameScope? = module
|
|
||||||
for(name in scopedName.dropLast(1)) {
|
|
||||||
scope = scope?.subScope(name)
|
|
||||||
if(scope==null)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
if(scope!=null) {
|
|
||||||
val result = scope.getLabelOrVariable(scopedName.last())
|
|
||||||
if(result!=null)
|
|
||||||
return result
|
|
||||||
return scope.subScope(scopedName.last()) as Statement?
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
} else {
|
|
||||||
// unqualified name
|
|
||||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
|
||||||
if(localContext.parent.parent is UntilLoop) {
|
|
||||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.getLabelOrVariable(scopedName[0])
|
|
||||||
if(symbolFromInnerScope!=null)
|
|
||||||
return symbolFromInnerScope
|
|
||||||
}
|
|
||||||
|
|
||||||
// find the scope the localContext is in, look in that first
|
|
||||||
var statementScope = localContext
|
|
||||||
while(statementScope !is ParentSentinel) {
|
|
||||||
val localScope = statementScope.definingScope
|
|
||||||
val result = localScope.getLabelOrVariable(scopedName[0])
|
|
||||||
if (result != null)
|
|
||||||
return result
|
|
||||||
val subscope = localScope.subScope(scopedName[0]) as Statement?
|
|
||||||
if (subscope != null)
|
|
||||||
return subscope
|
|
||||||
// not found in this scope, look one higher up
|
|
||||||
statementScope = statementScope.parent
|
|
||||||
}
|
|
||||||
return null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val containsCodeOrVars get() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm" }
|
|
||||||
val containsNoCodeNorVars get() = !containsCodeOrVars
|
|
||||||
|
|
||||||
fun remove(stmt: Statement) {
|
fun remove(stmt: Statement) {
|
||||||
if(!statements.remove(stmt))
|
if(!statements.remove(stmt))
|
||||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||||
@ -140,11 +34,11 @@ interface INameScope {
|
|||||||
fun getAllLabels(label: String): List<Label> {
|
fun getAllLabels(label: String): List<Label> {
|
||||||
val result = mutableListOf<Label>()
|
val result = mutableListOf<Label>()
|
||||||
|
|
||||||
fun find(scope: INameScope) {
|
fun find(scope: IStatementContainer) {
|
||||||
scope.statements.forEach {
|
scope.statements.forEach {
|
||||||
when(it) {
|
when(it) {
|
||||||
is Label -> result.add(it)
|
is Label -> result.add(it)
|
||||||
is INameScope -> find(it)
|
is IStatementContainer -> find(it)
|
||||||
is IfStatement -> {
|
is IfStatement -> {
|
||||||
find(it.truepart)
|
find(it.truepart)
|
||||||
find(it.elsepart)
|
find(it.elsepart)
|
||||||
@ -162,22 +56,6 @@ interface INameScope {
|
|||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
fun nextSibling(stmt: Statement): Statement? {
|
|
||||||
val nextIdx = statements.indexOfFirst { it===stmt } + 1
|
|
||||||
return if(nextIdx < statements.size)
|
|
||||||
statements[nextIdx]
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun previousSibling(stmt: Statement): Statement? {
|
|
||||||
val previousIdx = statements.indexOfFirst { it===stmt } - 1
|
|
||||||
return if(previousIdx>=0)
|
|
||||||
statements[previousIdx]
|
|
||||||
else
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
fun indexOfChild(stmt: Statement): Int {
|
fun indexOfChild(stmt: Statement): Int {
|
||||||
val idx = statements.indexOfFirst { it===stmt }
|
val idx = statements.indexOfFirst { it===stmt }
|
||||||
if(idx>=0)
|
if(idx>=0)
|
||||||
@ -185,6 +63,159 @@ interface INameScope {
|
|||||||
else
|
else
|
||||||
throw FatalAstException("attempt to find a non-child")
|
throw FatalAstException("attempt to find a non-child")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isEmpty(): Boolean = statements.isEmpty()
|
||||||
|
fun isNotEmpty(): Boolean = statements.isNotEmpty()
|
||||||
|
|
||||||
|
fun searchSymbol(name: String): Statement? {
|
||||||
|
// this is called quite a lot and could perhaps be optimized a bit more,
|
||||||
|
// but adding a memoization cache didn't make much of a practical runtime difference...
|
||||||
|
for (stmt in statements) {
|
||||||
|
when(stmt) {
|
||||||
|
// is INamedStatement -> {
|
||||||
|
// if(stmt.name==name) return stmt
|
||||||
|
// }
|
||||||
|
is VarDecl -> {
|
||||||
|
if(stmt.name==name) return stmt
|
||||||
|
}
|
||||||
|
is Label -> {
|
||||||
|
if(stmt.name==name) return stmt
|
||||||
|
}
|
||||||
|
is Subroutine -> {
|
||||||
|
if(stmt.name==name)
|
||||||
|
return stmt
|
||||||
|
}
|
||||||
|
is AnonymousScope -> {
|
||||||
|
val found = stmt.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is IfStatement -> {
|
||||||
|
val found = stmt.truepart.searchSymbol(name) ?: stmt.elsepart.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is BranchStatement -> {
|
||||||
|
val found = stmt.truepart.searchSymbol(name) ?: stmt.elsepart.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is ForLoop -> {
|
||||||
|
val found = stmt.body.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is WhileLoop -> {
|
||||||
|
val found = stmt.body.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is RepeatLoop -> {
|
||||||
|
val found = stmt.body.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is UntilLoop -> {
|
||||||
|
val found = stmt.body.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
is WhenStatement -> {
|
||||||
|
stmt.choices.forEach {
|
||||||
|
val found = it.statements.searchSymbol(name)
|
||||||
|
if(found!=null)
|
||||||
|
return found
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// NOTE: when other nodes containing AnonymousScope are introduced,
|
||||||
|
// these should be added here as well to look into!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
val allDefinedSymbols: List<Pair<String, Statement>>
|
||||||
|
get() {
|
||||||
|
return statements.filterIsInstance<INamedStatement>().map { Pair(it.name, it as Statement) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
interface INameScope: IStatementContainer, INamedStatement {
|
||||||
|
fun subScope(name: String): INameScope? = statements.firstOrNull { it is INameScope && it.name==name } as? INameScope
|
||||||
|
|
||||||
|
fun lookup(scopedName: List<String>) : Statement? {
|
||||||
|
return if(scopedName.size>1)
|
||||||
|
lookupQualified(scopedName)
|
||||||
|
else {
|
||||||
|
lookupUnqualified(scopedName[0])
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookupQualified(scopedName: List<String>): Statement? {
|
||||||
|
// a scoped name refers to a name in another namespace.
|
||||||
|
// look "up" from our current scope to search for the correct one.
|
||||||
|
val localScope = this.subScope(scopedName[0])
|
||||||
|
if(localScope!=null)
|
||||||
|
return resolveLocally(localScope, scopedName.drop(1))
|
||||||
|
|
||||||
|
var statementScope = this
|
||||||
|
while(statementScope !is GlobalNamespace) {
|
||||||
|
if(statementScope !is Module && statementScope.name==scopedName[0]) {
|
||||||
|
return resolveLocally(statementScope, scopedName.drop(1))
|
||||||
|
} else {
|
||||||
|
statementScope = (statementScope as Node).definingScope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// not found, try again but now assume it's a globally scoped name starting with the name of a block
|
||||||
|
for(module in (this as Node).definingModule.program.modules) {
|
||||||
|
module.statements.forEach {
|
||||||
|
if(it is Block && it.name==scopedName[0])
|
||||||
|
return it.lookup(scopedName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun resolveLocally(startScope: INameScope, name: List<String>): Statement? {
|
||||||
|
var scope: INameScope? = startScope
|
||||||
|
for(part in name.dropLast(1)) {
|
||||||
|
scope = scope!!.subScope(part)
|
||||||
|
if(scope==null)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
return scope!!.searchSymbol(name.last())
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun lookupUnqualified(name: String): Statement? {
|
||||||
|
val builtinFunctionsNames = (this as Node).definingModule.program.builtinFunctions.names
|
||||||
|
if(name in builtinFunctionsNames) {
|
||||||
|
// builtin functions always exist, return a dummy placeholder for them
|
||||||
|
val builtinPlaceholder = Label("builtin::$name", this.position)
|
||||||
|
builtinPlaceholder.parent = ParentSentinel
|
||||||
|
return builtinPlaceholder
|
||||||
|
}
|
||||||
|
|
||||||
|
// search for the unqualified name in the current scope (and possibly in any anonymousscopes it may contain)
|
||||||
|
// if it's not found there, jump up one higher in the namespaces and try again.
|
||||||
|
var statementScope = this
|
||||||
|
while(statementScope !is GlobalNamespace) {
|
||||||
|
val symbol = statementScope.searchSymbol(name)
|
||||||
|
if(symbol!=null)
|
||||||
|
return symbol
|
||||||
|
else
|
||||||
|
statementScope = (statementScope as Node).definingScope
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
// private fun getNamedSymbol(name: String): Statement? =
|
||||||
|
// statements.singleOrNull { it is INamedStatement && it.name==name }
|
||||||
|
|
||||||
|
val containsCodeOrVars get() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm" }
|
||||||
|
val containsNoCodeNorVars get() = !containsCodeOrVars
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -234,119 +265,6 @@ interface Node {
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
|
||||||
|
|
||||||
class Program(val name: String,
|
|
||||||
val builtinFunctions: IBuiltinFunctions,
|
|
||||||
val memsizer: IMemSizer): Node {
|
|
||||||
private val _modules = mutableListOf<Module>()
|
|
||||||
|
|
||||||
val modules: List<Module> = _modules
|
|
||||||
val namespace: GlobalNamespace = GlobalNamespace(modules, builtinFunctions.names)
|
|
||||||
|
|
||||||
init {
|
|
||||||
// insert a container module for all interned strings later
|
|
||||||
val internedStringsModule = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
|
|
||||||
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
|
||||||
internedStringsModule.statements.add(block)
|
|
||||||
|
|
||||||
_modules.add(0, internedStringsModule)
|
|
||||||
internedStringsModule.linkParents(namespace)
|
|
||||||
internedStringsModule.program = this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun addModule(module: Module): Program {
|
|
||||||
require(null == _modules.firstOrNull { it.name == module.name })
|
|
||||||
{ "module '${module.name}' already present" }
|
|
||||||
_modules.add(module)
|
|
||||||
module.linkIntoProgram(this)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
fun removeModule(module: Module) = _modules.remove(module)
|
|
||||||
|
|
||||||
fun moveModuleToFront(module: Module): Program {
|
|
||||||
require(_modules.contains(module))
|
|
||||||
{ "Not a module of this program: '${module.name}'"}
|
|
||||||
_modules.remove(module)
|
|
||||||
_modules.add(0, module)
|
|
||||||
return this
|
|
||||||
}
|
|
||||||
|
|
||||||
val allBlocks: List<Block>
|
|
||||||
get() = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
|
||||||
|
|
||||||
val entrypoint: Subroutine
|
|
||||||
get() {
|
|
||||||
val mainBlocks = allBlocks.filter { it.name == "main" }
|
|
||||||
return when (mainBlocks.size) {
|
|
||||||
0 -> throw FatalAstException("no 'main' block")
|
|
||||||
1 -> mainBlocks[0].subScope("start") as Subroutine
|
|
||||||
else -> throw FatalAstException("more than one 'main' block")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val toplevelModule: Module
|
|
||||||
get() = modules.first { it.name!=internedStringsModuleName }
|
|
||||||
|
|
||||||
val definedLoadAddress: Int
|
|
||||||
get() = toplevelModule.loadAddress
|
|
||||||
|
|
||||||
var actualLoadAddress: Int = 0
|
|
||||||
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
|
||||||
|
|
||||||
fun internString(string: StringLiteralValue): List<String> {
|
|
||||||
// Move a string literal into the internal, deduplicated, string pool
|
|
||||||
// replace it with a variable declaration that points to the entry in the pool.
|
|
||||||
|
|
||||||
if(string.parent is VarDecl) {
|
|
||||||
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
|
||||||
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getScopedName(string: StringLiteralValue): List<String> {
|
|
||||||
val internedStringsBlock = modules
|
|
||||||
.first { it.name == internedStringsModuleName }.statements
|
|
||||||
.first { it is Block && it.name == internedStringsModuleName } as Block
|
|
||||||
val varName = "string_${internedStringsBlock.statements.size}"
|
|
||||||
val decl = VarDecl(
|
|
||||||
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
|
|
||||||
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
|
|
||||||
)
|
|
||||||
internedStringsBlock.statements.add(decl)
|
|
||||||
decl.linkParents(internedStringsBlock)
|
|
||||||
return listOf(internedStringsModuleName, decl.name)
|
|
||||||
}
|
|
||||||
|
|
||||||
val key = Pair(string.value, string.altEncoding)
|
|
||||||
val existing = internedStringsUnique[key]
|
|
||||||
if (existing != null)
|
|
||||||
return existing
|
|
||||||
|
|
||||||
val scopedName = getScopedName(string)
|
|
||||||
internedStringsUnique[key] = scopedName
|
|
||||||
return scopedName
|
|
||||||
}
|
|
||||||
|
|
||||||
override val position: Position = Position.DUMMY
|
|
||||||
override var parent: Node
|
|
||||||
get() = throw FatalAstException("program has no parent")
|
|
||||||
set(_) = throw FatalAstException("can't set parent of program")
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
|
||||||
modules.forEach {
|
|
||||||
it.linkParents(namespace)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
|
||||||
require(node is Module && replacement is Module)
|
|
||||||
val idx = _modules.indexOfFirst { it===node }
|
|
||||||
_modules[idx] = replacement
|
|
||||||
replacement.linkIntoProgram(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
open class Module(final override var statements: MutableList<Statement>,
|
open class Module(final override var statements: MutableList<Statement>,
|
||||||
final override val position: Position,
|
final override val position: Position,
|
||||||
val source: SourceCode) : Node, INameScope {
|
val source: SourceCode) : Node, INameScope {
|
||||||
@ -393,12 +311,16 @@ open class Module(final override var statements: MutableList<Statement>,
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GlobalNamespace(val modules: Iterable<Module>, private val builtinFunctionNames: Set<String>): Node, INameScope {
|
class GlobalNamespace(val modules: Iterable<Module>): Node, INameScope {
|
||||||
override val name = "<<<global>>>"
|
override val name = "<<<global>>>"
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
override val statements = mutableListOf<Statement>() // not used
|
override val statements = mutableListOf<Statement>() // not used
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
|
|
||||||
|
override fun lookup(scopedName: List<String>): Statement? {
|
||||||
|
throw NotImplementedError("use lookup on actual ast node instead")
|
||||||
|
}
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
modules.forEach { it.linkParents(this) }
|
modules.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
@ -406,29 +328,6 @@ class GlobalNamespace(val modules: Iterable<Module>, private val builtinFunction
|
|||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
throw FatalAstException("cannot replace anything in the namespace")
|
throw FatalAstException("cannot replace anything in the namespace")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
|
||||||
if (scopedName.size == 1 && scopedName[0] in builtinFunctionNames) {
|
|
||||||
// builtin functions always exist, return a dummy localContext for them
|
|
||||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
|
||||||
builtinPlaceholder.parent = ParentSentinel
|
|
||||||
return builtinPlaceholder
|
|
||||||
}
|
|
||||||
|
|
||||||
// special case: the do....until statement can also look INSIDE the anonymous scope
|
|
||||||
if(localContext.parent.parent is UntilLoop) {
|
|
||||||
val symbolFromInnerScope = (localContext.parent.parent as UntilLoop).body.lookup(scopedName, localContext)
|
|
||||||
if(symbolFromInnerScope!=null)
|
|
||||||
return symbolFromInnerScope
|
|
||||||
}
|
|
||||||
|
|
||||||
// lookup something from the module.
|
|
||||||
return when (val stmt = localContext.definingModule.lookup(scopedName, localContext)) {
|
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
|
||||||
null -> null
|
|
||||||
else -> throw SyntaxError("invalid identifier target type", stmt.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
object BuiltinFunctionScopePlaceholder : INameScope {
|
||||||
|
@ -8,6 +8,6 @@ import prog8.ast.expressions.NumericLiteralValue
|
|||||||
interface IBuiltinFunctions {
|
interface IBuiltinFunctions {
|
||||||
val names: Set<String>
|
val names: Set<String>
|
||||||
val purefunctionNames: Set<String>
|
val purefunctionNames: Set<String>
|
||||||
fun constValue(name: String, args: List<Expression>, position: Position, memsizer: IMemSizer): NumericLiteralValue?
|
fun constValue(name: String, args: List<Expression>, position: Position): NumericLiteralValue?
|
||||||
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
|
fun returnType(name: String, args: MutableList<Expression>): InferredTypes.InferredType
|
||||||
}
|
}
|
||||||
|
@ -1,7 +0,0 @@
|
|||||||
package prog8.ast
|
|
||||||
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
|
|
||||||
interface IMemSizer {
|
|
||||||
fun memorySize(dt: DataType): Int
|
|
||||||
}
|
|
112
compilerAst/src/prog8/ast/Program.kt
Normal file
112
compilerAst/src/prog8/ast/Program.kt
Normal file
@ -0,0 +1,112 @@
|
|||||||
|
package prog8.ast
|
||||||
|
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.ast.statements.VarDecl
|
||||||
|
import prog8.ast.statements.ZeropageWish
|
||||||
|
import prog8.compilerinterface.IMemSizer
|
||||||
|
import prog8.compilerinterface.IStringEncoding
|
||||||
|
import prog8.parser.SourceCode
|
||||||
|
|
||||||
|
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||||
|
|
||||||
|
class Program(val name: String,
|
||||||
|
val builtinFunctions: IBuiltinFunctions,
|
||||||
|
val memsizer: IMemSizer,
|
||||||
|
val encoding: IStringEncoding
|
||||||
|
) {
|
||||||
|
private val _modules = mutableListOf<Module>()
|
||||||
|
|
||||||
|
val modules: List<Module> = _modules
|
||||||
|
val namespace: GlobalNamespace = GlobalNamespace(modules)
|
||||||
|
|
||||||
|
init {
|
||||||
|
// insert a container module for all interned strings later
|
||||||
|
val internedStringsModule =
|
||||||
|
Module(mutableListOf(), Position.DUMMY, SourceCode.Generated(internedStringsModuleName))
|
||||||
|
val block = Block(internedStringsModuleName, null, mutableListOf(), true, Position.DUMMY)
|
||||||
|
internedStringsModule.statements.add(block)
|
||||||
|
|
||||||
|
_modules.add(0, internedStringsModule)
|
||||||
|
internedStringsModule.linkParents(namespace)
|
||||||
|
internedStringsModule.program = this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun addModule(module: Module): Program {
|
||||||
|
require(null == _modules.firstOrNull { it.name == module.name })
|
||||||
|
{ "module '${module.name}' already present" }
|
||||||
|
_modules.add(module)
|
||||||
|
module.linkIntoProgram(this)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
fun removeModule(module: Module) = _modules.remove(module)
|
||||||
|
|
||||||
|
fun moveModuleToFront(module: Module): Program {
|
||||||
|
require(_modules.contains(module))
|
||||||
|
{ "Not a module of this program: '${module.name}'"}
|
||||||
|
_modules.remove(module)
|
||||||
|
_modules.add(0, module)
|
||||||
|
return this
|
||||||
|
}
|
||||||
|
|
||||||
|
val allBlocks: List<Block>
|
||||||
|
get() = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||||
|
|
||||||
|
val entrypoint: Subroutine
|
||||||
|
get() {
|
||||||
|
val mainBlocks = allBlocks.filter { it.name == "main" }
|
||||||
|
return when (mainBlocks.size) {
|
||||||
|
0 -> throw FatalAstException("no 'main' block")
|
||||||
|
1 -> mainBlocks[0].subScope("start") as Subroutine
|
||||||
|
else -> throw FatalAstException("more than one 'main' block")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val toplevelModule: Module
|
||||||
|
get() = modules.first { it.name!= internedStringsModuleName }
|
||||||
|
|
||||||
|
val definedLoadAddress: Int
|
||||||
|
get() = toplevelModule.loadAddress
|
||||||
|
|
||||||
|
var actualLoadAddress: Int = 0
|
||||||
|
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
|
||||||
|
|
||||||
|
fun internString(string: StringLiteralValue): List<String> {
|
||||||
|
// Move a string literal into the internal, deduplicated, string pool
|
||||||
|
// replace it with a variable declaration that points to the entry in the pool.
|
||||||
|
|
||||||
|
if(string.parent is VarDecl) {
|
||||||
|
// deduplication can only be performed safely for known-const strings (=string literals OUTSIDE OF A VARDECL)!
|
||||||
|
throw FatalAstException("cannot intern a string literal that's part of a vardecl")
|
||||||
|
}
|
||||||
|
|
||||||
|
fun getScopedName(string: StringLiteralValue): List<String> {
|
||||||
|
val internedStringsBlock = modules
|
||||||
|
.first { it.name == internedStringsModuleName }.statements
|
||||||
|
.first { it is Block && it.name == internedStringsModuleName } as Block
|
||||||
|
val varName = "string_${internedStringsBlock.statements.size}"
|
||||||
|
val decl = VarDecl(
|
||||||
|
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
|
||||||
|
isArray = false, autogeneratedDontRemove = true, sharedWithAsm = false, position = string.position
|
||||||
|
)
|
||||||
|
internedStringsBlock.statements.add(decl)
|
||||||
|
decl.linkParents(internedStringsBlock)
|
||||||
|
return listOf(internedStringsModuleName, decl.name)
|
||||||
|
}
|
||||||
|
|
||||||
|
val key = Pair(string.value, string.altEncoding)
|
||||||
|
val existing = internedStringsUnique[key]
|
||||||
|
if (existing != null)
|
||||||
|
return existing
|
||||||
|
|
||||||
|
val scopedName = getScopedName(string)
|
||||||
|
internedStringsUnique[key] = scopedName
|
||||||
|
return scopedName
|
||||||
|
}
|
||||||
|
}
|
@ -29,7 +29,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
pathString
|
pathString
|
||||||
}
|
}
|
||||||
// note: beware of TAB characters in the source text, they count as 1 column...
|
// note: beware of TAB characters in the source text, they count as 1 column...
|
||||||
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
return Position(filename, start.line, start.charPositionInLine, start.charPositionInLine + start.stopIndex - start.startIndex)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Prog8ANTLRParser.BlockContext.toAst(isInLibrary: Boolean) : Block {
|
internal fun Prog8ANTLRParser.BlockContext.toAst(isInLibrary: Boolean) : Block {
|
||||||
@ -186,7 +186,7 @@ private fun Prog8ANTLRParser.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, subdecl.returntypes,
|
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes,
|
||||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||||
subdecl.asmClobbers, null, true, inline, statements, toPosition())
|
subdecl.asmClobbers, null, true, inline, statements, toPosition())
|
||||||
}
|
}
|
||||||
@ -194,7 +194,7 @@ private fun Prog8ANTLRParser.AsmsubroutineContext.toAst(): Subroutine {
|
|||||||
private fun Prog8ANTLRParser.RomsubroutineContext.toAst(): Subroutine {
|
private fun Prog8ANTLRParser.RomsubroutineContext.toAst(): Subroutine {
|
||||||
val subdecl = asmsub_decl().toAst()
|
val subdecl = asmsub_decl().toAst()
|
||||||
val address = integerliteral().toAst().number.toInt()
|
val address = integerliteral().toAst().number.toInt()
|
||||||
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
return Subroutine(subdecl.name, subdecl.parameters.toMutableList(), subdecl.returntypes,
|
||||||
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
subdecl.asmParameterRegisters, subdecl.asmReturnvaluesRegisters,
|
||||||
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition()
|
subdecl.asmClobbers, address, true, inline = false, statements = mutableListOf(), position = toPosition()
|
||||||
)
|
)
|
||||||
@ -252,7 +252,9 @@ private fun Prog8ANTLRParser.Asmsub_returnsContext.toAst(): List<AsmSubroutineRe
|
|||||||
private fun Prog8ANTLRParser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
private fun Prog8ANTLRParser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||||
= asmsub_param().map {
|
= asmsub_param().map {
|
||||||
val vardecl = it.vardecl()
|
val vardecl = it.vardecl()
|
||||||
val datatype = vardecl.datatype()?.toAst() ?: DataType.UNDEFINED
|
var datatype = vardecl.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||||
|
if(vardecl.ARRAYSIG()!=null || vardecl.arrayindex()!=null)
|
||||||
|
datatype = ElementToArrayTypes.getValue(datatype)
|
||||||
val register = it.register().text
|
val register = it.register().text
|
||||||
var registerorpair: RegisterOrPair? = null
|
var registerorpair: RegisterOrPair? = null
|
||||||
var statusregister: Statusflag? = null
|
var statusregister: Statusflag? = null
|
||||||
@ -304,7 +306,7 @@ private fun Prog8ANTLRParser.SubroutineContext.toAst() : Subroutine {
|
|||||||
val inline = inline()!=null
|
val inline = inline()!=null
|
||||||
val returntypes = sub_return_part()?.toAst() ?: emptyList()
|
val returntypes = sub_return_part()?.toAst() ?: emptyList()
|
||||||
return Subroutine(identifier().text,
|
return Subroutine(identifier().text,
|
||||||
sub_params()?.toAst() ?: emptyList(),
|
sub_params()?.toAst()?.toMutableList() ?: mutableListOf(),
|
||||||
returntypes,
|
returntypes,
|
||||||
statement_block()?.toAst() ?: mutableListOf(),
|
statement_block()?.toAst() ?: mutableListOf(),
|
||||||
inline,
|
inline,
|
||||||
@ -318,7 +320,9 @@ private fun Prog8ANTLRParser.Sub_return_partContext.toAst(): List<DataType> {
|
|||||||
|
|
||||||
private fun Prog8ANTLRParser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
private fun Prog8ANTLRParser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||||
vardecl().map {
|
vardecl().map {
|
||||||
val datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
var datatype = it.datatype()?.toAst() ?: DataType.UNDEFINED
|
||||||
|
if(it.ARRAYSIG()!=null || it.arrayindex()!=null)
|
||||||
|
datatype = ElementToArrayTypes.getValue(datatype)
|
||||||
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -6,6 +6,7 @@ import prog8.ast.base.*
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.ast.walk.AstWalker
|
import prog8.ast.walk.AstWalker
|
||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
import prog8.compilerinterface.IMemSizer
|
||||||
import java.util.*
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
@ -436,9 +437,18 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
|
|
||||||
class CastValue(val isValid: Boolean, private val value: NumericLiteralValue?) {
|
class CastValue(val isValid: Boolean, private val value: NumericLiteralValue?) {
|
||||||
fun valueOrZero() = if(isValid) value!! else NumericLiteralValue(DataType.UBYTE, 0, Position.DUMMY)
|
fun valueOrZero() = if(isValid) value!! else NumericLiteralValue(DataType.UBYTE, 0, Position.DUMMY)
|
||||||
|
fun linkParent(parent: Node) {
|
||||||
|
value?.linkParents(parent)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fun cast(targettype: DataType): CastValue {
|
fun cast(targettype: DataType): CastValue {
|
||||||
|
val result = internalCast(targettype)
|
||||||
|
result.linkParent(this.parent)
|
||||||
|
return result
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun internalCast(targettype: DataType): CastValue {
|
||||||
if(type==targettype)
|
if(type==targettype)
|
||||||
return CastValue(true, this)
|
return CastValue(true, this)
|
||||||
val numval = number.toDouble()
|
val numval = number.toDouble()
|
||||||
@ -513,7 +523,10 @@ class CharLiteral(val value: Char,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun referencesIdentifier(vararg scopedName: String) = false
|
override fun referencesIdentifier(vararg scopedName: String) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null // TODO: CharLiteral.constValue can't be NumericLiteralValue...
|
override fun constValue(program: Program): NumericLiteralValue {
|
||||||
|
val bytevalue = program.encoding.encodeString(value.toString(), altEncoding).single()
|
||||||
|
return NumericLiteralValue(DataType.UBYTE, bytevalue, position)
|
||||||
|
}
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
@ -730,7 +743,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
if(nameInSource.size==1 && nameInSource[0] in program.builtinFunctions.names)
|
||||||
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
|
BuiltinFunctionStatementPlaceholder(nameInSource[0], position, parent)
|
||||||
else
|
else
|
||||||
program.namespace.lookup(nameInSource, this)
|
definingScope.lookup(nameInSource)
|
||||||
|
|
||||||
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
fun targetVarDecl(program: Program): VarDecl? = targetStatement(program) as? VarDecl
|
||||||
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
fun targetSubroutine(program: Program): Subroutine? = targetStatement(program) as? Subroutine
|
||||||
@ -747,8 +760,7 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val node = program.namespace.lookup(nameInSource, this)
|
val node = definingScope.lookup(nameInSource) ?: throw UndefinedSymbolError(this)
|
||||||
?: throw UndefinedSymbolError(this)
|
|
||||||
val vardecl = node as? VarDecl
|
val vardecl = node as? VarDecl
|
||||||
if(vardecl==null) {
|
if(vardecl==null) {
|
||||||
return null
|
return null
|
||||||
@ -816,7 +828,7 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
// lenghts of arrays and strings are constants that are determined at compile time!
|
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||||
if(target.nameInSource.size>1)
|
if(target.nameInSource.size>1)
|
||||||
return null
|
return null
|
||||||
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position, program.memsizer)
|
val resultValue: NumericLiteralValue? = program.builtinFunctions.constValue(target.nameInSource[0], args, position)
|
||||||
if(withDatatypeCheck) {
|
if(withDatatypeCheck) {
|
||||||
val resultDt = this.inferType(program)
|
val resultDt = this.inferType(program)
|
||||||
if(resultValue==null || resultDt istype resultValue.type)
|
if(resultValue==null || resultDt istype resultValue.type)
|
||||||
|
@ -7,7 +7,7 @@ import prog8.ast.walk.AstWalker
|
|||||||
import prog8.ast.walk.IAstVisitor
|
import prog8.ast.walk.IAstVisitor
|
||||||
|
|
||||||
|
|
||||||
interface ISymbolStatement {
|
interface INamedStatement {
|
||||||
val name: String
|
val name: String
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -32,6 +32,24 @@ sealed class Statement : Node {
|
|||||||
scope.add(name)
|
scope.add(name)
|
||||||
return scope.joinToString(".")
|
return scope.joinToString(".")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun nextSibling(): Statement? {
|
||||||
|
val statements = (parent as? IStatementContainer)?.statements ?: return null
|
||||||
|
val nextIdx = statements.indexOfFirst { it===this } + 1
|
||||||
|
return if(nextIdx < statements.size)
|
||||||
|
statements[nextIdx]
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
fun previousSibling(): Statement? {
|
||||||
|
val statements = (parent as? IStatementContainer)?.statements ?: return null
|
||||||
|
val previousIdx = statements.indexOfFirst { it===this } - 1
|
||||||
|
return if(previousIdx >= 0)
|
||||||
|
statements[previousIdx]
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -52,7 +70,7 @@ class Block(override val name: String,
|
|||||||
val address: Int?,
|
val address: Int?,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
val isInLibrary: Boolean,
|
val isInLibrary: Boolean,
|
||||||
override val position: Position) : Statement(), INameScope, ISymbolStatement {
|
override val position: Position) : Statement(), INameScope {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -99,7 +117,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Label(override val name: String, override val position: Position) : Statement(), ISymbolStatement {
|
data class Label(override val name: String, override val position: Position) : Statement(), INamedStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
@ -166,7 +184,7 @@ open class VarDecl(val type: VarDeclType,
|
|||||||
val isArray: Boolean,
|
val isArray: Boolean,
|
||||||
val autogeneratedDontRemove: Boolean,
|
val autogeneratedDontRemove: Boolean,
|
||||||
val sharedWithAsm: Boolean,
|
val sharedWithAsm: Boolean,
|
||||||
final override val position: Position) : Statement(), ISymbolStatement {
|
final override val position: Position) : Statement(), INamedStatement {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
var allowInitializeWithZero = true
|
var allowInitializeWithZero = true
|
||||||
|
|
||||||
@ -386,7 +404,7 @@ data class AssignTarget(var identifier: IdentifierReference?,
|
|||||||
|
|
||||||
fun inferType(program: Program): InferredTypes.InferredType {
|
fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
if (identifier != null) {
|
if (identifier != null) {
|
||||||
val symbol = program.namespace.lookup(identifier!!.nameInSource, this) ?: return InferredTypes.unknown()
|
val symbol = definingScope.lookup(identifier!!.nameInSource) ?: return InferredTypes.unknown()
|
||||||
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
if (symbol is VarDecl) return InferredTypes.knownFor(symbol.datatype)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -538,19 +556,9 @@ class InlineAssembly(val assembly: String, override val position: Position) : St
|
|||||||
}
|
}
|
||||||
|
|
||||||
class AnonymousScope(override var statements: MutableList<Statement>,
|
class AnonymousScope(override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : INameScope, Statement() { // TODO this isn't really a namescope...(names are scoped to the subroutine level)
|
override val position: Position) : IStatementContainer, Statement() {
|
||||||
override val name: String = "<anon-$sequenceNumber>"
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
companion object {
|
|
||||||
private var sequenceNumber = 1
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
// make sure it's an invalid soruce code identifier so user source code can never produce it
|
|
||||||
sequenceNumber++
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
statements.forEach { it.linkParents(this) }
|
statements.forEach { it.linkParents(this) }
|
||||||
@ -597,7 +605,7 @@ class AsmGenInfo {
|
|||||||
// and also the predefined/ROM/register-based subroutines.
|
// and also the predefined/ROM/register-based subroutines.
|
||||||
// (multiple return types can only occur for the latter type)
|
// (multiple return types can only occur for the latter type)
|
||||||
class Subroutine(override val name: String,
|
class Subroutine(override val name: String,
|
||||||
val parameters: List<SubroutineParameter>,
|
val parameters: MutableList<SubroutineParameter>,
|
||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
@ -606,9 +614,9 @@ class Subroutine(override val name: String,
|
|||||||
val isAsmSubroutine: Boolean,
|
val isAsmSubroutine: Boolean,
|
||||||
val inline: Boolean,
|
val inline: Boolean,
|
||||||
override var statements: MutableList<Statement>,
|
override var statements: MutableList<Statement>,
|
||||||
override val position: Position) : Statement(), INameScope, ISymbolStatement {
|
override val position: Position) : Statement(), INameScope {
|
||||||
|
|
||||||
constructor(name: String, parameters: List<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
|
constructor(name: String, parameters: MutableList<SubroutineParameter>, returntypes: List<DataType>, statements: MutableList<Statement>, inline: Boolean, position: Position)
|
||||||
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
|
: this(name, parameters, returntypes, emptyList(), determineReturnRegisters(returntypes), emptySet(), null, false, inline, statements, position)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
@ -635,10 +643,19 @@ class Subroutine(override val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun replaceChildNode(node: Node, replacement: Node) {
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
require(replacement is Statement)
|
when(replacement) {
|
||||||
val idx = statements.indexOfFirst { it===node }
|
is SubroutineParameter -> {
|
||||||
statements[idx] = replacement
|
val idx = parameters.indexOf(node)
|
||||||
replacement.parent = this
|
parameters[idx] = replacement
|
||||||
|
replacement.parent = this
|
||||||
|
}
|
||||||
|
is Statement -> {
|
||||||
|
val idx = statements.indexOfFirst { it===node }
|
||||||
|
statements[idx] = replacement
|
||||||
|
replacement.parent = this
|
||||||
|
}
|
||||||
|
else -> throw FatalAstException("can't replace")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
@ -2,6 +2,7 @@ package prog8.ast.walk
|
|||||||
|
|
||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
@ -9,7 +10,7 @@ import prog8.ast.statements.*
|
|||||||
interface IAstModification {
|
interface IAstModification {
|
||||||
fun perform()
|
fun perform()
|
||||||
|
|
||||||
class Remove(val node: Node, val parent: INameScope) : IAstModification {
|
class Remove(val node: Node, val parent: IStatementContainer) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
if (!parent.statements.remove(node) && parent !is GlobalNamespace)
|
||||||
throw FatalAstException("attempt to remove non-existing node $node")
|
throw FatalAstException("attempt to remove non-existing node $node")
|
||||||
@ -24,21 +25,21 @@ interface IAstModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertFirst(private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
class InsertFirst(private val stmt: Statement, private val parent: IStatementContainer) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
parent.statements.add(0, stmt)
|
parent.statements.add(0, stmt)
|
||||||
stmt.linkParents(parent as Node)
|
stmt.linkParents(parent as Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertLast(private val stmt: Statement, private val parent: INameScope) : IAstModification {
|
class InsertLast(private val stmt: Statement, private val parent: IStatementContainer) : IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
parent.statements.add(stmt)
|
parent.statements.add(stmt)
|
||||||
stmt.linkParents(parent as Node)
|
stmt.linkParents(parent as Node)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertAfter(private val after: Statement, private val stmt: Statement, private val parent: INameScope) :
|
class InsertAfter(private val after: Statement, private val stmt: Statement, private val parent: IStatementContainer) :
|
||||||
IAstModification {
|
IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
val idx = parent.statements.indexOfFirst { it===after } + 1
|
val idx = parent.statements.indexOfFirst { it===after } + 1
|
||||||
@ -47,7 +48,7 @@ interface IAstModification {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class InsertBefore(private val before: Statement, private val stmt: Statement, private val parent: INameScope) :
|
class InsertBefore(private val before: Statement, private val stmt: Statement, private val parent: IStatementContainer) :
|
||||||
IAstModification {
|
IAstModification {
|
||||||
override fun perform() {
|
override fun perform() {
|
||||||
val idx = parent.statements.indexOfFirst { it===before }
|
val idx = parent.statements.indexOfFirst { it===before }
|
||||||
@ -105,7 +106,7 @@ abstract class AstWalker {
|
|||||||
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(program: Program): Iterable<IAstModification> = noModifications
|
||||||
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
@ -146,7 +147,7 @@ abstract class AstWalker {
|
|||||||
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(program: Program, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(program: Program): Iterable<IAstModification> = noModifications
|
||||||
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(untilLoop: UntilLoop, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = noModifications
|
||||||
@ -196,9 +197,9 @@ abstract class AstWalker {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun visit(program: Program) {
|
fun visit(program: Program) {
|
||||||
track(before(program, program), program, program)
|
track(before(program), ParentSentinel, program.namespace)
|
||||||
program.modules.forEach { it.accept(this, program) }
|
program.modules.forEach { it.accept(this, program.namespace) }
|
||||||
track(after(program, program), program, program)
|
track(after(program), ParentSentinel, program.namespace)
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(module: Module, parent: Node) {
|
fun visit(module: Module, parent: Node) {
|
||||||
|
11
compilerAst/src/prog8/compilerinterface/IMemSizer.kt
Normal file
11
compilerAst/src/prog8/compilerinterface/IMemSizer.kt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
package prog8.compilerinterface
|
||||||
|
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
|
||||||
|
|
||||||
|
// note: this is a separate interface in the compilerAst module because
|
||||||
|
// otherwise a cyclic dependency with the compilerInterfaces module would be needed.
|
||||||
|
|
||||||
|
interface IMemSizer {
|
||||||
|
fun memorySize(dt: DataType): Int
|
||||||
|
}
|
@ -1,6 +1,6 @@
|
|||||||
package prog8.compiler
|
package prog8.compilerinterface
|
||||||
|
|
||||||
interface IStringEncoding {
|
interface IStringEncoding {
|
||||||
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
fun encodeString(str: String, altEncoding: Boolean): List<Short>
|
||||||
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
fun decodeString(bytes: List<Short>, altEncoding: Boolean): String
|
||||||
}
|
}
|
@ -8,14 +8,7 @@ import prog8.ast.statements.Block
|
|||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
|
|
||||||
|
|
||||||
open class ParsingFailedError(override var message: String) : Exception(message)
|
class ParseError(override var message: String, val position: Position, cause: RuntimeException): Exception(message, cause)
|
||||||
|
|
||||||
class ParseError(override var message: String, val position: Position, cause: RuntimeException)
|
|
||||||
: ParsingFailedError("${position.toClickableStr()}$message") {
|
|
||||||
init {
|
|
||||||
initCause(cause)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
object Prog8Parser {
|
object Prog8Parser {
|
||||||
|
|
||||||
@ -107,7 +100,7 @@ object Prog8Parser {
|
|||||||
val offending = this.offendingToken
|
val offending = this.offendingToken
|
||||||
val line = offending.line
|
val line = offending.line
|
||||||
val beginCol = offending.charPositionInLine
|
val beginCol = offending.charPositionInLine
|
||||||
val endCol = beginCol + offending.stopIndex - offending.startIndex // TODO: point to col *after* token? / why, what's wrong with endCol being inclusive
|
val endCol = beginCol + offending.stopIndex - offending.startIndex
|
||||||
return Position(file, line, beginCol, endCol)
|
return Position(file, line, beginCol, endCol)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -8,9 +8,7 @@ import java.nio.channels.Channels
|
|||||||
import java.nio.charset.CodingErrorAction
|
import java.nio.charset.CodingErrorAction
|
||||||
import java.nio.charset.StandardCharsets
|
import java.nio.charset.StandardCharsets
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.io.path.exists
|
import kotlin.io.path.*
|
||||||
import kotlin.io.path.isDirectory
|
|
||||||
import kotlin.io.path.isReadable
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Encapsulates - and ties together - actual source code (=text)
|
* Encapsulates - and ties together - actual source code (=text)
|
||||||
@ -45,9 +43,8 @@ sealed class SourceCode {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* The source code as plain string.
|
* The source code as plain string.
|
||||||
* *Note: this is meant for testing and debugging, do NOT use in application code!*
|
|
||||||
*/
|
*/
|
||||||
fun asString() = this.getCharStream().toString()
|
abstract fun readText(): String
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Deliberately does NOT return the actual text.
|
* Deliberately does NOT return the actual text.
|
||||||
@ -62,7 +59,7 @@ sealed class SourceCode {
|
|||||||
*/
|
*/
|
||||||
const val libraryFilePrefix = "library:"
|
const val libraryFilePrefix = "library:"
|
||||||
const val stringSourcePrefix = "<String@"
|
const val stringSourcePrefix = "<String@"
|
||||||
val curdir: Path = Path.of(".").toAbsolutePath()
|
val curdir: Path = Path(".").toAbsolutePath()
|
||||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||||
fun isRegularFilesystemPath(pathString: String) =
|
fun isRegularFilesystemPath(pathString: String) =
|
||||||
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
|
!(pathString.startsWith(libraryFilePrefix) || pathString.startsWith(stringSourcePrefix))
|
||||||
@ -77,6 +74,7 @@ sealed class SourceCode {
|
|||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
|
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
|
||||||
override fun getCharStream(): CharStream = CharStreams.fromString(text, origin)
|
override fun getCharStream(): CharStream = CharStreams.fromString(text, origin)
|
||||||
|
override fun readText() = text
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -107,6 +105,7 @@ sealed class SourceCode {
|
|||||||
override val isFromFilesystem = true
|
override val isFromFilesystem = true
|
||||||
override val origin = relative(normalized).toString()
|
override val origin = relative(normalized).toString()
|
||||||
override fun getCharStream(): CharStream = CharStreams.fromPath(normalized)
|
override fun getCharStream(): CharStream = CharStreams.fromPath(normalized)
|
||||||
|
override fun readText() = normalized.readText()
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -129,12 +128,17 @@ sealed class SourceCode {
|
|||||||
override val isFromResources = true
|
override val isFromResources = true
|
||||||
override val isFromFilesystem = false
|
override val isFromFilesystem = false
|
||||||
override val origin = "$libraryFilePrefix$normalized"
|
override val origin = "$libraryFilePrefix$normalized"
|
||||||
override fun getCharStream(): CharStream {
|
public override fun getCharStream(): CharStream {
|
||||||
val inpStr = object {}.javaClass.getResourceAsStream(normalized)!!
|
val inpStr = object {}.javaClass.getResourceAsStream(normalized)!!
|
||||||
// CharStreams.fromStream() doesn't allow us to set the stream name properly, so we use a lower level api
|
// CharStreams.fromStream() doesn't allow us to set the stream name properly, so we use a lower level api
|
||||||
val channel = Channels.newChannel(inpStr)
|
val channel = Channels.newChannel(inpStr)
|
||||||
return CharStreams.fromChannel(channel, StandardCharsets.UTF_8, 4096, CodingErrorAction.REPLACE, origin, -1);
|
return CharStreams.fromChannel(channel, StandardCharsets.UTF_8, 4096, CodingErrorAction.REPLACE, origin, -1);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun readText(): String {
|
||||||
|
val stream = object {}.javaClass.getResourceAsStream(normalized)
|
||||||
|
return stream!!.bufferedReader().use { r -> r.readText() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -145,15 +149,6 @@ sealed class SourceCode {
|
|||||||
override val isFromResources: Boolean = false
|
override val isFromResources: Boolean = false
|
||||||
override val isFromFilesystem: Boolean = false
|
override val isFromFilesystem: Boolean = false
|
||||||
override val origin: String = name
|
override val origin: String = name
|
||||||
|
override fun readText() = throw IOException("generated code nodes don't have a text representation")
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: possibly more, like fromURL(..)
|
|
||||||
/* // For `jar:..` URLs
|
|
||||||
// see https://stackoverflow.com/questions/22605666/java-access-files-in-jar-causes-java-nio-file-filesystemnotfoundexception
|
|
||||||
var url = URL("jar:file:/E:/x16/prog8(meisl)/compiler/build/libs/prog8compiler-7.0-BETA3-all.jar!/prog8lib/c64/textio.p8")
|
|
||||||
val uri = url.toURI()
|
|
||||||
val parts = uri.toString().split("!")
|
|
||||||
val fs = FileSystems.newFileSystem(URI.create(parts[0]), mutableMapOf(Pair("", "")) )
|
|
||||||
val path = fs.getPath(parts[1])
|
|
||||||
*/
|
|
||||||
}
|
}
|
||||||
|
@ -1,28 +1,29 @@
|
|||||||
package prog8tests
|
package prog8tests.ast
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.AstToSourceCode
|
import prog8.ast.AstToSourceTextConverter
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.internedStringsModuleName
|
import prog8.ast.internedStringsModuleName
|
||||||
import prog8.parser.ParseError
|
import prog8.parser.ParseError
|
||||||
import prog8.parser.Prog8Parser.parseModule
|
import prog8.parser.Prog8Parser.parseModule
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.DummyFunctions
|
import prog8tests.ast.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.ast.helpers.DummyMemsizer
|
||||||
|
import prog8tests.ast.helpers.DummyStringEncoder
|
||||||
import kotlin.test.assertContains
|
import kotlin.test.assertContains
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestAstToSourceCode {
|
class TestAstToSourceText {
|
||||||
|
|
||||||
private fun generateP8(module: Module) : String {
|
private fun generateP8(module: Module) : String {
|
||||||
val program = Program("test", DummyFunctions, DummyMemsizer)
|
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
.addModule(module)
|
.addModule(module)
|
||||||
|
|
||||||
var generatedText = ""
|
var generatedText = ""
|
||||||
val it = AstToSourceCode({ str -> generatedText += str }, program)
|
val it = AstToSourceTextConverter({ str -> generatedText += str }, program)
|
||||||
it.visit(program)
|
it.visit(program)
|
||||||
|
|
||||||
return generatedText
|
return generatedText
|
@ -1,4 +1,4 @@
|
|||||||
package prog8tests
|
package prog8tests.ast
|
||||||
|
|
||||||
import org.junit.jupiter.api.Nested
|
import org.junit.jupiter.api.Nested
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@ -6,6 +6,8 @@ import org.junit.jupiter.api.TestInstance
|
|||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Node
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.CharLiteral
|
import prog8.ast.expressions.CharLiteral
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
@ -15,9 +17,9 @@ import prog8.ast.statements.*
|
|||||||
import prog8.parser.ParseError
|
import prog8.parser.ParseError
|
||||||
import prog8.parser.Prog8Parser.parseModule
|
import prog8.parser.Prog8Parser.parseModule
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.assumeNotExists
|
import prog8tests.ast.helpers.*
|
||||||
import prog8tests.helpers.assumeReadableFile
|
import prog8tests.ast.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.fixturesDir
|
import prog8tests.ast.helpers.DummyMemsizer
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.io.path.isRegularFile
|
import kotlin.io.path.isRegularFile
|
||||||
import kotlin.io.path.name
|
import kotlin.io.path.name
|
||||||
@ -285,24 +287,16 @@ class TestProg8Parser {
|
|||||||
fun `in ParseError from bad string source code`() {
|
fun `in ParseError from bad string source code`() {
|
||||||
val srcText = "bad * { }\n"
|
val srcText = "bad * { }\n"
|
||||||
|
|
||||||
assertFailsWith<ParseError> { parseModule(SourceCode.Text(srcText)) }
|
val e = assertFailsWith<ParseError> { parseModule(SourceCode.Text(srcText)) }
|
||||||
try {
|
assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 1, 4, 4)
|
||||||
parseModule(SourceCode.Text(srcText))
|
|
||||||
} catch (e: ParseError) {
|
|
||||||
assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 1, 4, 4)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `in ParseError from bad file source code`() {
|
fun `in ParseError from bad file source code`() {
|
||||||
val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
val path = assumeReadableFile(fixturesDir, "file_with_syntax_error.p8")
|
||||||
|
|
||||||
assertFailsWith<ParseError> { parseModule(SourceCode.File(path)) }
|
val e = assertFailsWith<ParseError> { parseModule(SourceCode.File(path)) }
|
||||||
try {
|
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 6)
|
||||||
parseModule(SourceCode.File(path))
|
|
||||||
} catch (e: ParseError) {
|
|
||||||
assertPosition(e.position, SourceCode.relative(path).toString(), 2, 6) // TODO: endCol wrong
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -310,16 +304,16 @@ class TestProg8Parser {
|
|||||||
val srcText = """
|
val srcText = """
|
||||||
main {
|
main {
|
||||||
}
|
}
|
||||||
""".trimIndent()
|
"""
|
||||||
val module = parseModule(SourceCode.Text(srcText))
|
val module = parseModule(SourceCode.Text(srcText))
|
||||||
assertPositionOf(module, Regex("^<String@[0-9a-f\\-]+>$"), 1, 0) // TODO: endCol wrong
|
assertPositionOf(module, Regex("^<String@[0-9a-f\\-]+>$"), 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun `of Module parsed from a file`() {
|
fun `of Module parsed from a file`() {
|
||||||
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
|
val path = assumeReadableFile(fixturesDir, "simple_main.p8")
|
||||||
val module = parseModule(SourceCode.File(path))
|
val module = parseModule(SourceCode.File(path))
|
||||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong
|
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -328,27 +322,24 @@ class TestProg8Parser {
|
|||||||
|
|
||||||
val module = parseModule(SourceCode.File(path))
|
val module = parseModule(SourceCode.File(path))
|
||||||
val mpf = module.position.file
|
val mpf = module.position.file
|
||||||
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0) // TODO: endCol wrong
|
assertPositionOf(module, SourceCode.relative(path).toString(), 1, 0)
|
||||||
val mainBlock = module.statements.filterIsInstance<Block>()[0]
|
val mainBlock = module.statements.filterIsInstance<Block>()[0]
|
||||||
assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong!
|
assertPositionOf(mainBlock, mpf, 2, 0, 3)
|
||||||
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
||||||
assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong!
|
assertPositionOf(startSub, mpf, 3, 4, 6)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* TODO: this test is testing way too much at once
|
|
||||||
*/
|
|
||||||
@Test
|
@Test
|
||||||
fun `of non-root Nodes parsed from a string`() {
|
fun `of non-root Nodes parsed from a string`() {
|
||||||
val srcText = """
|
val srcText = """
|
||||||
%zeropage basicsafe ; DirectiveArg directly inherits from Node - neither an Expression nor a Statement..?
|
%zeropage basicsafe
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte foo = 42
|
ubyte foo = 42
|
||||||
ubyte bar
|
ubyte bar
|
||||||
when (foo) {
|
when (foo) {
|
||||||
23 -> bar = 'x' ; WhenChoice, also directly inheriting Node
|
23 -> bar = 'x'
|
||||||
42 -> bar = 'y'
|
42 -> bar = 'y'
|
||||||
else -> bar = 'z'
|
else -> bar = 'z'
|
||||||
}
|
}
|
||||||
@ -359,22 +350,22 @@ class TestProg8Parser {
|
|||||||
val mpf = module.position.file
|
val mpf = module.position.file
|
||||||
|
|
||||||
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
|
val targetDirective = module.statements.filterIsInstance<Directive>()[0]
|
||||||
assertPositionOf(targetDirective, mpf, 1, 0) // TODO: endCol wrong!
|
assertPositionOf(targetDirective, mpf, 1, 0, 8)
|
||||||
val mainBlock = module.statements.filterIsInstance<Block>()[0]
|
val mainBlock = module.statements.filterIsInstance<Block>()[0]
|
||||||
assertPositionOf(mainBlock, mpf, 2, 0) // TODO: endCol wrong!
|
assertPositionOf(mainBlock, mpf, 2, 0, 3)
|
||||||
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
val startSub = mainBlock.statements.filterIsInstance<Subroutine>()[0]
|
||||||
assertPositionOf(startSub, mpf, 3, 4) // TODO: endCol wrong!
|
assertPositionOf(startSub, mpf, 3, 4, 6)
|
||||||
val declFoo = startSub.statements.filterIsInstance<VarDecl>()[0]
|
val declFoo = startSub.statements.filterIsInstance<VarDecl>()[0]
|
||||||
assertPositionOf(declFoo, mpf, 4, 8) // TODO: endCol wrong!
|
assertPositionOf(declFoo, mpf, 4, 8, 12)
|
||||||
val rhsFoo = declFoo.value!!
|
val rhsFoo = declFoo.value!!
|
||||||
assertPositionOf(rhsFoo, mpf, 4, 20) // TODO: endCol wrong!
|
assertPositionOf(rhsFoo, mpf, 4, 20, 21)
|
||||||
val declBar = startSub.statements.filterIsInstance<VarDecl>()[1]
|
val declBar = startSub.statements.filterIsInstance<VarDecl>()[1]
|
||||||
assertPositionOf(declBar, mpf, 5, 8) // TODO: endCol wrong!
|
assertPositionOf(declBar, mpf, 5, 8, 12)
|
||||||
val whenStmt = startSub.statements.filterIsInstance<WhenStatement>()[0]
|
val whenStmt = startSub.statements.filterIsInstance<WhenStatement>()[0]
|
||||||
assertPositionOf(whenStmt, mpf, 6, 8) // TODO: endCol wrong!
|
assertPositionOf(whenStmt, mpf, 6, 8, 11)
|
||||||
assertPositionOf(whenStmt.choices[0], mpf, 7, 12) // TODO: endCol wrong!
|
assertPositionOf(whenStmt.choices[0], mpf, 7, 12, 13)
|
||||||
assertPositionOf(whenStmt.choices[1], mpf, 8, 12) // TODO: endCol wrong!
|
assertPositionOf(whenStmt.choices[1], mpf, 8, 12, 13)
|
||||||
assertPositionOf(whenStmt.choices[2], mpf, 9, 12) // TODO: endCol wrong!
|
assertPositionOf(whenStmt.choices[2], mpf, 9, 12, 15)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -581,4 +572,95 @@ class TestProg8Parser {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testCharLiteralConstValue() {
|
||||||
|
val char1 = CharLiteral('A', false, Position.DUMMY)
|
||||||
|
val char2 = CharLiteral('z', true, Position.DUMMY)
|
||||||
|
|
||||||
|
val program = Program("test", DummyFunctions, DummyMemsizer, AsciiStringEncoder)
|
||||||
|
assertEquals(65, char1.constValue(program).number.toInt())
|
||||||
|
assertEquals(122, char2.constValue(program).number.toInt())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLiteralValueComparisons() {
|
||||||
|
val ten = NumericLiteralValue(DataType.UWORD, 10, Position.DUMMY)
|
||||||
|
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position.DUMMY)
|
||||||
|
assertEquals(ten, ten)
|
||||||
|
assertNotEquals(ten, nine)
|
||||||
|
assertFalse(ten != ten)
|
||||||
|
assertTrue(ten != nine)
|
||||||
|
|
||||||
|
assertTrue(ten > nine)
|
||||||
|
assertTrue(ten >= nine)
|
||||||
|
assertTrue(ten >= ten)
|
||||||
|
assertFalse(ten > ten)
|
||||||
|
|
||||||
|
assertFalse(ten < nine)
|
||||||
|
assertFalse(ten <= nine)
|
||||||
|
assertTrue(ten <= ten)
|
||||||
|
assertFalse(ten < ten)
|
||||||
|
|
||||||
|
val abc = StringLiteralValue("abc", false, Position.DUMMY)
|
||||||
|
val abd = StringLiteralValue("abd", false, Position.DUMMY)
|
||||||
|
assertEquals(abc, abc)
|
||||||
|
assertTrue(abc!=abd)
|
||||||
|
assertFalse(abc!=abc)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testAnonScopeStillContainsVarsDirectlyAfterParse() {
|
||||||
|
val src = SourceCode.Text("""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
repeat {
|
||||||
|
ubyte xx = 99
|
||||||
|
xx++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
val module = parseModule(src)
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
|
val repeatbody = (start.statements.single() as RepeatLoop).body
|
||||||
|
assertFalse(mainBlock.statements.any { it is VarDecl }, "no vars moved to main block")
|
||||||
|
assertFalse(start.statements.any { it is VarDecl }, "no vars moved to start sub")
|
||||||
|
assertTrue(repeatbody.statements[0] is VarDecl, "var is still in repeat block (anonymousscope)")
|
||||||
|
val initvalue = (repeatbody.statements[0] as VarDecl).value as? NumericLiteralValue
|
||||||
|
assertEquals(99, initvalue?.number?.toInt())
|
||||||
|
assertTrue(repeatbody.statements[1] is PostIncrDecr)
|
||||||
|
// the ast processing steps used in the compiler, will eventually move the var up to the containing scope (subroutine).
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun testLabelsWithAnonScopesParsesFine() {
|
||||||
|
val src = SourceCode.Text("""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
goto mylabeloutside
|
||||||
|
|
||||||
|
if true {
|
||||||
|
if true {
|
||||||
|
goto labeloutside
|
||||||
|
goto iflabel
|
||||||
|
}
|
||||||
|
iflabel:
|
||||||
|
}
|
||||||
|
|
||||||
|
repeat {
|
||||||
|
goto labelinside
|
||||||
|
labelinside:
|
||||||
|
}
|
||||||
|
|
||||||
|
labeloutside:
|
||||||
|
}
|
||||||
|
}
|
||||||
|
""")
|
||||||
|
val module = parseModule(src)
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val start = mainBlock.statements.single() as Subroutine
|
||||||
|
val labels = start.statements.filterIsInstance<Label>()
|
||||||
|
assertEquals(1, labels.size, "only one label in subroutine scope")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
package prog8tests
|
package prog8tests.ast
|
||||||
|
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.core.StringStartsWith
|
import org.hamcrest.core.StringStartsWith
|
||||||
@ -6,7 +6,10 @@ import org.junit.jupiter.api.Test
|
|||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8.parser.SourceCode.Companion.libraryFilePrefix
|
import prog8.parser.SourceCode.Companion.libraryFilePrefix
|
||||||
import prog8tests.helpers.*
|
import prog8tests.ast.helpers.assumeNotExists
|
||||||
|
import prog8tests.ast.helpers.assumeReadableFile
|
||||||
|
import prog8tests.ast.helpers.fixturesDir
|
||||||
|
import prog8tests.ast.helpers.resourcesDir
|
||||||
import kotlin.io.path.Path
|
import kotlin.io.path.Path
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -55,7 +58,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.File(path)
|
val src = SourceCode.File(path)
|
||||||
val expectedOrigin = SourceCode.relative(path).toString()
|
val expectedOrigin = SourceCode.relative(path).toString()
|
||||||
assertEquals(expectedOrigin, src.origin)
|
assertEquals(expectedOrigin, src.origin)
|
||||||
assertEquals(path.toFile().readText(), src.asString())
|
assertEquals(path.toFile().readText(), src.readText())
|
||||||
assertFalse(src.isFromResources)
|
assertFalse(src.isFromResources)
|
||||||
assertTrue(src.isFromFilesystem)
|
assertTrue(src.isFromFilesystem)
|
||||||
}
|
}
|
||||||
@ -68,7 +71,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.File(path)
|
val src = SourceCode.File(path)
|
||||||
val expectedOrigin = SourceCode.relative(path).toString()
|
val expectedOrigin = SourceCode.relative(path).toString()
|
||||||
assertEquals(expectedOrigin, src.origin)
|
assertEquals(expectedOrigin, src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -78,7 +81,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.Resource(pathString)
|
val src = SourceCode.Resource(pathString)
|
||||||
|
|
||||||
assertEquals("$libraryFilePrefix/$pathString", src.origin)
|
assertEquals("$libraryFilePrefix/$pathString", src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
assertTrue(src.isFromResources)
|
assertTrue(src.isFromResources)
|
||||||
assertFalse(src.isFromFilesystem)
|
assertFalse(src.isFromFilesystem)
|
||||||
}
|
}
|
||||||
@ -90,7 +93,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.Resource(pathString)
|
val src = SourceCode.Resource(pathString)
|
||||||
|
|
||||||
assertEquals("$libraryFilePrefix$pathString", src.origin)
|
assertEquals("$libraryFilePrefix$pathString", src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -100,7 +103,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.Resource(pathString)
|
val src = SourceCode.Resource(pathString)
|
||||||
|
|
||||||
assertEquals("$libraryFilePrefix/$pathString", src.origin)
|
assertEquals("$libraryFilePrefix/$pathString", src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
assertTrue(src.isFromResources, ".isFromResources")
|
assertTrue(src.isFromResources, ".isFromResources")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -111,7 +114,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.Resource(pathString)
|
val src = SourceCode.Resource(pathString)
|
||||||
|
|
||||||
assertEquals("$libraryFilePrefix$pathString", src.origin)
|
assertEquals("$libraryFilePrefix$pathString", src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -121,7 +124,7 @@ class TestSourceCode {
|
|||||||
val src = SourceCode.Resource(pathString)
|
val src = SourceCode.Resource(pathString)
|
||||||
|
|
||||||
assertEquals("$libraryFilePrefix/prog8lib/math.p8", src.origin)
|
assertEquals("$libraryFilePrefix/prog8lib/math.p8", src.origin)
|
||||||
assertEquals(srcFile.readText(), src.asString())
|
assertEquals(srcFile.readText(), src.readText())
|
||||||
assertTrue(src.isFromResources, ".isFromResources")
|
assertTrue(src.isFromResources, ".isFromResources")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
69
compilerAst/test/TestSubroutines.kt
Normal file
69
compilerAst/test/TestSubroutines.kt
Normal file
@ -0,0 +1,69 @@
|
|||||||
|
package prog8tests.ast
|
||||||
|
|
||||||
|
import org.junit.jupiter.api.Test
|
||||||
|
import org.junit.jupiter.api.TestInstance
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.statements.Block
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.parser.Prog8Parser.parseModule
|
||||||
|
import prog8.parser.SourceCode
|
||||||
|
import kotlin.test.assertEquals
|
||||||
|
import kotlin.test.assertFalse
|
||||||
|
import kotlin.test.assertTrue
|
||||||
|
|
||||||
|
|
||||||
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
|
class TestSubroutines {
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun stringParameterAcceptedInParser() {
|
||||||
|
// note: the *parser* should accept this as it is valid *syntax*,
|
||||||
|
// however, the compiler itself may or may not complain about it later.
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
asmsub asmfunc(str thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(str thing) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val src = SourceCode.Text(text)
|
||||||
|
val module = parseModule(src)
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
|
assertTrue(asmfunc.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.STR, asmfunc.parameters.single().type)
|
||||||
|
assertTrue(asmfunc.statements.isEmpty())
|
||||||
|
assertFalse(func.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.STR, func.parameters.single().type)
|
||||||
|
assertTrue(func.statements.isEmpty())
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
fun arrayParameterAcceptedInParser() {
|
||||||
|
// note: the *parser* should accept this as it is valid *syntax*,
|
||||||
|
// however, the compiler itself may or may not complain about it later.
|
||||||
|
val text = """
|
||||||
|
main {
|
||||||
|
asmsub asmfunc(ubyte[] thing @AY) {
|
||||||
|
}
|
||||||
|
|
||||||
|
sub func(ubyte[22] thing) {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"""
|
||||||
|
val src = SourceCode.Text(text)
|
||||||
|
val module = parseModule(src)
|
||||||
|
val mainBlock = module.statements.single() as Block
|
||||||
|
val asmfunc = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="asmfunc"}
|
||||||
|
val func = mainBlock.statements.filterIsInstance<Subroutine>().single { it.name=="func"}
|
||||||
|
assertTrue(asmfunc.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.ARRAY_UB, asmfunc.parameters.single().type)
|
||||||
|
assertTrue(asmfunc.statements.isEmpty())
|
||||||
|
assertFalse(func.isAsmSubroutine)
|
||||||
|
assertEquals(DataType.ARRAY_UB, func.parameters.single().type)
|
||||||
|
assertTrue(func.statements.isEmpty())
|
||||||
|
}
|
||||||
|
}
|
@ -1,5 +1,4 @@
|
|||||||
package prog8tests.ast
|
package prog8tests.ast.ast
|
||||||
|
|
||||||
|
|
||||||
import org.hamcrest.MatcherAssert.assertThat
|
import org.hamcrest.MatcherAssert.assertThat
|
||||||
import org.hamcrest.Matchers.containsString
|
import org.hamcrest.Matchers.containsString
|
||||||
@ -12,8 +11,9 @@ import prog8.ast.Program
|
|||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.internedStringsModuleName
|
import prog8.ast.internedStringsModuleName
|
||||||
import prog8.parser.SourceCode
|
import prog8.parser.SourceCode
|
||||||
import prog8tests.helpers.DummyFunctions
|
import prog8tests.ast.helpers.DummyFunctions
|
||||||
import prog8tests.helpers.DummyMemsizer
|
import prog8tests.ast.helpers.DummyMemsizer
|
||||||
|
import prog8tests.ast.helpers.DummyStringEncoder
|
||||||
import kotlin.test.assertContains
|
import kotlin.test.assertContains
|
||||||
import kotlin.test.assertFailsWith
|
import kotlin.test.assertFailsWith
|
||||||
import kotlin.test.assertSame
|
import kotlin.test.assertSame
|
||||||
@ -26,7 +26,7 @@ class ProgramTests {
|
|||||||
inner class Constructor {
|
inner class Constructor {
|
||||||
@Test
|
@Test
|
||||||
fun withNameBuiltinsAndMemsizer() {
|
fun withNameBuiltinsAndMemsizer() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
assertThat(program.modules.size, equalTo(1))
|
assertThat(program.modules.size, equalTo(1))
|
||||||
assertThat(program.modules[0].name, equalTo(internedStringsModuleName))
|
assertThat(program.modules[0].name, equalTo(internedStringsModuleName))
|
||||||
assertSame(program, program.modules[0].program)
|
assertSame(program, program.modules[0].program)
|
||||||
@ -39,7 +39,7 @@ class ProgramTests {
|
|||||||
inner class AddModule {
|
inner class AddModule {
|
||||||
@Test
|
@Test
|
||||||
fun withEmptyModule() {
|
fun withEmptyModule() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
||||||
|
|
||||||
val retVal = program.addModule(m1)
|
val retVal = program.addModule(m1)
|
||||||
@ -63,7 +63,7 @@ class ProgramTests {
|
|||||||
inner class MoveModuleToFront {
|
inner class MoveModuleToFront {
|
||||||
@Test
|
@Test
|
||||||
fun withInternedStringsModule() {
|
fun withInternedStringsModule() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val m = program.modules[0]
|
val m = program.modules[0]
|
||||||
assertThat(m.name, equalTo(internedStringsModuleName))
|
assertThat(m.name, equalTo(internedStringsModuleName))
|
||||||
|
|
||||||
@ -73,14 +73,14 @@ class ProgramTests {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun withForeignModule() {
|
fun withForeignModule() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val m = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
val m = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
||||||
|
|
||||||
assertFailsWith<IllegalArgumentException> { program.moveModuleToFront(m) }
|
assertFailsWith<IllegalArgumentException> { program.moveModuleToFront(m) }
|
||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun withFirstOfPreviouslyAddedModules() {
|
fun withFirstOfPreviouslyAddedModules() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
||||||
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
|
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
|
||||||
program.addModule(m1)
|
program.addModule(m1)
|
||||||
@ -92,7 +92,7 @@ class ProgramTests {
|
|||||||
}
|
}
|
||||||
@Test
|
@Test
|
||||||
fun withSecondOfPreviouslyAddedModules() {
|
fun withSecondOfPreviouslyAddedModules() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
val m1 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("bar"))
|
||||||
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
|
val m2 = Module(mutableListOf(), Position.DUMMY, SourceCode.Generated("qmbl"))
|
||||||
program.addModule(m1)
|
program.addModule(m1)
|
||||||
@ -108,7 +108,7 @@ class ProgramTests {
|
|||||||
inner class Properties {
|
inner class Properties {
|
||||||
@Test
|
@Test
|
||||||
fun modules() {
|
fun modules() {
|
||||||
val program = Program("foo", DummyFunctions, DummyMemsizer)
|
val program = Program("foo", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||||
|
|
||||||
val ms1 = program.modules
|
val ms1 = program.modules
|
||||||
val ms2 = program.modules
|
val ms2 = program.modules
|
||||||
|
44
compilerAst/test/helpers/Dummies.kt
Normal file
44
compilerAst/test/helpers/Dummies.kt
Normal file
@ -0,0 +1,44 @@
|
|||||||
|
package prog8tests.ast.helpers
|
||||||
|
|
||||||
|
import prog8.ast.IBuiltinFunctions
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.Position
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.InferredTypes
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.compilerinterface.IMemSizer
|
||||||
|
import prog8.compilerinterface.IStringEncoding
|
||||||
|
|
||||||
|
internal val DummyFunctions = object : IBuiltinFunctions {
|
||||||
|
override val names: Set<String> = emptySet()
|
||||||
|
override val purefunctionNames: Set<String> = emptySet()
|
||||||
|
override fun constValue(
|
||||||
|
name: String,
|
||||||
|
args: List<Expression>,
|
||||||
|
position: Position,
|
||||||
|
): NumericLiteralValue? = null
|
||||||
|
|
||||||
|
override fun returnType(name: String, args: MutableList<Expression>) = InferredTypes.InferredType.unknown()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val DummyMemsizer = object : IMemSizer {
|
||||||
|
override fun memorySize(dt: DataType): Int = 0
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val DummyStringEncoder = object : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> {
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||||
|
return ""
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
internal val AsciiStringEncoder = object : IStringEncoding {
|
||||||
|
override fun encodeString(str: String, altEncoding: Boolean): List<Short> = str.map { it.code.toShort() }
|
||||||
|
|
||||||
|
override fun decodeString(bytes: List<Short>, altEncoding: Boolean): String {
|
||||||
|
return bytes.joinToString()
|
||||||
|
}
|
||||||
|
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user