mirror of
https://github.com/irmen/prog8.git
synced 2026-10-23 12:02:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
53a3c59a91 | ||
|
|
df36983049 | ||
|
|
bda016bb3b | ||
|
|
cc174b7b85 | ||
|
|
bf9d120081 | ||
|
|
775c85fc18 | ||
|
|
5a756aaed9 | ||
|
|
dca092fd7c | ||
|
|
c6e92ecac4 | ||
|
|
93008ff605 | ||
|
|
43c7b935df | ||
|
|
8f9a0a244a | ||
|
|
fd13bd864e | ||
|
|
710f27afa9 | ||
|
|
f537793b0b | ||
|
|
f7183e38ee | ||
|
|
0a65dfdd10 | ||
|
|
3075578245 | ||
|
|
b042b7705e | ||
|
|
d56eb397f9 | ||
|
|
3054a1d32d | ||
|
|
0a3cd652b0 | ||
|
|
f70b914779 | ||
|
|
46ca0ac10d | ||
|
|
031f647952 | ||
|
|
8f1c86f550 | ||
|
|
926fdecd13 | ||
|
|
af2ca7a67e | ||
|
|
9e3e2ff81a | ||
|
|
a9fe6472d9 | ||
|
|
a862a81480 | ||
|
|
dbb92881a1 | ||
|
|
10bf7f5d07 | ||
|
|
1e61d84fd1 | ||
|
|
8618ba1b60 | ||
|
|
3c8c44155d | ||
|
|
2002412026 | ||
|
|
7f69517fd4 | ||
|
|
851f8645b5 | ||
|
|
c40cfaa388 | ||
|
|
0349d1d57c | ||
|
|
53049c02ee | ||
|
|
73a3a61729 | ||
|
|
5fe6aa2800 | ||
|
|
c7eafd7c79 | ||
|
|
10b5fb5d72 | ||
|
|
c4eaa944e2 | ||
|
|
a735939d1e |
No files matched your search
+7
-7
@@ -1,21 +1,21 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.assertions.core.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.1.0" />
|
||||
<properties maven-id="io.kotest:kotest-assertions-core-jvm:5.2.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.2.3/kotest-assertions-core-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.2.3/kotest-common-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
+11
-11
@@ -1,22 +1,22 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.property.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.1.0" />
|
||||
<properties maven-id="io.kotest:kotest-property-jvm:5.2.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.1.0/kotest-property-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-property-jvm/5.2.3/kotest-property-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.2.3/kotest-common-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/curious-odd-man/rgxgen/1.3/rgxgen-1.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-reflect/1.6.10/kotlin-reflect-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
+16
-16
@@ -1,26 +1,26 @@
|
||||
<component name="libraryTable">
|
||||
<library name="io.kotest.runner.junit5.jvm" type="repository">
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.1.0" />
|
||||
<properties maven-id="io.kotest:kotest-runner-junit5-jvm:5.2.3" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.1.0/kotest-runner-junit5-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.1.0/kotest-framework-api-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.0/kotlinx-coroutines-test-jvm-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.1.0/kotest-assertions-shared-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-runner-junit5-jvm/5.2.3/kotest-runner-junit5-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-api-jvm/5.2.3/kotest-framework-api-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-shared-jvm/5.2.3/kotest-assertions-shared-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/java-diff-utils/java-diff-utils/4.11/java-diff-utils-4.11.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.1.0/kotest-common-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.1.0/kotest-framework-engine-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-test-jvm/1.6.1/kotlinx-coroutines-test-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-common-jvm/5.2.3/kotest-common-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-engine-jvm/5.2.3/kotest-framework-engine-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/github/classgraph/classgraph/4.8.138/classgraph-4.8.138.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/mordant/1.2.1/mordant-1.2.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/github/ajalt/colormath/1.2.0/colormath-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.0/kotlinx-coroutines-debug-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-debug/1.6.1/kotlinx-coroutines-debug-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna/5.9.0/jna-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/java/dev/jna/jna-platform/5.9.0/jna-platform-5.9.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/opentest4j/opentest4j/1.2.0/opentest4j-1.2.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.1.0/kotest-framework-discovery-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.1.0/kotest-assertions-core-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.0/kotlinx-coroutines-jdk8-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.1.0/kotest-assertions-api-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.1.0/kotest-extensions-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-discovery-jvm/5.2.3/kotest-framework-discovery-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-core-jvm/5.2.3/kotest-assertions-core-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-jdk8/1.6.1/kotlinx-coroutines-jdk8-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-assertions-api-jvm/5.2.3/kotest-assertions-api-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-extensions-jvm/5.2.3/kotest-extensions-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/commons-io/commons-io/2.11.0/commons-io-2.11.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk/1.12.2/mockk-1.12.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/mockk/mockk-dsl-jvm/1.12.2/mockk-dsl-jvm-1.12.2.jar!/" />
|
||||
@@ -32,8 +32,8 @@
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/objenesis/objenesis/3.1/objenesis-3.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy/1.12.5/byte-buddy-1.12.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/net/bytebuddy/byte-buddy-agent/1.12.5/byte-buddy-agent-1.12.5.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.1.0/kotest-framework-concurrency-jvm-5.1.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.0/kotlinx-coroutines-core-jvm-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/io/kotest/kotest-framework-concurrency-jvm/5.2.3/kotest-framework-concurrency-jvm-5.2.3.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlinx/kotlinx-coroutines-core-jvm/1.6.1/kotlinx-coroutines-core-jvm-1.6.1.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.0/kotlin-stdlib-common-1.6.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/junit/platform/junit-platform-engine/1.7.2/junit-platform-engine-1.7.2.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apiguardian/apiguardian-api/1.1.0/apiguardian-api-1.1.0.jar!/" />
|
||||
|
||||
+6
-6
@@ -1,13 +1,13 @@
|
||||
<component name="libraryTable">
|
||||
<library name="michael.bull.kotlin.result.jvm" type="repository">
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14" />
|
||||
<properties maven-id="com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.14/kotlin-result-jvm-1.1.14.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.10/kotlin-stdlib-jdk8-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.10/kotlin-stdlib-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/michael-bull/kotlin-result/kotlin-result-jvm/1.1.16/kotlin-result-jvm-1.1.16.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.20/kotlin-stdlib-common-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/1.6.20/kotlin-stdlib-jdk8-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/1.6.20/kotlin-stdlib-1.6.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.10/kotlin-stdlib-jdk7-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-common/1.6.10/kotlin-stdlib-common-1.6.10.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/1.6.20/kotlin-stdlib-jdk7-1.6.20.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
Generated
+7
-3
@@ -1,11 +1,15 @@
|
||||
<component name="libraryTable">
|
||||
<library name="takes" type="repository">
|
||||
<properties maven-id="org.takes:takes:1.19" />
|
||||
<properties maven-id="org.takes:takes:1.20" />
|
||||
<CLASSES>
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.19/takes-1.19.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.42/cactoos-0.42.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/takes/takes/1.20/takes-1.20.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/cactoos/cactoos/0.50/cactoos-0.50.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/javax/xml/bind/jaxb-api/2.3.0/jaxb-api-2.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/slf4j/slf4j-api/1.7.25/slf4j-api-1.7.25.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-text/1.4/commons-text-1.4.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/org/apache/commons/commons-lang3/3.7/commons-lang3-3.7.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-core/2.3.0/jaxb-core-2.3.0.jar!/" />
|
||||
<root url="jar://$MAVEN_REPOSITORY$/com/sun/xml/bind/jaxb-impl/2.3.0/jaxb-impl-2.3.0.jar!/" />
|
||||
</CLASSES>
|
||||
<JAVADOC />
|
||||
<SOURCES />
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
|
||||
This sofware license is for Prog8 the compiler + associated libraries.
|
||||
This sofware license is for Prog8 the compiler + associated library files.
|
||||
|
||||
Any and all outputs generated by the compiler (intermediary
|
||||
source code and compiled binary programs) are excluded from that
|
||||
and you can do with them whatever you want.
|
||||
Exception: All output files generated by the compiler (intermediary files
|
||||
and compiled binary programs) are excluded from this; you can do with those
|
||||
whatever you want.
|
||||
|
||||
|
||||
|
||||
|
||||
@@ -16,10 +16,11 @@ https://prog8.readthedocs.io/
|
||||
|
||||
Software license
|
||||
----------------
|
||||
GNU GPL 3.0, see file LICENSE
|
||||
GNU GPL 3.0 (see file LICENSE), with exception for generated code:
|
||||
|
||||
- prog8 (the compiler + libraries) is licensed under GNU GPL 3.0
|
||||
- *exception:* the resulting files created by running the compiler are free to use in whatever way desired.
|
||||
- The compiler and its libraries are free to use according to the terms of the GNU GPL 3.0
|
||||
- *exception:* the resulting files (intermediate source codes and resulting binary program) created by the compiler
|
||||
are excluded from the GPL and are free to use in whatever way desired, commercially or not.
|
||||
|
||||
|
||||
What does Prog8 provide?
|
||||
|
||||
@@ -149,19 +149,6 @@ class PtNumber(type: DataType, val number: Double, position: Position) : PtExpre
|
||||
}
|
||||
|
||||
|
||||
class PtPipe(type: DataType, val void: Boolean, position: Position) : PtExpression(type, position) {
|
||||
init {
|
||||
if(!void)
|
||||
require(type!=DataType.UNDEFINED)
|
||||
}
|
||||
|
||||
val segments: List<PtExpression>
|
||||
get() = children.map { it as PtExpression }
|
||||
|
||||
override fun printProperties() {}
|
||||
}
|
||||
|
||||
|
||||
class PtPrefix(val operator: String, type: DataType, position: Position): PtExpression(type, position) {
|
||||
val value: PtExpression
|
||||
get() = children.single() as PtExpression
|
||||
|
||||
@@ -26,7 +26,7 @@ compileTestKotlin {
|
||||
dependencies {
|
||||
// should have no dependencies to other modules
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
}
|
||||
|
||||
sourceSets {
|
||||
|
||||
@@ -53,13 +53,7 @@ enum class DataType {
|
||||
enum class CpuRegister {
|
||||
A,
|
||||
X,
|
||||
Y;
|
||||
|
||||
fun asRegisterOrPair(): RegisterOrPair = when(this) {
|
||||
A -> RegisterOrPair.A
|
||||
X -> RegisterOrPair.X
|
||||
Y -> RegisterOrPair.Y
|
||||
}
|
||||
Y
|
||||
}
|
||||
|
||||
enum class RegisterOrPair {
|
||||
@@ -120,7 +114,6 @@ val WordDatatypes = arrayOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = arrayOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val SignedDatatypes = arrayOf(DataType.BYTE, DataType.WORD, DataType.FLOAT)
|
||||
val IntegerArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W)
|
||||
val ArrayDatatypes = arrayOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||
val StringlyDatatypes = arrayOf(DataType.STR, DataType.ARRAY_UB, DataType.ARRAY_B, DataType.UWORD)
|
||||
val IterableDatatypes = arrayOf(
|
||||
|
||||
@@ -1,16 +1,22 @@
|
||||
package prog8.code.core
|
||||
|
||||
import java.nio.file.InvalidPathException
|
||||
import kotlin.io.path.Path
|
||||
import kotlin.io.path.absolute
|
||||
|
||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
||||
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||
fun toClickableStr(): String {
|
||||
val path = Path(file).absolute().normalize()
|
||||
return "file://$path:$line:$startCol:"
|
||||
return try {
|
||||
val path = Path(file).absolute().normalize().toString()
|
||||
"file://$path:$line:$startCol:"
|
||||
} catch(x: InvalidPathException) {
|
||||
// this can occur on Windows when the source origin contains "invalid" characters such as ':'
|
||||
"file://$file:$line:$startCol:"
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
val DUMMY = Position("<dummy>", 0, 0, 0)
|
||||
val DUMMY = Position("~dummy~", 0, 0, 0)
|
||||
}
|
||||
}
|
||||
@@ -34,7 +34,7 @@ sealed class SourceCode {
|
||||
* Where this [SourceCode] instance came from.
|
||||
* This can be one of the following:
|
||||
* * a normal string representation of a [java.nio.file.Path], if it originates from a file (see [File])
|
||||
* * `$stringSourcePrefix44c56085>` if was created via [String]
|
||||
* * `string:44c56085` if was created via [String]
|
||||
* * `library:/x/y/z.ext` if it is a library file that was loaded from resources (see [Resource])
|
||||
*/
|
||||
abstract val origin: String
|
||||
@@ -55,7 +55,7 @@ sealed class SourceCode {
|
||||
* filename prefix to designate library files that will be retreived from internal resources rather than disk
|
||||
*/
|
||||
const val libraryFilePrefix = "library:"
|
||||
const val stringSourcePrefix = "<String@"
|
||||
const val stringSourcePrefix = "string:"
|
||||
val curdir: Path = Path(".").toAbsolutePath()
|
||||
fun relative(path: Path): Path = curdir.relativize(path.toAbsolutePath())
|
||||
fun isRegularFilesystemPath(pathString: String) =
|
||||
@@ -64,12 +64,12 @@ sealed class SourceCode {
|
||||
|
||||
/**
|
||||
* Turn a plain String into a [SourceCode] object.
|
||||
* [origin] will be something like `$stringSourcePrefix44c56085>`.
|
||||
* [origin] will be something like `string:44c56085`.
|
||||
*/
|
||||
class Text(override val text: String): SourceCode() {
|
||||
override val isFromResources = false
|
||||
override val isFromFilesystem = false
|
||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}>"
|
||||
override val origin = "$stringSourcePrefix${System.identityHashCode(text).toString(16)}"
|
||||
override val name = "<unnamed-text>"
|
||||
}
|
||||
|
||||
|
||||
@@ -29,7 +29,7 @@ dependencies {
|
||||
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.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -85,7 +85,7 @@ class AsmGen(internal val program: Program,
|
||||
}
|
||||
|
||||
internal fun out(str: String, splitlines: Boolean = true) {
|
||||
val fragment = (if(" | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||
val fragment = (if(splitlines && " | " in str) str.replace("|", "\n") else str).trim('\n')
|
||||
if (splitlines) {
|
||||
for (line in fragment.splitToSequence('\n')) {
|
||||
val trimmed = if (line.startsWith(' ')) "\t" + line.trim() else line
|
||||
@@ -334,7 +334,6 @@ class AsmGen(internal val program: Program,
|
||||
is RepeatLoop -> translate(stmt)
|
||||
is When -> translate(stmt)
|
||||
is AnonymousScope -> translate(stmt)
|
||||
is Pipe -> translatePipeExpression(stmt.source, stmt.segments, stmt, isStatement = true, pushResultOnEstack = false)
|
||||
is VarDecl -> { /* do nothing; variables are handled elsewhere */ }
|
||||
is BuiltinFunctionPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore")
|
||||
is UntilLoop -> throw AssemblyError("do..until should have been converted to jumps")
|
||||
@@ -444,12 +443,6 @@ class AsmGen(internal val program: Program,
|
||||
internal fun translateBuiltinFunctionCallExpression(bfc: BuiltinFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?) =
|
||||
builtinFunctionsAsmGen.translateFunctioncallExpression(bfc, resultToStack, resultRegister)
|
||||
|
||||
private fun translateBuiltinFunctionCallExpression(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine): DataType =
|
||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, false, scope)
|
||||
|
||||
private fun translateBuiltinFunctionCallStatement(bfc: IFunctionCall, firstArg: AsmAssignSource, scope: Subroutine) =
|
||||
builtinFunctionsAsmGen.translateFunctionCallWithFirstArg(bfc, firstArg, true, scope)
|
||||
|
||||
internal fun translateFunctionCall(functionCallExpr: FunctionCallExpression, isExpression: Boolean) =
|
||||
functioncallAsmGen.translateFunctionCall(functionCallExpr, isExpression)
|
||||
|
||||
@@ -2812,123 +2805,6 @@ $repeatLabel lda $counterVar
|
||||
}
|
||||
}
|
||||
|
||||
internal fun translatePipeExpression(source: Expression, segments: List<Expression>, scope: Node, isStatement: Boolean, pushResultOnEstack: Boolean) {
|
||||
|
||||
// TODO more efficient code generation to avoid needless assignments to the temp var
|
||||
|
||||
// the source: an expression (could be anything) producing a value.
|
||||
// one or more segment expressions, all are a IFunctionCall node, and LACKING the implicit first argument.
|
||||
// when 'isStatement'=true, the last segment expression should be treated as a funcion call statement (discarding any result value if there is one)
|
||||
|
||||
val subroutine = scope.definingSubroutine!!
|
||||
var valueDt = source.inferType(program).getOrElse { throw FatalAstException("invalid dt") }
|
||||
var valueSource: AsmAssignSource =
|
||||
if(source is IFunctionCall) {
|
||||
val resultReg = returnRegisterOfFunction(source.target)
|
||||
assignExpressionToRegister(source, resultReg, valueDt in listOf(DataType.BYTE, DataType.WORD, DataType.FLOAT))
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
||||
} else {
|
||||
AsmAssignSource.fromAstSource(source, program, this)
|
||||
}
|
||||
|
||||
// the segments (except the last one): function calls taking one or more parameters and producing a value.
|
||||
// directly assign their first argument from the previous call's returnvalue (and take the rest, if any, from the call itself)
|
||||
segments.dropLast(1).forEach {
|
||||
it as IFunctionCall
|
||||
valueDt = translateFunctionCallWithFirstArg(it, valueSource, false, subroutine)
|
||||
val resultReg = returnRegisterOfFunction(it.target)
|
||||
valueSource = AsmAssignSource(SourceStorageKind.REGISTER, program, this, valueDt, register = resultReg)
|
||||
}
|
||||
// the last segment: function call taking one or more parameters and optionally producing a result value.
|
||||
val lastCall = segments.last() as IFunctionCall
|
||||
if(isStatement) {
|
||||
translateFunctionCallWithFirstArg(lastCall, valueSource, true, subroutine)
|
||||
} else {
|
||||
valueDt = translateFunctionCallWithFirstArg(lastCall, valueSource, false, subroutine)
|
||||
if(pushResultOnEstack) {
|
||||
when (valueDt) {
|
||||
in ByteDatatypes -> {
|
||||
out(" sta P8ESTACK_LO,x | dex")
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
out(" jsr floats.push_fac1")
|
||||
}
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFunctionCallWithFirstArg(
|
||||
fcall: IFunctionCall,
|
||||
firstArg: AsmAssignSource,
|
||||
isStatement: Boolean,
|
||||
scope: Subroutine
|
||||
): DataType {
|
||||
|
||||
if(fcall.args.isNotEmpty())
|
||||
TODO("deal with additional args (non-unary function): ${fcall.target.nameInSource} (... , ${fcall.args.joinToString(", ")})")
|
||||
|
||||
when(val targetStmt = fcall.target.targetStatement(program)!!) {
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
return if(isStatement) {
|
||||
translateBuiltinFunctionCallStatement(fcall, firstArg, scope)
|
||||
DataType.UNDEFINED
|
||||
} else {
|
||||
translateBuiltinFunctionCallExpression(fcall, firstArg, scope)
|
||||
}
|
||||
}
|
||||
is Subroutine -> {
|
||||
val argDt = targetStmt.parameters.single().type
|
||||
if(targetStmt.isAsmSubroutine) {
|
||||
// argument via registers
|
||||
val argRegister = targetStmt.asmParameterRegisters.single().registerOrPair!!
|
||||
val assignArgument = AsmAssignment(
|
||||
firstArg,
|
||||
AsmAssignTarget.fromRegisters(argRegister, argDt in SignedDatatypes, scope, program, this),
|
||||
false, program.memsizer, fcall.position
|
||||
)
|
||||
translateNormalAssignment(assignArgument)
|
||||
} else {
|
||||
val assignArgument: AsmAssignment =
|
||||
if(functioncallAsmGen.optimizeIntArgsViaRegisters(targetStmt)) {
|
||||
// argument goes via registers as optimization
|
||||
val paramReg: RegisterOrPair = when(argDt) {
|
||||
in ByteDatatypes -> RegisterOrPair.A
|
||||
in WordDatatypes -> RegisterOrPair.AY
|
||||
DataType.FLOAT -> RegisterOrPair.FAC1
|
||||
else -> throw AssemblyError("invalid dt")
|
||||
}
|
||||
AsmAssignment(
|
||||
firstArg,
|
||||
AsmAssignTarget(TargetStorageKind.REGISTER, program, this, argDt, scope, register = paramReg),
|
||||
false, program.memsizer, fcall.position
|
||||
)
|
||||
} else {
|
||||
// arg goes via parameter variable
|
||||
val argVarName = asmVariableName(targetStmt.scopedName + targetStmt.parameters.single().name)
|
||||
AsmAssignment(
|
||||
firstArg,
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, this, argDt, scope, argVarName),
|
||||
false, program.memsizer, fcall.position
|
||||
)
|
||||
}
|
||||
translateNormalAssignment(assignArgument)
|
||||
}
|
||||
if(targetStmt.shouldSaveX())
|
||||
saveRegisterLocal(CpuRegister.X, scope)
|
||||
out(" jsr ${asmSymbolName(fcall.target)}")
|
||||
if(targetStmt.shouldSaveX())
|
||||
restoreRegisterLocal(CpuRegister.X)
|
||||
return if(isStatement) DataType.UNDEFINED else targetStmt.returntypes.single()
|
||||
}
|
||||
else -> throw AssemblyError("invalid call target")
|
||||
}
|
||||
}
|
||||
|
||||
internal fun popCpuStack(dt: DataType, target: VarDecl, scope: Subroutine?) {
|
||||
// note: because A is pushed first so popped last, saving A is often not required here.
|
||||
val parameter = target.subroutineParameter
|
||||
@@ -3034,6 +2910,9 @@ $repeatLabel lda $counterVar
|
||||
}
|
||||
}
|
||||
|
||||
internal fun needAsaveForExpr(arg: Expression): Boolean =
|
||||
arg !is NumericLiteral && arg !is IdentifierReference && (arg !is DirectMemoryRead || !arg.isSimple)
|
||||
|
||||
private val subroutineExtrasCache = mutableMapOf<Subroutine, SubroutineExtraAsmInfo>()
|
||||
|
||||
internal fun subroutineExtra(sub: Subroutine): SubroutineExtraAsmInfo {
|
||||
|
||||
@@ -6,13 +6,11 @@ import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.BuiltinFunctionCallStatement
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.*
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.compiler.FSignature
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
|
||||
|
||||
internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
@@ -30,44 +28,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
translateFunctioncall(fcall, func, discardResult = true, resultToStack = false, resultRegister = null)
|
||||
}
|
||||
|
||||
internal fun translateFunctionCallWithFirstArg(bfc: IFunctionCall, singleArg: AsmAssignSource, isStatement: Boolean, scope: Subroutine): DataType {
|
||||
val name = bfc.target.nameInSource.single()
|
||||
val func = BuiltinFunctions.getValue(name)
|
||||
val argExpression =
|
||||
when(singleArg.kind) {
|
||||
SourceStorageKind.LITERALNUMBER -> singleArg.number!!
|
||||
SourceStorageKind.EXPRESSION -> singleArg.expression!!
|
||||
SourceStorageKind.ARRAY -> singleArg.array!!
|
||||
else -> {
|
||||
// TODO make it so that we can assign efficiently from something else as an expression....namely: register(s)
|
||||
// this is useful in pipe expressions for instance, to skip the use of a temporary variable
|
||||
// but for now, just assign it to a temporary variable and use that as a source
|
||||
// Idea: to do this without having to rewrite every single function in translateFunctioncall(),
|
||||
// hack a special IdentifierReference like "!6502.A/X/Y/AX/AY/XY" to reference a cpu register
|
||||
val tempvar = asmgen.getTempVarName(singleArg.datatype)
|
||||
val assignTempvar = AsmAssignment(
|
||||
singleArg,
|
||||
AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, singleArg.datatype, scope, variableAsmName = asmgen.asmVariableName(tempvar)),
|
||||
false, program.memsizer, Position.DUMMY
|
||||
)
|
||||
assignAsmGen.translateNormalAssignment(assignTempvar)
|
||||
// now use an expression to assign this tempvar
|
||||
val ident = IdentifierReference(tempvar, Position.DUMMY)
|
||||
ident.linkParents(scope)
|
||||
ident
|
||||
}
|
||||
}
|
||||
val argExpressions = mutableListOf(argExpression)
|
||||
val fcall = BuiltinFunctionCall(IdentifierReference(listOf(name), Position.DUMMY), argExpressions, Position.DUMMY)
|
||||
fcall.linkParents(scope)
|
||||
translateFunctioncall(fcall, func, discardResult = false, resultToStack = false, null)
|
||||
return if(isStatement) {
|
||||
DataType.UNDEFINED
|
||||
} else {
|
||||
builtinFunctionReturnType(func.name).getOrElse { throw AssemblyError("unknown dt") }
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean, resultToStack: Boolean, resultRegister: RegisterOrPair?) {
|
||||
if (discardResult && func.pure)
|
||||
return // can just ignore the whole function call altogether
|
||||
@@ -82,9 +42,9 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
"lsb" -> funcLsb(fcall, resultToStack, resultRegister)
|
||||
"mkword" -> funcMkword(fcall, resultToStack, resultRegister)
|
||||
"abs" -> funcAbs(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"swap" -> funcSwap(fcall)
|
||||
"any", "all" -> funcAnyAll(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"sgn" -> funcSgn(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"boolean" -> funcBoolean(fcall, resultToStack, resultRegister, sscope)
|
||||
"rnd", "rndw" -> funcRnd(func, resultToStack, resultRegister, sscope)
|
||||
"sqrt16" -> funcSqrt16(fcall, func, resultToStack, resultRegister, sscope)
|
||||
"rol" -> funcRol(fcall)
|
||||
@@ -512,6 +472,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
@@ -520,6 +481,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ ror ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -613,6 +575,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
} else {
|
||||
val ptrAndIndex = asmgen.pointerViaIndexRegisterPossible(what.addressExpression)
|
||||
if(ptrAndIndex!=null) {
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.second, RegisterOrPair.X)
|
||||
asmgen.saveRegisterLocal(CpuRegister.X, (fcall as Node).definingSubroutine!!)
|
||||
asmgen.assignExpressionToRegister(ptrAndIndex.first, RegisterOrPair.AY)
|
||||
@@ -621,6 +584,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
sta (+) + 1
|
||||
sty (+) + 2
|
||||
+ rol ${'$'}ffff,x ; modified""")
|
||||
asmgen.restoreRegisterLocal(CpuRegister.X)
|
||||
} else {
|
||||
asmgen.assignExpressionToRegister(what.addressExpression, RegisterOrPair.AY)
|
||||
asmgen.out("""
|
||||
@@ -655,7 +619,13 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
}
|
||||
|
||||
private fun translateRolRorArrayArgs(arrayvar: IdentifierReference, indexer: ArrayIndex, operation: String, dt: Char) {
|
||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||
if(arrayvar.targetVarDecl(program)!!.datatype==DataType.UWORD) {
|
||||
if(dt!='b')
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
asmgen.assignExpressionToVariable(arrayvar, "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||
} else {
|
||||
asmgen.assignExpressionToVariable(AddressOf(arrayvar, arrayvar.position), "prog8_lib.${operation}_array_u${dt}._arg_target", DataType.UWORD, null)
|
||||
}
|
||||
asmgen.assignExpressionToVariable(indexer.indexExpr, "prog8_lib.${operation}_array_u${dt}._arg_index", DataType.UBYTE, null)
|
||||
}
|
||||
|
||||
@@ -705,426 +675,6 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun funcSwap(fcall: IFunctionCall) {
|
||||
val first = fcall.args[0]
|
||||
val second = fcall.args[1]
|
||||
|
||||
// optimized simple case: swap two variables
|
||||
if(first is IdentifierReference && second is IdentifierReference) {
|
||||
val firstName = asmgen.asmVariableName(first)
|
||||
val secondName = asmgen.asmVariableName(second)
|
||||
val dt = first.inferType(program)
|
||||
if(dt istype DataType.BYTE || dt istype DataType.UBYTE) {
|
||||
asmgen.out(" ldy $firstName | lda $secondName | sta $firstName | sty $secondName")
|
||||
return
|
||||
}
|
||||
if(dt istype DataType.WORD || dt istype DataType.UWORD) {
|
||||
asmgen.out("""
|
||||
ldy $firstName
|
||||
lda $secondName
|
||||
sta $firstName
|
||||
sty $secondName
|
||||
ldy $firstName+1
|
||||
lda $secondName+1
|
||||
sta $firstName+1
|
||||
sty $secondName+1
|
||||
""")
|
||||
return
|
||||
}
|
||||
if(dt istype DataType.FLOAT) {
|
||||
asmgen.out("""
|
||||
lda #<$firstName
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>$firstName
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #<$secondName
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>$secondName
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
jsr floats.func_swap_f
|
||||
""")
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// optimized simple case: swap two memory locations
|
||||
if(first is DirectMemoryRead && second is DirectMemoryRead) {
|
||||
val addr1 = (first.addressExpression as? NumericLiteral)?.number?.toHex()
|
||||
val addr2 = (second.addressExpression as? NumericLiteral)?.number?.toHex()
|
||||
val name1 = if(first.addressExpression is IdentifierReference) asmgen.asmVariableName(first.addressExpression as IdentifierReference) else null
|
||||
val name2 = if(second.addressExpression is IdentifierReference) asmgen.asmVariableName(second.addressExpression as IdentifierReference) else null
|
||||
|
||||
when {
|
||||
addr1!=null && addr2!=null -> {
|
||||
asmgen.out(" ldy $addr1 | lda $addr2 | sta $addr1 | sty $addr2")
|
||||
return
|
||||
}
|
||||
addr1!=null && name2!=null -> {
|
||||
asmgen.out(" ldy $addr1 | lda $name2 | sta $addr1 | sty $name2")
|
||||
return
|
||||
}
|
||||
name1!=null && addr2 != null -> {
|
||||
asmgen.out(" ldy $name1 | lda $addr2 | sta $name1 | sty $addr2")
|
||||
return
|
||||
}
|
||||
name1!=null && name2!=null -> {
|
||||
asmgen.out(" ldy $name1 | lda $name2 | sta $name1 | sty $name2")
|
||||
return
|
||||
}
|
||||
addr1==null && addr2==null && name1==null && name2==null -> {
|
||||
val firstExpr = first.addressExpression as? BinaryExpression
|
||||
val secondExpr = second.addressExpression as? BinaryExpression
|
||||
if(firstExpr!=null && secondExpr!=null) {
|
||||
val pointerVariable = firstExpr.left as? IdentifierReference
|
||||
val firstOffset = firstExpr.right
|
||||
val secondOffset = secondExpr.right
|
||||
if(pointerVariable != null
|
||||
&& pointerVariable isSameAs secondExpr.left
|
||||
&& firstExpr.operator == "+" && secondExpr.operator == "+"
|
||||
&& (firstOffset is NumericLiteral || firstOffset is IdentifierReference || firstOffset is TypecastExpression)
|
||||
&& (secondOffset is NumericLiteral || secondOffset is IdentifierReference || secondOffset is TypecastExpression)
|
||||
) {
|
||||
if(firstOffset is NumericLiteral && secondOffset is NumericLiteral) {
|
||||
if(firstOffset!=secondOffset) {
|
||||
swapArrayValues(
|
||||
DataType.UBYTE,
|
||||
asmgen.asmVariableName(pointerVariable), firstOffset,
|
||||
asmgen.asmVariableName(pointerVariable), secondOffset
|
||||
)
|
||||
return
|
||||
}
|
||||
} else if(firstOffset is TypecastExpression && secondOffset is TypecastExpression) {
|
||||
if(firstOffset.type in WordDatatypes && secondOffset.type in WordDatatypes) {
|
||||
val firstOffsetVar = firstOffset.expression as? IdentifierReference
|
||||
val secondOffsetVar = secondOffset.expression as? IdentifierReference
|
||||
if(firstOffsetVar!=null && secondOffsetVar!=null) {
|
||||
if(firstOffsetVar!=secondOffsetVar) {
|
||||
swapArrayValues(
|
||||
DataType.UBYTE,
|
||||
asmgen.asmVariableName(pointerVariable), firstOffsetVar,
|
||||
asmgen.asmVariableName(pointerVariable), secondOffsetVar
|
||||
)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if(firstOffset is IdentifierReference || secondOffset is IdentifierReference) {
|
||||
throw AssemblyError("expected a typecast-to-word for index variable at ${firstOffset.position} and/or ${secondOffset.position}")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(first is ArrayIndexedExpression && second is ArrayIndexedExpression) {
|
||||
val arrayVarName1 = asmgen.asmVariableName(first.arrayvar)
|
||||
val arrayVarName2 = asmgen.asmVariableName(second.arrayvar)
|
||||
val elementIDt = first.inferType(program)
|
||||
val elementDt = elementIDt.getOrElse { throw AssemblyError("unknown dt") }
|
||||
|
||||
val firstNum = first.indexer.indexExpr as? NumericLiteral
|
||||
val firstVar = first.indexer.indexExpr as? IdentifierReference
|
||||
val secondNum = second.indexer.indexExpr as? NumericLiteral
|
||||
val secondVar = second.indexer.indexExpr as? IdentifierReference
|
||||
|
||||
if(firstNum!=null && secondNum!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondNum)
|
||||
return
|
||||
} else if(firstVar!=null && secondVar!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondVar)
|
||||
return
|
||||
} else if(firstNum!=null && secondVar!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, firstNum, arrayVarName2, secondVar)
|
||||
return
|
||||
} else if(firstVar!=null && secondNum!=null) {
|
||||
swapArrayValues(elementDt, arrayVarName1, firstVar, arrayVarName2, secondNum)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// all other types of swap() calls are done via a temporary variable
|
||||
|
||||
fun targetFromExpr(expr: Expression, datatype: DataType): AsmAssignTarget {
|
||||
return when (expr) {
|
||||
is IdentifierReference -> AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, datatype, expr.definingSubroutine, variableAsmName = asmgen.asmVariableName(expr))
|
||||
is ArrayIndexedExpression -> AsmAssignTarget(TargetStorageKind.ARRAY, program, asmgen, datatype, expr.definingSubroutine, array = expr)
|
||||
is DirectMemoryRead -> AsmAssignTarget(TargetStorageKind.MEMORY, program, asmgen, datatype, expr.definingSubroutine, memory = DirectMemoryWrite(expr.addressExpression, expr.position))
|
||||
else -> throw AssemblyError("invalid expression object $expr")
|
||||
}
|
||||
}
|
||||
|
||||
when(val datatype: DataType = first.inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes, in WordDatatypes -> {
|
||||
asmgen.assignExpressionToVariable(first, "P8ZP_SCRATCH_W1", datatype, null)
|
||||
asmgen.assignExpressionToVariable(second, "P8ZP_SCRATCH_W2", datatype, null)
|
||||
val assignFirst = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W2"),
|
||||
targetFromExpr(first, datatype),
|
||||
false, program.memsizer, first.position
|
||||
)
|
||||
val assignSecond = AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, variableAsmName = "P8ZP_SCRATCH_W1"),
|
||||
targetFromExpr(second, datatype),
|
||||
false, program.memsizer, second.position
|
||||
)
|
||||
asmgen.translateNormalAssignment(assignFirst)
|
||||
asmgen.translateNormalAssignment(assignSecond)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
// via temp variable and FAC1
|
||||
asmgen.assignExpressionTo(first, AsmAssignTarget(TargetStorageKind.VARIABLE, program, asmgen, DataType.FLOAT, first.definingSubroutine, "floats.tempvar_swap_float"))
|
||||
asmgen.assignExpressionTo(second, AsmAssignTarget(TargetStorageKind.REGISTER, program, asmgen, DataType.FLOAT, null, register=RegisterOrPair.FAC1))
|
||||
asmgen.translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.REGISTER, program, asmgen, datatype, register = RegisterOrPair.FAC1),
|
||||
targetFromExpr(first, datatype),
|
||||
false, program.memsizer, first.position
|
||||
)
|
||||
)
|
||||
asmgen.translateNormalAssignment(
|
||||
AsmAssignment(
|
||||
AsmAssignSource(SourceStorageKind.VARIABLE, program, asmgen, datatype, "floats.tempvar_swap_float"),
|
||||
targetFromExpr(second, datatype),
|
||||
false, program.memsizer, second.position
|
||||
)
|
||||
)
|
||||
}
|
||||
else -> throw AssemblyError("weird swap dt")
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexValue2: NumericLiteral) {
|
||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1+$index1
|
||||
ldy $arrayVarName2+$index2
|
||||
sta $arrayVarName2+$index2
|
||||
sty $arrayVarName1+$index1
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1+$index1
|
||||
ldy $arrayVarName2+$index2
|
||||
sta $arrayVarName2+$index2
|
||||
sty $arrayVarName1+$index1
|
||||
lda $arrayVarName1+$index1+1
|
||||
ldy $arrayVarName2+$index2+1
|
||||
sta $arrayVarName2+$index2+1
|
||||
sty $arrayVarName1+$index1+1
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #<(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
jsr floats.func_swap_f
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
stx P8ZP_SCRATCH_REG
|
||||
ldx $idxAsmName1
|
||||
ldy $idxAsmName2
|
||||
lda $arrayVarName1,x
|
||||
pha
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1,x
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
stx P8ZP_SCRATCH_REG
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
tax
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
tay
|
||||
lda $arrayVarName1,x
|
||||
pha
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1,x
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
lda $arrayVarName1+1,x
|
||||
pha
|
||||
lda $arrayVarName2+1,y
|
||||
sta $arrayVarName1+1,x
|
||||
pla
|
||||
sta $arrayVarName2+1,y
|
||||
ldx P8ZP_SCRATCH_REG
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #>$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName1
|
||||
adc #<$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ lda #>$arrayVarName2
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName2
|
||||
adc #<$arrayVarName2
|
||||
sta P8ZP_SCRATCH_W2
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W2+1
|
||||
+ jsr floats.func_swap_f
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexValue1: NumericLiteral, arrayVarName2: String, indexName2: IdentifierReference) {
|
||||
val index1 = indexValue1.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
val idxAsmName2 = asmgen.asmVariableName(indexName2)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1 + $index1
|
||||
pha
|
||||
ldy $idxAsmName2
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1 + $index1
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName1 + $index1
|
||||
pha
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
tay
|
||||
lda $arrayVarName2,y
|
||||
sta $arrayVarName1 + $index1
|
||||
pla
|
||||
sta $arrayVarName2,y
|
||||
lda $arrayVarName1 + $index1+1
|
||||
pha
|
||||
lda $arrayVarName2+1,y
|
||||
sta $arrayVarName1 + $index1+1
|
||||
pla
|
||||
sta $arrayVarName2+1,y
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #<(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1
|
||||
lda #>(${arrayVarName1}+$index1)
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda #>$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda $idxAsmName2
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName2
|
||||
adc #<$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ jsr floats.func_swap_f
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun swapArrayValues(elementDt: DataType, arrayVarName1: String, indexName1: IdentifierReference, arrayVarName2: String, indexValue2: NumericLiteral) {
|
||||
val idxAsmName1 = asmgen.asmVariableName(indexName1)
|
||||
val index2 = indexValue2.number.toInt() * program.memsizer.memorySize(elementDt)
|
||||
when(elementDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName2 + $index2
|
||||
pha
|
||||
ldy $idxAsmName1
|
||||
lda $arrayVarName1,y
|
||||
sta $arrayVarName2 + $index2
|
||||
pla
|
||||
sta $arrayVarName1,y
|
||||
""")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
asmgen.out("""
|
||||
lda $arrayVarName2 + $index2
|
||||
pha
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
tay
|
||||
lda $arrayVarName1,y
|
||||
sta $arrayVarName2 + $index2
|
||||
pla
|
||||
sta $arrayVarName1,y
|
||||
lda $arrayVarName2 + $index2+1
|
||||
pha
|
||||
lda $arrayVarName1+1,y
|
||||
sta $arrayVarName2 + $index2+1
|
||||
pla
|
||||
sta $arrayVarName1+1,y
|
||||
""")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
asmgen.out("""
|
||||
lda #>$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1+1
|
||||
lda $idxAsmName1
|
||||
asl a
|
||||
asl a
|
||||
clc
|
||||
adc $idxAsmName1
|
||||
adc #<$arrayVarName1
|
||||
sta P8ZP_SCRATCH_W1
|
||||
bcc +
|
||||
inc P8ZP_SCRATCH_W1+1
|
||||
+ lda #<(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2
|
||||
lda #>(${arrayVarName2}+$index2)
|
||||
sta P8ZP_SCRATCH_W2+1
|
||||
jsr floats.func_swap_f
|
||||
""")
|
||||
}
|
||||
else -> throw AssemblyError("invalid aray elt type")
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcAbs(fcall: IFunctionCall, func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
translateArguments(fcall.args, func, scope)
|
||||
val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)
|
||||
@@ -1148,6 +698,28 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun funcBoolean(fcall: IFunctionCall, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
when (val dt = fcall.args.single().inferType(program).getOr(DataType.UNDEFINED)) {
|
||||
in ByteDatatypes -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.A, dt==DataType.BYTE)
|
||||
}
|
||||
in WordDatatypes -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.AY, dt==DataType.WORD)
|
||||
asmgen.out(" sty P8ZP_SCRATCH_B1 | ora P8ZP_SCRATCH_B1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
assignAsmGen.assignExpressionToRegister(fcall.args.single(), RegisterOrPair.FAC1, true)
|
||||
asmgen.out(" jsr floats.SIGN")
|
||||
}
|
||||
else -> throw AssemblyError("weird type")
|
||||
}
|
||||
|
||||
if(resultToStack)
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
else if(resultRegister!=null)
|
||||
assignAsmGen.assignRegisterByte(AsmAssignTarget.fromRegisters(resultRegister, false, scope, program, asmgen), CpuRegister.A)
|
||||
}
|
||||
|
||||
private fun funcRnd(func: FSignature, resultToStack: Boolean, resultRegister: RegisterOrPair?, scope: Subroutine?) {
|
||||
when(func.name) {
|
||||
"rnd" -> {
|
||||
@@ -1312,12 +884,12 @@ internal class BuiltinFunctionsAsmGen(private val program: Program,
|
||||
asmgen.out(" sta P8ESTACK_LO,x | tya | sta P8ESTACK_HI,x | dex")
|
||||
} else {
|
||||
val reg = resultRegister ?: RegisterOrPair.AY
|
||||
var needAsave = !(fcall.args[0] is DirectMemoryRead || fcall.args[0] is NumericLiteral || fcall.args[0] is IdentifierReference)
|
||||
var needAsave = asmgen.needAsaveForExpr(fcall.args[0])
|
||||
if(!needAsave) {
|
||||
val mr0 = fcall.args[0] as? DirectMemoryRead
|
||||
val mr1 = fcall.args[1] as? DirectMemoryRead
|
||||
if (mr0 != null)
|
||||
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
|
||||
needAsave = mr0.addressExpression !is NumericLiteral && mr0.addressExpression !is IdentifierReference
|
||||
if (mr1 != null)
|
||||
needAsave = needAsave or (mr1.addressExpression !is NumericLiteral && mr1.addressExpression !is IdentifierReference)
|
||||
}
|
||||
|
||||
@@ -34,8 +34,6 @@ internal class ExpressionsAsmGen(private val program: Program,
|
||||
is IdentifierReference -> translateExpression(expression)
|
||||
is FunctionCallExpression -> translateFunctionCallResultOntoStack(expression)
|
||||
is BuiltinFunctionCall -> asmgen.translateBuiltinFunctionCallExpression(expression, true, null)
|
||||
is PipeExpression -> asmgen.translatePipeExpression(expression.source, expression.segments,
|
||||
expression, isStatement = false, pushResultOnEstack = true )
|
||||
is ContainmentCheck -> throw AssemblyError("containment check as complex expression value is not supported")
|
||||
is ArrayLiteral, is StringLiteral -> throw AssemblyError("no asm gen for string/array literal value assignment - should have been replaced by a variable")
|
||||
is RangeExpression -> throw AssemblyError("range expression should have been changed into array values")
|
||||
@@ -692,6 +690,25 @@ internal class ExpressionsAsmGen(private val program: Program,
|
||||
throw AssemblyError("unknown dt")
|
||||
val elementDt = elementIDt.getOr(DataType.UNDEFINED)
|
||||
val arrayVarName = asmgen.asmVariableName(arrayExpr.arrayvar)
|
||||
|
||||
val arrayVarDecl = arrayExpr.arrayvar.targetVarDecl(program)!!
|
||||
if(arrayVarDecl.datatype==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(arrayExpr.inferType(program) isnot DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(arrayExpr, elementDt, CpuRegister.Y)
|
||||
if(asmgen.isZpVar(arrayExpr.arrayvar)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
asmgen.out(" sta P8ESTACK_LO,x | dex")
|
||||
return
|
||||
}
|
||||
|
||||
val constIndexNum = arrayExpr.indexer.constIndex()
|
||||
if(constIndexNum!=null) {
|
||||
val indexValue = constIndexNum * program.memsizer.memorySize(elementDt)
|
||||
|
||||
@@ -3,10 +3,7 @@ package prog8.codegen.cpu6502
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.AddressOf
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.assignment.AsmAssignSource
|
||||
@@ -111,10 +108,10 @@ internal class FunctionCallAsmGen(private val program: Program, private val asmg
|
||||
} else {
|
||||
// 2 byte params, second in Y, first in A
|
||||
argumentViaRegister(sub, IndexedValue(0, sub.parameters[0]), call.args[0], RegisterOrPair.A)
|
||||
if(!call.args[1].isSimple)
|
||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||
asmgen.out(" pha")
|
||||
argumentViaRegister(sub, IndexedValue(1, sub.parameters[1]), call.args[1], RegisterOrPair.Y)
|
||||
if(!call.args[1].isSimple)
|
||||
if(asmgen.needAsaveForExpr(call.args[1]))
|
||||
asmgen.out(" pla")
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -589,7 +589,7 @@ internal class ProgramAndVarsGen(
|
||||
}
|
||||
|
||||
private fun outputStringvar(varname: String, encoding: Encoding, value: String) {
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"")
|
||||
asmgen.out("$varname\t; $encoding:\"${value.escape().replace("\u0000", "<NULL>")}\"", false)
|
||||
val bytes = compTarget.encodeString(value, encoding).plus(0.toUByte())
|
||||
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
|
||||
for (chunk in outputBytes.chunked(16))
|
||||
|
||||
@@ -116,9 +116,9 @@ internal class VariableAllocator(private val symboltable: SymbolTable,
|
||||
}
|
||||
}
|
||||
|
||||
println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||
println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||
println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||
// println(" number of allocated vars: $numberOfAllocatableVariables")
|
||||
// println(" put into zeropage: $numVariablesAllocatedInZP, non-zp allocatable: ${numberOfNonIntegerVariables+numberOfExplicitNonZpVariables}")
|
||||
// println(" zeropage free space: ${zeropage.free.size} bytes")
|
||||
}
|
||||
|
||||
private fun collectAllVariables(st: SymbolTable): Collection<StStaticVariable> {
|
||||
|
||||
@@ -2,7 +2,10 @@ package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen
|
||||
|
||||
@@ -37,7 +40,6 @@ internal class AsmAssignTarget(val kind: TargetStorageKind,
|
||||
val origAstTarget: AssignTarget? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
||||
val asmVarname: String by lazy {
|
||||
if (array == null)
|
||||
@@ -122,9 +124,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
val expression: Expression? = null
|
||||
)
|
||||
{
|
||||
val constMemoryAddress by lazy { memory?.addressExpression?.constValue(program)?.number?.toUInt() ?: 0u}
|
||||
val constArrayIndexValue by lazy { array?.indexer?.constIndex()?.toUInt() }
|
||||
|
||||
val asmVarname: String
|
||||
get() = if(array==null)
|
||||
variableAsmName!!
|
||||
@@ -132,8 +131,6 @@ internal class AsmAssignSource(val kind: SourceStorageKind,
|
||||
asmgen.asmVariableName(array.arrayvar)
|
||||
|
||||
companion object {
|
||||
fun fromAstSource(indexer: ArrayIndex, program: Program, asmgen: AsmGen): AsmAssignSource = fromAstSource(indexer.indexExpr, program, asmgen)
|
||||
|
||||
fun fromAstSource(value: Expression, program: Program, asmgen: AsmGen): AsmAssignSource {
|
||||
val cv = value.constValue(program)
|
||||
if(cv!=null)
|
||||
|
||||
@@ -2,6 +2,7 @@ package prog8.codegen.cpu6502.assignment
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.getTempRegisterName
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.*
|
||||
import prog8.codegen.cpu6502.AsmGen
|
||||
@@ -72,6 +73,25 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
val value = assign.source.array!!
|
||||
val elementDt = assign.source.datatype
|
||||
val arrayVarName = asmgen.asmVariableName(value.arrayvar)
|
||||
|
||||
val arrayVarDecl = value.arrayvar.targetVarDecl(program)!!
|
||||
if(arrayVarDecl.datatype==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(elementDt !in ByteDatatypes)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(value.inferType(program) isnot DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
asmgen.loadScaledArrayIndexIntoRegister(value, elementDt, CpuRegister.Y)
|
||||
if(asmgen.isZpVar(value.arrayvar)) {
|
||||
asmgen.out(" lda ($arrayVarName),y")
|
||||
} else {
|
||||
asmgen.out(" lda $arrayVarName | sta P8ZP_SCRATCH_W1 | lda $arrayVarName+1 | sta P8ZP_SCRATCH_W1+1")
|
||||
asmgen.out(" lda (P8ZP_SCRATCH_W1),y")
|
||||
}
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
return
|
||||
}
|
||||
|
||||
val constIndex = value.indexer.constIndex()
|
||||
if (constIndex!=null) {
|
||||
// constant array index value
|
||||
@@ -271,35 +291,8 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
containmentCheckIntoA(value)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
}
|
||||
is PipeExpression -> {
|
||||
asmgen.translatePipeExpression(value.source, value.segments, value, false, false)
|
||||
val resultDt = value.inferType(program)
|
||||
val register =
|
||||
if(resultDt.isBytes) RegisterOrPair.A
|
||||
else if(resultDt.isWords) RegisterOrPair.AY
|
||||
else if(resultDt istype DataType.FLOAT) RegisterOrPair.FAC1
|
||||
else throw AssemblyError("invalid dt")
|
||||
asmgen.assignRegister(register, assign.target)
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
if(value.operator in ComparisonOperators) {
|
||||
// TODO real optimized code for comparison expressions that yield a boolean result value
|
||||
assignConstantByte(assign.target, 0)
|
||||
val origTarget = assign.target.origAstTarget
|
||||
if(origTarget!=null) {
|
||||
val assignTrue = AnonymousScope(mutableListOf(
|
||||
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||
), assign.position)
|
||||
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||
val ifelse = IfElse(value.copy(), assignTrue, assignFalse, assign.position)
|
||||
ifelse.linkParents(value)
|
||||
asmgen.translate(ifelse)
|
||||
}
|
||||
else {
|
||||
// no orig ast assign target so can't use the workaround, so fallback to stack eval
|
||||
fallbackToStackEval(assign)
|
||||
}
|
||||
} else {
|
||||
if(!attemptAssignOptimizedBinexpr(value, assign)) {
|
||||
// All remaining binary expressions just evaluate via the stack for now.
|
||||
// (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...)
|
||||
@@ -310,6 +303,216 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun attemptAssignOptimizedBinexpr(expr: BinaryExpression, assign: AsmAssignment): Boolean {
|
||||
if(expr.operator in ComparisonOperators) {
|
||||
assignConstantByte(assign.target, 0)
|
||||
val origTarget = assign.target.origAstTarget
|
||||
if(origTarget!=null) {
|
||||
val assignTrue = AnonymousScope(mutableListOf(
|
||||
Assignment(origTarget, NumericLiteral.fromBoolean(true, assign.position), AssignmentOrigin.ASMGEN, assign.position)
|
||||
), assign.position)
|
||||
val assignFalse = AnonymousScope(mutableListOf(), assign.position)
|
||||
val ifelse = IfElse(expr.copy(), assignTrue, assignFalse, assign.position)
|
||||
ifelse.linkParents(expr)
|
||||
asmgen.translate(ifelse)
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
if(!expr.inferType(program).isInteger)
|
||||
return false
|
||||
|
||||
// optimized code for logical expressions
|
||||
// TODO assume (hope) cx16.r9 isn't used for anything else during the use of this...
|
||||
if(expr.operator=="and") {
|
||||
val iDt = expr.left.inferType(program)
|
||||
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
val tmpReg = getTempRegisterName(iDt).joinToString(".")
|
||||
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" and $tmpReg")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for and, expected byte $expr @${expr.position}")
|
||||
}
|
||||
else if(expr.operator=="or") {
|
||||
val iDt = expr.left.inferType(program)
|
||||
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
val tmpReg = getTempRegisterName(iDt).joinToString(".")
|
||||
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" ora $tmpReg")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for or, expected byte $expr @${expr.position}")
|
||||
}
|
||||
else if(expr.operator=="xor") {
|
||||
val iDt = expr.left.inferType(program)
|
||||
val dt = iDt.getOrElse { throw AssemblyError("weird dt") }
|
||||
if (dt in ByteDatatypes) {
|
||||
val tmpReg = getTempRegisterName(iDt).joinToString(".")
|
||||
assignExpressionToVariable(expr.left, tmpReg, dt, expr.definingSubroutine)
|
||||
assignExpressionToRegister(expr.right, RegisterOrPair.A, dt==DataType.BYTE || dt==DataType.WORD)
|
||||
asmgen.out(" eor $tmpReg")
|
||||
if(assign.target.datatype in ByteDatatypes)
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
else {
|
||||
asmgen.out(" ldy #0")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
}
|
||||
return true
|
||||
}
|
||||
else throw AssemblyError("weird dt for xor, expected byte $expr @${expr.position}")
|
||||
}
|
||||
|
||||
if(expr.operator!="+" && expr.operator!="-")
|
||||
return false
|
||||
|
||||
val dt = expr.inferType(program).getOrElse { throw AssemblyError("invalid dt") }
|
||||
val left = expr.left
|
||||
val right = expr.right
|
||||
if(dt in ByteDatatypes) {
|
||||
when (right) {
|
||||
is IdentifierReference -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
val symname = asmgen.asmVariableName(right)
|
||||
if(expr.operator=="+")
|
||||
asmgen.out(" clc | adc $symname")
|
||||
else
|
||||
asmgen.out(" sec | sbc $symname")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
return true
|
||||
}
|
||||
is NumericLiteral -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.A, dt==DataType.BYTE)
|
||||
if(expr.operator=="+")
|
||||
asmgen.out(" clc | adc #${right.number.toHex()}")
|
||||
else
|
||||
asmgen.out(" sec | sbc #${right.number.toHex()}")
|
||||
assignRegisterByte(assign.target, CpuRegister.A)
|
||||
return true
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
} else if(dt in WordDatatypes) {
|
||||
when (right) {
|
||||
is AddressOf -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||
val symbol = asmgen.asmVariableName(right.identifier)
|
||||
if(expr.operator=="+")
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<$symbol
|
||||
pha
|
||||
tya
|
||||
adc #>$symbol
|
||||
tay
|
||||
pla""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #<$symbol
|
||||
pha
|
||||
tya
|
||||
sbc #>$symbol
|
||||
tay
|
||||
pla""")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
val symname = asmgen.asmVariableName(right)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||
if(expr.operator=="+")
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $symname
|
||||
pha
|
||||
tya
|
||||
adc $symname+1
|
||||
tay
|
||||
pla""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $symname
|
||||
pha
|
||||
tya
|
||||
sbc $symname+1
|
||||
tay
|
||||
pla""")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
is NumericLiteral -> {
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||
if(expr.operator=="+") {
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc #<${right.number.toHex()}
|
||||
pha
|
||||
tya
|
||||
adc #>${right.number.toHex()}
|
||||
tay
|
||||
pla""")
|
||||
} else if(expr.operator=="-") {
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc #<${right.number.toHex()}
|
||||
pha
|
||||
tya
|
||||
sbc #>${right.number.toHex()}
|
||||
tay
|
||||
pla""")
|
||||
}
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
is TypecastExpression -> {
|
||||
val castedValue = right.expression
|
||||
if(right.type in WordDatatypes && castedValue.inferType(program).isBytes) {
|
||||
if(castedValue is IdentifierReference) {
|
||||
val castedSymname = asmgen.asmVariableName(castedValue)
|
||||
assignExpressionToRegister(left, RegisterOrPair.AY, dt==DataType.WORD)
|
||||
if(expr.operator=="+")
|
||||
asmgen.out("""
|
||||
clc
|
||||
adc $castedSymname
|
||||
bcc +
|
||||
iny
|
||||
+""")
|
||||
else
|
||||
asmgen.out("""
|
||||
sec
|
||||
sbc $castedSymname
|
||||
bcs +
|
||||
dey
|
||||
+""")
|
||||
assignRegisterpairWord(assign.target, RegisterOrPair.AY)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> return false
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private fun fallbackToStackEval(assign: AsmAssignment) {
|
||||
// TODO DON'T STACK-EVAL... perhaps by using a temp var? so that it becomes augmentable assignment expression?
|
||||
// or don't try to solve it here in this one case and rather rewrite the whole stack based value evaluation.
|
||||
@@ -634,7 +837,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
when(sourceDt) {
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
@@ -657,7 +860,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
@@ -687,7 +890,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
DataType.WORD -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@@ -708,7 +911,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
DataType.UWORD -> {
|
||||
asmgen.out(" lda $sourceAsmVarName | sta $targetAsmVarName | lda $sourceAsmVarName+1 | sta $targetAsmVarName+1")
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
@@ -749,7 +952,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
when(sourceDt) {
|
||||
DataType.UBYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
DataType.BYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD, DataType.WORD -> {
|
||||
@@ -783,7 +986,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when(targetDt) {
|
||||
DataType.UBYTE, DataType.BYTE -> {
|
||||
DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase()} $targetAsmVarName")
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
@@ -830,7 +1033,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
DataType.WORD -> {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
@@ -858,7 +1061,7 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
DataType.BYTE, DataType.UBYTE -> {
|
||||
asmgen.out(" st${regs.toString().lowercase().first()} $targetAsmVarName")
|
||||
}
|
||||
DataType.WORD, DataType.UWORD -> {
|
||||
DataType.UWORD -> {
|
||||
when(regs) {
|
||||
RegisterOrPair.AX -> asmgen.out(" sta $targetAsmVarName | stx $targetAsmVarName+1")
|
||||
RegisterOrPair.AY -> asmgen.out(" sta $targetAsmVarName | sty $targetAsmVarName+1")
|
||||
@@ -1365,6 +1568,22 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||
// assigning an indexed pointer var
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda $sourceName")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||
asmgen.out(" lda $sourceName | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda $sourceName | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val scaledIdx = target.constArrayIndexValue!! * program.memsizer.memorySize(target.datatype).toUInt()
|
||||
asmgen.out(" lda $sourceName | sta ${target.asmVarname}+$scaledIdx")
|
||||
@@ -1897,6 +2116,22 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||
// assigning an indexed pointer var
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #0")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||
asmgen.out(" lda #0 | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #0 | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val indexValue = target.constArrayIndexValue!!
|
||||
asmgen.out(" stz ${target.asmVarname}+$indexValue")
|
||||
@@ -1940,13 +2175,29 @@ internal class AssignmentAsmGen(private val program: Program,
|
||||
storeRegisterAInMemoryAddress(target.memory!!)
|
||||
}
|
||||
TargetStorageKind.ARRAY -> {
|
||||
if(target.origAstTarget?.arrayindexed?.arrayvar?.targetVarDecl(program)?.datatype==DataType.UWORD) {
|
||||
// assigning an indexed pointer var
|
||||
if (target.constArrayIndexValue==0u) {
|
||||
asmgen.out(" lda #${byte.toHex()}")
|
||||
asmgen.storeAIntoPointerVar(target.origAstTarget.arrayindexed!!.arrayvar)
|
||||
} else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
if (asmgen.isZpVar(target.origAstTarget.arrayindexed!!.arrayvar)) {
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (${target.asmVarname}),y")
|
||||
} else {
|
||||
asmgen.out(" lda ${target.asmVarname} | sta P8ZP_SCRATCH_W2 | lda ${target.asmVarname}+1 | sta P8ZP_SCRATCH_W2+1")
|
||||
asmgen.out(" lda #${byte.toHex()} | sta (P8ZP_SCRATCH_W2),y")
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
if (target.constArrayIndexValue!=null) {
|
||||
val indexValue = target.constArrayIndexValue!!
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname}+$indexValue")
|
||||
}
|
||||
else {
|
||||
asmgen.loadScaledArrayIndexIntoRegister(target.array!!, DataType.UBYTE, CpuRegister.Y)
|
||||
asmgen.out(" lda #<${byte.toHex()} | sta ${target.asmVarname},y")
|
||||
asmgen.out(" lda #${byte.toHex()} | sta ${target.asmVarname},y")
|
||||
}
|
||||
}
|
||||
TargetStorageKind.REGISTER -> when(target.register!!) {
|
||||
|
||||
@@ -28,7 +28,7 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -172,7 +172,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
||||
is PtMemoryByte -> write(it)
|
||||
is PtMemMapped -> write(it)
|
||||
is PtNumber -> write(it)
|
||||
is PtPipe -> write(it)
|
||||
is PtPostIncrDecr -> write(it)
|
||||
is PtPrefix -> write(it)
|
||||
is PtRange -> write(it)
|
||||
@@ -204,14 +203,6 @@ class AstToXmlConverter(internal val program: PtProgram,
|
||||
xml.endElt()
|
||||
}
|
||||
|
||||
private fun write(pipe: PtPipe) {
|
||||
xml.elt("pipe")
|
||||
xml.attr("type", pipe.type.name)
|
||||
xml.startChildren()
|
||||
pipe.children.forEach { writeNode(it) }
|
||||
xml.endElt()
|
||||
}
|
||||
|
||||
private fun write(array: PtArray) {
|
||||
xml.elt("array")
|
||||
xml.attr("type", array.type.name)
|
||||
|
||||
@@ -29,7 +29,7 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -82,34 +82,34 @@ internal class VmCodeInstruction(
|
||||
value: Int?=null, // 0-$ffff
|
||||
fpValue: Float?=null,
|
||||
labelSymbol: List<String>?=null // alternative to value for branch/call/jump labels
|
||||
): VmCodeLine() {
|
||||
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
|
||||
): VmCodeLine() {
|
||||
val ins = Instruction(opcode, type, reg1, reg2, fpReg1, fpReg2, value, fpValue, labelSymbol)
|
||||
|
||||
init {
|
||||
if(reg1!=null && (reg1<0 || reg1>65536))
|
||||
throw IllegalArgumentException("reg1 out of bounds")
|
||||
if(reg2!=null && (reg2<0 || reg2>65536))
|
||||
throw IllegalArgumentException("reg2 out of bounds")
|
||||
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
|
||||
throw IllegalArgumentException("fpReg1 out of bounds")
|
||||
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
|
||||
throw IllegalArgumentException("fpReg2 out of bounds")
|
||||
init {
|
||||
if(reg1!=null && (reg1<0 || reg1>65536))
|
||||
throw IllegalArgumentException("reg1 out of bounds")
|
||||
if(reg2!=null && (reg2<0 || reg2>65536))
|
||||
throw IllegalArgumentException("reg2 out of bounds")
|
||||
if(fpReg1!=null && (fpReg1<0 || fpReg1>65536))
|
||||
throw IllegalArgumentException("fpReg1 out of bounds")
|
||||
if(fpReg2!=null && (fpReg2<0 || fpReg2>65536))
|
||||
throw IllegalArgumentException("fpReg2 out of bounds")
|
||||
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IllegalArgumentException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IllegalArgumentException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT, null -> {}
|
||||
if(value!=null && opcode !in OpcodesWithAddress) {
|
||||
when (type) {
|
||||
VmDataType.BYTE -> {
|
||||
if (value < -128 || value > 255)
|
||||
throw IllegalArgumentException("value out of range for byte: $value")
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
if (value < -32768 || value > 65535)
|
||||
throw IllegalArgumentException("value out of range for word: $value")
|
||||
}
|
||||
VmDataType.FLOAT, null -> {}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
internal class VmCodeLabel(val name: List<String>): VmCodeLine()
|
||||
internal class VmCodeComment(val comment: String): VmCodeLine()
|
||||
|
||||
@@ -159,6 +159,19 @@ internal class AssignmentGen(private val codeGen: CodeGen, private val expressio
|
||||
val variable = array.variable.targetName
|
||||
var variableAddr = codeGen.allocations.get(variable)
|
||||
val itemsize = codeGen.program.memsizer.memorySize(array.type)
|
||||
|
||||
if(array.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(itemsize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(array.index.type!=DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
val idxReg = codeGen.vmRegisters.nextFree()
|
||||
code += expressionEval.translateExpression(array.index, idxReg, -1)
|
||||
code += VmCodeInstruction(Opcode.STOREIX, vmDt, reg1=resultRegister, reg2=idxReg, value = variableAddr)
|
||||
return code
|
||||
}
|
||||
|
||||
val fixedIndex = constIntValue(array.index)
|
||||
if(zero) {
|
||||
if(fixedIndex!=null) {
|
||||
|
||||
@@ -32,6 +32,7 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
"callrom" -> throw AssemblyError("callrom() is for cx16 target only")
|
||||
"msb" -> funcMsb(call, resultRegister)
|
||||
"lsb" -> funcLsb(call, resultRegister)
|
||||
"boolean" -> funcBoolean(call, resultRegister)
|
||||
"memory" -> funcMemory(call, resultRegister)
|
||||
"peek" -> funcPeek(call, resultRegister)
|
||||
"peekw" -> funcPeekW(call, resultRegister)
|
||||
@@ -41,11 +42,10 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
"mkword" -> funcMkword(call, resultRegister)
|
||||
"sort" -> funcSort(call)
|
||||
"reverse" -> funcReverse(call)
|
||||
"swap" -> funcSwap(call)
|
||||
"rol" -> funcRolRor2(Opcode.ROXL, call, resultRegister)
|
||||
"ror" -> funcRolRor2(Opcode.ROXR, call, resultRegister)
|
||||
"rol2" -> funcRolRor2(Opcode.ROL, call, resultRegister)
|
||||
"ror2" -> funcRolRor2(Opcode.ROR, call, resultRegister)
|
||||
"rol" -> funcRolRor(Opcode.ROXL, call, resultRegister)
|
||||
"ror" -> funcRolRor(Opcode.ROXR, call, resultRegister)
|
||||
"rol2" -> funcRolRor(Opcode.ROL, call, resultRegister)
|
||||
"ror2" -> funcRolRor(Opcode.ROR, call, resultRegister)
|
||||
else -> TODO("builtinfunc ${call.name}")
|
||||
}
|
||||
}
|
||||
@@ -184,19 +184,6 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcSwap(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||
val left = call.args[0]
|
||||
val right = call.args[1]
|
||||
val leftReg = codeGen.vmRegisters.nextFree()
|
||||
val rightReg = codeGen.vmRegisters.nextFree()
|
||||
val code = VmCodeChunk()
|
||||
code += exprGen.translateExpression(left, leftReg, -1)
|
||||
code += exprGen.translateExpression(right, rightReg, -1)
|
||||
code += assignRegisterTo(left, rightReg)
|
||||
code += assignRegisterTo(right, leftReg)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcReverse(call: PtBuiltinFunctionCall): VmCodeChunk {
|
||||
val arrayName = call.args[0] as PtIdentifier
|
||||
val array = codeGen.symbolTable.flat.getValue(arrayName.targetName) as StStaticVariable
|
||||
@@ -368,7 +355,31 @@ internal class BuiltinFuncGen(private val codeGen: CodeGen, private val exprGen:
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcRolRor2(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
private fun funcBoolean(call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
val vmDt = codeGen.vmType(call.args[0].type)
|
||||
when (vmDt) {
|
||||
VmDataType.FLOAT -> {
|
||||
val fpValueReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
val fpSignReg = codeGen.vmRegisters.nextFreeFloat()
|
||||
code += exprGen.translateExpression(call.args.single(), -1, fpValueReg)
|
||||
code += VmCodeInstruction(Opcode.SGN, VmDataType.FLOAT, fpReg1 = fpSignReg, fpReg2 = fpValueReg)
|
||||
code += VmCodeInstruction(Opcode.FTOSB, VmDataType.FLOAT, reg1 = resultRegister, fpReg1 = fpSignReg)
|
||||
}
|
||||
VmDataType.WORD -> {
|
||||
val msbReg = codeGen.vmRegisters.nextFree()
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||
code += VmCodeInstruction(Opcode.MSIG, VmDataType.BYTE, reg1 = msbReg, reg2=resultRegister)
|
||||
code += VmCodeInstruction(Opcode.OR, VmDataType.BYTE, reg1=resultRegister, reg2=msbReg)
|
||||
}
|
||||
else -> {
|
||||
code += exprGen.translateExpression(call.args.single(), resultRegister, -1)
|
||||
}
|
||||
}
|
||||
return code
|
||||
}
|
||||
|
||||
private fun funcRolRor(opcode: Opcode, call: PtBuiltinFunctionCall, resultRegister: Int): VmCodeChunk {
|
||||
val vmDt = codeGen.vmType(call.args[0].type)
|
||||
val code = VmCodeChunk()
|
||||
code += exprGen.translateExpression(call.args[0], resultRegister, -1)
|
||||
|
||||
@@ -84,7 +84,6 @@ class CodeGen(internal val program: PtProgram,
|
||||
is PtReturn -> translate(node)
|
||||
is PtJump -> translate(node)
|
||||
is PtWhen -> translate(node)
|
||||
is PtPipe -> expressionEval.translate(node, 0)
|
||||
is PtForLoop -> translate(node)
|
||||
is PtIfElse -> translate(node)
|
||||
is PtPostIncrDecr -> translate(node)
|
||||
|
||||
@@ -63,7 +63,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
is PtBuiltinFunctionCall -> code += codeGen.translateBuiltinFunc(expr, resultRegister)
|
||||
is PtFunctionCall -> code += translate(expr, resultRegister, resultFpRegister)
|
||||
is PtContainmentCheck -> code += translate(expr, resultRegister, resultFpRegister)
|
||||
is PtPipe -> code += translate(expr, resultRegister)
|
||||
is PtRange,
|
||||
is PtArray,
|
||||
is PtString -> throw AssemblyError("range/arrayliteral/string should no longer occur as expression")
|
||||
@@ -72,46 +71,6 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
return code
|
||||
}
|
||||
|
||||
internal fun translate(pipe: PtPipe, resultRegister: Int): VmCodeChunk {
|
||||
val segments = pipe.segments
|
||||
var valueDt = segments[0].type
|
||||
var valueReg = if(pipe.void) codeGen.vmRegisters.nextFree() else resultRegister
|
||||
|
||||
fun addImplicitArgToSegment(segment: PtExpression, sourceReg: Int, sourceDt: DataType): PtExpression {
|
||||
return when (segment) {
|
||||
is PtFunctionCall -> {
|
||||
val segWithArg = PtFunctionCall(segment.functionName, segment.void, segment.type, segment.position)
|
||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
||||
segWithArg.children.addAll(segment.args)
|
||||
segWithArg
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val segWithArg = PtBuiltinFunctionCall(segment.name, segment.void, segment.hasNoSideEffects, segment.type, segment.position)
|
||||
segWithArg.children.add(PtMachineRegister(sourceReg, sourceDt, segment.position))
|
||||
segWithArg.children.addAll(segment.args)
|
||||
segWithArg
|
||||
}
|
||||
else -> throw AssemblyError("weird segment type")
|
||||
}
|
||||
}
|
||||
|
||||
val code = VmCodeChunk()
|
||||
code += translateExpression(segments[0], valueReg, -1)
|
||||
for (segment in segments.subList(1, segments.size-1)) {
|
||||
val sourceReg = valueReg
|
||||
val sourceDt = valueDt
|
||||
if(segment.type!=valueDt) {
|
||||
valueDt = segment.type
|
||||
valueReg = codeGen.vmRegisters.nextFree()
|
||||
}
|
||||
val segmentWithImplicitArgument = addImplicitArgToSegment(segment, sourceReg, sourceDt)
|
||||
code += translateExpression(segmentWithImplicitArgument, valueReg, -1)
|
||||
}
|
||||
val segWithArg = addImplicitArgToSegment(segments.last(), valueReg, valueDt)
|
||||
code += translateExpression(segWithArg, resultRegister, -1)
|
||||
return code
|
||||
}
|
||||
|
||||
private fun translate(check: PtContainmentCheck, resultRegister: Int, resultFpRegister: Int): VmCodeChunk {
|
||||
val code = VmCodeChunk()
|
||||
code += translateExpression(check.element, resultRegister, -1) // load the element to check in resultRegister
|
||||
@@ -149,6 +108,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
val code = VmCodeChunk()
|
||||
val idxReg = codeGen.vmRegisters.nextFree()
|
||||
val arrayLocation = codeGen.allocations.get(arrayIx.variable.targetName)
|
||||
|
||||
if(arrayIx.variable.type==DataType.UWORD) {
|
||||
// indexing a pointer var instead of a real array or string
|
||||
if(eltSize!=1)
|
||||
throw AssemblyError("non-array var indexing requires bytes dt")
|
||||
if(arrayIx.index.type!=DataType.UBYTE)
|
||||
throw AssemblyError("non-array var indexing requires bytes index")
|
||||
code += translateExpression(arrayIx.index, idxReg, -1)
|
||||
code += VmCodeInstruction(Opcode.LOADIX, vmDt, reg1=resultRegister, reg2=idxReg, value = arrayLocation)
|
||||
return code
|
||||
}
|
||||
|
||||
if(arrayIx.index is PtNumber) {
|
||||
// optimized code when index is known - just calculate the memory address here
|
||||
val memOffset = (arrayIx.index as PtNumber).number.toInt() * eltSize
|
||||
@@ -253,18 +224,18 @@ internal class ExpressionGen(private val codeGen: CodeGen) {
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
when(cast.value.type) {
|
||||
code += when(cast.value.type) {
|
||||
DataType.UBYTE -> {
|
||||
code += VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
VmCodeInstruction(Opcode.FFROMUB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
code += VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
VmCodeInstruction(Opcode.FFROMSB, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
code += VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
VmCodeInstruction(Opcode.FFROMUW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
code += VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
VmCodeInstruction(Opcode.FFROMSW, VmDataType.FLOAT, reg1=actualResultReg, fpReg1 = actualResultFpReg)
|
||||
}
|
||||
else -> throw AssemblyError("weird cast value type")
|
||||
}
|
||||
|
||||
@@ -6,6 +6,7 @@ import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.UndefinedSymbolError
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.VarDeclType
|
||||
@@ -192,6 +193,21 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
modifications += IAstModification.ReplaceNode(expr, result, parent)
|
||||
}
|
||||
|
||||
if(leftconst==null && rightconst!=null && rightconst.number<0.0) {
|
||||
if (expr.operator == "-") {
|
||||
// X - -1 ---> X + 1
|
||||
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||
val plusExpr = BinaryExpression(expr.left, "+", posNumber, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||
}
|
||||
else if (expr.operator == "+") {
|
||||
// X + -1 ---> X - 1
|
||||
val posNumber = NumericLiteral.optimalNumeric(-rightconst.number, rightconst.position)
|
||||
val plusExpr = BinaryExpression(expr.left, "-", posNumber, expr.position)
|
||||
return listOf(IAstModification.ReplaceNode(expr, plusExpr, parent))
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
val leftBinExpr = expr.left as? BinaryExpression
|
||||
val rightBinExpr = expr.right as? BinaryExpression
|
||||
@@ -423,7 +439,7 @@ class ConstantFoldingOptimizer(private val program: Program) : AstWalker() {
|
||||
// both operators are the same.
|
||||
|
||||
// If associative, we can simply shuffle the const operands around to optimize.
|
||||
if(expr.operator in AssociativeOperators) {
|
||||
if(expr.operator in AssociativeOperators && maySwapOperandOrder(expr)) {
|
||||
return if(leftIsConst) {
|
||||
if(subleftIsConst)
|
||||
ShuffleOperands(expr, null, subExpr, subExpr.right, null, null, expr.left)
|
||||
|
||||
@@ -1,19 +1,28 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.maySwapOperandOrder
|
||||
import prog8.ast.statements.AnonymousScope
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.IfElse
|
||||
import prog8.ast.statements.Jump
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.AssociativeOperators
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.IntegerDatatypes
|
||||
import prog8.code.core.NumericDatatypes
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
import kotlin.math.pow
|
||||
|
||||
// TODO add more peephole expression optimizations? Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||
|
||||
class ExpressionSimplifier(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||
class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||
|
||||
@@ -80,12 +89,12 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null)
|
||||
if (leftVal != null && expr.operator in AssociativeOperators && rightVal == null && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// NonBinaryExpression <associativeoperator> BinaryExpression --> BinaryExpression <associativeoperator> NonBinaryExpression
|
||||
if (expr.operator in AssociativeOperators && expr.left !is BinaryExpression && expr.right is BinaryExpression) {
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target))
|
||||
if(parent !is Assignment || !(expr.left isSameAs parent.target) && maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
}
|
||||
|
||||
@@ -331,30 +340,6 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(pipeExpr: PipeExpression, parent: Node) = processPipe(pipeExpr, parent)
|
||||
override fun after(pipe: Pipe, parent: Node) = processPipe(pipe, parent)
|
||||
|
||||
private fun processPipe(pipe: IPipe, parent: Node): Iterable<IAstModification> {
|
||||
if(pipe.source.isSimple) {
|
||||
val segments = pipe.segments
|
||||
if(segments.size==1) {
|
||||
// replace the whole pipe with a normal function call
|
||||
val funcname = (segments[0] as IFunctionCall).target
|
||||
val call = if(pipe is Pipe)
|
||||
FunctionCallStatement(funcname, mutableListOf(pipe.source), true, pipe.position)
|
||||
else
|
||||
FunctionCallExpression(funcname, mutableListOf(pipe.source), pipe.position)
|
||||
return listOf(IAstModification.ReplaceNode(pipe as Node, call, parent))
|
||||
} else if(segments.size>1) {
|
||||
// replace source+firstsegment by firstsegment(source) call as the new source
|
||||
val firstSegment = segments.removeAt(0) as IFunctionCall
|
||||
val call = FunctionCallExpression(firstSegment.target, mutableListOf(pipe.source), pipe.position)
|
||||
return listOf(IAstModification.ReplaceNode(pipe.source, call, pipe as Node))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||
return when {
|
||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||
@@ -646,7 +631,7 @@ class ExpressionSimplifier(private val program: Program, private val errors: IEr
|
||||
}
|
||||
|
||||
private fun reorderAssociativeWithConstant(expr: BinaryExpression, leftVal: NumericLiteral?): BinExprWithConstants {
|
||||
if (expr.operator in AssociativeOperators && leftVal != null) {
|
||||
if (expr.operator in AssociativeOperators && leftVal != null && maySwapOperandOrder(expr)) {
|
||||
// swap left and right so that right is always the constant
|
||||
val tmp = expr.left
|
||||
expr.left = expr.right
|
||||
|
||||
@@ -60,8 +60,8 @@ fun Program.inlineSubroutines(): Int {
|
||||
return inliner.applyModifications()
|
||||
}
|
||||
|
||||
fun Program.simplifyExpressions(errors: IErrorReporter) : Int {
|
||||
val opti = ExpressionSimplifier(this, errors)
|
||||
fun Program.simplifyExpressions() : Int {
|
||||
val opti = ExpressionSimplifier(this)
|
||||
opti.visit(this)
|
||||
return opti.applyModifications()
|
||||
}
|
||||
|
||||
@@ -14,6 +14,8 @@ import prog8.code.core.InternalCompilerException
|
||||
private fun isEmptyReturn(stmt: Statement): Boolean = stmt is Return && stmt.value==null
|
||||
|
||||
|
||||
// inliner potentially enables *ONE LINED* subroutines, wihtout to be inlined.
|
||||
|
||||
class Inliner(val program: Program): AstWalker() {
|
||||
|
||||
class DetermineInlineSubs(val program: Program): IAstVisitor {
|
||||
@@ -142,8 +144,10 @@ class Inliner(val program: Program): AstWalker() {
|
||||
}
|
||||
|
||||
private fun makeFullyScoped(call: FunctionCallExpression) {
|
||||
val sub = call.target.targetSubroutine(program)!!
|
||||
val scopedName = IdentifierReference(sub.scopedName, call.target.position)
|
||||
val scopedArgs = makeScopedArgs(call.args)
|
||||
val scopedCall = FunctionCallExpression(call.target.copy(), scopedArgs.toMutableList(), call.position)
|
||||
val scopedCall = FunctionCallExpression(scopedName, scopedArgs.toMutableList(), call.position)
|
||||
modifications += IAstModification.ReplaceNode(call, scopedCall, call.parent)
|
||||
}
|
||||
|
||||
@@ -168,41 +172,70 @@ class Inliner(val program: Program): AstWalker() {
|
||||
|
||||
override fun after(gosub: GoSub, parent: Node): Iterable<IAstModification> {
|
||||
val sub = gosub.identifier.targetStatement(program) as? Subroutine
|
||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||
return if(sub==null)
|
||||
noModifications
|
||||
else
|
||||
possibleInlineFcallStmt(sub, gosub, parent)
|
||||
|
||||
}
|
||||
|
||||
private fun possibleInlineFcallStmt(sub: Subroutine, origNode: Node, parent: Node): Iterable<IAstModification> {
|
||||
if(sub.inline && sub.parameters.isEmpty()) {
|
||||
require(sub.statements.size == 1 || (sub.statements.size == 2 && isEmptyReturn(sub.statements[1])))
|
||||
return if(sub.isAsmSubroutine) {
|
||||
// simply insert the asm for the argument-less routine
|
||||
listOf(IAstModification.ReplaceNode(gosub, sub.statements.single().copy(), parent))
|
||||
listOf(IAstModification.ReplaceNode(origNode, sub.statements.single().copy(), parent))
|
||||
} else {
|
||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||
when (val toInline = sub.statements.first()) {
|
||||
is Return -> noModifications
|
||||
else -> listOf(IAstModification.ReplaceNode(gosub, toInline.copy(), parent))
|
||||
is Return -> {
|
||||
val fcall = toInline.value as? FunctionCallExpression
|
||||
if(fcall!=null) {
|
||||
// insert the function call expression as a void function call directly
|
||||
val call = FunctionCallStatement(fcall.target.copy(), fcall.args.map { it.copy() }.toMutableList(), true, fcall.position)
|
||||
listOf(IAstModification.ReplaceNode(origNode, call, parent))
|
||||
} else
|
||||
noModifications
|
||||
}
|
||||
else -> listOf(IAstModification.ReplaceNode(origNode, toInline.copy(), parent))
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallStatement.target.targetStatement(program) as? Subroutine
|
||||
return if(sub==null)
|
||||
noModifications
|
||||
else
|
||||
possibleInlineFcallStmt(sub, functionCallStatement, parent)
|
||||
}
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
val sub = functionCallExpr.target.targetStatement(program) as? Subroutine
|
||||
if(sub!=null && sub.inline && sub.parameters.isEmpty()) {
|
||||
require(sub.statements.size==1 || (sub.statements.size==2 && isEmptyReturn(sub.statements[1])))
|
||||
return if(sub.isAsmSubroutine) {
|
||||
// simply insert the asm for the argument-less routine
|
||||
listOf(IAstModification.ReplaceNode(functionCallStatement, sub.statements.single().copy(), parent))
|
||||
// cannot inline assembly directly in the Ast here as an Asm node is not an expression....
|
||||
noModifications
|
||||
} else {
|
||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||
when (val toInline = sub.statements.first()) {
|
||||
is Return -> noModifications
|
||||
else -> listOf(IAstModification.ReplaceNode(functionCallStatement, toInline.copy(), parent))
|
||||
is Return -> {
|
||||
// is an expression, so we have to have a Return here in the inlined sub
|
||||
// note that we don't have to process any args, because we online inline parameterless subroutines.
|
||||
if(toInline.value!=null)
|
||||
listOf(IAstModification.ReplaceNode(functionCallExpr, toInline.value!!.copy(), parent))
|
||||
else
|
||||
noModifications
|
||||
}
|
||||
else -> noModifications
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
// TODO also inline function call expressions, and remove it from the StatementOptimizer
|
||||
}
|
||||
|
||||
@@ -16,46 +16,6 @@ class StatementOptimizer(private val program: Program,
|
||||
private val compTarget: ICompilationTarget
|
||||
) : AstWalker() {
|
||||
|
||||
override fun before(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
// if the first instruction in the called subroutine is a return statement with a simple value (NOT being a parameter),
|
||||
// remove the jump altogeter and inline the returnvalue directly. (only if not part of a pipe expression)
|
||||
|
||||
fun scopePrefix(variable: IdentifierReference): IdentifierReference {
|
||||
val target = variable.targetStatement(program) as INamedStatement
|
||||
return IdentifierReference(target.scopedName, variable.position)
|
||||
}
|
||||
|
||||
val subroutine = functionCallExpr.target.targetSubroutine(program)
|
||||
if(subroutine!=null) {
|
||||
val first = subroutine.statements.asSequence().filterNot { it is VarDecl || it is Directive }.firstOrNull()
|
||||
if(first is Return && first.value?.isSimple==true && parent !is IPipe) {
|
||||
val copy = when(val orig = first.value!!) {
|
||||
is AddressOf -> {
|
||||
val scoped = scopePrefix(orig.identifier)
|
||||
AddressOf(scoped, orig.position)
|
||||
}
|
||||
is DirectMemoryRead -> {
|
||||
when(val expr = orig.addressExpression) {
|
||||
is NumericLiteral -> DirectMemoryRead(expr.copy(), orig.position)
|
||||
else -> return noModifications
|
||||
}
|
||||
}
|
||||
is IdentifierReference -> {
|
||||
if(orig.targetVarDecl(program)?.origin == VarDeclOrigin.SUBROUTINEPARAM)
|
||||
return noModifications
|
||||
else
|
||||
scopePrefix(orig)
|
||||
}
|
||||
is NumericLiteral -> orig.copy()
|
||||
is StringLiteral -> orig.copy()
|
||||
else -> return noModifications
|
||||
}
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, copy, parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.size==1) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
@@ -313,7 +273,7 @@ class StatementOptimizer(private val program: Program,
|
||||
val op1 = binExpr.operator
|
||||
val op2 = rExpr.operator
|
||||
|
||||
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators) {
|
||||
if(rExpr.left is NumericLiteral && op2 in AssociativeOperators && maySwapOperandOrder(binExpr)) {
|
||||
// associative operator, make sure the constant numeric value is second (right)
|
||||
return listOf(IAstModification.SwapOperands(rExpr))
|
||||
}
|
||||
@@ -352,7 +312,7 @@ class StatementOptimizer(private val program: Program,
|
||||
if(binExpr.operator in AssociativeOperators && binExpr.right isSameAs assignment.target) {
|
||||
// associative operator, swap the operands so that the assignment target is first (left)
|
||||
// unless the other operand is the same in which case we don't swap (endless loop!)
|
||||
if (!(binExpr.left isSameAs binExpr.right))
|
||||
if (!(binExpr.left isSameAs binExpr.right) && maySwapOperandOrder(binExpr))
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,10 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.expressions.PrefixExpression
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
@@ -28,23 +31,23 @@ class UnusedCodeRemover(private val program: Program,
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(breakStmt)
|
||||
return emptyList()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(jump: Jump, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(jump)
|
||||
return emptyList()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||
reportUnreachable(returnStmt)
|
||||
return emptyList()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallStatement.target.nameInSource.last() == "exit")
|
||||
reportUnreachable(functionCallStatement)
|
||||
return emptyList()
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun reportUnreachable(stmt: Statement) {
|
||||
@@ -236,7 +239,6 @@ class UnusedCodeRemover(private val program: Program,
|
||||
is PrefixExpression,
|
||||
is BinaryExpression,
|
||||
is TypecastExpression,
|
||||
is PipeExpression,
|
||||
is IFunctionCall -> { /* don't remove */ }
|
||||
else -> {
|
||||
if(assign1.value !is IFunctionCall)
|
||||
|
||||
@@ -38,9 +38,9 @@ dependencies {
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect"
|
||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli:0.3.4'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.1.0'
|
||||
testImplementation 'io.kotest:kotest-runner-junit5-jvm:5.2.3'
|
||||
}
|
||||
|
||||
configurations.all {
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
; Prog8 definitions for the Atari800XL
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
|
||||
atari {
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Atari 800XL
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore 128
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%option enable_floats
|
||||
%import floats_functions
|
||||
|
||||
@@ -1,8 +1,5 @@
|
||||
; Prog8 definitions for the Commodore-128
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
|
||||
c64 {
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
; Prog8 definitions for floating point handling on the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%option enable_floats
|
||||
%import floats_functions
|
||||
@@ -13,7 +9,6 @@ floats {
|
||||
const float PI = 3.141592653589793
|
||||
const float TWOPI = 6.283185307179586
|
||||
|
||||
float tempvar_swap_float ; used for some swap() operations
|
||||
|
||||
; ---- C64 basic and kernal ROM float constants and functions ----
|
||||
|
||||
|
||||
@@ -38,8 +38,12 @@ graphics {
|
||||
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
cx16.r0 = x1
|
||||
x1 = x2
|
||||
x2 = cx16.r0
|
||||
cx16.r0L = y1
|
||||
y1 = y2
|
||||
y2 = cx16.r0L
|
||||
}
|
||||
word @zp dx = (x2 as word)-x1
|
||||
word @zp dy = (y2 as word)-y1
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
; Prog8 definitions for the Commodore-64
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
c64 {
|
||||
&ubyte TIME_HI = $a0 ; software jiffy clock, hi byte
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the Commodore-64
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
; Number conversions routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
conv {
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Cx16 specific disk drive I/O routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import diskio
|
||||
|
||||
@@ -73,4 +71,100 @@ cx16diskio {
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
; replacement function that makes use of fast block read capability of the X16
|
||||
; use this in place of regular diskio.f_read()
|
||||
sub f_read(uword bufferpointer, uword num_bytes) -> uword {
|
||||
; -- read from the currently open file, up to the given number of bytes.
|
||||
; returns the actual number of bytes read. (checks for End-of-file and error conditions)
|
||||
if not diskio.iteration_in_progress or not num_bytes
|
||||
return 0
|
||||
|
||||
diskio.list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
if diskio.have_first_byte {
|
||||
diskio.have_first_byte=false
|
||||
@(bufferpointer) = diskio.first_byte
|
||||
bufferpointer++
|
||||
diskio.list_blocks++
|
||||
num_bytes--
|
||||
}
|
||||
|
||||
void c64.CHKIN(11) ; use #11 as input channel again
|
||||
|
||||
; commander X16 supports fast block-read via macptr() kernal call
|
||||
uword size
|
||||
while num_bytes {
|
||||
size = 255
|
||||
if num_bytes<size
|
||||
size = num_bytes
|
||||
size = cx16.macptr(lsb(size), bufferpointer)
|
||||
if_cs
|
||||
goto byte_read_loop ; macptr block read not supported, do fallback loop
|
||||
diskio.list_blocks += size
|
||||
bufferpointer += size
|
||||
num_bytes -= size
|
||||
if c64.READST() & $40 {
|
||||
diskio.f_close() ; end of file, close it
|
||||
break
|
||||
}
|
||||
}
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
|
||||
byte_read_loop: ; fallback if macptr() isn't supported on the device
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
lda bufferpointer+1
|
||||
sta m_in_buffer+2
|
||||
}}
|
||||
repeat num_bytes {
|
||||
%asm {{
|
||||
jsr c64.CHRIN
|
||||
sta cx16.r5
|
||||
m_in_buffer sta $ffff
|
||||
inc m_in_buffer+1
|
||||
bne +
|
||||
inc m_in_buffer+2
|
||||
+ inc diskio.list_blocks
|
||||
bne +
|
||||
inc diskio.list_blocks+1
|
||||
+
|
||||
}}
|
||||
|
||||
if cx16.r5==$0d { ; chance on I/o error status?
|
||||
diskio.first_byte = c64.READST()
|
||||
if diskio.first_byte & $40 {
|
||||
diskio.f_close() ; end of file, close it
|
||||
diskio.list_blocks-- ; don't count that last CHRIN read
|
||||
}
|
||||
if diskio.first_byte
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
}
|
||||
}
|
||||
return diskio.list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
; replacement function that makes use of fast block read capability of the X16
|
||||
; use this in place of regular diskio.f_read_all()
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
; -- read the full contents of the file, returns number of bytes read.
|
||||
if not diskio.iteration_in_progress
|
||||
return 0
|
||||
|
||||
uword total_read = 0
|
||||
if diskio.have_first_byte {
|
||||
diskio.have_first_byte=false
|
||||
@(bufferpointer) = diskio.first_byte
|
||||
bufferpointer++
|
||||
total_read = 1
|
||||
}
|
||||
|
||||
while not c64.READST() {
|
||||
uword size = cx16diskio.f_read(bufferpointer, 256)
|
||||
total_read += size
|
||||
bufferpointer += size
|
||||
}
|
||||
return total_read
|
||||
}
|
||||
|
||||
}
|
||||
@@ -1,8 +1,4 @@
|
||||
; Prog8 definitions for floating point handling on the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%option enable_floats
|
||||
%import floats_functions
|
||||
|
||||
@@ -394,8 +394,12 @@ _done
|
||||
; This code special-cases various quadrant loops to allow simple ++ and -- operations.
|
||||
if y1>y2 {
|
||||
; make sure dy is always positive to have only 4 instead of 8 special cases
|
||||
swap(x1, x2)
|
||||
swap(y1, y2)
|
||||
cx16.r0 = x1
|
||||
x1 = x2
|
||||
x2 = cx16.r0
|
||||
cx16.r0 = y1
|
||||
y1 = y2
|
||||
y2 = cx16.r0
|
||||
}
|
||||
word @zp dx = (x2 as word)-x1
|
||||
word @zp dy = (y2 as word)-y1
|
||||
|
||||
@@ -13,20 +13,20 @@ palette {
|
||||
cx16.vpoke(1, vera_palette_ptr, msb(color))
|
||||
}
|
||||
|
||||
sub set_rgb4(uword palette_bytes_ptr, uword num_colors) {
|
||||
; 2 bytes per color entry, the Vera uses this, but the R/GB bytes order is swapped
|
||||
sub set_rgb_be(uword palette_ptr, uword num_colors) {
|
||||
; 1 word per color entry, $0rgb in big endian format
|
||||
vera_palette_ptr = $fa00
|
||||
repeat num_colors {
|
||||
cx16.vpoke(1, vera_palette_ptr+1, @(palette_bytes_ptr))
|
||||
palette_bytes_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palette_bytes_ptr))
|
||||
palette_bytes_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr+1, @(palette_ptr))
|
||||
palette_ptr++
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palette_ptr))
|
||||
palette_ptr++
|
||||
vera_palette_ptr+=2
|
||||
}
|
||||
}
|
||||
|
||||
sub set_rgb(uword palette_words_ptr, uword num_colors) {
|
||||
; 1 word per color entry (in little endian format so $gb0r)
|
||||
; 1 word per color entry (in little endian format as layed out in video memory, so $gb0r)
|
||||
vera_palette_ptr = $fa00
|
||||
repeat num_colors*2 {
|
||||
cx16.vpoke(1, vera_palette_ptr, @(palette_words_ptr))
|
||||
|
||||
@@ -1,9 +1,5 @@
|
||||
; Prog8 definitions for the CommanderX16
|
||||
; Including memory registers, I/O registers, Basic and Kernal subroutines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
c64 {
|
||||
|
||||
@@ -367,7 +363,7 @@ romsub $fed5 = console_set_paging_message(uword msgptr @R0) clobbers(A,X,Y)
|
||||
romsub $fecf = entropy_get() -> ubyte @A, ubyte @X, ubyte @Y
|
||||
romsub $fecc = monitor() clobbers(A,X,Y)
|
||||
|
||||
romsub $ff44 = macptr() clobbers(A,X,Y)
|
||||
romsub $ff44 = macptr(ubyte length @A, uword buffer @XY) clobbers(A) -> ubyte @Pc, uword @XY
|
||||
romsub $ff47 = enter_basic(ubyte cold_or_warm @Pc) clobbers(A,X,Y)
|
||||
romsub $ff4d = clock_set_date_time(uword yearmonth @R0, uword dayhours @R1, uword minsecs @R2, ubyte jiffies @R3) clobbers(A, X, Y)
|
||||
romsub $ff50 = clock_get_date_time() clobbers(A, X, Y) -> uword @R0, uword @R1, uword @R2, ubyte @R3 ; result registers see clock_set_date_time()
|
||||
@@ -415,28 +411,28 @@ asmsub mouse_pos() -> ubyte @A {
|
||||
inline asmsub rombank(ubyte bank @A) {
|
||||
; -- set the rom banks
|
||||
%asm {{
|
||||
sta $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
||||
sta $01
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub rambank(ubyte bank @A) {
|
||||
; -- set the ram bank
|
||||
%asm {{
|
||||
sta $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
||||
sta $00
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub getrombank() -> ubyte @A {
|
||||
; -- get the current rom bank
|
||||
%asm {{
|
||||
lda $01 ; rom bank register (v39+, used to be cx16.d1prb $9f60 in v38)
|
||||
lda $01
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub getrambank() -> ubyte @A {
|
||||
; -- get the current ram bank
|
||||
%asm {{
|
||||
lda $00 ; ram bank register (v39+, used to be cx16.d1pra $9f61 in v38)
|
||||
lda $00
|
||||
}}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,8 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O and Screen routines for the CommanderX16
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; indent format: TABS, size=8
|
||||
|
||||
%import syslib
|
||||
%import conv
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; C64 and Cx16 disk drive I/O routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import textio
|
||||
%import string
|
||||
@@ -241,6 +239,7 @@ close_end:
|
||||
}
|
||||
|
||||
void c64.CHKIN(11) ; use #11 as input channel again
|
||||
|
||||
%asm {{
|
||||
lda bufferpointer
|
||||
sta m_in_buffer+1
|
||||
@@ -263,13 +262,15 @@ m_in_buffer sta $ffff
|
||||
|
||||
if cx16.r5==$0d { ; chance on I/o error status?
|
||||
first_byte = c64.READST()
|
||||
if first_byte & $40
|
||||
if first_byte & $40 {
|
||||
f_close() ; end of file, close it
|
||||
list_blocks-- ; don't count that last CHRIN read
|
||||
}
|
||||
if first_byte
|
||||
return list_blocks
|
||||
return list_blocks ; number of bytes read
|
||||
}
|
||||
}
|
||||
return list_blocks
|
||||
return list_blocks ; number of bytes read
|
||||
}
|
||||
|
||||
sub f_read_all(uword bufferpointer) -> uword {
|
||||
@@ -277,19 +278,20 @@ m_in_buffer sta $ffff
|
||||
if not iteration_in_progress
|
||||
return 0
|
||||
|
||||
list_blocks = 0 ; we reuse this variable for the total number of bytes read
|
||||
uword total_read = 0
|
||||
if have_first_byte {
|
||||
have_first_byte=false
|
||||
@(bufferpointer) = first_byte
|
||||
bufferpointer++
|
||||
list_blocks++
|
||||
total_read = 1
|
||||
}
|
||||
|
||||
while not c64.READST() {
|
||||
list_blocks += f_read(bufferpointer, 256)
|
||||
bufferpointer += 256
|
||||
uword size = f_read(bufferpointer, 256)
|
||||
total_read += size
|
||||
bufferpointer += size
|
||||
}
|
||||
return list_blocks
|
||||
return total_read
|
||||
}
|
||||
|
||||
asmsub f_readline(uword bufptr @AY) clobbers(X) -> ubyte @Y {
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
; Internal Math library routines - always included by the compiler
|
||||
; Generic machine independent 6502 code.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
; some more interesting routines can be found here:
|
||||
; http://6502org.wikidot.com/software-math
|
||||
; http://codebase64.org/doku.php?id=base:6502_6510_maths
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Internal Math library routines - always included by the compiler
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
math {
|
||||
%asminclude "library:math.asm"
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
; Generic machine independent 6502 code.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
prog8_lib {
|
||||
%asminclude "library:prog8_lib.asm"
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
; 0-terminated string manipulation routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
string {
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
; Number conversions routines.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
conv {
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Prog8 definitions for floating point handling on the VirtualMachine
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%option enable_floats
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Internal Math library routines - always included by the compiler
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
math {
|
||||
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Internal library routines - always included by the compiler
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import textio
|
||||
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
; 0-terminated string manipulation routines. For the Virtual Machine target.
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
|
||||
string {
|
||||
sub length(str st) -> ubyte {
|
||||
|
||||
@@ -1,7 +1,4 @@
|
||||
; Prog8 definitions for the Virtual Machine
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
;
|
||||
|
||||
sys {
|
||||
; ------- lowlevel system routines --------
|
||||
|
||||
@@ -1,6 +1,4 @@
|
||||
; Prog8 definitions for the Text I/O console routines for the Virtual Machine
|
||||
;
|
||||
; Written by Irmen de Jong (irmen@razorvine.net) - license: GNU GPL 3.0
|
||||
|
||||
%import conv
|
||||
|
||||
|
||||
@@ -1 +1 @@
|
||||
8.1
|
||||
8.2
|
||||
@@ -347,7 +347,7 @@ private fun optimizeAst(program: Program, compilerOptions: CompilationOptions, e
|
||||
remover.applyModifications()
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = program.simplifyExpressions(errors)
|
||||
val optsDone1 = program.simplifyExpressions()
|
||||
val optsDone2 = program.splitBinaryExpressions(compilerOptions)
|
||||
val optsDone3 = program.optimizeStatements(errors, functions, compTarget)
|
||||
val optsDone4 = program.inlineSubroutines()
|
||||
|
||||
@@ -33,7 +33,7 @@ internal class ErrorReporter: IErrorReporter {
|
||||
if(msg !in alreadyReportedMessages) {
|
||||
when(it.severity) {
|
||||
MessageSeverity.ERROR -> printer.print("\u001b[91mERROR\u001B[0m ") // bright red
|
||||
MessageSeverity.WARNING -> printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow
|
||||
MessageSeverity.WARNING -> printer.print("\u001b[93mWARN\u001B[0m ") // bright yellow
|
||||
}
|
||||
printer.println(msg)
|
||||
alreadyReportedMessages.add(msg)
|
||||
|
||||
@@ -50,7 +50,6 @@ class IntermediateAstMaker(val program: Program) {
|
||||
is InlineAssembly -> transform(statement)
|
||||
is Jump -> transform(statement)
|
||||
is Label -> transform(statement)
|
||||
is Pipe -> transform(statement)
|
||||
is PostIncrDecr -> transform(statement)
|
||||
is RepeatLoop -> transform(statement)
|
||||
is Return -> transform(statement)
|
||||
@@ -80,7 +79,6 @@ class IntermediateAstMaker(val program: Program) {
|
||||
is FunctionCallExpression -> transform(expr)
|
||||
is IdentifierReference -> transform(expr)
|
||||
is NumericLiteral -> transform(expr)
|
||||
is PipeExpression -> transform(expr)
|
||||
is PrefixExpression -> transform(expr)
|
||||
is RangeExpression -> transform(expr)
|
||||
is StringLiteral -> transform(expr)
|
||||
@@ -225,11 +223,7 @@ class IntermediateAstMaker(val program: Program) {
|
||||
private fun transform(srcCall: FunctionCallExpression): PtFunctionCall {
|
||||
val (target, _) = targetOf(srcCall.target)
|
||||
val type = srcCall.inferType(program).getOrElse {
|
||||
if((srcCall.parent as? Pipe)?.segments?.last() === srcCall)
|
||||
// for a pipe, the last segment is allowed to be a call to a function not returning anything.
|
||||
DataType.UNDEFINED
|
||||
else
|
||||
throw FatalAstException("unknown dt $srcCall")
|
||||
throw FatalAstException("unknown dt $srcCall")
|
||||
}
|
||||
val call = PtFunctionCall(target, type==DataType.UNDEFINED, type, srcCall.position)
|
||||
for (arg in srcCall.args)
|
||||
@@ -287,14 +281,6 @@ class IntermediateAstMaker(val program: Program) {
|
||||
private fun transform(label: Label): PtLabel =
|
||||
PtLabel(label.name, label.position)
|
||||
|
||||
private fun transform(srcPipe: Pipe): PtPipe {
|
||||
val pipe = PtPipe(DataType.UNDEFINED, true, srcPipe.position)
|
||||
pipe.add(transformExpression(srcPipe.source))
|
||||
for (segment in srcPipe.segments)
|
||||
pipe.add(transformExpression(segment))
|
||||
return pipe
|
||||
}
|
||||
|
||||
private fun transform(src: PostIncrDecr): PtPostIncrDecr {
|
||||
val post = PtPostIncrDecr(src.operator, src.position)
|
||||
post.add(transform(src.target))
|
||||
@@ -459,15 +445,6 @@ class IntermediateAstMaker(val program: Program) {
|
||||
private fun transform(number: NumericLiteral): PtNumber =
|
||||
PtNumber(number.type, number.number, number.position)
|
||||
|
||||
private fun transform(srcPipe: PipeExpression): PtPipe {
|
||||
val type = srcPipe.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val pipe = PtPipe(type, false, srcPipe.position)
|
||||
pipe.add(transformExpression(srcPipe.source))
|
||||
for (segment in srcPipe.segments)
|
||||
pipe.add(transformExpression(segment))
|
||||
return pipe
|
||||
}
|
||||
|
||||
private fun transform(srcPrefix: PrefixExpression): PtPrefix {
|
||||
val type = srcPrefix.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||
val prefix = PtPrefix(srcPrefix.operator, type, srcPrefix.position)
|
||||
|
||||
@@ -9,6 +9,7 @@ import prog8.ast.walk.IAstVisitor
|
||||
import prog8.code.core.*
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.compiler.InplaceModifyingBuiltinFunctions
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
@@ -298,11 +299,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
||||
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
||||
if(!subroutine.isAsmSubroutine && compilerOptions.compTarget.name!=VMTarget.NAME)
|
||||
subroutine.inline = false
|
||||
|
||||
if(subroutine.parent !is Block && subroutine.parent !is Subroutine)
|
||||
err("subroutines can only be defined in the scope of a block or within another subroutine")
|
||||
|
||||
@@ -507,7 +503,7 @@ internal class AstChecker(private val program: Program,
|
||||
if(constVal==null) {
|
||||
val sourceDatatype = assignment.value.inferType(program)
|
||||
if (sourceDatatype.isUnknown) {
|
||||
if (assignment.value !is FunctionCallExpression && assignment.value !is PipeExpression)
|
||||
if (assignment.value !is FunctionCallExpression)
|
||||
errors.err("assignment value is invalid or has no proper datatype, maybe forgot '&' (address-of)", assignment.value.position)
|
||||
} else {
|
||||
checkAssignmentCompatible(targetDatatype.getOr(DataType.UNDEFINED),
|
||||
@@ -868,13 +864,9 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
"and", "or", "xor" -> {
|
||||
// only integer numeric operands accepted, and if literal constants, only boolean values accepted (0 or 1)
|
||||
// only integer numeric operands accepted
|
||||
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
|
||||
errors.err("logical operator can only be used on boolean operands", expr.right.position)
|
||||
val constLeft = expr.left.constValue(program)
|
||||
val constRight = expr.right.constValue(program)
|
||||
if(constLeft!=null && constLeft.number.toInt() !in 0..1 || constRight!=null && constRight.number.toInt() !in 0..1)
|
||||
errors.err("const literal argument of logical operator must be boolean (0 or 1)", expr.position)
|
||||
}
|
||||
"&", "|", "^" -> {
|
||||
// only integer numeric operands accepted
|
||||
@@ -1013,7 +1005,7 @@ internal class AstChecker(private val program: Program,
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||
if(targetStatement!=null) {
|
||||
checkFunctionCall(targetStatement, functionCallStatement.args, functionCallStatement.position)
|
||||
checkUnusedReturnValues(functionCallStatement, targetStatement, program, errors)
|
||||
checkUnusedReturnValues(functionCallStatement, targetStatement, errors)
|
||||
}
|
||||
|
||||
val funcName = functionCallStatement.target.nameInSource
|
||||
@@ -1041,7 +1033,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
if(funcName[0] in arrayOf("rol", "ror", "rol2", "ror2", "swap", "sort", "reverse")) {
|
||||
if(funcName[0] in InplaceModifyingBuiltinFunctions) {
|
||||
// in-place modification, can't be done on literals
|
||||
if(functionCallStatement.args.any { it !is IdentifierReference && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
|
||||
errors.err("invalid argument to a in-place modifying function", functionCallStatement.args.first().position)
|
||||
@@ -1062,20 +1054,7 @@ internal class AstChecker(private val program: Program,
|
||||
errors.err("cannot use arguments when calling a label", position)
|
||||
|
||||
if(target is BuiltinFunctionPlaceholder) {
|
||||
if(target.name=="swap") {
|
||||
// swap() is a bit weird because this one is translated into an operations directly, instead of being a function call
|
||||
val dt1 = args[0].inferType(program)
|
||||
val dt2 = args[1].inferType(program)
|
||||
if (dt1 != dt2)
|
||||
errors.err("swap requires 2 args of identical type", position)
|
||||
else if (args[0].constValue(program) != null || args[1].constValue(program) != null)
|
||||
errors.err("swap requires 2 variables, not constant value(s)", position)
|
||||
else if(args[0] isSameAs args[1])
|
||||
errors.err("swap should have 2 different args", position)
|
||||
else if(!dt1.isNumeric)
|
||||
errors.err("swap requires args of numerical type", position)
|
||||
}
|
||||
else if(target.name=="all" || target.name=="any") {
|
||||
if(target.name=="all" || target.name=="any") {
|
||||
if((args[0] as? AddressOf)?.identifier?.targetVarDecl(program)?.datatype == DataType.STR
|
||||
|| args[0].inferType(program).getOr(DataType.STR) == DataType.STR) {
|
||||
errors.err("any/all on a string is useless (is always true unless the string is empty)", position)
|
||||
@@ -1117,33 +1096,6 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(pipe: PipeExpression) {
|
||||
process(pipe)
|
||||
super.visit(pipe)
|
||||
}
|
||||
|
||||
override fun visit(pipe: Pipe) {
|
||||
process(pipe)
|
||||
super.visit(pipe)
|
||||
}
|
||||
|
||||
private fun process(pipe: IPipe) {
|
||||
if(pipe.source in pipe.segments)
|
||||
throw InternalCompilerException("pipe source and segments should all be different nodes")
|
||||
if (pipe.segments.isEmpty())
|
||||
throw FatalAstException("pipe is missing one or more expressions")
|
||||
if(pipe.segments.any { it !is IFunctionCall })
|
||||
throw FatalAstException("pipe segments can only be function calls")
|
||||
|
||||
if(compilerOptions.compTarget !is VMTarget) {
|
||||
pipe.segments.forEach {
|
||||
it as IFunctionCall
|
||||
if (it.args.size > 0)
|
||||
errors.err("only unary functions supported in pipe expressions for now", it.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||
if(postIncrDecr.target.identifier != null) {
|
||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||
@@ -1176,8 +1128,8 @@ internal class AstChecker(private val program: Program,
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
val target = arrayIndexedExpression.arrayvar.targetStatement(program)
|
||||
if(target is VarDecl) {
|
||||
if(target.datatype !in IterableDatatypes)
|
||||
errors.err("indexing requires an iterable variable", arrayIndexedExpression.position)
|
||||
if(target.datatype !in IterableDatatypes && target.datatype!=DataType.UWORD)
|
||||
errors.err("indexing requires an iterable or address uword variable", arrayIndexedExpression.position)
|
||||
val arraysize = target.arraysize?.constIndex()
|
||||
if(arraysize!=null) {
|
||||
// check out of bounds
|
||||
@@ -1515,7 +1467,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, program: Program, errors: IErrorReporter) {
|
||||
internal fun checkUnusedReturnValues(call: FunctionCallStatement, target: Statement, errors: IErrorReporter) {
|
||||
if (!call.void) {
|
||||
// check for unused return values
|
||||
if (target is Subroutine && target.returntypes.isNotEmpty()) {
|
||||
|
||||
@@ -94,7 +94,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
||||
checker2.visit(this)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
val transforms = AstVariousTransforms(this)
|
||||
val transforms = AstOnetimeTransforms(this, options)
|
||||
transforms.visit(this)
|
||||
transforms.applyModifications()
|
||||
val lit2decl = LiteralsToAutoVars(this, options.compTarget, errors)
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IPipe
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
@@ -54,12 +53,16 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
if(decl.name in compTarget.machine.opcodeNames)
|
||||
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||
|
||||
val existing = decl.parent.definingScope.lookup(listOf(decl.name))
|
||||
if (existing != null && existing !== decl && existing is VarDecl) {
|
||||
if(existing.parent!==decl.parent)
|
||||
nameShadowWarning(decl.name, decl.position, existing)
|
||||
val existingInSameScope = decl.definingScope.lookup(listOf(decl.name))
|
||||
if(existingInSameScope!=null && existingInSameScope!==decl)
|
||||
nameError(decl.name, decl.position, existingInSameScope)
|
||||
|
||||
val existingOuter = decl.parent.definingScope.lookup(listOf(decl.name))
|
||||
if (existingOuter != null && existingOuter !== decl && existingOuter is VarDecl) {
|
||||
if(existingOuter.parent!==decl.parent)
|
||||
nameShadowWarning(decl.name, decl.position, existingOuter)
|
||||
else
|
||||
nameError(decl.name, decl.position, existing)
|
||||
nameError(decl.name, decl.position, existingOuter)
|
||||
}
|
||||
|
||||
if(decl.definingBlock.name==decl.name)
|
||||
@@ -84,9 +87,9 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
val existing = subroutine.lookup(listOf(subroutine.name))
|
||||
if (existing != null && existing !== subroutine) {
|
||||
if(existing.parent!==existing.parent)
|
||||
nameShadowWarning(subroutine.name, subroutine.position, existing)
|
||||
nameShadowWarning(subroutine.name, existing.position, subroutine)
|
||||
else
|
||||
nameError(subroutine.name, subroutine.position, existing)
|
||||
nameError(subroutine.name, existing.position, subroutine)
|
||||
}
|
||||
|
||||
// check that there are no local symbols (variables, labels, subs) that redefine the subroutine's parameters.
|
||||
@@ -147,32 +150,20 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) = visitFunctionCall(functionCallStatement)
|
||||
|
||||
private fun visitFunctionCall(call: IFunctionCall) {
|
||||
val isPartOfPipeSegments = (call.parent as? IPipe)?.segments?.contains(call as Node) == true
|
||||
val errormessageAboutArgs = if(isPartOfPipeSegments) "invalid number of arguments in piped call" else "invalid number of arguments"
|
||||
when (val target = call.target.targetStatement(program)) {
|
||||
is Subroutine -> {
|
||||
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
|
||||
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
|
||||
target.parameters.size - 1
|
||||
} else {
|
||||
target.parameters.size
|
||||
}
|
||||
val expectedNumberOfArgs: Int = target.parameters.size
|
||||
if(call.args.size != expectedNumberOfArgs) {
|
||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||
errors.err(errormessageAboutArgs, pos)
|
||||
errors.err("invalid number of arguments", pos)
|
||||
}
|
||||
}
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
// if the call is part of a Pipe, the number of arguments in the call should be 1 less than the number of parameters
|
||||
val expectedNumberOfArgs: Int = if(isPartOfPipeSegments) {
|
||||
func.parameters.size-1
|
||||
} else {
|
||||
func.parameters.size
|
||||
}
|
||||
val expectedNumberOfArgs: Int = func.parameters.size
|
||||
if(call.args.size != expectedNumberOfArgs) {
|
||||
val pos = (if(call.args.any()) call.args[0] else (call as Node)).position
|
||||
errors.err(errormessageAboutArgs, pos)
|
||||
errors.err("invalid number of arguments", pos)
|
||||
}
|
||||
if(func.name=="memory") {
|
||||
val name = call.args[0] as? StringLiteral
|
||||
|
||||
@@ -0,0 +1,62 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.CompilationOptions
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.VMTarget
|
||||
|
||||
|
||||
internal class AstOnetimeTransforms(private val program: Program, private val options: CompilationOptions) : AstWalker() {
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(parent !is VarDecl) {
|
||||
// TODO move this / remove this, and make the codegen better instead.
|
||||
// If the expression is pointervar[idx] where pointervar is uword and not a real array,
|
||||
// replace it by a @(pointervar+idx) expression.
|
||||
// Don't replace the initializer value in a vardecl - this will be moved to a separate
|
||||
// assignment statement soon in after(VarDecl)
|
||||
return replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression, parent)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun replacePointerVarIndexWithMemreadOrMemwrite(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(options.compTarget.name== VMTarget.NAME)
|
||||
return noModifications // vm codegen deals correctly with all cases
|
||||
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||
if(parent is AssignTarget) {
|
||||
val assignment = parent.parent as? Assignment
|
||||
if(assignment?.value is NumericLiteral || assignment?.value is IdentifierReference) {
|
||||
// ONLY for a constant assignment, or direct variable assignment, the codegen contains correct optimized code.
|
||||
return noModifications
|
||||
}
|
||||
// Other cases aren't covered correctly by the 6502 codegen, and there are a LOT of cases.
|
||||
// So rewrite assignment target pointervar[index] into @(pointervar+index)
|
||||
// (the 6502 codegen covers all cases correctly for a direct memory assignment).
|
||||
val indexer = arrayIndexedExpression.indexer
|
||||
val add: Expression =
|
||||
if(indexer.indexExpr.constValue(program)?.number==0.0)
|
||||
arrayIndexedExpression.arrayvar.copy()
|
||||
else
|
||||
BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||
val memwrite = DirectMemoryWrite(add, arrayIndexedExpression.position)
|
||||
val newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
return listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IPipe
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.SyntaxError
|
||||
@@ -8,7 +7,10 @@ import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.*
|
||||
import prog8.code.core.Encoding
|
||||
import prog8.code.core.ICompilationTarget
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.NumericDatatypes
|
||||
|
||||
|
||||
class AstPreprocessor(val program: Program, val errors: IErrorReporter, val compTarget: ICompilationTarget) : AstWalker() {
|
||||
@@ -111,29 +113,25 @@ class AstPreprocessor(val program: Program, val errors: IErrorReporter, val comp
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun before(pipe: Pipe, parent: Node): Iterable<IAstModification> {
|
||||
if(pipe.source is PipeExpression) {
|
||||
// correct Antlr parse tree quirk: turn nested pipe into single flat pipe
|
||||
val psrc = pipe.source as PipeExpression
|
||||
val newSource = psrc.source
|
||||
val newSegments = psrc.segments
|
||||
newSegments += pipe.segments.single()
|
||||
return listOf(IAstModification.ReplaceNode(pipe as Node, Pipe(newSource, newSegments, pipe.position), parent))
|
||||
} else if(pipe.source is IPipe)
|
||||
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
|
||||
return noModifications
|
||||
}
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
val symbolsInSub = subroutine.allDefinedSymbols
|
||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||
if(subroutine.asmAddress==null) {
|
||||
if(!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty()) {
|
||||
val vars = subroutine.statements.asSequence().filterIsInstance<VarDecl>().map { it.name }.toSet()
|
||||
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
|
||||
return subroutine.parameters
|
||||
.filter { it.name !in namesInSub }
|
||||
.map {
|
||||
val vardecl = VarDecl.fromParameter(it)
|
||||
IAstModification.InsertFirst(vardecl, subroutine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun before(pipeExpr: PipeExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(pipeExpr.source is PipeExpression) {
|
||||
// correct Antlr parse tree quirk; turn nested pipe into single flat pipe
|
||||
val psrc = pipeExpr.source as PipeExpression
|
||||
val newSource = psrc.source
|
||||
val newSegments = psrc.segments
|
||||
newSegments += pipeExpr.segments.single()
|
||||
return listOf(IAstModification.ReplaceNode(pipeExpr as Node, PipeExpression(newSource, newSegments, pipeExpr.position), parent))
|
||||
} else if(pipeExpr.source is IPipe)
|
||||
throw InternalCompilerException("pipe source should have been adjusted to be a normal expression")
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
@@ -1,66 +0,0 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.ArrayIndexedExpression
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.DirectMemoryRead
|
||||
import prog8.ast.statements.AssignTarget
|
||||
import prog8.ast.statements.DirectMemoryWrite
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.core.DataType
|
||||
|
||||
|
||||
internal class AstVariousTransforms(private val program: Program) : AstWalker() {
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
// For non-kernal subroutines and non-asm parameters:
|
||||
// inject subroutine params as local variables (if they're not there yet).
|
||||
val symbolsInSub = subroutine.allDefinedSymbols
|
||||
val namesInSub = symbolsInSub.map{ it.first }.toSet()
|
||||
if(subroutine.asmAddress==null) {
|
||||
if(!subroutine.isAsmSubroutine && subroutine.parameters.isNotEmpty()) {
|
||||
val vars = subroutine.statements.asSequence().filterIsInstance<VarDecl>().map { it.name }.toSet()
|
||||
if(!vars.containsAll(subroutine.parameters.map{it.name})) {
|
||||
return subroutine.parameters
|
||||
.filter { it.name !in namesInSub }
|
||||
.map {
|
||||
val vardecl = VarDecl.fromParameter(it)
|
||||
IAstModification.InsertFirst(vardecl, subroutine)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
return replacePointerVarIndexWithMemreadOrMemwrite(program, arrayIndexedExpression, parent)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
internal fun replacePointerVarIndexWithMemreadOrMemwrite(program: Program, arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
val arrayVar = arrayIndexedExpression.arrayvar.targetVarDecl(program)
|
||||
if(arrayVar!=null && arrayVar.datatype == DataType.UWORD) {
|
||||
// rewrite pointervar[index] into @(pointervar+index)
|
||||
val indexer = arrayIndexedExpression.indexer
|
||||
val add = BinaryExpression(arrayIndexedExpression.arrayvar.copy(), "+", indexer.indexExpr, arrayIndexedExpression.position)
|
||||
return if(parent is AssignTarget) {
|
||||
// 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 newtarget = AssignTarget(null, null, memwrite, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(parent, newtarget, parent.parent))
|
||||
} else {
|
||||
val memread = DirectMemoryRead(add, arrayIndexedExpression.position)
|
||||
listOf(IAstModification.ReplaceNode(arrayIndexedExpression, memread, parent))
|
||||
}
|
||||
}
|
||||
|
||||
return emptyList()
|
||||
}
|
||||
@@ -120,6 +120,12 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
}
|
||||
|
||||
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||
|
||||
// Most code generation targets only support subroutine inlining on asmsub subroutines
|
||||
// So we reset the flag here to be sure it doesn't cause problems down the line in the codegen.
|
||||
if(!subroutine.isAsmSubroutine && options.compTarget.name!=VMTarget.NAME)
|
||||
subroutine.inline = false
|
||||
|
||||
val mods = mutableListOf<IAstModification>()
|
||||
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernal routine.
|
||||
@@ -229,7 +235,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
// in that case: do *not* split it off but just keep it as it is (otherwise code size increases)
|
||||
// NOTE: do NOT move this to an earler ast transform phase (such as StatementReorderer or StatementOptimizer) - it WILL result in larger code.
|
||||
|
||||
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimizer for Vm target
|
||||
if(options.compTarget.name==VMTarget.NAME) // don't apply this optimization for Vm target
|
||||
return CondExprSimplificationResult(null, null, null, null)
|
||||
|
||||
var leftAssignment: Assignment? = null
|
||||
@@ -279,7 +285,7 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(options.compTarget.name!=VMTarget.NAME) {
|
||||
if(options.compTarget.name!=VMTarget.NAME) { // don't apply this optimization for Vm target
|
||||
val index = arrayIndexedExpression.indexer.indexExpr
|
||||
if (index !is NumericLiteral && index !is IdentifierReference) {
|
||||
// replace complex indexing expression with a temp variable to hold the computed index first
|
||||
@@ -343,5 +349,4 @@ internal class BeforeAsmAstChanger(val program: Program,
|
||||
)
|
||||
return modifications
|
||||
}
|
||||
|
||||
}
|
||||
@@ -18,7 +18,7 @@ internal class BeforeAsmTypecastCleaner(val program: Program,
|
||||
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||
// see if we can remove redundant typecasts (outside of expressions)
|
||||
// such as casting byte<->ubyte, word<->uword or even redundant casts (sourcetype = target type).
|
||||
// Also the special typecast of a reference type (str, array) to an UWORD will be changed into address-of,
|
||||
// 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)
|
||||
if (typecast.type in ByteDatatypes && sourceDt in ByteDatatypes
|
||||
|
||||
@@ -189,21 +189,15 @@ internal class StatementReorderer(val program: Program,
|
||||
return modifications + parameterChanges + varsChanges
|
||||
}
|
||||
|
||||
override fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> {
|
||||
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> {
|
||||
|
||||
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||
// (this should be done by the ExpressionSimplifier when optimizing is enabled,
|
||||
// but the current assembly code generator for IF statements now also depends on it, so we do it here regardless of optimization.)
|
||||
if (expr.left.constValue(program) != null && expr.operator in AssociativeOperators && expr.right.constValue(program) == null)
|
||||
if (expr.left.constValue(program) != null
|
||||
&& expr.operator in AssociativeOperators
|
||||
&& expr.right.constValue(program) == null
|
||||
&& maySwapOperandOrder(expr))
|
||||
return listOf(IAstModification.SwapOperands(expr))
|
||||
|
||||
// when using a simple bit shift and assigning it to a variable of a different type,
|
||||
@@ -254,12 +248,12 @@ internal class StatementReorderer(val program: Program,
|
||||
}
|
||||
}
|
||||
else if(expr.operator in LogicalOperators) {
|
||||
// make sure that logical expressions like "var and other-logical-expression
|
||||
// is rewritten as "var!=0 and other-logical-expression", to avoid bitwise boolean and
|
||||
// generating the wrong results later
|
||||
|
||||
fun wrapped(expr: Expression): Expression =
|
||||
BinaryExpression(expr, "!=", NumericLiteral(DataType.UBYTE, 0.0, expr.position), expr.position)
|
||||
fun wrapped(expr: Expression): Expression {
|
||||
return if(expr.inferType(program).isBytes)
|
||||
expr
|
||||
else
|
||||
FunctionCallExpression(IdentifierReference(listOf("boolean"), expr.position), mutableListOf(expr), expr.position)
|
||||
}
|
||||
|
||||
fun isLogicalExpr(expr: Expression?): Boolean {
|
||||
if(expr is BinaryExpression && expr.operator in (LogicalOperators + ComparisonOperators))
|
||||
@@ -331,7 +325,7 @@ internal class StatementReorderer(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
if(binExpr.operator in AssociativeOperators) {
|
||||
if(binExpr.operator in AssociativeOperators && maySwapOperandOrder(binExpr)) {
|
||||
if (binExpr.right isSameAs assignment.target) {
|
||||
// A = v <associative-operator> A ==> A = A <associative-operator> v
|
||||
return listOf(IAstModification.SwapOperands(binExpr))
|
||||
@@ -428,7 +422,7 @@ internal class StatementReorderer(val program: Program,
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
val function = functionCallStatement.target.targetStatement(program)
|
||||
?: throw FatalAstException("no target for $functionCallStatement")
|
||||
checkUnusedReturnValues(functionCallStatement, function, program, errors)
|
||||
checkUnusedReturnValues(functionCallStatement, function, errors)
|
||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||
val nextAssign = assignment.nextSibling() as? Assignment
|
||||
if(nextAssign!=null && nextAssign.target.isSameAs(assignment.target, program)) {
|
||||
if(nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates
|
||||
if(!nextAssign.isAugmentable && nextAssign.value isSameAs assignment.value && assignment.value !is IFunctionCall) // don't remove function calls even when they're duplicates
|
||||
return listOf(IAstModification.Remove(assignment, parent as IStatementContainer))
|
||||
}
|
||||
|
||||
@@ -233,5 +233,14 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
||||
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||
return tryReplaceCallWithGosub(functionCallStatement, parent, program, options)
|
||||
}
|
||||
|
||||
override fun after(functionCallExpr: FunctionCallExpression, parent: Node): Iterable<IAstModification> {
|
||||
if(functionCallExpr.target.nameInSource==listOf("boolean")) {
|
||||
if(functionCallExpr.args[0].inferType(program).isBytes) {
|
||||
return listOf(IAstModification.ReplaceNode(functionCallExpr, functionCallExpr.args[0], parent))
|
||||
}
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,13 +1,9 @@
|
||||
package prog8.compiler.astprocessing
|
||||
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IPipe
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.FunctionCallExpression
|
||||
import prog8.ast.expressions.PipeExpression
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.IAstVisitor
|
||||
@@ -15,7 +11,6 @@ import prog8.code.core.DataType
|
||||
import prog8.code.core.IErrorReporter
|
||||
import prog8.code.core.Position
|
||||
import prog8.compiler.BuiltinFunctions
|
||||
import prog8.compiler.builtinFunctionReturnType
|
||||
|
||||
internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorReporter) : IAstVisitor {
|
||||
|
||||
@@ -54,16 +49,10 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
||||
return Pair("argument ${firstUnknownDt+1} invalid argument type", call.args[firstUnknownDt].position)
|
||||
val argtypes = argITypes.map { it.getOr(DataType.UNDEFINED) }
|
||||
val target = call.target.targetStatement(program)
|
||||
val isPartOfPipeSegments = (call.parent as? IPipe)?.segments?.contains(call as Node) == true
|
||||
val errormessageAboutArgs = if(isPartOfPipeSegments) "invalid number of arguments in piped call" else "invalid number of arguments"
|
||||
if (target is Subroutine) {
|
||||
val consideredParamTypes: List<DataType> = if(isPartOfPipeSegments) {
|
||||
target.parameters.drop(1).map { it.type } // skip first one (the implicit first arg), this is checked elsewhere
|
||||
} else {
|
||||
target.parameters.map { it.type }
|
||||
}
|
||||
val consideredParamTypes: List<DataType> = target.parameters.map { it.type }
|
||||
if(argtypes.size != consideredParamTypes.size)
|
||||
return Pair(errormessageAboutArgs, call.position)
|
||||
return Pair("invalid number of arguments", call.position)
|
||||
val mismatch = argtypes.zip(consideredParamTypes).indexOfFirst { !argTypeCompatible(it.first, it.second) }
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch].toString()
|
||||
@@ -90,13 +79,9 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
||||
}
|
||||
else if (target is BuiltinFunctionPlaceholder) {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
val consideredParamTypes: List<Array<DataType>> = if(isPartOfPipeSegments) {
|
||||
func.parameters.drop(1).map { it.possibleDatatypes } // skip first one (the implicit first arg), this is checked elsewhere
|
||||
} else {
|
||||
func.parameters.map { it.possibleDatatypes }
|
||||
}
|
||||
val consideredParamTypes: List<Array<DataType>> = func.parameters.map { it.possibleDatatypes }
|
||||
if(argtypes.size != consideredParamTypes.size)
|
||||
return Pair(errormessageAboutArgs, call.position)
|
||||
return Pair("invalid number of arguments", call.position)
|
||||
argtypes.zip(consideredParamTypes).forEachIndexed { index, pair ->
|
||||
val anyCompatible = pair.second.any { argTypeCompatible(pair.first, it) }
|
||||
if (!anyCompatible) {
|
||||
@@ -115,88 +100,4 @@ internal class VerifyFunctionArgTypes(val program: Program, val errors: IErrorRe
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(pipe: PipeExpression) {
|
||||
processPipe(pipe.source, pipe.segments)
|
||||
if(errors.noErrors()) {
|
||||
val last = (pipe.segments.last() as IFunctionCall).target
|
||||
when (val target = last.targetStatement(program)!!) {
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
if (BuiltinFunctions.getValue(target.name).returnType==null)
|
||||
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||
}
|
||||
is Subroutine -> {
|
||||
if (target.returntypes.isEmpty())
|
||||
errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||
else if (target.returntypes.size != 1)
|
||||
errors.err("invalid pipe expression; last term doesn't return a single value", last.position)
|
||||
}
|
||||
else -> errors.err("invalid pipe expression; last term doesn't return a value", last.position)
|
||||
}
|
||||
super.visit(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(pipe: Pipe) {
|
||||
processPipe(pipe.source, pipe.segments)
|
||||
if(errors.noErrors()) {
|
||||
super.visit(pipe)
|
||||
}
|
||||
}
|
||||
|
||||
private fun processPipe(source: Expression, segments: List<Expression>) {
|
||||
|
||||
val sourceArg = (source as? IFunctionCall)?.args?.firstOrNull()
|
||||
if(sourceArg!=null && segments.any { (it as IFunctionCall).args.firstOrNull() === sourceArg})
|
||||
throw FatalAstException("some pipe segment first arg is replicated from the source functioncall arg")
|
||||
|
||||
// invalid size and other issues will be handled by the ast checker later.
|
||||
var valueDt = source.inferType(program).getOrElse {
|
||||
throw FatalAstException("invalid dt")
|
||||
}
|
||||
|
||||
for(funccall in segments) {
|
||||
val target = (funccall as IFunctionCall).target.targetStatement(program)
|
||||
if(target!=null) {
|
||||
when (target) {
|
||||
is BuiltinFunctionPlaceholder -> {
|
||||
val func = BuiltinFunctions.getValue(target.name)
|
||||
if(func.parameters.isEmpty())
|
||||
errors.err("function must have at least one parameter", funccall.position)
|
||||
else if(func.returnType==null && funccall !== segments.last())
|
||||
errors.err("function must return a single value", funccall.position)
|
||||
|
||||
val paramDts = func.parameters.firstOrNull()?.possibleDatatypes
|
||||
if(paramDts!=null && !paramDts.any { valueDt isAssignableTo it })
|
||||
errors.err("pipe value datatype $valueDt incompatible with function argument ${paramDts.toList()}", funccall.position)
|
||||
|
||||
if(errors.noErrors()) {
|
||||
valueDt = builtinFunctionReturnType(func.name).getOrElse { DataType.UNDEFINED }
|
||||
}
|
||||
}
|
||||
is Subroutine -> {
|
||||
if(target.parameters.isEmpty())
|
||||
errors.err("function must have at least one parameter", funccall.position)
|
||||
else if(target.returntypes.size!=1 && funccall !== segments.last())
|
||||
errors.err("function must return a single value", funccall.position)
|
||||
|
||||
val paramDt = target.parameters.firstOrNull()?.type
|
||||
if(paramDt!=null && !(valueDt isAssignableTo paramDt))
|
||||
errors.err("pipe value datatype $valueDt incompatible with function argument $paramDt", funccall.position)
|
||||
|
||||
if(target.returntypes.isNotEmpty())
|
||||
valueDt = target.returntypes.single()
|
||||
}
|
||||
is VarDecl -> {
|
||||
if(!(valueDt isAssignableTo target.datatype))
|
||||
errors.err("final pipe value datatype can't be stored in pipe ending variable", funccall.position)
|
||||
}
|
||||
else -> {
|
||||
throw FatalAstException("weird function")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -6,10 +6,15 @@ import io.kotest.matchers.doubles.plusOrMinus
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.InternalCompilerException
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.toHex
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.cbm.Mflpt5
|
||||
import prog8.compiler.printProgram
|
||||
import prog8tests.helpers.ErrorReporterForTests
|
||||
import prog8tests.helpers.compileText
|
||||
|
||||
@@ -180,4 +185,27 @@ class TestNumbers: FunSpec({
|
||||
"""
|
||||
compileText(C64Target(), true, src, writeAssembly = false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("signed negative numbers cast to unsigned allowed") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
uword uw1 = -32768
|
||||
uword uw = -1
|
||||
ubyte ub = -1
|
||||
uw = -2 as uword
|
||||
ub = -2 as ubyte
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||
val statements = result.program.entrypoint.statements
|
||||
statements.size shouldBe 8
|
||||
printProgram(result.program)
|
||||
(statements[1] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 32768.0, Position.DUMMY)
|
||||
(statements[3] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65535.0, Position.DUMMY)
|
||||
(statements[5] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 255.0, Position.DUMMY)
|
||||
(statements[6] as Assignment).value shouldBe NumericLiteral(DataType.UWORD, 65534.0, Position.DUMMY)
|
||||
(statements[7] as Assignment).value shouldBe NumericLiteral(DataType.UBYTE, 254.0, Position.DUMMY)
|
||||
}
|
||||
})
|
||||
@@ -152,4 +152,35 @@ class TestNumericLiteral: FunSpec({
|
||||
(NumericLiteral(DataType.FLOAT, 100.0, dummyPos) <= NumericLiteral(DataType.FLOAT, 99.9, dummyPos)) shouldBe false
|
||||
}
|
||||
|
||||
test("optimalInteger") {
|
||||
NumericLiteral.optimalInteger(10, Position.DUMMY).type shouldBe DataType.UBYTE
|
||||
NumericLiteral.optimalInteger(10, Position.DUMMY).number shouldBe 10.0
|
||||
NumericLiteral.optimalInteger(-10, Position.DUMMY).type shouldBe DataType.BYTE
|
||||
NumericLiteral.optimalInteger(-10, Position.DUMMY).number shouldBe -10.0
|
||||
NumericLiteral.optimalInteger(1000, Position.DUMMY).type shouldBe DataType.UWORD
|
||||
NumericLiteral.optimalInteger(-1000, Position.DUMMY).number shouldBe -1000.0
|
||||
NumericLiteral.optimalInteger(1000u, Position.DUMMY).type shouldBe DataType.UWORD
|
||||
NumericLiteral.optimalInteger(1000u, Position.DUMMY).number shouldBe 1000.0
|
||||
}
|
||||
|
||||
test("optimalNumeric") {
|
||||
NumericLiteral.optimalNumeric(10, Position.DUMMY).type shouldBe DataType.UBYTE
|
||||
NumericLiteral.optimalNumeric(10, Position.DUMMY).number shouldBe 10.0
|
||||
NumericLiteral.optimalNumeric(-10, Position.DUMMY).type shouldBe DataType.BYTE
|
||||
NumericLiteral.optimalNumeric(-10, Position.DUMMY).number shouldBe -10.0
|
||||
NumericLiteral.optimalNumeric(1000, Position.DUMMY).type shouldBe DataType.UWORD
|
||||
NumericLiteral.optimalNumeric(1000, Position.DUMMY).number shouldBe 1000.0
|
||||
NumericLiteral.optimalNumeric(-1000, Position.DUMMY).type shouldBe DataType.WORD
|
||||
NumericLiteral.optimalNumeric(-1000, Position.DUMMY).number shouldBe -1000.0
|
||||
NumericLiteral.optimalNumeric(1.123, Position.DUMMY).type shouldBe DataType.FLOAT
|
||||
NumericLiteral.optimalNumeric(1.123, Position.DUMMY).number shouldBe 1.123
|
||||
NumericLiteral.optimalNumeric(1.0, Position.DUMMY).type shouldBe DataType.UBYTE
|
||||
NumericLiteral.optimalNumeric(1.0, Position.DUMMY).number shouldBe 1.0
|
||||
NumericLiteral.optimalNumeric(-1.0, Position.DUMMY).type shouldBe DataType.BYTE
|
||||
NumericLiteral.optimalNumeric(-1.0, Position.DUMMY).number shouldBe -1.0
|
||||
NumericLiteral.optimalNumeric(1234.0, Position.DUMMY).type shouldBe DataType.UWORD
|
||||
NumericLiteral.optimalNumeric(1234.0, Position.DUMMY).number shouldBe 1234.0
|
||||
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).type shouldBe DataType.WORD
|
||||
NumericLiteral.optimalNumeric(-1234.0, Position.DUMMY).number shouldBe -1234.0
|
||||
}
|
||||
})
|
||||
@@ -253,7 +253,7 @@ class TestOptimization: FunSpec({
|
||||
(initY2.value as NumericLiteral).number shouldBe 11.0
|
||||
}
|
||||
|
||||
test("not-typecasted assignment from ubyte logical expression to uword var") {
|
||||
test("not-typecasted assignment from ubyte logical expression to uword var should be auto upcasted") {
|
||||
val src = """
|
||||
main {
|
||||
sub start() {
|
||||
@@ -266,10 +266,11 @@ class TestOptimization: FunSpec({
|
||||
val result = compileText(C64Target(), false, src, writeAssembly = false)!!
|
||||
|
||||
val wwAssign = result.program.entrypoint.statements.last() as Assignment
|
||||
val expr = wwAssign.value as BinaryExpression
|
||||
val expr = wwAssign.value as TypecastExpression
|
||||
expr.type shouldBe DataType.UWORD
|
||||
|
||||
wwAssign.target.identifier?.nameInSource shouldBe listOf("ww")
|
||||
expr.inferType(result.program) istype DataType.UWORD shouldBe true
|
||||
expr.expression.inferType(result.program) istype DataType.UBYTE shouldBe true
|
||||
}
|
||||
|
||||
test("intermediate assignment steps have correct types for codegen phase (BeforeAsmGenerationAstChanger)") {
|
||||
@@ -565,17 +566,31 @@ class TestOptimization: FunSpec({
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared z1 = - 1
|
||||
ubyte @shared z1 = - 200
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=true, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "type of value BYTE doesn't match target UBYTE"
|
||||
errors.errors[0] shouldContain "type of value WORD doesn't match target UBYTE"
|
||||
errors.errors[1] shouldContain "out of range"
|
||||
}
|
||||
|
||||
test("out of range cast should give error") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
ubyte @shared z1 = - 200 as ubyte
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors = errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "can't cast"
|
||||
}
|
||||
|
||||
test("test augmented expression asmgen") {
|
||||
val src = """
|
||||
main {
|
||||
@@ -757,4 +772,19 @@ class TestOptimization: FunSpec({
|
||||
val ifStmt = stmts[4] as IfElse
|
||||
ifStmt.condition shouldBe instanceOf<BinaryExpression>()
|
||||
}
|
||||
|
||||
test("pointer indexing inside other expression ok") {
|
||||
val src="""
|
||||
main{
|
||||
sub start () {
|
||||
uword eRef
|
||||
if eRef[3] and 10 {
|
||||
return
|
||||
}
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize=true, src, writeAssembly=false)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 3
|
||||
}
|
||||
})
|
||||
@@ -1,512 +0,0 @@
|
||||
package prog8tests
|
||||
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Pipe
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
import prog8.code.core.SourceCode
|
||||
import prog8.code.target.C64Target
|
||||
import prog8.code.target.VMTarget
|
||||
import prog8.compiler.astprocessing.AstPreprocessor
|
||||
import prog8.parser.Prog8Parser.parseModule
|
||||
import prog8tests.helpers.*
|
||||
|
||||
|
||||
class TestPipes: FunSpec({
|
||||
|
||||
test("pipe expression parse tree after preprocessing") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
uword xx = 9999 |> func1() |> func2()
|
||||
|> func1() |> func2()
|
||||
|> func1()
|
||||
}
|
||||
sub func1(uword arg) -> uword {
|
||||
return arg+1111
|
||||
}
|
||||
sub func2(uword arg) -> uword {
|
||||
return arg+2222
|
||||
}
|
||||
}"""
|
||||
val src = SourceCode.Text(text)
|
||||
val module = parseModule(src)
|
||||
val errors = ErrorReporterForTests()
|
||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
program.addModule(module)
|
||||
val preprocess = AstPreprocessor(program, errors, C64Target())
|
||||
preprocess.visit(program)
|
||||
errors.errors.size shouldBe 0
|
||||
preprocess.applyModifications()
|
||||
|
||||
program.entrypoint.statements.size shouldBe 1
|
||||
val pipe = (program.entrypoint.statements.single() as VarDecl).value as PipeExpression
|
||||
pipe.source shouldBe NumericLiteral(DataType.UWORD, 9999.0, Position.DUMMY)
|
||||
pipe.segments.size shouldBe 5
|
||||
var call = pipe.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func1")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func2")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[2] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func1")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[3] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func2")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[4] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func1")
|
||||
call.args.size shouldBe 0
|
||||
}
|
||||
|
||||
test("pipe statement parse tree after preprocessing") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
9999 |> func1() |> func2()
|
||||
|> func1() |> func2()
|
||||
|> func3()
|
||||
}
|
||||
sub func1(uword arg) -> uword {
|
||||
return arg+1111
|
||||
}
|
||||
sub func2(uword arg) -> uword {
|
||||
return arg+2222
|
||||
}
|
||||
sub func3(uword arg) {
|
||||
; nothing
|
||||
}
|
||||
}"""
|
||||
val src = SourceCode.Text(text)
|
||||
val module = parseModule(src)
|
||||
val errors = ErrorReporterForTests()
|
||||
val program = Program("test", DummyFunctions, DummyMemsizer, DummyStringEncoder)
|
||||
program.addModule(module)
|
||||
val preprocess = AstPreprocessor(program, errors, C64Target())
|
||||
preprocess.visit(program)
|
||||
errors.errors.size shouldBe 0
|
||||
preprocess.applyModifications()
|
||||
|
||||
program.entrypoint.statements.size shouldBe 1
|
||||
val pipe = program.entrypoint.statements.single() as Pipe
|
||||
pipe.source shouldBe NumericLiteral(DataType.UWORD, 9999.0, Position.DUMMY)
|
||||
pipe.segments.size shouldBe 5
|
||||
var call = pipe.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func1")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func2")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[2] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func1")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[3] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func2")
|
||||
call.args.size shouldBe 0
|
||||
call = pipe.segments[4] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("func3")
|
||||
call.args.size shouldBe 0
|
||||
}
|
||||
|
||||
test("correct pipe statements (no opt)") {
|
||||
val text = """
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
1.234 |> addfloat()
|
||||
|> floats.print_f()
|
||||
|
||||
startvalue(99) |> addword()
|
||||
|> txt.print_uw()
|
||||
|
||||
9999 |> abs() |> txt.print_uw()
|
||||
9999 |> txt.print_uw()
|
||||
99 |> abs() |> lsb() |> txt.print_ub()
|
||||
99 |> txt.print_ub()
|
||||
}
|
||||
|
||||
sub startvalue(ubyte arg) -> uword {
|
||||
return arg+9999
|
||||
}
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize = false, text, writeAssembly = true)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
val pipef = stmts[0] as Pipe
|
||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
||||
pipef.segments.size shouldBe 2
|
||||
var call = pipef.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addfloat")
|
||||
call = pipef.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("floats", "print_f")
|
||||
|
||||
val pipew = stmts[1] as Pipe
|
||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
||||
pipew.segments.size shouldBe 2
|
||||
call = pipew.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addword")
|
||||
call = pipew.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||
|
||||
stmts[2] shouldBe instanceOf<Pipe>()
|
||||
stmts[3] shouldBe instanceOf<Pipe>()
|
||||
stmts[4] shouldBe instanceOf<Pipe>()
|
||||
stmts[5] shouldBe instanceOf<Pipe>()
|
||||
}
|
||||
|
||||
test("correct pipe statements (with opt)") {
|
||||
val text = """
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
1.234 |> addfloat()
|
||||
|> floats.print_f()
|
||||
|
||||
startvalue(99) |> addword()
|
||||
|> txt.print_uw()
|
||||
|
||||
; these should be optimized into just the function calls:
|
||||
9999 |> abs() |> txt.print_uw()
|
||||
9999 |> txt.print_uw()
|
||||
99 |> abs() |> txt.print_ub()
|
||||
99 |> txt.print_ub()
|
||||
}
|
||||
|
||||
sub startvalue(ubyte arg) -> uword {
|
||||
return arg+9999
|
||||
}
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize = true, text, writeAssembly = true)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
val pipef = stmts[0] as Pipe
|
||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
||||
(pipef.source as IFunctionCall).target.nameInSource shouldBe listOf("addfloat")
|
||||
pipef.segments.size shouldBe 1
|
||||
val callf = pipef.segments[0] as IFunctionCall
|
||||
callf.target.nameInSource shouldBe listOf("floats", "print_f")
|
||||
|
||||
val pipew = stmts[1] as Pipe
|
||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
||||
(pipew.source as IFunctionCall).target.nameInSource shouldBe listOf("startvalue")
|
||||
pipew.segments.size shouldBe 2
|
||||
val callw = pipew.segments[1] as IFunctionCall
|
||||
callw.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||
|
||||
var stmt = stmts[2] as FunctionCallStatement
|
||||
stmt.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||
stmt = stmts[3] as FunctionCallStatement
|
||||
stmt.target.nameInSource shouldBe listOf("txt", "print_uw")
|
||||
stmt = stmts[4] as FunctionCallStatement
|
||||
stmt.target.nameInSource shouldBe listOf("txt", "print_ub")
|
||||
stmt = stmts[5] as FunctionCallStatement
|
||||
stmt.target.nameInSource shouldBe listOf("txt", "print_ub")
|
||||
}
|
||||
|
||||
test("incorrect type in pipe statement") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
|
||||
1.234 |> addfloat()
|
||||
|> addword() |> addword()
|
||||
}
|
||||
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "incompatible"
|
||||
}
|
||||
|
||||
test("correct pipe expressions (no opt)") {
|
||||
val text = """
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 1.234 |> addfloat()
|
||||
|> addfloat()
|
||||
|
||||
uword @shared ww = startvalue(99) |> addword()
|
||||
|> addword()
|
||||
|
||||
ubyte @shared cc = 30 |> abs() |> sqrt16()
|
||||
cc = cc |> abs() |> sqrt16()
|
||||
}
|
||||
|
||||
sub startvalue(ubyte arg) -> uword {
|
||||
return arg+9999
|
||||
}
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}"""
|
||||
val result = compileText(C64Target(), optimize = false, text, writeAssembly = true)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 8
|
||||
val assignf = stmts[1] as Assignment
|
||||
val pipef = assignf.value as PipeExpression
|
||||
pipef.source shouldBe instanceOf<NumericLiteral>()
|
||||
pipef.segments.size shouldBe 2
|
||||
var call = pipef.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addfloat")
|
||||
call = pipef.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addfloat")
|
||||
|
||||
|
||||
val assignw = stmts[3] as Assignment
|
||||
val pipew = assignw.value as PipeExpression
|
||||
pipew.source shouldBe instanceOf<IFunctionCall>()
|
||||
pipew.segments.size shouldBe 2
|
||||
call = pipew.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addword")
|
||||
call = pipew.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("addword")
|
||||
|
||||
var assigncc = stmts[5] as Assignment
|
||||
val value = assigncc.value as PipeExpression
|
||||
value.source shouldBe NumericLiteral(DataType.UBYTE, 30.0, Position.DUMMY)
|
||||
value.segments.size shouldBe 2
|
||||
call = value.segments[0] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("abs")
|
||||
call = value.segments[1] as IFunctionCall
|
||||
call.target.nameInSource shouldBe listOf("sqrt16")
|
||||
|
||||
assigncc = stmts[6] as Assignment
|
||||
val pipecc = assigncc.value as PipeExpression
|
||||
pipecc.source shouldBe instanceOf<IdentifierReference>()
|
||||
pipecc.segments.size shouldBe 2
|
||||
pipecc.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
pipecc.segments[1] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
}
|
||||
|
||||
test("correct pipe expressions (with opt)") {
|
||||
val text = """
|
||||
%import floats
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
float @shared fl = 1.234 |> addfloat()
|
||||
|> addfloat()
|
||||
|
||||
uword @shared ww = startvalue(99) |> addword()
|
||||
|> addword()
|
||||
|
||||
ubyte @shared cc = 80 |> abs() |> sqrt16() ; will be optimized away into a const number
|
||||
cc = cc |> abs() |> sqrt16()
|
||||
}
|
||||
|
||||
sub startvalue(ubyte arg) -> uword {
|
||||
return arg+9999
|
||||
}
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), optimize = true, text, writeAssembly = true)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 8
|
||||
val assignf = stmts[1] as Assignment
|
||||
val pipef = assignf.value as PipeExpression
|
||||
pipef.source shouldBe instanceOf<FunctionCallExpression>()
|
||||
pipef.segments.size shouldBe 1
|
||||
pipef.segments[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||
|
||||
val assignw = stmts[3] as Assignment
|
||||
val pipew = assignw.value as PipeExpression
|
||||
pipew.source shouldBe instanceOf<FunctionCallExpression>()
|
||||
pipew.segments.size shouldBe 2
|
||||
pipew.segments[0] shouldBe instanceOf<FunctionCallExpression>()
|
||||
pipew.segments[1] shouldBe instanceOf<FunctionCallExpression>()
|
||||
|
||||
var assigncc = stmts[5] as Assignment
|
||||
val value = assigncc.value as NumericLiteral
|
||||
value.number shouldBe 8.0
|
||||
|
||||
assigncc = stmts[6] as Assignment
|
||||
val pipecc = assigncc.value as PipeExpression
|
||||
pipecc.source shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
(pipecc.source as BuiltinFunctionCall).target.nameInSource shouldBe listOf("abs")
|
||||
|
||||
pipecc.segments.size shouldBe 1
|
||||
pipecc.segments[0] shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
(pipecc.segments[0] as BuiltinFunctionCall).target.nameInSource shouldBe listOf("sqrt16")
|
||||
}
|
||||
|
||||
test("incorrect type in pipe expression") {
|
||||
val text = """
|
||||
%option enable_floats
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
uword result = 1.234 |> addfloat()
|
||||
|> addword() |> addword()
|
||||
}
|
||||
|
||||
sub addfloat(float fl) -> float {
|
||||
return fl+2.22
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 1
|
||||
errors.errors[0] shouldContain "incompatible"
|
||||
}
|
||||
|
||||
test("correct pipe statement with builtin expression") {
|
||||
val text = """
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
uword ww = 9999
|
||||
ubyte bb = 99
|
||||
ww |> abs() |> txt.print_uw()
|
||||
bb |> abs() |> lsb() |> txt.print_ub()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val result = compileText(C64Target(), true, text, writeAssembly = true)!!
|
||||
val stmts = result.program.entrypoint.statements
|
||||
stmts.size shouldBe 7
|
||||
val pipeww = stmts[4] as Pipe
|
||||
pipeww.source shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
pipeww.segments.size shouldBe 1
|
||||
pipeww.segments[0] shouldBe instanceOf<IFunctionCall>()
|
||||
|
||||
val pipebb = stmts[5] as Pipe
|
||||
pipebb.source shouldBe instanceOf<BuiltinFunctionCall>()
|
||||
pipebb.segments.size shouldBe 2
|
||||
pipebb.segments[0] shouldBe instanceOf<IFunctionCall>()
|
||||
pipebb.segments[1] shouldBe instanceOf<IFunctionCall>()
|
||||
}
|
||||
|
||||
test("pipe statement with type errors") {
|
||||
val text = """
|
||||
%import textio
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
uword ww = 9999
|
||||
9999 |> abs() |> txt.print_ub()
|
||||
ww |> abs() |> txt.print_ub()
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize = false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "UWORD incompatible"
|
||||
errors.errors[1] shouldContain "UWORD incompatible"
|
||||
}
|
||||
|
||||
test("pipe detects invalid number of args") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
uword ww = startvalue() |> addword()
|
||||
|> addword()
|
||||
|
||||
ubyte cc = 30 |> abs(99) |> sqrt16(22)
|
||||
}
|
||||
|
||||
sub startvalue(ubyte arg) -> uword {
|
||||
return arg+9999
|
||||
}
|
||||
sub addword(uword ww) -> uword {
|
||||
return ww+2222
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize = false, text, writeAssembly = false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 3
|
||||
errors.errors[0] shouldContain ":4:32: invalid number of arguments"
|
||||
errors.errors[1] shouldContain ":7:42: invalid number of arguments"
|
||||
errors.errors[2] shouldContain ":7:56: invalid number of arguments"
|
||||
}
|
||||
|
||||
test("non-unary funcions in pipe ok for target virtual") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared wvalue = add(3,4) |> add(48) |> mkword(234)
|
||||
}
|
||||
sub add(ubyte first, ubyte second) -> ubyte {
|
||||
return first+second
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
val result = compileText(VMTarget(), optimize = false, text, writeAssembly = true, errors=errors)!!
|
||||
errors.errors.size shouldBe 0
|
||||
errors.warnings.size shouldBe 0
|
||||
result.program.entrypoint.statements.size shouldBe 3
|
||||
}
|
||||
|
||||
test("non-unary funcions in pipe not yet ok for other targets") {
|
||||
// NOTE: once other targets also support this, merge this into the test above
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
uword @shared wvalue = add(3,4) |> add(48) |> mkword(234)
|
||||
}
|
||||
sub add(ubyte first, ubyte second) -> ubyte {
|
||||
return first+second
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize = false, text, writeAssembly = true, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "only unary"
|
||||
errors.errors[1] shouldContain "only unary"
|
||||
}
|
||||
})
|
||||
@@ -384,4 +384,56 @@ class TestScoping: FunSpec({
|
||||
errors.errors[0] shouldContain "wrong address"
|
||||
errors.errors[1] shouldContain "takes parameters"
|
||||
}
|
||||
|
||||
test("name shadowing and redefinition errors") {
|
||||
val text = """
|
||||
main {
|
||||
ubyte var1Warn
|
||||
|
||||
sub start() {
|
||||
ubyte var1Warn
|
||||
ubyte var1Warn
|
||||
ubyte main
|
||||
ubyte start
|
||||
ubyte outer ; is ok
|
||||
ubyte internalOk
|
||||
ubyte internalOk ; double defined
|
||||
}
|
||||
|
||||
sub outer() {
|
||||
ubyte var1Warn
|
||||
ubyte internalOk
|
||||
}
|
||||
}
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), false, text, writeAssembly = false, errors = errors) shouldBe null
|
||||
println(errors.errors)
|
||||
errors.warnings.size shouldBe 3
|
||||
errors.warnings[0] shouldContain "var1Warn"
|
||||
errors.warnings[0] shouldContain "shadows"
|
||||
errors.warnings[0] shouldContain "line 3"
|
||||
errors.warnings[1] shouldContain "var1Warn"
|
||||
errors.warnings[1] shouldContain "shadows"
|
||||
errors.warnings[1] shouldContain "line 3"
|
||||
errors.warnings[2] shouldContain "var1Warn"
|
||||
errors.warnings[2] shouldContain "shadows"
|
||||
errors.warnings[2] shouldContain "line 3"
|
||||
errors.errors.size shouldBe 5
|
||||
errors.errors[0] shouldContain "name conflict"
|
||||
errors.errors[0] shouldContain "start"
|
||||
errors.errors[0] shouldContain "line 5"
|
||||
errors.errors[1] shouldContain "name conflict"
|
||||
errors.errors[1] shouldContain "var1Warn"
|
||||
errors.errors[1] shouldContain "line 6"
|
||||
errors.errors[2] shouldContain "name conflict"
|
||||
errors.errors[2] shouldContain "main"
|
||||
errors.errors[2] shouldContain "line 2"
|
||||
errors.errors[3] shouldContain "name conflict"
|
||||
errors.errors[3] shouldContain "start"
|
||||
errors.errors[3] shouldContain "line 5"
|
||||
errors.errors[4] shouldContain "name conflict"
|
||||
errors.errors[4] shouldContain "internalOk"
|
||||
errors.errors[4] shouldContain "line 11"
|
||||
}
|
||||
})
|
||||
@@ -3,13 +3,9 @@ package prog8tests
|
||||
import io.kotest.assertions.withClue
|
||||
import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import io.kotest.matchers.shouldNotBe
|
||||
import io.kotest.matchers.string.shouldContain
|
||||
import io.kotest.matchers.types.instanceOf
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.DirectMemoryRead
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteral
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.target.C64Target
|
||||
@@ -192,81 +188,6 @@ class TestSubroutines: FunSpec({
|
||||
errors.errors[1] shouldContain "pass-by-reference type can't be used"
|
||||
}
|
||||
|
||||
test("uword param and normal varindexed as array work as DirectMemoryRead") {
|
||||
val text="""
|
||||
main {
|
||||
sub thing(uword rr) {
|
||||
ubyte @shared xx = rr[1] ; should still work as var initializer that will be rewritten
|
||||
ubyte @shared yy
|
||||
yy = rr[2]
|
||||
uword @shared other
|
||||
ubyte zz = other[3]
|
||||
}
|
||||
|
||||
sub start() {
|
||||
ubyte[] array=[1,2,3]
|
||||
thing(array)
|
||||
}
|
||||
}
|
||||
"""
|
||||
|
||||
val result = compileText(C64Target(), false, text, writeAssembly = true)!!
|
||||
val module = result.program.toplevelModule
|
||||
val block = module.statements.single() as Block
|
||||
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
||||
block.name shouldBe "main"
|
||||
thing.statements.size shouldBe 10 // rr paramdecl, xx, xx assign, yy decl, yy assign, other, other assign 0, zz, zz assign, return
|
||||
val xx = thing.statements[1] as VarDecl
|
||||
withClue("vardecl init values must have been moved to separate assignments") {
|
||||
xx.value shouldBe null
|
||||
}
|
||||
val assignXX = thing.statements[2] as Assignment
|
||||
val assignYY = thing.statements[4] as Assignment
|
||||
val assignZZ = thing.statements[8] as Assignment
|
||||
assignXX.target.identifier!!.nameInSource shouldBe listOf("xx")
|
||||
assignYY.target.identifier!!.nameInSource shouldBe listOf("yy")
|
||||
assignZZ.target.identifier!!.nameInSource shouldBe listOf("zz")
|
||||
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
|
||||
(valueXXexpr.left as IdentifierReference).nameInSource shouldBe listOf("rr")
|
||||
(valueYYexpr.left as IdentifierReference).nameInSource shouldBe listOf("rr")
|
||||
(valueZZexpr.left as IdentifierReference).nameInSource shouldBe listOf("other")
|
||||
(valueXXexpr.right as NumericLiteral).number.toInt() shouldBe 1
|
||||
(valueYYexpr.right as NumericLiteral).number.toInt() shouldBe 2
|
||||
(valueZZexpr.right as NumericLiteral).number.toInt() shouldBe 3
|
||||
}
|
||||
|
||||
test("uword param and normal varindexed as array work as MemoryWrite") {
|
||||
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)!!
|
||||
val module = result.program.toplevelModule
|
||||
val block = module.statements.single() as Block
|
||||
val thing = block.statements.filterIsInstance<Subroutine>().single {it.name=="thing"}
|
||||
block.name shouldBe "main"
|
||||
thing.statements.size shouldBe 3 // "rr, rr assign, return void"
|
||||
val assignRR = thing.statements[1] as Assignment
|
||||
(assignRR.value as NumericLiteral).number.toInt() shouldBe 42
|
||||
val memwrite = assignRR.target.memoryAddress
|
||||
memwrite shouldNotBe null
|
||||
val addressExpr = memwrite!!.addressExpression as BinaryExpression
|
||||
(addressExpr.left as IdentifierReference).nameInSource shouldBe listOf("rr")
|
||||
addressExpr.operator shouldBe "+"
|
||||
(addressExpr.right as NumericLiteral).number.toInt() shouldBe 10
|
||||
}
|
||||
|
||||
test("invalid number of args check on normal subroutine") {
|
||||
val text="""
|
||||
main {
|
||||
|
||||
@@ -29,42 +29,6 @@ main {
|
||||
length=string.length(name)
|
||||
if length!=3 txt.print("error strlen2\n")
|
||||
|
||||
; MAX
|
||||
ub = max(ubarr)
|
||||
if ub!=199 txt.print("error max1\n")
|
||||
bb = max(barr)
|
||||
if bb!=99 txt.print("error max2\n")
|
||||
uw = max(uwarr)
|
||||
if uw!=4444 txt.print("error max3\n")
|
||||
ww = max(warr)
|
||||
if ww!=999 txt.print("error max4\n")
|
||||
ff = max(farr)
|
||||
if ff!=999.9 txt.print("error max5\n")
|
||||
|
||||
; MIN
|
||||
ub = min(ubarr)
|
||||
if ub!=0 txt.print("error min1\n")
|
||||
bb = min(barr)
|
||||
if bb!=-122 txt.print("error min2\n")
|
||||
uw = min(uwarr)
|
||||
if uw!=0 txt.print("error min3\n")
|
||||
ww = min(warr)
|
||||
if ww!=-4444 txt.print("error min4\n")
|
||||
ff = min(farr)
|
||||
if ff!=-4444.4 txt.print("error min5\n")
|
||||
|
||||
; SUM
|
||||
uw = sum(ubarr)
|
||||
if uw!=420 txt.print("error sum1\n")
|
||||
ww = sum(barr)
|
||||
if ww!=-101 txt.print("error sum2\n")
|
||||
uw = sum(uwarr)
|
||||
if uw!=6665 txt.print("error sum3\n")
|
||||
ww = sum(warr)
|
||||
if ww!=-4223 txt.print("error sum4\n")
|
||||
ff = sum(farr)
|
||||
if ff!=-4222.4 txt.print("error sum5\n")
|
||||
|
||||
; ANY
|
||||
ub = any(ubarr)
|
||||
if ub==0 txt.print("error any1\n")
|
||||
|
||||
@@ -339,79 +339,23 @@ main {
|
||||
txt.nl()
|
||||
|
||||
bb = -100
|
||||
bb = abs(bb)
|
||||
bb = abs(bb) as byte
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
bb = -100
|
||||
bb = zero+abs(bb)*1+zero
|
||||
bb = zero+(abs(bb) as byte)*1+zero
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
|
||||
ww = -1000
|
||||
ww = abs(ww)
|
||||
ww = abs(ww) as word
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
ww = -1000
|
||||
ww = zero+abs(ww)*1+zero
|
||||
ww = zero+(abs(ww) as word)*1+zero
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
ub = min(ubarr)
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+min(ubarr)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
bb = min(barr)
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
bb = zero+min(barr)*1+zero
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
|
||||
uw = min(uwarr)
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
uw = zero+min(uwarr)*1+zero
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
ww = min(warr)
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
ww = zero+min(warr)*1+zero
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
ub = max(ubarr)
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
ub = zero+max(ubarr)*1+zero
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
bb = max(barr)
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
bb = zero+max(barr)*1+zero
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
|
||||
uw = max(uwarr)
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
uw = zero+max(uwarr)*1+zero
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
ww = max(warr)
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
ww = zero+max(warr)*1+zero
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
|
||||
ub = any(ubarr)
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
@@ -468,35 +412,6 @@ main {
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
|
||||
|
||||
uw = sum(ubarr)
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
uw = zero+sum(ubarr)*1+zero
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
ww = sum(barr)
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
ww = zero+sum(barr)*1+zero
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
uw = sum(uwarr)
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
uw = zero+sum(uwarr)*1+zero
|
||||
txt.print_uw(uw)
|
||||
txt.nl()
|
||||
|
||||
ww = sum(warr)
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
ww = zero+sum(warr)*1+zero
|
||||
txt.print_w(ww)
|
||||
txt.nl()
|
||||
|
||||
sort(ubarr)
|
||||
sort(barr)
|
||||
sort(uwarr)
|
||||
@@ -520,114 +435,6 @@ main {
|
||||
float fl
|
||||
float fzero=0.0
|
||||
|
||||
fl = -9.9
|
||||
fl = abs(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+abs(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 9.9
|
||||
fl = atan(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 9.9
|
||||
fl = fzero+atan(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
fl = ceil(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+ceil(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
fl = cos(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+cos(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
fl = sin(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+sin(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 9.9
|
||||
fl = tan(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 9.9
|
||||
fl = fzero+tan(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 3.1415927
|
||||
fl = deg(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 3.1415927
|
||||
fl = fzero+deg(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 90
|
||||
fl = rad(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 90
|
||||
fl = fzero+rad(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
fl = floor(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+floor(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 3.1415927
|
||||
fl = ln(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 3.1415927
|
||||
fl = fzero+ln(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = 3.1415927
|
||||
fl = log2(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 3.1415927
|
||||
fl = fzero+log2(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
fl = round(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = -9.9
|
||||
fl = fzero+round(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = -9.9
|
||||
bb = sgn(fl)
|
||||
txt.print_b(bb)
|
||||
@@ -637,25 +444,6 @@ main {
|
||||
txt.print_b(bb)
|
||||
txt.nl()
|
||||
|
||||
fl = 3.1415927
|
||||
fl = sqrt(fl)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = 3.1415927
|
||||
fl = fzero+sqrt(fl)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
fl = rndf()
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = fzero+rndf()*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
swap(fl, fzero)
|
||||
swap(fzero, fl)
|
||||
|
||||
ub = any(flarr)
|
||||
txt.print_ub(ub)
|
||||
txt.nl()
|
||||
@@ -675,24 +463,6 @@ main {
|
||||
txt.chrout(',')
|
||||
}
|
||||
txt.nl()
|
||||
fl = max(flarr)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = fzero+max(flarr)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = min(flarr)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = fzero+min(flarr)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = sum(flarr)
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
fl = fzero+sum(flarr)*1.0+fzero
|
||||
floats.print_f(fl)
|
||||
txt.nl()
|
||||
|
||||
test_stack.test()
|
||||
}
|
||||
|
||||
@@ -87,7 +87,7 @@ main {
|
||||
|
||||
sub div_float(float a1, float a2, float c) {
|
||||
float r = a1/a2
|
||||
if abs(r-c)<0.00001
|
||||
if floats.fabs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
|
||||
@@ -98,7 +98,7 @@ main {
|
||||
|
||||
sub minus_float(float a1, float a2, float c) {
|
||||
float r = a1-a2
|
||||
if abs(r-c)<0.00001
|
||||
if floats.fabs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else {
|
||||
txt.print("err! ")
|
||||
|
||||
@@ -89,7 +89,7 @@ main {
|
||||
|
||||
sub mul_float(float a1, float a2, float c) {
|
||||
float r = a1*a2
|
||||
if abs(r-c)<0.00001
|
||||
if floats.fabs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
|
||||
@@ -93,7 +93,7 @@ main {
|
||||
|
||||
sub plus_float(float a1, float a2, float c) {
|
||||
float r = a1+a2
|
||||
if abs(r-c)<0.00001
|
||||
if floats.fabs(r-c)<0.00001
|
||||
txt.print(" ok ")
|
||||
else
|
||||
txt.print("err! ")
|
||||
|
||||
@@ -21,7 +21,7 @@ class TestIntermediateAst: FunSpec({
|
||||
ubyte cc
|
||||
ubyte[] array = [1,2,3]
|
||||
cc = 11 in array
|
||||
cc = cc |> lsb() |> sqrt16()
|
||||
cc = sqrt16(lsb(cc))
|
||||
}
|
||||
}
|
||||
"""
|
||||
@@ -53,9 +53,9 @@ class TestIntermediateAst: FunSpec({
|
||||
|
||||
val containment = (entry.children[2] as PtAssignment).value as PtContainmentCheck
|
||||
(containment.element as PtNumber).number shouldBe 11.0
|
||||
val pipe = (entry.children[3] as PtAssignment).value as PtPipe
|
||||
pipe.void shouldBe false
|
||||
pipe.type shouldBe DataType.UBYTE
|
||||
val fcall = (entry.children[3] as PtAssignment).value as PtFunctionCall
|
||||
fcall.void shouldBe false
|
||||
fcall.type shouldBe DataType.UBYTE
|
||||
ast.print()
|
||||
}
|
||||
|
||||
|
||||
@@ -204,7 +204,7 @@ class TestProg8Parser: FunSpec( {
|
||||
val module = parseModule(SourceCode.Text(srcText))
|
||||
|
||||
// Note: assertContains has *actual* as first param
|
||||
module.name shouldContain Regex("^<String@[0-9a-f\\-]+>$")
|
||||
module.name shouldContain Regex("^string:[0-9a-f\\-]+$")
|
||||
}
|
||||
|
||||
test("parsed from a file") {
|
||||
@@ -267,7 +267,7 @@ class TestProg8Parser: FunSpec( {
|
||||
val srcText = "bad * { }\n"
|
||||
|
||||
val e = shouldThrow<ParseError> { parseModule(SourceCode.Text(srcText)) }
|
||||
assertPosition(e.position, Regex("^<String@[0-9a-f\\-]+>$"), 1, 4, 4)
|
||||
assertPosition(e.position, Regex("^string:[0-9a-f\\-]+$"), 1, 4, 4)
|
||||
}
|
||||
|
||||
test("in ParseError from bad file source code") {
|
||||
@@ -283,7 +283,7 @@ class TestProg8Parser: FunSpec( {
|
||||
}
|
||||
"""
|
||||
val module = parseModule(SourceCode.Text(srcText))
|
||||
assertPositionOf(module, Regex("^<String@[0-9a-f\\-]+>$"), 1, 0)
|
||||
assertPositionOf(module, Regex("^string:[0-9a-f\\-]+$"), 1, 0)
|
||||
}
|
||||
|
||||
test("of Module parsed from a file") {
|
||||
|
||||
@@ -19,7 +19,7 @@ class TestSourceCode: AnnotationSpec() {
|
||||
"""
|
||||
val src = SourceCode.Text(text)
|
||||
|
||||
src.origin shouldContain Regex("^<String@[0-9a-f\\-]+>$")
|
||||
src.origin shouldContain Regex("^string:[0-9a-f\\-]+$")
|
||||
src.text shouldBe text
|
||||
src.isFromResources shouldBe false
|
||||
src.isFromFilesystem shouldBe false
|
||||
|
||||
@@ -50,4 +50,18 @@ class TestVariables: FunSpec({
|
||||
"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
|
||||
test("pipe character in string literal") {
|
||||
val text = """
|
||||
main {
|
||||
sub start() {
|
||||
str name = "first|second"
|
||||
str name2 = "first | second"
|
||||
}
|
||||
}
|
||||
"""
|
||||
compileText(C64Target(), false, text, writeAssembly = true) shouldNotBe null
|
||||
}
|
||||
|
||||
})
|
||||
@@ -25,7 +25,7 @@ dependencies {
|
||||
implementation project(':codeCore')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8"
|
||||
implementation 'org.antlr:antlr4-runtime:4.10.1'
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.14"
|
||||
implementation "com.michael-bull.kotlin-result:kotlin-result-jvm:1.1.16"
|
||||
implementation project(':parser')
|
||||
}
|
||||
|
||||
|
||||
@@ -461,26 +461,4 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
||||
whenChoice.statements.accept(this)
|
||||
outputln("")
|
||||
}
|
||||
|
||||
override fun visit(pipe: Pipe) {
|
||||
printPipe(pipe.source, pipe.segments)
|
||||
}
|
||||
|
||||
override fun visit(pipe: PipeExpression) {
|
||||
printPipe(pipe.source, pipe.segments)
|
||||
}
|
||||
|
||||
private fun printPipe(source: Expression, segments: Iterable<Expression>) {
|
||||
source.accept(this)
|
||||
output(" |> ")
|
||||
segments.first().accept(this)
|
||||
outputln("")
|
||||
scopelevel++
|
||||
segments.drop(1).forEach {
|
||||
outputi("|> ")
|
||||
it.accept(this)
|
||||
outputln("")
|
||||
}
|
||||
scopelevel--
|
||||
}
|
||||
}
|
||||
@@ -31,13 +31,6 @@ interface IFunctionCall {
|
||||
var parent: Node // will be linked correctly later (late init)
|
||||
}
|
||||
|
||||
interface IPipe {
|
||||
var source: Expression
|
||||
val segments: MutableList<Expression> // are all function calls
|
||||
val position: Position
|
||||
var parent: Node // will be linked correctly later (late init)
|
||||
}
|
||||
|
||||
interface IStatementContainer {
|
||||
val statements: MutableList<Statement>
|
||||
|
||||
|
||||
@@ -1,9 +1,7 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.InferredTypes
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.code.core.DataType
|
||||
import prog8.code.core.Position
|
||||
@@ -39,7 +37,8 @@ fun Program.getTempVar(dt: DataType, altNames: Boolean=false): Pair<List<String>
|
||||
// add new temp variable to the ast directly (we can do this here because we're not iterating inside those container blocks)
|
||||
val decl = VarDecl(
|
||||
VarDeclType.VAR, VarDeclOrigin.AUTOGENERATED, dt, ZeropageWish.DONTCARE,
|
||||
null, tmpvarName[1], null, false, false, null, Position.DUMMY)
|
||||
null, tmpvarName[1], null, isArray = false, sharedWithAsm = false, subroutineParameter = null, position = Position.DUMMY
|
||||
)
|
||||
block.statements.add(decl)
|
||||
decl.linkParents(block)
|
||||
return Pair(tmpvarName, decl)
|
||||
@@ -85,3 +84,14 @@ fun determineGosubArguments(gosub: GoSub): Map<String, Expression> {
|
||||
}
|
||||
return arguments
|
||||
}
|
||||
|
||||
fun maySwapOperandOrder(binexpr: BinaryExpression): Boolean {
|
||||
fun ok(expr: Expression): Boolean {
|
||||
return when(expr) {
|
||||
is BinaryExpression -> expr.left.isSimple
|
||||
is IFunctionCall -> false
|
||||
else -> expr.isSimple
|
||||
}
|
||||
}
|
||||
return ok(binexpr.left) || ok(binexpr.right)
|
||||
}
|
||||
Loaded 100 of 130 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user