mirror of
https://github.com/irmen/prog8.git
synced 2026-10-23 12:02:29 +00:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
3581017489 | ||
|
|
9bc36b4d99 | ||
|
|
e8caf6d319 | ||
|
|
5b9cc9592f | ||
|
|
3cf87536ff | ||
|
|
cc452dffb8 | ||
|
|
e414d301a4 | ||
|
|
5ff79073f4 | ||
|
|
70462ffe6d | ||
|
|
158fe7596b | ||
|
|
f4f113da7b | ||
|
|
d6b6254b72 | ||
|
|
65fa8c4613 | ||
|
|
c1102393bb | ||
|
|
dbe048158c | ||
|
|
2b3382ff8e | ||
|
|
c970d899fa | ||
|
|
3c563d281a | ||
|
|
1794f704e7 | ||
|
|
ade7a4c398 | ||
|
|
5a27b035b0 | ||
|
|
e84bb8d94a | ||
|
|
5ed0893d96 | ||
|
|
89314a0e1a | ||
|
|
fd0abf61df |
No files matched your search
Generated
+3
@@ -0,0 +1,3 @@
|
||||
<component name="ProjectDictionaryState">
|
||||
<dictionary name="irmen" />
|
||||
</component>
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
<component name="InspectionProjectProfileManager">
|
||||
<profile version="1.0">
|
||||
<option name="myName" value="Project Default" />
|
||||
<inspection_tool class="SpellCheckingInspection" enabled="true" level="TYPO" enabled_by_default="true">
|
||||
<option name="processCode" value="false" />
|
||||
<option name="processLiterals" value="true" />
|
||||
<option name="processComments" value="false" />
|
||||
</inspection_tool>
|
||||
</profile>
|
||||
</component>
|
||||
+36
-23
@@ -1,21 +1,36 @@
|
||||
plugins {
|
||||
id "org.jetbrains.kotlin.jvm" version "1.3.40"
|
||||
id 'application'
|
||||
id 'org.jetbrains.dokka' version "0.9.18"
|
||||
buildscript {
|
||||
dependencies {
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
|
||||
}
|
||||
}
|
||||
|
||||
plugins {
|
||||
// id "org.jetbrains.kotlin.jvm" version $kotlinVersion
|
||||
id 'application'
|
||||
id 'org.jetbrains.dokka' version "0.9.18"
|
||||
id 'com.github.johnrengelman.shadow' version '5.1.0'
|
||||
id 'java'
|
||||
}
|
||||
|
||||
apply plugin: "kotlin"
|
||||
apply plugin: "java"
|
||||
|
||||
|
||||
repositories {
|
||||
mavenLocal()
|
||||
mavenCentral()
|
||||
jcenter()
|
||||
}
|
||||
|
||||
def kotlinVersion = '1.3.40'
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
def prog8version = rootProject.file('compiler/res/version.txt').text.trim()
|
||||
|
||||
dependencies {
|
||||
implementation project(':parser')
|
||||
implementation "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
|
||||
implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
// implementation "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
// runtime "org.jetbrains.kotlin:kotlin-reflect:$kotlinVersion"
|
||||
runtime 'org.antlr:antlr4-runtime:4.7.2'
|
||||
runtime project(':parser')
|
||||
|
||||
@@ -29,6 +44,7 @@ dependencies {
|
||||
compileKotlin {
|
||||
kotlinOptions {
|
||||
jvmTarget = "1.8"
|
||||
verbose = true
|
||||
// freeCompilerArgs += "-XXLanguage:+NewInference"
|
||||
}
|
||||
}
|
||||
@@ -49,15 +65,19 @@ sourceSets {
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
startScripts.enabled = true
|
||||
|
||||
application {
|
||||
mainClassName = 'prog8.CompilerMainKt'
|
||||
applicationName = 'p8compile'
|
||||
}
|
||||
|
||||
artifacts {
|
||||
archives shadowJar
|
||||
}
|
||||
|
||||
task p8vmScript(type: CreateStartScripts) {
|
||||
mainClassName = "prog8.StackVmMainKt"
|
||||
mainClassName = "prog8.vm.stackvm.MainKt"
|
||||
applicationName = "p8vm"
|
||||
outputDir = new File(project.buildDir, 'scripts')
|
||||
classpath = jar.outputs.files + project.configurations.runtime
|
||||
@@ -68,23 +88,16 @@ applicationDistribution.into("bin") {
|
||||
fileMode = 0755
|
||||
}
|
||||
|
||||
task fatJar(type: Jar) {
|
||||
manifest {
|
||||
attributes 'Main-Class': 'prog8.CompilerMainKt'
|
||||
}
|
||||
archiveBaseName = 'prog8compiler'
|
||||
destinationDirectory = rootProject.projectDir
|
||||
from {
|
||||
project.configurations.runtime.collect { it.isDirectory() ? it : zipTree(it) }
|
||||
}
|
||||
with jar
|
||||
// To create a fat-jar use the 'create_compiler_jar' script for now
|
||||
// @todo investigate https://imperceptiblethoughts.com/shadow/introduction/
|
||||
|
||||
shadowJar {
|
||||
baseName = 'prog8compiler'
|
||||
version = prog8version
|
||||
// minimize()
|
||||
}
|
||||
// build.finalizedBy(fatJar)
|
||||
|
||||
|
||||
// Java target version
|
||||
sourceCompatibility = 1.8
|
||||
|
||||
test {
|
||||
// Enable JUnit 5 (Gradle 4.6+).
|
||||
useJUnitPlatform()
|
||||
|
||||
@@ -11,7 +11,6 @@
|
||||
<orderEntry type="jdk" jdkName="1.8" jdkType="JavaSDK" />
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||
<orderEntry type="library" name="antlr-runtime-4.7.2" level="project" />
|
||||
<orderEntry type="module" module-name="parser" />
|
||||
<orderEntry type="library" name="unittest-libs" level="project" />
|
||||
|
||||
@@ -41,25 +41,25 @@
|
||||
; note: for subtraction and division, the left operand is in fac2, the right operand in fac1.
|
||||
|
||||
; checked functions below:
|
||||
asmsub MOVFM (uword mflpt @ AY) -> clobbers(A,Y) -> () = $bba2 ; load mflpt value from memory in A/Y into fac1
|
||||
asmsub FREADMEM () -> clobbers(A,Y) -> () = $bba6 ; load mflpt value from memory in $22/$23 into fac1
|
||||
asmsub CONUPK (uword mflpt @ AY) -> clobbers(A,Y) -> () = $ba8c ; load mflpt value from memory in A/Y into fac2
|
||||
asmsub FAREADMEM () -> clobbers(A,Y) -> () = $ba90 ; load mflpt value from memory in $22/$23 into fac2
|
||||
asmsub MOVFA () -> clobbers(A,X) -> () = $bbfc ; copy fac2 to fac1
|
||||
asmsub MOVAF () -> clobbers(A,X) -> () = $bc0c ; copy fac1 to fac2 (rounded)
|
||||
asmsub MOVEF () -> clobbers(A,X) -> () = $bc0f ; copy fac1 to fac2
|
||||
asmsub MOVMF (uword mflpt @ XY) -> clobbers(A,Y) -> () = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
asmsub MOVFM (uword mflpt @ AY) clobbers(A,Y) = $bba2 ; load mflpt value from memory in A/Y into fac1
|
||||
asmsub FREADMEM () clobbers(A,Y) = $bba6 ; load mflpt value from memory in $22/$23 into fac1
|
||||
asmsub CONUPK (uword mflpt @ AY) clobbers(A,Y) = $ba8c ; load mflpt value from memory in A/Y into fac2
|
||||
asmsub FAREADMEM () clobbers(A,Y) = $ba90 ; load mflpt value from memory in $22/$23 into fac2
|
||||
asmsub MOVFA () clobbers(A,X) = $bbfc ; copy fac2 to fac1
|
||||
asmsub MOVAF () clobbers(A,X) = $bc0c ; copy fac1 to fac2 (rounded)
|
||||
asmsub MOVEF () clobbers(A,X) = $bc0f ; copy fac1 to fac2
|
||||
asmsub MOVMF (uword mflpt @ XY) clobbers(A,Y) = $bbd4 ; store fac1 to memory X/Y as 5-byte mflpt
|
||||
|
||||
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
|
||||
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
asmsub FTOSWORDYA () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b1aa
|
||||
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa
|
||||
|
||||
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
|
||||
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
|
||||
asmsub GETADR () -> clobbers(X) -> (ubyte @ Y, ubyte @ A) = $b7f7
|
||||
asmsub GETADR () clobbers(X) -> ubyte @ Y, ubyte @ A = $b7f7
|
||||
|
||||
asmsub QINT () -> clobbers(A,X,Y) -> () = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||
asmsub AYINT () -> clobbers(A,X,Y) -> () = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
asmsub QINT () clobbers(A,X,Y) = $bc9b ; fac1 -> 4-byte signed integer in 98-101 ($62-$65), with the MSB FIRST.
|
||||
asmsub AYINT () clobbers(A,X,Y) = $b1bf ; fac1-> signed word in 100-101 ($64-$65) MSB FIRST. (might throw ILLEGAL QUANTITY)
|
||||
|
||||
; GIVAYF: signed word in Y/A (note different lsb/msb order) -> float in fac1
|
||||
; (tip: use c64flt.GIVAYFAY to use A/Y input; lo/hi switched to normal order)
|
||||
@@ -67,50 +67,50 @@ asmsub AYINT () -> clobbers(A,X,Y) -> () = $b1bf ; fac1-> signed word in 100
|
||||
; there is also c64flt.FREADS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also c64flt.FREADUS32 that reads from 98-101 ($62-$65) MSB FIRST
|
||||
; there is also c64flt.FREADS24AXY that reads signed int24 into fac1 from A/X/Y (lo/mid/hi bytes)
|
||||
asmsub GIVAYF (ubyte lo @ Y, ubyte hi @ A) -> clobbers(A,X,Y) -> () = $b391
|
||||
asmsub GIVAYF (ubyte lo @ Y, ubyte hi @ A) clobbers(A,X,Y) = $b391
|
||||
|
||||
asmsub FREADUY (ubyte value @ Y) -> clobbers(A,X,Y) -> () = $b3a2 ; 8 bit unsigned Y -> float in fac1
|
||||
asmsub FREADSA (byte value @ A) -> clobbers(A,X,Y) -> () = $bc3c ; 8 bit signed A -> float in fac1
|
||||
asmsub FREADSTR (ubyte length @ A) -> clobbers(A,X,Y) -> () = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
|
||||
asmsub FPRINTLN () -> clobbers(A,X,Y) -> () = $aabc ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
asmsub FOUT () -> clobbers(X) -> (uword @ AY) = $bddd ; fac1 -> string, address returned in AY ($0100)
|
||||
asmsub FREADUY (ubyte value @ Y) clobbers(A,X,Y) = $b3a2 ; 8 bit unsigned Y -> float in fac1
|
||||
asmsub FREADSA (byte value @ A) clobbers(A,X,Y) = $bc3c ; 8 bit signed A -> float in fac1
|
||||
asmsub FREADSTR (ubyte length @ A) clobbers(A,X,Y) = $b7b5 ; str -> fac1, $22/23 must point to string, A=string length
|
||||
asmsub FPRINTLN () clobbers(A,X,Y) = $aabc ; print string of fac1, on one line (= with newline) destroys fac1. (consider FOUT + STROUT as well)
|
||||
asmsub FOUT () clobbers(X) -> uword @ AY = $bddd ; fac1 -> string, address returned in AY ($0100)
|
||||
|
||||
asmsub FADDH () -> clobbers(A,X,Y) -> () = $b849 ; fac1 += 0.5, for rounding- call this before INT
|
||||
asmsub MUL10 () -> clobbers(A,X,Y) -> () = $bae2 ; fac1 *= 10
|
||||
asmsub DIV10 () -> clobbers(A,X,Y) -> () = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
asmsub FCOMP (uword mflpt @ AY) -> clobbers(X,Y) -> (ubyte @ A) = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
asmsub FADDH () clobbers(A,X,Y) = $b849 ; fac1 += 0.5, for rounding- call this before INT
|
||||
asmsub MUL10 () clobbers(A,X,Y) = $bae2 ; fac1 *= 10
|
||||
asmsub DIV10 () clobbers(A,X,Y) = $bafe ; fac1 /= 10 , CAUTION: result is always positive!
|
||||
asmsub FCOMP (uword mflpt @ AY) clobbers(X,Y) -> ubyte @ A = $bc5b ; A = compare fac1 to mflpt in A/Y, 0=equal 1=fac1 is greater, 255=fac1 is less than
|
||||
|
||||
asmsub FADDT () -> clobbers(A,X,Y) -> () = $b86a ; fac1 += fac2
|
||||
asmsub FADD (uword mflpt @ AY) -> clobbers(A,X,Y) -> () = $b867 ; fac1 += mflpt value from A/Y
|
||||
asmsub FSUBT () -> clobbers(A,X,Y) -> () = $b853 ; fac1 = fac2-fac1 mind the order of the operands
|
||||
asmsub FSUB (uword mflpt @ AY) -> clobbers(A,X,Y) -> () = $b850 ; fac1 = mflpt from A/Y - fac1
|
||||
asmsub FMULTT () -> clobbers(A,X,Y) -> () = $ba2b ; fac1 *= fac2
|
||||
asmsub FMULT (uword mflpt @ AY) -> clobbers(A,X,Y) -> () = $ba28 ; fac1 *= mflpt value from A/Y
|
||||
asmsub FDIVT () -> clobbers(A,X,Y) -> () = $bb12 ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
asmsub FDIV (uword mflpt @ AY) -> clobbers(A,X,Y) -> () = $bb0f ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
asmsub FPWRT () -> clobbers(A,X,Y) -> () = $bf7b ; fac1 = fac2 ** fac1
|
||||
asmsub FPWR (uword mflpt @ AY) -> clobbers(A,X,Y) -> () = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
||||
asmsub FADDT () clobbers(A,X,Y) = $b86a ; fac1 += fac2
|
||||
asmsub FADD (uword mflpt @ AY) clobbers(A,X,Y) = $b867 ; fac1 += mflpt value from A/Y
|
||||
asmsub FSUBT () clobbers(A,X,Y) = $b853 ; fac1 = fac2-fac1 mind the order of the operands
|
||||
asmsub FSUB (uword mflpt @ AY) clobbers(A,X,Y) = $b850 ; fac1 = mflpt from A/Y - fac1
|
||||
asmsub FMULTT () clobbers(A,X,Y) = $ba2b ; fac1 *= fac2
|
||||
asmsub FMULT (uword mflpt @ AY) clobbers(A,X,Y) = $ba28 ; fac1 *= mflpt value from A/Y
|
||||
asmsub FDIVT () clobbers(A,X,Y) = $bb12 ; fac1 = fac2/fac1 (remainder in fac2) mind the order of the operands
|
||||
asmsub FDIV (uword mflpt @ AY) clobbers(A,X,Y) = $bb0f ; fac1 = mflpt in A/Y / fac1 (remainder in fac2)
|
||||
asmsub FPWRT () clobbers(A,X,Y) = $bf7b ; fac1 = fac2 ** fac1
|
||||
asmsub FPWR (uword mflpt @ AY) clobbers(A,X,Y) = $bf78 ; fac1 = fac2 ** mflpt from A/Y
|
||||
|
||||
asmsub NOTOP () -> clobbers(A,X,Y) -> () = $aed4 ; fac1 = NOT(fac1)
|
||||
asmsub INT () -> clobbers(A,X,Y) -> () = $bccc ; INT() truncates, use FADDH first to round instead of trunc
|
||||
asmsub LOG () -> clobbers(A,X,Y) -> () = $b9ea ; fac1 = LN(fac1) (natural log)
|
||||
asmsub SGN () -> clobbers(A,X,Y) -> () = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
asmsub SIGN () -> clobbers() -> (ubyte @ A) = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
asmsub ABS () -> clobbers() -> () = $bc58 ; fac1 = ABS(fac1)
|
||||
asmsub SQR () -> clobbers(A,X,Y) -> () = $bf71 ; fac1 = SQRT(fac1)
|
||||
asmsub SQRA () -> clobbers(A,X,Y) -> () = $bf74 ; fac1 = SQRT(fac2)
|
||||
asmsub EXP () -> clobbers(A,X,Y) -> () = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
||||
asmsub NEGOP () -> clobbers(A) -> () = $bfb4 ; switch the sign of fac1
|
||||
asmsub RND () -> clobbers(A,X,Y) -> () = $e097 ; fac1 = RND(fac1) float random number generator
|
||||
asmsub COS () -> clobbers(A,X,Y) -> () = $e264 ; fac1 = COS(fac1)
|
||||
asmsub SIN () -> clobbers(A,X,Y) -> () = $e26b ; fac1 = SIN(fac1)
|
||||
asmsub TAN () -> clobbers(A,X,Y) -> () = $e2b4 ; fac1 = TAN(fac1)
|
||||
asmsub ATN () -> clobbers(A,X,Y) -> () = $e30e ; fac1 = ATN(fac1)
|
||||
asmsub NOTOP () clobbers(A,X,Y) = $aed4 ; fac1 = NOT(fac1)
|
||||
asmsub INT () clobbers(A,X,Y) = $bccc ; INT() truncates, use FADDH first to round instead of trunc
|
||||
asmsub LOG () clobbers(A,X,Y) = $b9ea ; fac1 = LN(fac1) (natural log)
|
||||
asmsub SGN () clobbers(A,X,Y) = $bc39 ; fac1 = SGN(fac1), result of SIGN (-1, 0 or 1)
|
||||
asmsub SIGN () -> ubyte @ A = $bc2b ; SIGN(fac1) to A, $ff, $0, $1 for negative, zero, positive
|
||||
asmsub ABS () = $bc58 ; fac1 = ABS(fac1)
|
||||
asmsub SQR () clobbers(A,X,Y) = $bf71 ; fac1 = SQRT(fac1)
|
||||
asmsub SQRA () clobbers(A,X,Y) = $bf74 ; fac1 = SQRT(fac2)
|
||||
asmsub EXP () clobbers(A,X,Y) = $bfed ; fac1 = EXP(fac1) (e ** fac1)
|
||||
asmsub NEGOP () clobbers(A) = $bfb4 ; switch the sign of fac1
|
||||
asmsub RND () clobbers(A,X,Y) = $e097 ; fac1 = RND(fac1) float random number generator
|
||||
asmsub COS () clobbers(A,X,Y) = $e264 ; fac1 = COS(fac1)
|
||||
asmsub SIN () clobbers(A,X,Y) = $e26b ; fac1 = SIN(fac1)
|
||||
asmsub TAN () clobbers(A,X,Y) = $e2b4 ; fac1 = TAN(fac1)
|
||||
asmsub ATN () clobbers(A,X,Y) = $e30e ; fac1 = ATN(fac1)
|
||||
|
||||
|
||||
|
||||
|
||||
asmsub FREADS32 () -> clobbers(A,X,Y) -> () {
|
||||
asmsub FREADS32 () clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
lda $62
|
||||
@@ -122,7 +122,7 @@ asmsub FREADS32 () -> clobbers(A,X,Y) -> () {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADUS32 () -> clobbers(A,X,Y) -> () {
|
||||
asmsub FREADUS32 () clobbers(A,X,Y) {
|
||||
; ---- fac1 = uint32 from $62-$65 big endian (MSB FIRST)
|
||||
%asm {{
|
||||
sec
|
||||
@@ -132,7 +132,7 @@ asmsub FREADUS32 () -> clobbers(A,X,Y) -> () {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) -> clobbers(A,X,Y) -> () {
|
||||
asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) clobbers(A,X,Y) {
|
||||
; ---- fac1 = signed int24 (A/X/Y contain lo/mid/hi bytes)
|
||||
; note: there is no FREADU24AXY (unsigned), use FREADUS32 instead.
|
||||
%asm {{
|
||||
@@ -149,7 +149,7 @@ asmsub FREADS24AXY (ubyte lo @ A, ubyte mid @ X, ubyte hi @ Y) -> clobbers(A,X
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVUAYFAY (uword value @ AY) -> clobbers(A,X,Y) -> () {
|
||||
asmsub GIVUAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- unsigned 16 bit word in A/Y (lo/hi) to fac1
|
||||
%asm {{
|
||||
sty $62
|
||||
@@ -160,7 +160,7 @@ asmsub GIVUAYFAY (uword value @ AY) -> clobbers(A,X,Y) -> () {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GIVAYFAY (uword value @ AY) -> clobbers(A,X,Y) -> () {
|
||||
asmsub GIVAYFAY (uword value @ AY) clobbers(A,X,Y) {
|
||||
; ---- signed 16 bit word in A/Y (lo/hi) to float in fac1
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPREG
|
||||
@@ -170,7 +170,7 @@ asmsub GIVAYFAY (uword value @ AY) -> clobbers(A,X,Y) -> () {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub FTOSWRDAY () -> clobbers(X) -> (uword @ AY) {
|
||||
asmsub FTOSWRDAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to signed word in A/Y
|
||||
%asm {{
|
||||
jsr FTOSWORDYA ; note the inverse Y/A order
|
||||
@@ -181,7 +181,7 @@ asmsub FTOSWRDAY () -> clobbers(X) -> (uword @ AY) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub GETADRAY () -> clobbers(X) -> (uword @ AY) {
|
||||
asmsub GETADRAY () clobbers(X) -> uword @ AY {
|
||||
; ---- fac1 to unsigned word in A/Y
|
||||
%asm {{
|
||||
jsr GETADR ; this uses the inverse order, Y/A
|
||||
|
||||
@@ -63,8 +63,8 @@
|
||||
&ubyte SP6Y = $d00d
|
||||
&ubyte SP7X = $d00e
|
||||
&ubyte SP7Y = $d00f
|
||||
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
||||
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
||||
&ubyte[16] SPXY = $d000 ; the 8 sprite X and Y registers as an array.
|
||||
&uword[8] SPXYW = $d000 ; the 8 sprite X and Y registers as a combined xy word array.
|
||||
|
||||
&ubyte MSIGX = $d010
|
||||
&ubyte SCROLY = $d011
|
||||
@@ -76,7 +76,7 @@
|
||||
&ubyte YXPAND = $d017
|
||||
&ubyte VMCSB = $d018
|
||||
&ubyte VICIRQ = $d019
|
||||
&ubyte IREQMASK = $d01a
|
||||
&ubyte IREQMASK = $d01a
|
||||
&ubyte SPBGPR = $d01b
|
||||
&ubyte SPMC = $d01c
|
||||
&ubyte XXPAND = $d01d
|
||||
@@ -98,7 +98,7 @@
|
||||
&ubyte SP5COL = $d02c
|
||||
&ubyte SP6COL = $d02d
|
||||
&ubyte SP7COL = $d02e
|
||||
&ubyte[8] SPCOL = $d027
|
||||
&ubyte[8] SPCOL = $d027
|
||||
|
||||
|
||||
; ---- end of VIC-II registers ----
|
||||
@@ -107,8 +107,8 @@
|
||||
|
||||
&ubyte CIA1PRA = $DC00 ; CIA 1 DRA, keyboard column drive (and joystick control port #2)
|
||||
&ubyte CIA1PRB = $DC01 ; CIA 1 DRB, keyboard row port (and joystick control port #1)
|
||||
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||
&ubyte CIA1DDRA = $DC02 ; CIA 1 DDRA, keyboard column
|
||||
&ubyte CIA1DDRB = $DC03 ; CIA 1 DDRB, keyboard row
|
||||
&ubyte CIA1TAL = $DC04 ; CIA 1 timer A low byte
|
||||
&ubyte CIA1TAH = $DC05 ; CIA 1 timer A high byte
|
||||
&ubyte CIA1TBL = $DC06 ; CIA 1 timer B low byte
|
||||
@@ -124,8 +124,8 @@
|
||||
|
||||
&ubyte CIA2PRA = $DD00 ; CIA 2 DRA, serial port and video address
|
||||
&ubyte CIA2PRB = $DD01 ; CIA 2 DRB, RS232 port / USERPORT
|
||||
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
||||
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
||||
&ubyte CIA2DDRA = $DD02 ; CIA 2 DDRA, serial port and video address
|
||||
&ubyte CIA2DDRB = $DD03 ; CIA 2 DDRB, RS232 port / USERPORT
|
||||
&ubyte CIA2TAL = $DD04 ; CIA 2 timer A low byte
|
||||
&ubyte CIA2TAH = $DD05 ; CIA 2 timer A high byte
|
||||
&ubyte CIA2TBL = $DD06 ; CIA 2 timer B low byte
|
||||
@@ -186,8 +186,8 @@
|
||||
|
||||
; ---- C64 basic routines ----
|
||||
|
||||
asmsub CLEARSCR () -> clobbers(A,X,Y) -> () = $E544 ; clear the screen
|
||||
asmsub HOMECRSR () -> clobbers(A,X,Y) -> () = $E566 ; cursor to top left of screen
|
||||
asmsub CLEARSCR () clobbers(A,X,Y) = $E544 ; clear the screen
|
||||
asmsub HOMECRSR () clobbers(A,X,Y) = $E566 ; cursor to top left of screen
|
||||
|
||||
|
||||
; ---- end of C64 basic routines ----
|
||||
@@ -195,48 +195,48 @@ asmsub HOMECRSR () -> clobbers(A,X,Y) -> () = $E566 ; cursor to top left of sc
|
||||
|
||||
; ---- C64 kernal routines ----
|
||||
|
||||
asmsub STROUT (uword strptr @ AY) -> clobbers(A, X, Y) -> () = $AB1E ; print null-terminated string (use c64scr.print instead)
|
||||
asmsub IRQDFRT () -> clobbers(A,X,Y) -> () = $EA31 ; default IRQ routine
|
||||
asmsub IRQDFEND () -> clobbers(A,X,Y) -> () = $EA81 ; default IRQ end/cleanup
|
||||
asmsub CINT () -> clobbers(A,X,Y) -> () = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
||||
asmsub IOINIT () -> clobbers(A, X) -> () = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
|
||||
asmsub RAMTAS () -> clobbers(A,X,Y) -> () = $FF87 ; initialize RAM, tape buffer, screen
|
||||
asmsub RESTOR () -> clobbers(A,X,Y) -> () = $FF8A ; restore default I/O vectors
|
||||
asmsub VECTOR (ubyte dir @ Pc, uword userptr @ XY) -> clobbers(A,Y) -> () = $FF8D ; read/set I/O vector table
|
||||
asmsub SETMSG (ubyte value @ A) -> clobbers() -> () = $FF90 ; set Kernal message control flag
|
||||
asmsub SECOND (ubyte address @ A) -> clobbers(A) -> () = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
asmsub TKSA (ubyte address @ A) -> clobbers(A) -> () = $FF96 ; (alias: TALKSA) send secondary address after TALK
|
||||
asmsub MEMTOP (ubyte dir @ Pc, uword address @ XY) -> clobbers() -> (uword @ XY) = $FF99 ; read/set top of memory pointer
|
||||
asmsub MEMBOT (ubyte dir @ Pc, uword address @ XY) -> clobbers() -> (uword @ XY) = $FF9C ; read/set bottom of memory pointer
|
||||
asmsub SCNKEY () -> clobbers(A,X,Y) -> () = $FF9F ; scan the keyboard
|
||||
asmsub SETTMO (ubyte timeout @ A) -> clobbers() -> () = $FFA2 ; set time-out flag for IEEE bus
|
||||
asmsub ACPTR () -> clobbers() -> (ubyte @ A) = $FFA5 ; (alias: IECIN) input byte from serial bus
|
||||
asmsub CIOUT (ubyte databyte @ A) -> clobbers() -> () = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
||||
asmsub UNTLK () -> clobbers(A) -> () = $FFAB ; command serial bus device to UNTALK
|
||||
asmsub UNLSN () -> clobbers(A) -> () = $FFAE ; command serial bus device to UNLISTEN
|
||||
asmsub LISTEN (ubyte device @ A) -> clobbers(A) -> () = $FFB1 ; command serial bus device to LISTEN
|
||||
asmsub TALK (ubyte device @ A) -> clobbers(A) -> () = $FFB4 ; command serial bus device to TALK
|
||||
asmsub READST () -> clobbers() -> (ubyte @ A) = $FFB7 ; read I/O status word
|
||||
asmsub SETLFS (ubyte logical @ A, ubyte device @ X, ubyte address @ Y) -> clobbers() -> () = $FFBA ; set logical file parameters
|
||||
asmsub SETNAM (ubyte namelen @ A, str filename @ XY) -> clobbers() -> () = $FFBD ; set filename parameters
|
||||
asmsub OPEN () -> clobbers(A,X,Y) -> () = $FFC0 ; (via 794 ($31A)) open a logical file
|
||||
asmsub CLOSE (ubyte logical @ A) -> clobbers(A,X,Y) -> () = $FFC3 ; (via 796 ($31C)) close a logical file
|
||||
asmsub CHKIN (ubyte logical @ X) -> clobbers(A,X) -> () = $FFC6 ; (via 798 ($31E)) define an input channel
|
||||
asmsub CHKOUT (ubyte logical @ X) -> clobbers(A,X) -> () = $FFC9 ; (via 800 ($320)) define an output channel
|
||||
asmsub CLRCHN () -> clobbers(A,X) -> () = $FFCC ; (via 802 ($322)) restore default devices
|
||||
asmsub CHRIN () -> clobbers(Y) -> (ubyte @ A) = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
asmsub CHROUT (ubyte char @ A) -> clobbers() -> () = $FFD2 ; (via 806 ($326)) output a character
|
||||
asmsub LOAD (ubyte verify @ A, uword address @ XY) -> clobbers() -> (ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y) = $FFD5 ; (via 816 ($330)) load from device
|
||||
asmsub SAVE (ubyte zp_startaddr @ A, uword endaddr @ XY) -> clobbers() -> (ubyte @ Pc, ubyte @ A) = $FFD8 ; (via 818 ($332)) save to a device
|
||||
asmsub SETTIM (ubyte low @ A, ubyte middle @ X, ubyte high @ Y) -> clobbers() -> () = $FFDB ; set the software clock
|
||||
asmsub RDTIM () -> clobbers() -> (ubyte @ A, ubyte @ X, ubyte @ Y) = $FFDE ; read the software clock
|
||||
asmsub STOP () -> clobbers(A,X) -> (ubyte @ Pz, ubyte @ Pc) = $FFE1 ; (via 808 ($328)) check the STOP key
|
||||
asmsub GETIN () -> clobbers(X,Y) -> (ubyte @ A) = $FFE4 ; (via 810 ($32A)) get a character
|
||||
asmsub CLALL () -> clobbers(A,X) -> () = $FFE7 ; (via 812 ($32C)) close all files
|
||||
asmsub UDTIM () -> clobbers(A,X) -> () = $FFEA ; update the software clock
|
||||
asmsub SCREEN () -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFED ; read number of screen rows and columns
|
||||
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> clobbers() -> (ubyte @ X, ubyte @ Y) = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||
asmsub IOBASE () -> clobbers() -> (uword @ XY) = $FFF3 ; read base address of I/O devices
|
||||
asmsub STROUT (uword strptr @ AY) clobbers(A, X, Y) = $AB1E ; print null-terminated string (use c64scr.print instead)
|
||||
asmsub IRQDFRT () clobbers(A,X,Y) = $EA31 ; default IRQ routine
|
||||
asmsub IRQDFEND () clobbers(A,X,Y) = $EA81 ; default IRQ end/cleanup
|
||||
asmsub CINT () clobbers(A,X,Y) = $FF81 ; (alias: SCINIT) initialize screen editor and video chip
|
||||
asmsub IOINIT () clobbers(A, X) = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
|
||||
asmsub RAMTAS () clobbers(A,X,Y) = $FF87 ; initialize RAM, tape buffer, screen
|
||||
asmsub RESTOR () clobbers(A,X,Y) = $FF8A ; restore default I/O vectors
|
||||
asmsub VECTOR (ubyte dir @ Pc, uword userptr @ XY) clobbers(A,Y) = $FF8D ; read/set I/O vector table
|
||||
asmsub SETMSG (ubyte value @ A) = $FF90 ; set Kernal message control flag
|
||||
asmsub SECOND (ubyte address @ A) clobbers(A) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
|
||||
asmsub TKSA (ubyte address @ A) clobbers(A) = $FF96 ; (alias: TALKSA) send secondary address after TALK
|
||||
asmsub MEMTOP (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF99 ; read/set top of memory pointer
|
||||
asmsub MEMBOT (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
|
||||
asmsub SCNKEY () clobbers(A,X,Y) = $FF9F ; scan the keyboard
|
||||
asmsub SETTMO (ubyte timeout @ A) = $FFA2 ; set time-out flag for IEEE bus
|
||||
asmsub ACPTR () -> ubyte @ A = $FFA5 ; (alias: IECIN) input byte from serial bus
|
||||
asmsub CIOUT (ubyte databyte @ A) = $FFA8 ; (alias: IECOUT) output byte to serial bus
|
||||
asmsub UNTLK () clobbers(A) = $FFAB ; command serial bus device to UNTALK
|
||||
asmsub UNLSN () clobbers(A) = $FFAE ; command serial bus device to UNLISTEN
|
||||
asmsub LISTEN (ubyte device @ A) clobbers(A) = $FFB1 ; command serial bus device to LISTEN
|
||||
asmsub TALK (ubyte device @ A) clobbers(A) = $FFB4 ; command serial bus device to TALK
|
||||
asmsub READST () -> ubyte @ A = $FFB7 ; read I/O status word
|
||||
asmsub SETLFS (ubyte logical @ A, ubyte device @ X, ubyte address @ Y) = $FFBA ; set logical file parameters
|
||||
asmsub SETNAM (ubyte namelen @ A, str filename @ XY) = $FFBD ; set filename parameters
|
||||
asmsub OPEN () clobbers(A,X,Y) = $FFC0 ; (via 794 ($31A)) open a logical file
|
||||
asmsub CLOSE (ubyte logical @ A) clobbers(A,X,Y) = $FFC3 ; (via 796 ($31C)) close a logical file
|
||||
asmsub CHKIN (ubyte logical @ X) clobbers(A,X) = $FFC6 ; (via 798 ($31E)) define an input channel
|
||||
asmsub CHKOUT (ubyte logical @ X) clobbers(A,X) = $FFC9 ; (via 800 ($320)) define an output channel
|
||||
asmsub CLRCHN () clobbers(A,X) = $FFCC ; (via 802 ($322)) restore default devices
|
||||
asmsub CHRIN () clobbers(Y) -> ubyte @ A = $FFCF ; (via 804 ($324)) input a character (for keyboard, read a whole line from the screen) A=byte read.
|
||||
asmsub CHROUT (ubyte char @ A) = $FFD2 ; (via 806 ($326)) output a character
|
||||
asmsub LOAD (ubyte verify @ A, uword address @ XY) -> ubyte @Pc, ubyte @ A, ubyte @ X, ubyte @ Y = $FFD5 ; (via 816 ($330)) load from device
|
||||
asmsub SAVE (ubyte zp_startaddr @ A, uword endaddr @ XY) -> ubyte @ Pc, ubyte @ A = $FFD8 ; (via 818 ($332)) save to a device
|
||||
asmsub SETTIM (ubyte low @ A, ubyte middle @ X, ubyte high @ Y) = $FFDB ; set the software clock
|
||||
asmsub RDTIM () -> ubyte @ A, ubyte @ X, ubyte @ Y = $FFDE ; read the software clock
|
||||
asmsub STOP () clobbers(A,X) -> ubyte @ Pz, ubyte @ Pc = $FFE1 ; (via 808 ($328)) check the STOP key
|
||||
asmsub GETIN () clobbers(X,Y) -> ubyte @ A = $FFE4 ; (via 810 ($32A)) get a character
|
||||
asmsub CLALL () clobbers(A,X) = $FFE7 ; (via 812 ($32C)) close all files
|
||||
asmsub UDTIM () clobbers(A,X) = $FFEA ; update the software clock
|
||||
asmsub SCREEN () -> ubyte @ X, ubyte @ Y = $FFED ; read number of screen rows and columns
|
||||
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
|
||||
asmsub IOBASE () -> uword @ XY = $FFF3 ; read base address of I/O devices
|
||||
|
||||
; ---- end of C64 kernal routines ----
|
||||
|
||||
|
||||
@@ -17,7 +17,7 @@
|
||||
|
||||
; ----- utility functions ----
|
||||
|
||||
asmsub ubyte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X, ubyte @ A) {
|
||||
asmsub ubyte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ X, ubyte @ A {
|
||||
; ---- A to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
|
||||
%asm {{
|
||||
ldy #$2f
|
||||
@@ -34,7 +34,7 @@ asmsub ubyte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X,
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub byte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X, ubyte @ A) {
|
||||
asmsub byte2decimal (ubyte value @ A) -> ubyte @ Y, ubyte @ X, ubyte @ A {
|
||||
; ---- A (signed byte) to decimal string in Y/X/A (100s in Y, 10s in X, 1s in A)
|
||||
; note: the '-' is not part of the conversion here if it's a negative number
|
||||
%asm {{
|
||||
@@ -47,7 +47,7 @@ asmsub byte2decimal (ubyte value @ A) -> clobbers() -> (ubyte @ Y, ubyte @ X,
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub ubyte2hex (ubyte value @ A) -> clobbers() -> (ubyte @ A, ubyte @ Y) {
|
||||
asmsub ubyte2hex (ubyte value @ A) -> ubyte @ A, ubyte @ Y {
|
||||
; ---- A to hex string in AY (first hex char in A, second hex char in Y)
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -70,7 +70,7 @@ _hex_digits .text "0123456789abcdef" ; can probably be reused for other stuff as
|
||||
}
|
||||
|
||||
|
||||
asmsub uword2hex (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub uword2hex (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- convert 16 bit uword in A/Y into 4-character hexadecimal string 'uword2hex.output' (0-terminated)
|
||||
%asm {{
|
||||
sta c64.SCRATCH_ZPREG
|
||||
@@ -87,7 +87,7 @@ output .text "0000", $00 ; 0-terminated output buffer (to make printing ea
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub uword2bcd (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub uword2bcd (uword value @ AY) clobbers(A,Y) {
|
||||
; Convert an 16 bit binary value to BCD
|
||||
;
|
||||
; This function converts a 16 bit binary value in A/Y into a 24 bit BCD. It
|
||||
@@ -134,7 +134,7 @@ bcdbuff .byte 0,0,0
|
||||
}
|
||||
|
||||
|
||||
asmsub uword2decimal (uword value @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
asmsub uword2decimal (uword value @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- convert 16 bit uword in A/Y into 0-terminated decimal string into memory 'uword2decimal.output'
|
||||
; returns length of resulting string in Y
|
||||
%asm {{
|
||||
@@ -173,7 +173,7 @@ output .text "00000", $00 ; 0 terminated
|
||||
}
|
||||
|
||||
|
||||
asmsub str2uword(str string @ AY) -> clobbers() -> (uword @ AY) {
|
||||
asmsub str2uword(str string @ AY) -> uword @ AY {
|
||||
; -- returns the unsigned word value of the string number argument in AY
|
||||
; the number may NOT be preceded by a + sign and may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
@@ -228,7 +228,7 @@ _result_times_10 ; (W*4 + W)*2
|
||||
}
|
||||
|
||||
|
||||
asmsub str2word(str string @ AY) -> clobbers() -> (word @ AY) {
|
||||
asmsub str2word(str string @ AY) -> word @ AY {
|
||||
; -- returns the signed word value of the string number argument in AY
|
||||
; the number may be preceded by a + or - sign but may NOT contain spaces
|
||||
; (any non-digit character will terminate the number string that is parsed)
|
||||
@@ -284,7 +284,7 @@ _negative .byte 0
|
||||
}
|
||||
|
||||
|
||||
asmsub set_irqvec_excl() -> clobbers(A) -> () {
|
||||
asmsub set_irqvec_excl() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
@@ -303,7 +303,7 @@ _irq_handler jsr set_irqvec._irq_handler_init
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_irqvec() -> clobbers(A) -> () {
|
||||
asmsub set_irqvec() clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<_irq_handler
|
||||
@@ -373,7 +373,7 @@ IRQ_SCRATCH_ZPWORD2 .word 0
|
||||
}
|
||||
|
||||
|
||||
asmsub restore_irqvec() -> clobbers() -> () {
|
||||
asmsub restore_irqvec() {
|
||||
%asm {{
|
||||
sei
|
||||
lda #<c64.IRQDFRT
|
||||
@@ -390,7 +390,7 @@ asmsub restore_irqvec() -> clobbers() -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub set_rasterirq(uword rasterpos @ AY) -> clobbers(A) -> () {
|
||||
asmsub set_rasterirq(uword rasterpos @ AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
jsr _setup_raster_irq
|
||||
@@ -431,7 +431,7 @@ _setup_raster_irq
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) -> clobbers(A) -> () {
|
||||
asmsub set_rasterirq_excl(uword rasterpos @ AY) clobbers(A) {
|
||||
%asm {{
|
||||
sei
|
||||
jsr set_rasterirq._setup_raster_irq
|
||||
@@ -465,7 +465,7 @@ _raster_irq_handler
|
||||
; ---- this block contains (character) Screen and text I/O related functions ----
|
||||
|
||||
|
||||
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) -> clobbers(A) -> () {
|
||||
asmsub clear_screen (ubyte char @ A, ubyte color @ Y) clobbers(A) {
|
||||
; ---- clear the character screen with the given fill character and character color.
|
||||
; (assumes screen and color matrix are at their default addresses)
|
||||
|
||||
@@ -481,7 +481,7 @@ asmsub clear_screen (ubyte char @ A, ubyte color @ Y) -> clobbers(A) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub clear_screenchars (ubyte char @ A) -> clobbers(Y) -> () {
|
||||
asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
||||
; ---- clear the character screen with the given fill character (leaves colors)
|
||||
; (assumes screen matrix is at the default address)
|
||||
%asm {{
|
||||
@@ -501,7 +501,7 @@ _loop sta c64.Screen,y
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub clear_screencolors (ubyte color @ A) -> clobbers(Y) -> () {
|
||||
asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
||||
; ---- clear the character screen colors with the given color (leaves characters).
|
||||
; (assumes color matrix is at the default address)
|
||||
%asm {{
|
||||
@@ -522,7 +522,7 @@ _loop sta c64.Colors,y
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) -> clobbers(A, Y) -> () {
|
||||
asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
||||
; ---- scroll the whole screen 1 character to the left
|
||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -583,7 +583,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_right_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character to the right
|
||||
; contents of the leftmost column are unchanged, you should clear/refill this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -636,7 +636,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character up
|
||||
; contents of the bottom row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -689,7 +689,7 @@ _scroll_screen ; scroll the screen memory
|
||||
}
|
||||
|
||||
|
||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) -> clobbers(A) -> () {
|
||||
asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
|
||||
; ---- scroll the whole screen 1 character down
|
||||
; contents of the top row are unchanged, you should refill/clear this yourself
|
||||
; Carry flag determines if screen color data must be scrolled too
|
||||
@@ -743,7 +743,7 @@ _scroll_screen ; scroll the screen memory
|
||||
|
||||
|
||||
|
||||
asmsub print (str text @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub print (str text @ AY) clobbers(A,Y) {
|
||||
; ---- print null terminated string from A/Y
|
||||
; note: the compiler contains an optimization that will replace
|
||||
; a call to this subroutine with a string argument of just one char,
|
||||
@@ -762,7 +762,7 @@ asmsub print (str text @ AY) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub print_ub0 (ubyte value @ A) -> clobbers(A,Y) -> () {
|
||||
asmsub print_ub0 (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, with left padding 0s (3 positions total)
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -780,7 +780,7 @@ asmsub print_ub0 (ubyte value @ A) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub print_ub (ubyte value @ A) -> clobbers(A,Y) -> () {
|
||||
asmsub print_ub (ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -803,7 +803,7 @@ _end pla
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_b (byte value @ A) -> clobbers(A,Y) -> () {
|
||||
asmsub print_b (byte value @ A) clobbers(A,Y) {
|
||||
; ---- print the byte in A in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -821,7 +821,7 @@ asmsub print_b (byte value @ A) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) -> () {
|
||||
asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -840,7 +840,7 @@ asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) -> ()
|
||||
}
|
||||
|
||||
|
||||
asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) ->() {
|
||||
asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
|
||||
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
@@ -862,7 +862,7 @@ asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) -> clobbers(A,Y) ->()
|
||||
}
|
||||
|
||||
|
||||
asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) ->() {
|
||||
asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
|
||||
%asm {{
|
||||
pha
|
||||
@@ -875,7 +875,7 @@ asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) ->()
|
||||
}
|
||||
|
||||
|
||||
asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in hexadecimal form (4 digits)
|
||||
; (if Carry is set, a radix prefix '$' is printed as well)
|
||||
%asm {{
|
||||
@@ -889,7 +889,7 @@ asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) -> clobbers(A,Y) -> ()
|
||||
}
|
||||
|
||||
|
||||
asmsub print_uw0 (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub print_uw0 (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, with left padding 0s (5 positions total)
|
||||
%asm {{
|
||||
jsr c64utils.uword2decimal
|
||||
@@ -904,7 +904,7 @@ asmsub print_uw0 (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
}
|
||||
|
||||
|
||||
asmsub print_uw (uword value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
||||
; ---- print the uword in A/Y in decimal form, without left padding 0s
|
||||
%asm {{
|
||||
jsr c64utils.uword2decimal
|
||||
@@ -936,7 +936,7 @@ _pr_decimal
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () {
|
||||
asmsub print_w (word value @ AY) clobbers(A,Y) {
|
||||
; ---- print the (signed) word in A/Y in decimal form, without left padding 0's
|
||||
%asm {{
|
||||
cpy #0
|
||||
@@ -957,7 +957,7 @@ asmsub print_w (word value @ AY) -> clobbers(A,Y) -> () {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub input_chars (uword buffer @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
asmsub input_chars (uword buffer @ AY) clobbers(A) -> ubyte @ Y {
|
||||
; ---- Input a string (max. 80 chars) from the keyboard. Returns length in Y. (string is terminated with a 0 byte as well)
|
||||
; It assumes the keyboard is selected as I/O channel!
|
||||
|
||||
@@ -978,7 +978,7 @@ asmsub input_chars (uword buffer @ AY) -> clobbers(A) -> (ubyte @ Y) {
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setchr (ubyte col @Y, ubyte row @A) -> clobbers(A) -> () {
|
||||
asmsub setchr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
||||
; ---- set the character in SCRATCH_ZPB1 on the screen matrix at the given position
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPREG
|
||||
@@ -1000,7 +1000,7 @@ _screenrows .word $0400 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getchr (ubyte col @Y, ubyte row @A) -> clobbers(Y) -> (ubyte @ A) {
|
||||
asmsub getchr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the character in the screen matrix at the given location
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPB1
|
||||
@@ -1019,7 +1019,7 @@ _mod lda $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub setclr (ubyte col @Y, ubyte row @A) -> clobbers(A) -> () {
|
||||
asmsub setclr (ubyte col @Y, ubyte row @A) clobbers(A) {
|
||||
; ---- set the color in SCRATCH_ZPB1 on the screen matrix at the given position
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPREG
|
||||
@@ -1041,7 +1041,7 @@ _colorrows .word $d800 + range(0, 1000, 40)
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub getclr (ubyte col @Y, ubyte row @A) -> clobbers(Y) -> (ubyte @ A) {
|
||||
asmsub getclr (ubyte col @Y, ubyte row @A) clobbers(Y) -> ubyte @ A {
|
||||
; ---- get the color in the screen color matrix at the given location
|
||||
%asm {{
|
||||
sty c64.SCRATCH_ZPB1
|
||||
@@ -1086,7 +1086,7 @@ _colormod sta $ffff ; modified
|
||||
}}
|
||||
}
|
||||
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) -> clobbers(A) -> () {
|
||||
asmsub plot (ubyte col @ Y, ubyte row @ A) clobbers(A) {
|
||||
; ---- safe wrapper around PLOT kernel routine, to save the X register.
|
||||
%asm {{
|
||||
stx c64.SCRATCH_ZPREGX
|
||||
|
||||
@@ -1 +1 @@
|
||||
1.8
|
||||
1.9
|
||||
@@ -1,23 +1,10 @@
|
||||
package prog8
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.astvm.AstVm
|
||||
import prog8.vm.astvm.AstVm
|
||||
import prog8.vm.stackvm.stackVmMain
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.c64.AsmGen
|
||||
import prog8.compiler.target.c64.C64Zeropage
|
||||
import prog8.optimizing.constantFold
|
||||
import prog8.optimizing.optimizeStatements
|
||||
import prog8.optimizing.simplifyExpressions
|
||||
import prog8.parser.ParsingFailedError
|
||||
import prog8.parser.importLibraryModule
|
||||
import prog8.parser.importModule
|
||||
import prog8.parser.moduleName
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.lang.Exception
|
||||
import java.nio.file.Paths
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
|
||||
fun main(args: Array<String>) {
|
||||
@@ -75,115 +62,9 @@ private fun compileMain(args: Array<String>) {
|
||||
usage()
|
||||
|
||||
val filepath = Paths.get(moduleFile).normalize()
|
||||
var programname = "?"
|
||||
lateinit var programAst: Program
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
// import main module and everything it needs
|
||||
println("Parsing...")
|
||||
programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||
importModule(programAst, filepath)
|
||||
|
||||
val compilerOptions = determineCompilationOptions(programAst)
|
||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||
|
||||
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries
|
||||
if(compilerOptions.launcher==LauncherType.BASIC || compilerOptions.output==OutputType.PRG) {
|
||||
importLibraryModule(programAst, "c64lib")
|
||||
importLibraryModule(programAst, "c64utils")
|
||||
}
|
||||
|
||||
// always import prog8lib and math
|
||||
importLibraryModule(programAst, "math")
|
||||
importLibraryModule(programAst, "prog8lib")
|
||||
|
||||
|
||||
// perform initial syntax checks and constant folding
|
||||
println("Syntax check...")
|
||||
val time1= measureTimeMillis {
|
||||
programAst.checkIdentifiers()
|
||||
}
|
||||
//println(" time1: $time1")
|
||||
val time2 = measureTimeMillis {
|
||||
programAst.constantFold()
|
||||
}
|
||||
//println(" time2: $time2")
|
||||
val time3 = measureTimeMillis {
|
||||
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
||||
}
|
||||
//println(" time3: $time3")
|
||||
val time4 = measureTimeMillis {
|
||||
programAst.checkValid(compilerOptions) // check if tree is valid
|
||||
}
|
||||
//println(" time4: $time4")
|
||||
|
||||
programAst.checkIdentifiers()
|
||||
if(optimize) {
|
||||
// optimize the parse tree
|
||||
println("Optimizing...")
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = programAst.simplifyExpressions()
|
||||
val optsDone2 = programAst.optimizeStatements(optimizeInlining)
|
||||
if (optsDone1 + optsDone2 == 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
programAst.checkValid(compilerOptions) // check if final tree is valid
|
||||
programAst.checkRecursion() // check if there are recursive subroutine calls
|
||||
|
||||
// namespace.debugPrint()
|
||||
|
||||
// compile the syntax tree into stackvmProg form, and optimize that
|
||||
val compiler = Compiler(programAst)
|
||||
val intermediate = compiler.compile(compilerOptions)
|
||||
if(optimize)
|
||||
intermediate.optimize()
|
||||
|
||||
if(writeVmCode) {
|
||||
val stackVmFilename = intermediate.name + ".vm.txt"
|
||||
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
||||
intermediate.writeCode(stackvmFile)
|
||||
stackvmFile.close()
|
||||
println("StackVM program code written to '$stackVmFilename'")
|
||||
}
|
||||
|
||||
if(writeAssembly) {
|
||||
val zeropage = C64Zeropage(compilerOptions)
|
||||
intermediate.allocateZeropage(zeropage)
|
||||
val assembly = AsmGen(compilerOptions, intermediate, programAst.heap, zeropage).compileToAssembly(optimize)
|
||||
assembly.assemble(compilerOptions)
|
||||
programname = assembly.name
|
||||
}
|
||||
}
|
||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||
|
||||
} catch (px: ParsingFailedError) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
System.err.println(px.message)
|
||||
System.err.print("\u001b[0m") // reset
|
||||
exitProcess(1)
|
||||
} catch (ax: AstException) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
System.err.println(ax.toString())
|
||||
System.err.print("\u001b[0m") // reset
|
||||
exitProcess(1)
|
||||
} catch (x: Exception) {
|
||||
print("\u001b[91m") // bright red
|
||||
println("\n* internal error *")
|
||||
print("\u001b[0m") // reset
|
||||
System.out.flush()
|
||||
throw x
|
||||
} catch (x: NotImplementedError) {
|
||||
print("\u001b[91m") // bright red
|
||||
println("\n* internal error: missing feature/code *")
|
||||
print("\u001b[0m") // reset
|
||||
System.out.flush()
|
||||
throw x
|
||||
}
|
||||
val (programAst, programName) = compileProgram(filepath, optimize, optimizeInlining,
|
||||
!launchAstVm, writeVmCode, writeAssembly)
|
||||
|
||||
if(launchAstVm) {
|
||||
println("\nLaunching AST-based vm...")
|
||||
@@ -192,51 +73,19 @@ private fun compileMain(args: Array<String>) {
|
||||
}
|
||||
|
||||
if(emulatorToStart.isNotEmpty()) {
|
||||
println("\nStarting C-64 emulator $emulatorToStart...")
|
||||
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "$programname.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programname+".prg")
|
||||
val process = ProcessBuilder(cmdline).inheritIO().start()
|
||||
process.waitFor()
|
||||
if(programName==null)
|
||||
println("\nCan't start emulator because no program was assembled.")
|
||||
else {
|
||||
println("\nStarting C-64 emulator $emulatorToStart...")
|
||||
val cmdline = listOf(emulatorToStart, "-silent", "-moncommands", "$programName.vice-mon-list",
|
||||
"-autostartprgmode", "1", "-autostart-warp", "-autostart", programName + ".prg")
|
||||
val process = ProcessBuilder(cmdline).inheritIO().start()
|
||||
process.waitFor()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
val mainModule = program.modules.first()
|
||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" }
|
||||
as? Directive)?.args?.single()?.int ?: 0
|
||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||
val zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
val zpReserved = mainModule.statements
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled
|
||||
)
|
||||
}
|
||||
|
||||
private fun usage() {
|
||||
System.err.println("Missing argument(s):")
|
||||
System.err.println(" [-emu] auto-start the 'x64' C-64 emulator after successful compilation")
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
@@ -1,120 +0,0 @@
|
||||
package prog8.ast
|
||||
|
||||
/**
|
||||
* Checks for the occurrence of recursive subroutine calls
|
||||
*/
|
||||
|
||||
internal fun Program.checkRecursion() {
|
||||
val checker = AstRecursionChecker(namespace)
|
||||
checker.process(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
private class DirectedGraph<VT> {
|
||||
private val graph = mutableMapOf<VT, MutableSet<VT>>()
|
||||
private var uniqueVertices = mutableSetOf<VT>()
|
||||
val numVertices : Int
|
||||
get() = uniqueVertices.size
|
||||
|
||||
fun add(from: VT, to: VT) {
|
||||
var targets = graph[from]
|
||||
if(targets==null) {
|
||||
targets = mutableSetOf()
|
||||
graph[from] = targets
|
||||
}
|
||||
targets.add(to)
|
||||
uniqueVertices.add(from)
|
||||
uniqueVertices.add(to)
|
||||
}
|
||||
|
||||
fun print() {
|
||||
println("#vertices: $numVertices")
|
||||
graph.forEach { (from, to) ->
|
||||
println("$from CALLS:")
|
||||
to.forEach { println(" $it") }
|
||||
}
|
||||
val cycle = checkForCycle()
|
||||
if(cycle.isNotEmpty()) {
|
||||
println("CYCLIC! $cycle")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForCycle(): MutableList<VT> {
|
||||
val visited = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val recStack = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val cycle = mutableListOf<VT>()
|
||||
for(node in uniqueVertices) {
|
||||
if(isCyclicUntil(node, visited, recStack, cycle))
|
||||
return cycle
|
||||
}
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
private fun isCyclicUntil(node: VT,
|
||||
visited: MutableMap<VT, Boolean>,
|
||||
recStack: MutableMap<VT, Boolean>,
|
||||
cycleNodes: MutableList<VT>): Boolean {
|
||||
|
||||
if(recStack[node]==true) return true
|
||||
if(visited[node]==true) return false
|
||||
|
||||
// mark current node as visited and add to recursion stack
|
||||
visited[node] = true
|
||||
recStack[node] = true
|
||||
|
||||
// recurse for all neighbours
|
||||
val neighbors = graph[node]
|
||||
if(neighbors!=null) {
|
||||
for (neighbour in neighbors) {
|
||||
if (isCyclicUntil(neighbour, visited, recStack, cycleNodes)) {
|
||||
cycleNodes.add(node)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pop node from recursion stack
|
||||
recStack[node] = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class AstRecursionChecker(private val namespace: INameScope) : IAstProcessor {
|
||||
private val callGraph = DirectedGraph<INameScope>()
|
||||
|
||||
internal fun result(): List<AstException> {
|
||||
val cycle = callGraph.checkForCycle()
|
||||
if(cycle.isEmpty())
|
||||
return emptyList()
|
||||
val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
||||
return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val scope = functionCallStatement.definingScope()
|
||||
val targetStatement = functionCallStatement.target.targetStatement(namespace)
|
||||
if(targetStatement!=null) {
|
||||
val targetScope = when (targetStatement) {
|
||||
is Subroutine -> targetStatement
|
||||
else -> targetStatement.definingScope()
|
||||
}
|
||||
callGraph.add(scope, targetScope)
|
||||
}
|
||||
return super.process(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
val scope = functionCall.definingScope()
|
||||
val targetStatement = functionCall.target.targetStatement(namespace)
|
||||
if(targetStatement!=null) {
|
||||
val targetScope = when (targetStatement) {
|
||||
is Subroutine -> targetStatement
|
||||
else -> targetStatement.definingScope()
|
||||
}
|
||||
callGraph.add(scope, targetScope)
|
||||
}
|
||||
return super.process(functionCall)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,92 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.NameError
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.Label
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||
|
||||
class Program(val name: String, val modules: MutableList<Module>) {
|
||||
val namespace = GlobalNamespace(modules)
|
||||
val heap = HeapValues()
|
||||
|
||||
val loadAddress: Int
|
||||
get() = modules.first().loadAddress
|
||||
|
||||
fun entrypoint(): Subroutine? {
|
||||
val mainBlocks = modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
|
||||
if(mainBlocks.size > 1)
|
||||
throw FatalAstException("more than one 'main' block")
|
||||
return if(mainBlocks.isEmpty()) {
|
||||
null
|
||||
} else {
|
||||
mainBlocks[0].subScopes()["start"] as Subroutine?
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class Module(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position,
|
||||
val isLibraryModule: Boolean,
|
||||
val source: Path) : Node, INameScope {
|
||||
override lateinit var parent: Node
|
||||
lateinit var program: Program
|
||||
val importedBy = mutableListOf<Module>()
|
||||
val imports = mutableSetOf<Module>()
|
||||
|
||||
var loadAddress: Int = 0 // can be set with the %address directive
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach {it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun definingScope(): INameScope = program.namespace
|
||||
|
||||
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
||||
}
|
||||
|
||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||
override val name = "<<<global>>>"
|
||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||
override val statements = mutableListOf<IStatement>()
|
||||
override var parent: Node = ParentSentinel
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
modules.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: List<String>, localContext: Node): IStatement? {
|
||||
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
||||
// builtin functions always exist, return a dummy localContext for them
|
||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
||||
builtinPlaceholder.parent = ParentSentinel
|
||||
return builtinPlaceholder
|
||||
}
|
||||
|
||||
val stmt = localContext.definingModule().lookup(scopedName, localContext)
|
||||
return when (stmt) {
|
||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||
null -> null
|
||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||
override var statements = mutableListOf<IStatement>()
|
||||
override var parent: Node = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
}
|
||||
@@ -0,0 +1,197 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
|
||||
interface Node {
|
||||
val position: Position
|
||||
var parent: Node // will be linked correctly later (late init)
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun definingModule(): Module {
|
||||
if(this is Module)
|
||||
return this
|
||||
return findParentNode<Module>(this)!!
|
||||
}
|
||||
|
||||
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
||||
|
||||
fun definingScope(): INameScope {
|
||||
val scope = findParentNode<INameScope>(this)
|
||||
if(scope!=null) {
|
||||
return scope
|
||||
}
|
||||
if(this is Label && this.name.startsWith("builtin::")) {
|
||||
return BuiltinFunctionScopePlaceholder
|
||||
}
|
||||
if(this is GlobalNamespace)
|
||||
return this
|
||||
throw FatalAstException("scope missing from $this")
|
||||
}
|
||||
}
|
||||
|
||||
interface IStatement : Node {
|
||||
fun accept(visitor: IAstModifyingVisitor) : IStatement
|
||||
fun accept(visitor: IAstVisitor)
|
||||
fun makeScopedName(name: String): String {
|
||||
// easy way out is to always return the full scoped name.
|
||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||
// and like this, we can cache the name even,
|
||||
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
||||
val scope = mutableListOf<String>()
|
||||
var statementScope = this.parent
|
||||
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
||||
if(statementScope is INameScope) {
|
||||
scope.add(0, statementScope.name)
|
||||
}
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
if(name.isNotEmpty())
|
||||
scope.add(name)
|
||||
return scope.joinToString(".")
|
||||
}
|
||||
|
||||
val expensiveToInline: Boolean
|
||||
|
||||
fun definingBlock(): Block {
|
||||
if(this is Block)
|
||||
return this
|
||||
return findParentNode<Block>(this)!!
|
||||
}
|
||||
}
|
||||
|
||||
interface IFunctionCall {
|
||||
var target: IdentifierReference
|
||||
var arglist: MutableList<IExpression>
|
||||
}
|
||||
|
||||
interface INameScope {
|
||||
val name: String
|
||||
val position: Position
|
||||
val statements: MutableList<IStatement>
|
||||
val parent: Node
|
||||
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun subScopes(): Map<String, INameScope> {
|
||||
val subscopes = mutableMapOf<String, INameScope>()
|
||||
for(stmt in statements) {
|
||||
when(stmt) {
|
||||
is INameScope -> subscopes[stmt.name] = stmt
|
||||
is ForLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is RepeatLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is BranchStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is IfStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
}
|
||||
}
|
||||
return subscopes
|
||||
}
|
||||
|
||||
fun getLabelOrVariable(name: String): IStatement? {
|
||||
// TODO this is called A LOT and could perhaps be optimized a bit more, but adding a cache didn't make much of a practical runtime difference
|
||||
for (stmt in statements) {
|
||||
if (stmt is VarDecl && stmt.name==name) return stmt
|
||||
if (stmt is Label && stmt.name==name) return stmt
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun allDefinedSymbols(): List<Pair<String, IStatement>> {
|
||||
return statements.mapNotNull {
|
||||
when (it) {
|
||||
is Label -> it.name to it
|
||||
is VarDecl -> it.name to it
|
||||
is Subroutine -> it.name to it
|
||||
is Block -> it.name to it
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lookup(scopedName: List<String>, localContext: Node) : IStatement? {
|
||||
if(scopedName.size>1) {
|
||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||
for(module in localContext.definingModule().program.modules) {
|
||||
var scope: INameScope? = module
|
||||
for(name in scopedName.dropLast(1)) {
|
||||
scope = scope?.subScopes()?.get(name)
|
||||
if(scope==null)
|
||||
break
|
||||
}
|
||||
if(scope!=null) {
|
||||
val result = scope.getLabelOrVariable(scopedName.last())
|
||||
if(result!=null)
|
||||
return result
|
||||
return scope.subScopes()[scopedName.last()] as IStatement?
|
||||
}
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
// unqualified name, find the scope the localContext is in, look in that first
|
||||
var statementScope = localContext
|
||||
while(statementScope !is ParentSentinel) {
|
||||
val localScope = statementScope.definingScope()
|
||||
val result = localScope.getLabelOrVariable(scopedName[0])
|
||||
if (result != null)
|
||||
return result
|
||||
val subscope = localScope.subScopes()[scopedName[0]] as IStatement?
|
||||
if (subscope != null)
|
||||
return subscope
|
||||
// not found in this scope, look one higher up
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||
|
||||
fun remove(stmt: IStatement) {
|
||||
if(!statements.remove(stmt))
|
||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||
}
|
||||
}
|
||||
|
||||
interface IExpression: Node {
|
||||
fun constValue(program: Program): LiteralValue?
|
||||
fun accept(visitor: IAstModifyingVisitor): IExpression
|
||||
fun accept(visitor: IAstVisitor)
|
||||
fun referencesIdentifier(name: String): Boolean
|
||||
fun inferType(program: Program): DataType?
|
||||
|
||||
infix fun isSameAs(other: IExpression): Boolean {
|
||||
if(this===other)
|
||||
return true
|
||||
when(this) {
|
||||
is RegisterExpr ->
|
||||
return (other is RegisterExpr && other.register==register)
|
||||
is IdentifierReference ->
|
||||
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
||||
is PrefixExpression ->
|
||||
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||
is BinaryExpression ->
|
||||
return (other is BinaryExpression && other.operator==operator
|
||||
&& other.left isSameAs left
|
||||
&& other.right isSameAs right)
|
||||
is ArrayIndexedExpression -> {
|
||||
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||
}
|
||||
is LiteralValue -> return (other is LiteralValue && other==this)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,590 @@
|
||||
package prog8.ast.antlr
|
||||
|
||||
import org.antlr.v4.runtime.IntStream
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import java.io.CharConversionException
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.parser.CustomLexer
|
||||
import prog8.parser.prog8Parser
|
||||
|
||||
|
||||
/***************** Antlr Extension methods to create AST ****************/
|
||||
|
||||
private data class NumericLiteral(val number: Number, val datatype: DataType)
|
||||
|
||||
|
||||
fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module {
|
||||
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
|
||||
return Module(nameWithoutSuffix, modulestatement().asSequence().map { it.toAst(isLibrary) }.toMutableList(), toPosition(), isLibrary, source)
|
||||
}
|
||||
|
||||
|
||||
private fun ParserRuleContext.toPosition() : Position {
|
||||
val customTokensource = this.start.tokenSource as? CustomLexer
|
||||
val filename =
|
||||
when {
|
||||
customTokensource!=null -> customTokensource.modulePath.fileName.toString()
|
||||
start.tokenSource.sourceName == IntStream.UNKNOWN_SOURCE_NAME -> "@internal@"
|
||||
else -> File(start.inputStream.sourceName).name
|
||||
}
|
||||
// note: be ware of TAB characters in the source text, they count as 1 column...
|
||||
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
|
||||
val directive = directive()?.toAst()
|
||||
if(directive!=null) return directive
|
||||
|
||||
val block = block()?.toAst(isInLibrary)
|
||||
if(block!=null) return block
|
||||
|
||||
throw FatalAstException(text)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
|
||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||
|
||||
|
||||
private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
vardecl()?.let {
|
||||
return VarDecl(VarDeclType.VAR,
|
||||
it.datatype().toAst(),
|
||||
it.ZEROPAGE() != null,
|
||||
it.arrayindex()?.toAst(),
|
||||
it.identifier().text,
|
||||
null,
|
||||
it.ARRAYSIG() != null || it.arrayindex() != null,
|
||||
false,
|
||||
it.toPosition())
|
||||
}
|
||||
|
||||
varinitializer()?.let {
|
||||
val vd = it.vardecl()
|
||||
return VarDecl(VarDeclType.VAR,
|
||||
vd.datatype().toAst(),
|
||||
vd.ZEROPAGE() != null,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.identifier().text,
|
||||
it.expression().toAst(),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
it.toPosition())
|
||||
}
|
||||
|
||||
constdecl()?.let {
|
||||
val cvarinit = it.varinitializer()
|
||||
val vd = cvarinit.vardecl()
|
||||
return VarDecl(VarDeclType.CONST,
|
||||
vd.datatype().toAst(),
|
||||
vd.ZEROPAGE() != null,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.identifier().text,
|
||||
cvarinit.expression().toAst(),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
cvarinit.toPosition())
|
||||
}
|
||||
|
||||
memoryvardecl()?.let {
|
||||
val mvarinit = it.varinitializer()
|
||||
val vd = mvarinit.vardecl()
|
||||
return VarDecl(VarDeclType.MEMORY,
|
||||
vd.datatype().toAst(),
|
||||
vd.ZEROPAGE() != null,
|
||||
vd.arrayindex()?.toAst(),
|
||||
vd.identifier().text,
|
||||
mvarinit.expression().toAst(),
|
||||
vd.ARRAYSIG() != null || vd.arrayindex() != null,
|
||||
false,
|
||||
mvarinit.toPosition())
|
||||
}
|
||||
|
||||
assignment()?.let {
|
||||
return Assignment(it.assign_targets().toAst(), null, it.expression().toAst(), it.toPosition())
|
||||
}
|
||||
|
||||
augassignment()?.let {
|
||||
return Assignment(listOf(it.assign_target().toAst()),
|
||||
it.operator.text,
|
||||
it.expression().toAst(),
|
||||
it.toPosition())
|
||||
}
|
||||
|
||||
postincrdecr()?.let {
|
||||
return PostIncrDecr(it.assign_target().toAst(), it.operator.text, it.toPosition())
|
||||
}
|
||||
|
||||
val directive = directive()?.toAst()
|
||||
if(directive!=null) return directive
|
||||
|
||||
val label = labeldef()?.toAst()
|
||||
if(label!=null) return label
|
||||
|
||||
val jump = unconditionaljump()?.toAst()
|
||||
if(jump!=null) return jump
|
||||
|
||||
val fcall = functioncall_stmt()?.toAst()
|
||||
if(fcall!=null) return fcall
|
||||
|
||||
val ifstmt = if_stmt()?.toAst()
|
||||
if(ifstmt!=null) return ifstmt
|
||||
|
||||
val returnstmt = returnstmt()?.toAst()
|
||||
if(returnstmt!=null) return returnstmt
|
||||
|
||||
val sub = subroutine()?.toAst()
|
||||
if(sub!=null) return sub
|
||||
|
||||
val asm = inlineasm()?.toAst()
|
||||
if(asm!=null) return asm
|
||||
|
||||
val branchstmt = branch_stmt()?.toAst()
|
||||
if(branchstmt!=null) return branchstmt
|
||||
|
||||
val forloop = forloop()?.toAst()
|
||||
if(forloop!=null) return forloop
|
||||
|
||||
val repeatloop = repeatloop()?.toAst()
|
||||
if(repeatloop!=null) return repeatloop
|
||||
|
||||
val whileloop = whileloop()?.toAst()
|
||||
if(whileloop!=null) return whileloop
|
||||
|
||||
val breakstmt = breakstmt()?.toAst()
|
||||
if(breakstmt!=null) return breakstmt
|
||||
|
||||
val continuestmt = continuestmt()?.toAst()
|
||||
if(continuestmt!=null) return continuestmt
|
||||
|
||||
val asmsubstmt = asmsubroutine()?.toAst()
|
||||
if(asmsubstmt!=null) return asmsubstmt
|
||||
|
||||
val whenstmt = whenstmt()?.toAst()
|
||||
if(whenstmt!=null) return whenstmt
|
||||
|
||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||
}
|
||||
|
||||
private fun prog8Parser.Assign_targetsContext.toAst(): List<AssignTarget> = assign_target().map { it.toAst() }
|
||||
|
||||
|
||||
private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
|
||||
val name = identifier().text
|
||||
val address = asmsub_address()?.address?.toAst()?.number?.toInt()
|
||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||
val returns = asmsub_returns()?.toAst() ?: emptyList()
|
||||
val normalParameters = params.map { SubroutineParameter(it.name, it.type, it.position) }
|
||||
val normalReturnvalues = returns.map { it.type }
|
||||
val paramRegisters = params.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||
val returnRegisters = returns.map { RegisterOrStatusflag(it.registerOrPair, it.statusflag, it.stack) }
|
||||
val clobbers = asmsub_clobbers()?.clobber()?.toAst() ?: emptySet()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf()
|
||||
return Subroutine(name, normalParameters, normalReturnvalues,
|
||||
paramRegisters, returnRegisters, clobbers, address, true, statements, toPosition())
|
||||
}
|
||||
|
||||
private class AsmSubroutineParameter(name: String,
|
||||
type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
val stack: Boolean,
|
||||
position: Position) : SubroutineParameter(name, type, position)
|
||||
|
||||
private class AsmSubroutineReturn(val type: DataType,
|
||||
val registerOrPair: RegisterOrPair?,
|
||||
val statusflag: Statusflag?,
|
||||
val stack: Boolean,
|
||||
val position: Position)
|
||||
|
||||
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
|
||||
= this.register().asSequence().map { it.toAst() }.toSet()
|
||||
|
||||
|
||||
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
||||
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
|
||||
|
||||
private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
|
||||
= asmsub_param().map {
|
||||
AsmSubroutineParameter(it.vardecl().identifier().text, it.vardecl().datatype().toAst(),
|
||||
it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
||||
|
||||
|
||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): IStatement {
|
||||
val location = scoped_identifier().toAst()
|
||||
return if(expression_list() == null)
|
||||
FunctionCallStatement(location, mutableListOf(), toPosition())
|
||||
else
|
||||
FunctionCallStatement(location, expression_list().toAst().toMutableList(), toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
|
||||
val location = scoped_identifier().toAst()
|
||||
return if(expression_list() == null)
|
||||
FunctionCall(location, mutableListOf(), toPosition())
|
||||
else
|
||||
FunctionCall(location, expression_list().toAst().toMutableList(), toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.InlineasmContext.toAst() =
|
||||
InlineAssembly(INLINEASMBLOCK().text, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.ReturnstmtContext.toAst() : Return {
|
||||
val values = expression_list()
|
||||
return Return(values?.toAst() ?: emptyList(), toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
||||
val address = integerliteral()?.toAst()?.number?.toInt()
|
||||
val identifier = scoped_identifier()?.toAst()
|
||||
return Jump(address, identifier, null, toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.LabeldefContext.toAst(): IStatement =
|
||||
Label(children[0].text, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
|
||||
return Subroutine(identifier().text,
|
||||
sub_params()?.toAst() ?: emptyList(),
|
||||
sub_return_part()?.toAst() ?: emptyList(),
|
||||
emptyList(),
|
||||
emptyList(),
|
||||
emptySet(),
|
||||
null,
|
||||
false,
|
||||
statement_block()?.toAst() ?: mutableListOf(),
|
||||
toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.Sub_return_partContext.toAst(): List<DataType> {
|
||||
val returns = sub_returns() ?: return emptyList()
|
||||
return returns.datatype().map { it.toAst() }
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||
vardecl().map {
|
||||
SubroutineParameter(it.identifier().text, it.datatype().toAst(), it.toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
||||
val register = register()?.toAst()
|
||||
val identifier = scoped_identifier()
|
||||
return when {
|
||||
register!=null -> AssignTarget(register, null, null, null, toPosition())
|
||||
identifier!=null -> AssignTarget(null, identifier.toAst(), null, null, toPosition())
|
||||
arrayindexed()!=null -> AssignTarget(null, null, arrayindexed().toAst(), null, toPosition())
|
||||
directmemory()!=null -> AssignTarget(null, null, null, DirectMemoryWrite(directmemory().expression().toAst(), toPosition()), toPosition())
|
||||
else -> AssignTarget(null, scoped_identifier()?.toAst(), null, null, toPosition())
|
||||
}
|
||||
}
|
||||
|
||||
private fun prog8Parser.RegisterContext.toAst() = Register.valueOf(text.toUpperCase())
|
||||
|
||||
private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperCase())
|
||||
|
||||
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
|
||||
|
||||
|
||||
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
||||
ArrayIndex(expression().toAst(), toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
||||
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg =
|
||||
DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
||||
val integer: Int
|
||||
var datatype = DataType.UBYTE
|
||||
when (radix) {
|
||||
10 -> {
|
||||
integer = try {
|
||||
text.toInt()
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid decimal literal ${x.message}")
|
||||
}
|
||||
datatype = when(integer) {
|
||||
in 0..255 -> DataType.UBYTE
|
||||
in -128..127 -> DataType.BYTE
|
||||
in 0..65535 -> DataType.UWORD
|
||||
in -32768..32767 -> DataType.WORD
|
||||
else -> DataType.FLOAT
|
||||
}
|
||||
}
|
||||
2 -> {
|
||||
if(text.length>8)
|
||||
datatype = DataType.UWORD
|
||||
try {
|
||||
integer = text.toInt(2)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid binary literal ${x.message}")
|
||||
}
|
||||
}
|
||||
16 -> {
|
||||
if(text.length>2)
|
||||
datatype = DataType.UWORD
|
||||
try {
|
||||
integer = text.toInt(16)
|
||||
} catch(x: NumberFormatException) {
|
||||
throw AstException("${toPosition()} invalid hexadecimal literal ${x.message}")
|
||||
}
|
||||
}
|
||||
else -> throw FatalAstException("invalid radix")
|
||||
}
|
||||
return NumericLiteral(integer, if (forceWord) DataType.UWORD else datatype)
|
||||
}
|
||||
val terminal: TerminalNode = children[0] as TerminalNode
|
||||
val integerPart = this.intpart.text
|
||||
return when (terminal.symbol.type) {
|
||||
prog8Parser.DEC_INTEGER -> makeLiteral(integerPart, 10, wordsuffix()!=null)
|
||||
prog8Parser.HEX_INTEGER -> makeLiteral(integerPart.substring(1), 16, wordsuffix()!=null)
|
||||
prog8Parser.BIN_INTEGER -> makeLiteral(integerPart.substring(1), 2, wordsuffix()!=null)
|
||||
else -> throw FatalAstException(terminal.text)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
||||
|
||||
val litval = literalvalue()
|
||||
if(litval!=null) {
|
||||
val booleanlit = litval.booleanliteral()?.toAst()
|
||||
return if(booleanlit!=null) {
|
||||
LiteralValue.fromBoolean(booleanlit, litval.toPosition())
|
||||
}
|
||||
else {
|
||||
val intLit = litval.integerliteral()?.toAst()
|
||||
when {
|
||||
intLit!=null -> when(intLit.datatype) {
|
||||
DataType.UBYTE -> LiteralValue(DataType.UBYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, bytevalue = intLit.number.toShort(), position = litval.toPosition())
|
||||
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = intLit.number.toInt(), position = litval.toPosition())
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = intLit.number.toDouble(), position = litval.toPosition())
|
||||
else -> throw FatalAstException("invalid datatype for numeric literal")
|
||||
}
|
||||
litval.floatliteral()!=null -> LiteralValue(DataType.FLOAT, floatvalue = litval.floatliteral().toAst(), position = litval.toPosition())
|
||||
litval.stringliteral()!=null -> LiteralValue(DataType.STR, strvalue = unescape(litval.stringliteral().text, litval.toPosition()), position = litval.toPosition())
|
||||
litval.charliteral()!=null -> {
|
||||
try {
|
||||
LiteralValue(DataType.UBYTE, bytevalue = Petscii.encodePetscii(unescape(litval.charliteral().text, litval.toPosition()), true)[0], position = litval.toPosition())
|
||||
} catch (ce: CharConversionException) {
|
||||
throw SyntaxError(ce.message ?: ce.toString(), litval.toPosition())
|
||||
}
|
||||
}
|
||||
litval.arrayliteral()!=null -> {
|
||||
val array = litval.arrayliteral()?.toAst()
|
||||
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
|
||||
// the ConstantFolder takes care of that and converts the type if needed.
|
||||
LiteralValue(DataType.ARRAY_UB, arrayvalue = array, position = litval.toPosition())
|
||||
}
|
||||
else -> throw FatalAstException("invalid parsed literal")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(register()!=null)
|
||||
return RegisterExpr(register().toAst(), register().toPosition())
|
||||
|
||||
if(scoped_identifier()!=null)
|
||||
return scoped_identifier().toAst()
|
||||
|
||||
if(bop!=null)
|
||||
return BinaryExpression(left.toAst(), bop.text, right.toAst(), toPosition())
|
||||
|
||||
if(prefix!=null)
|
||||
return PrefixExpression(prefix.text, expression(0).toAst(), toPosition())
|
||||
|
||||
val funcall = functioncall()?.toAst()
|
||||
if(funcall!=null) return funcall
|
||||
|
||||
if (rangefrom!=null && rangeto!=null) {
|
||||
val step = rangestep?.toAst() ?: LiteralValue(DataType.UBYTE, 1, position = toPosition())
|
||||
return RangeExpr(rangefrom.toAst(), rangeto.toAst(), step, toPosition())
|
||||
}
|
||||
|
||||
if(childCount==3 && children[0].text=="(" && children[2].text==")")
|
||||
return expression(0).toAst() // expression within ( )
|
||||
|
||||
if(arrayindexed()!=null)
|
||||
return arrayindexed().toAst()
|
||||
|
||||
if(typecast()!=null)
|
||||
return TypecastExpression(expression(0).toAst(), typecast().datatype().toAst(), false, toPosition())
|
||||
|
||||
if(directmemory()!=null)
|
||||
return DirectMemoryRead(directmemory().expression().toAst(), toPosition())
|
||||
|
||||
if(addressof()!=null)
|
||||
return AddressOf(addressof().scoped_identifier().toAst(), toPosition())
|
||||
|
||||
throw FatalAstException(text)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
||||
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
||||
arrayindex().toAst(),
|
||||
toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() }
|
||||
|
||||
|
||||
private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
|
||||
IdentifierReference(listOf(text), toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.Scoped_identifierContext.toAst() : IdentifierReference =
|
||||
IdentifierReference(NAME().map { it.text }, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.FloatliteralContext.toAst() = text.toDouble()
|
||||
|
||||
|
||||
private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
||||
"true" -> true
|
||||
"false" -> false
|
||||
else -> throw FatalAstException(text)
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
||||
expression().map { it.toAst() }.toTypedArray()
|
||||
|
||||
|
||||
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||
val condition = expression().toAst()
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return IfStatement(condition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.Else_partContext.toAst(): MutableList<IStatement> {
|
||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
||||
val branchcondition = branchcondition().toAst()
|
||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val elseStatements = else_part()?.toAst() ?: mutableListOf()
|
||||
val trueScope = AnonymousScope(trueStatements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
val elseScope = AnonymousScope(elseStatements, else_part()?.toPosition() ?: toPosition())
|
||||
return BranchStatement(branchcondition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||
|
||||
|
||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||
val loopregister = register()?.toAst()
|
||||
val datatype = datatype()?.toAst()
|
||||
val zeropage = ZEROPAGE()!=null
|
||||
val loopvar = identifier()?.toAst()
|
||||
val iterable = expression()!!.toAst()
|
||||
val scope =
|
||||
if(statement()!=null)
|
||||
AnonymousScope(mutableListOf(statement().toAst()), statement().toPosition())
|
||||
else
|
||||
AnonymousScope(statement_block().toAst(), statement_block().toPosition())
|
||||
return ForLoop(loopregister, datatype, zeropage, loopvar, iterable, scope, toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
||||
|
||||
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
||||
val condition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return WhileLoop(condition, scope, toPosition())
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||
val untilCondition = expression().toAst()
|
||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||
?: statement().toPosition())
|
||||
return RepeatLoop(scope, untilCondition, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.WhenstmtContext.toAst(): WhenStatement {
|
||||
val condition = expression().toAst()
|
||||
val choices = this.when_choice()?.map { it.toAst() }?.toMutableList() ?: mutableListOf()
|
||||
return WhenStatement(condition, choices, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.When_choiceContext.toAst(): WhenChoice {
|
||||
val value = expression()?.toAst()
|
||||
val stmt = statement()?.toAst()
|
||||
val stmt_block = statement_block()?.toAst()?.toMutableList() ?: mutableListOf()
|
||||
if(stmt!=null)
|
||||
stmt_block.add(stmt)
|
||||
val scope = AnonymousScope(stmt_block, toPosition())
|
||||
return WhenChoice(value, scope, toPosition())
|
||||
}
|
||||
|
||||
internal fun escape(str: String) = str.replace("\t", "\\t").replace("\n", "\\n").replace("\r", "\\r")
|
||||
|
||||
internal fun unescape(str: String, position: Position): String {
|
||||
val result = mutableListOf<Char>()
|
||||
val iter = str.iterator()
|
||||
while(iter.hasNext()) {
|
||||
val c = iter.nextChar()
|
||||
if(c=='\\') {
|
||||
val ec = iter.nextChar()
|
||||
result.add(when(ec) {
|
||||
'\\' -> '\\'
|
||||
'n' -> '\n'
|
||||
'r' -> '\r'
|
||||
'"' -> '"'
|
||||
'u' -> {
|
||||
"${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}${iter.nextChar()}".toInt(16).toChar()
|
||||
}
|
||||
else -> throw SyntaxError("invalid escape char in string: \\$ec", position)
|
||||
})
|
||||
} else {
|
||||
result.add(c)
|
||||
}
|
||||
}
|
||||
return result.joinToString("")
|
||||
}
|
||||
@@ -0,0 +1,130 @@
|
||||
package prog8.ast.base
|
||||
|
||||
import prog8.ast.Node
|
||||
|
||||
/**************************** AST Data classes ****************************/
|
||||
|
||||
enum class DataType {
|
||||
UBYTE,
|
||||
BYTE,
|
||||
UWORD,
|
||||
WORD,
|
||||
FLOAT,
|
||||
STR,
|
||||
STR_S,
|
||||
ARRAY_UB,
|
||||
ARRAY_B,
|
||||
ARRAY_UW,
|
||||
ARRAY_W,
|
||||
ARRAY_F;
|
||||
|
||||
/**
|
||||
* is the type assignable to the given other type?
|
||||
*/
|
||||
infix fun isAssignableTo(targetType: DataType) =
|
||||
// what types are assignable to others without loss of precision?
|
||||
when(this) {
|
||||
UBYTE -> targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
||||
BYTE -> targetType == BYTE || targetType == UBYTE || targetType == UWORD || targetType==WORD || targetType == FLOAT
|
||||
UWORD -> targetType == UWORD || targetType == FLOAT
|
||||
WORD -> targetType == WORD || targetType==UWORD || targetType == FLOAT
|
||||
FLOAT -> targetType == FLOAT
|
||||
STR -> targetType == STR || targetType==STR_S
|
||||
STR_S -> targetType == STR || targetType==STR_S
|
||||
in ArrayDatatypes -> targetType === this
|
||||
else -> false
|
||||
}
|
||||
|
||||
|
||||
infix fun isAssignableTo(targetTypes: Set<DataType>) = targetTypes.any { this isAssignableTo it }
|
||||
|
||||
infix fun biggerThan(other: DataType) =
|
||||
when(this) {
|
||||
in ByteDatatypes -> false
|
||||
in WordDatatypes -> other in ByteDatatypes
|
||||
else -> true
|
||||
}
|
||||
}
|
||||
|
||||
enum class Register {
|
||||
A,
|
||||
X,
|
||||
Y
|
||||
}
|
||||
|
||||
enum class RegisterOrPair {
|
||||
A,
|
||||
X,
|
||||
Y,
|
||||
AX,
|
||||
AY,
|
||||
XY
|
||||
} // only used in parameter and return value specs in asm subroutines
|
||||
|
||||
enum class Statusflag {
|
||||
Pc,
|
||||
Pz,
|
||||
Pv,
|
||||
Pn
|
||||
}
|
||||
|
||||
enum class BranchCondition {
|
||||
CS,
|
||||
CC,
|
||||
EQ,
|
||||
Z,
|
||||
NE,
|
||||
NZ,
|
||||
VS,
|
||||
VC,
|
||||
MI,
|
||||
NEG,
|
||||
PL,
|
||||
POS
|
||||
}
|
||||
|
||||
enum class VarDeclType {
|
||||
VAR,
|
||||
CONST,
|
||||
MEMORY
|
||||
}
|
||||
|
||||
val IterableDatatypes = setOf(
|
||||
DataType.STR, DataType.STR_S,
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W,
|
||||
DataType.ARRAY_F)
|
||||
val ByteDatatypes = setOf(DataType.UBYTE, DataType.BYTE)
|
||||
val WordDatatypes = setOf(DataType.UWORD, DataType.WORD)
|
||||
val IntegerDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD)
|
||||
val NumericDatatypes = setOf(DataType.UBYTE, DataType.BYTE, DataType.UWORD, DataType.WORD, DataType.FLOAT)
|
||||
val StringDatatypes = setOf(DataType.STR, DataType.STR_S)
|
||||
val ArrayDatatypes = setOf(DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W, DataType.ARRAY_F)
|
||||
val ArrayElementTypes = mapOf(
|
||||
DataType.ARRAY_B to DataType.BYTE,
|
||||
DataType.ARRAY_UB to DataType.UBYTE,
|
||||
DataType.ARRAY_W to DataType.WORD,
|
||||
DataType.ARRAY_UW to DataType.UWORD,
|
||||
DataType.ARRAY_F to DataType.FLOAT)
|
||||
|
||||
// find the parent node of a specific type or interface
|
||||
// (useful to figure out in what namespace/block something is defined, etc)
|
||||
inline fun <reified T> findParentNode(node: Node): T? {
|
||||
var candidate = node.parent
|
||||
while(candidate !is T && candidate !is ParentSentinel)
|
||||
candidate = candidate.parent
|
||||
return if(candidate is ParentSentinel)
|
||||
null
|
||||
else
|
||||
candidate as T
|
||||
}
|
||||
|
||||
object ParentSentinel : Node {
|
||||
override val position = Position("<<sentinel>>", 0, 0, 0)
|
||||
override var parent: Node = this
|
||||
override fun linkParents(parent: Node) {}
|
||||
}
|
||||
|
||||
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}]"
|
||||
}
|
||||
@@ -0,0 +1,37 @@
|
||||
package prog8.ast.base
|
||||
|
||||
import prog8.parser.ParsingFailedError
|
||||
|
||||
|
||||
fun printErrors(errors: List<Any>, moduleName: String) {
|
||||
val reportedMessages = mutableSetOf<String>()
|
||||
print("\u001b[91m") // bright red
|
||||
errors.forEach {
|
||||
val msg = it.toString()
|
||||
if(msg !in reportedMessages) {
|
||||
System.err.println(msg)
|
||||
reportedMessages.add(msg)
|
||||
}
|
||||
}
|
||||
print("\u001b[0m") // reset color
|
||||
if(reportedMessages.isNotEmpty())
|
||||
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
|
||||
}
|
||||
|
||||
|
||||
fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
|
||||
print("\u001b[93m") // bright yellow
|
||||
print("$position Warning: $msg")
|
||||
if(detailInfo==null)
|
||||
print("\n")
|
||||
else
|
||||
println(": $detailInfo\n")
|
||||
print("\u001b[0m") // normal
|
||||
}
|
||||
|
||||
|
||||
fun printWarning(msg: String) {
|
||||
print("\u001b[93m") // bright yellow
|
||||
print("Warning: $msg")
|
||||
print("\u001b[0m\n") // normal
|
||||
}
|
||||
@@ -0,0 +1,22 @@
|
||||
package prog8.ast.base
|
||||
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
|
||||
class FatalAstException (override var message: String) : Exception(message)
|
||||
|
||||
open class AstException (override var message: String) : Exception(message)
|
||||
|
||||
class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
||||
override fun toString() = "$position Syntax error: $message"
|
||||
}
|
||||
|
||||
class NameError(override var message: String, val position: Position) : AstException(message) {
|
||||
override fun toString() = "$position Name error: $message"
|
||||
}
|
||||
|
||||
open class ExpressionError(message: String, val position: Position) : AstException(message) {
|
||||
override fun toString() = "$position Error: $message"
|
||||
}
|
||||
|
||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||
: ExpressionError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||
@@ -0,0 +1,84 @@
|
||||
package prog8.ast.base
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.processing.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.ForLoop
|
||||
import prog8.compiler.CompilationOptions
|
||||
|
||||
|
||||
// the name of the subroutine that should be called for every block to initialize its variables
|
||||
internal const val initvarsSubName="prog8_init_vars"
|
||||
|
||||
|
||||
// prefix for literal values that are turned into a variable on the heap
|
||||
internal const val autoHeapValuePrefix = "auto_heap_value_"
|
||||
|
||||
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
||||
val checker = AstChecker(this, compilerOptions)
|
||||
checker.visit(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
internal fun Program.reorderStatements() {
|
||||
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
||||
initvalueCreator.visit(this)
|
||||
|
||||
val checker = StatementReorderer(this)
|
||||
checker.visit(this)
|
||||
}
|
||||
|
||||
internal fun Module.checkImportedValid() {
|
||||
val checker = ImportedModuleDirectiveRemover()
|
||||
checker.visit(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
internal fun Program.checkRecursion() {
|
||||
val checker = AstRecursionChecker(namespace)
|
||||
checker.visit(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
internal fun Program.checkIdentifiers() {
|
||||
val checker = AstIdentifiersChecker(namespace)
|
||||
checker.visit(this)
|
||||
|
||||
if(modules.map {it.name}.toSet().size != modules.size) {
|
||||
throw FatalAstException("modules should all be unique")
|
||||
}
|
||||
|
||||
// add any anonymous variables for heap values that are used,
|
||||
// and replace an iterable literalvalue by identifierref to new local variable
|
||||
for (variable in checker.anonymousVariablesFromHeap.values) {
|
||||
val scope = variable.first.definingScope()
|
||||
scope.statements.add(variable.second)
|
||||
val parent = variable.first.parent
|
||||
when {
|
||||
parent is Assignment && parent.value === variable.first -> {
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.value = idref
|
||||
}
|
||||
parent is IFunctionCall -> {
|
||||
val parameterPos = parent.arglist.indexOf(variable.first)
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.arglist[parameterPos] = idref
|
||||
}
|
||||
parent is ForLoop -> {
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.iterable = idref
|
||||
}
|
||||
else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
|
||||
}
|
||||
variable.second.linkParents(scope as Node)
|
||||
}
|
||||
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
@@ -0,0 +1,791 @@
|
||||
package prog8.ast.expressions
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.IntegerOrAddressOf
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import prog8.functions.NotConstArgumentException
|
||||
import prog8.functions.builtinFunctionReturnType
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||
|
||||
|
||||
class PrefixExpression(val operator: String, var expression: IExpression, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
expression.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun inferType(program: Program): DataType? = expression.inferType(program)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Prefix($operator $expression)"
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
left.linkParents(this)
|
||||
right.linkParents(this)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "[$left $operator $right]"
|
||||
}
|
||||
|
||||
// binary expression should actually have been optimized away into a single value, before const value was requested...
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String) = left.referencesIdentifier(name) || right.referencesIdentifier(name)
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val leftDt = left.inferType(program)
|
||||
val rightDt = right.inferType(program)
|
||||
return when (operator) {
|
||||
"+", "-", "*", "**", "%" -> if (leftDt == null || rightDt == null) null else {
|
||||
try {
|
||||
arithmeticOpDt(leftDt, rightDt)
|
||||
} catch (x: FatalAstException) {
|
||||
null
|
||||
}
|
||||
}
|
||||
"/" -> if (leftDt == null || rightDt == null) null else divisionOpDt(leftDt, rightDt)
|
||||
"&" -> leftDt
|
||||
"|" -> leftDt
|
||||
"^" -> leftDt
|
||||
"and", "or", "xor",
|
||||
"<", ">",
|
||||
"<=", ">=",
|
||||
"==", "!=" -> DataType.UBYTE
|
||||
"<<", ">>" -> leftDt
|
||||
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
||||
}
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun divisionOpDt(leftDt: DataType, rightDt: DataType): DataType {
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UBYTE
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.BYTE
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.BYTE -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.BYTE
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.UWORD -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.WORD -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.WORD
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.FLOAT -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
}
|
||||
|
||||
fun arithmeticOpDt(leftDt: DataType, rightDt: DataType): DataType {
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> when (rightDt) {
|
||||
DataType.UBYTE -> DataType.UBYTE
|
||||
DataType.BYTE -> DataType.BYTE
|
||||
DataType.UWORD -> DataType.UWORD
|
||||
DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.BYTE -> when (rightDt) {
|
||||
in ByteDatatypes -> DataType.BYTE
|
||||
in WordDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.UWORD -> when (rightDt) {
|
||||
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
|
||||
DataType.BYTE, DataType.WORD -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.WORD -> when (rightDt) {
|
||||
in IntegerDatatypes -> DataType.WORD
|
||||
DataType.FLOAT -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
DataType.FLOAT -> when (rightDt) {
|
||||
in NumericDatatypes -> DataType.FLOAT
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun commonDatatype(leftDt: DataType, rightDt: DataType,
|
||||
left: IExpression, right: IExpression): Pair<DataType, IExpression?> {
|
||||
// byte + byte -> byte
|
||||
// byte + word -> word
|
||||
// word + byte -> word
|
||||
// word + word -> word
|
||||
// a combination with a float will be float (but give a warning about this!)
|
||||
|
||||
if(this.operator=="/") {
|
||||
// division is a bit weird, don't cast the operands
|
||||
val commondt = divisionOpDt(leftDt, rightDt)
|
||||
return Pair(commondt, null)
|
||||
}
|
||||
|
||||
return when (leftDt) {
|
||||
DataType.UBYTE -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.UBYTE, null)
|
||||
DataType.BYTE -> Pair(DataType.BYTE, left)
|
||||
DataType.UWORD -> Pair(DataType.UWORD, left)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.BYTE, right)
|
||||
DataType.BYTE -> Pair(DataType.BYTE, null)
|
||||
DataType.UWORD -> Pair(DataType.WORD, left)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.UWORD, right)
|
||||
DataType.BYTE -> Pair(DataType.UWORD, right)
|
||||
DataType.UWORD -> Pair(DataType.UWORD, null)
|
||||
DataType.WORD -> Pair(DataType.WORD, left)
|
||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
when (rightDt) {
|
||||
DataType.UBYTE -> Pair(DataType.WORD, right)
|
||||
DataType.BYTE -> Pair(DataType.WORD, right)
|
||||
DataType.UWORD -> Pair(DataType.WORD, right)
|
||||
DataType.WORD -> Pair(DataType.WORD, null)
|
||||
DataType.FLOAT -> Pair(DataType.FLOAT, left)
|
||||
else -> throw FatalAstException("non-numeric datatype $rightDt")
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
Pair(DataType.FLOAT, right)
|
||||
}
|
||||
else -> throw FatalAstException("non-numeric datatype $leftDt")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
var arrayspec: ArrayIndex,
|
||||
override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
identifier.linkParents(this)
|
||||
arrayspec.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String) = identifier.referencesIdentifier(name)
|
||||
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val target = identifier.targetStatement(program.namespace)
|
||||
if (target is VarDecl) {
|
||||
return when (target.datatype) {
|
||||
in NumericDatatypes -> null
|
||||
in StringDatatypes -> DataType.UBYTE
|
||||
DataType.ARRAY_UB -> DataType.UBYTE
|
||||
DataType.ARRAY_B -> DataType.BYTE
|
||||
DataType.ARRAY_UW -> DataType.UWORD
|
||||
DataType.ARRAY_W -> DataType.WORD
|
||||
DataType.ARRAY_F -> DataType.FLOAT
|
||||
else -> throw FatalAstException("invalid dt")
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "ArrayIndexed(ident=$identifier, arraysize=$arrayspec; pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class TypecastExpression(var expression: IExpression, var type: DataType, val implicit: Boolean, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
expression.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String) = expression.referencesIdentifier(name)
|
||||
override fun inferType(program: Program): DataType? = type
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
val cv = expression.constValue(program) ?: return null
|
||||
return cv.cast(type)
|
||||
// val value = RuntimeValue(cv.type, cv.asNumericValue!!).cast(type)
|
||||
// return LiteralValue.fromNumber(value.numericValue(), value.type, position).cast(type)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Typecast($expression as $type)"
|
||||
}
|
||||
}
|
||||
|
||||
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
identifier.parent=this
|
||||
}
|
||||
|
||||
var scopedname: String? = null // will be set in a later state by the compiler
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun inferType(program: Program) = DataType.UWORD
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
this.addressExpression.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String) = false
|
||||
override fun inferType(program: Program): DataType? = DataType.UBYTE
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryRead($addressExpression)"
|
||||
}
|
||||
}
|
||||
|
||||
open class LiteralValue(val type: DataType,
|
||||
val bytevalue: Short? = null,
|
||||
val wordvalue: Int? = null,
|
||||
val floatvalue: Double? = null,
|
||||
val strvalue: String? = null,
|
||||
val arrayvalue: Array<IExpression>? = null,
|
||||
initHeapId: Int? =null,
|
||||
override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun referencesIdentifier(name: String) = arrayvalue?.any { it.referencesIdentifier(name) } ?: false
|
||||
|
||||
val isString = type in StringDatatypes
|
||||
val isNumeric = type in NumericDatatypes
|
||||
val isArray = type in ArrayDatatypes
|
||||
var heapId = initHeapId
|
||||
private set
|
||||
|
||||
companion object {
|
||||
fun fromBoolean(bool: Boolean, position: Position) =
|
||||
LiteralValue(DataType.UBYTE, bytevalue = if (bool) 1 else 0, position = position)
|
||||
|
||||
fun fromNumber(value: Number, type: DataType, position: Position) : LiteralValue {
|
||||
return when(type) {
|
||||
in ByteDatatypes -> LiteralValue(type, bytevalue = value.toShort(), position = position)
|
||||
in WordDatatypes -> LiteralValue(type, wordvalue = value.toInt(), position = position)
|
||||
DataType.FLOAT -> LiteralValue(type, floatvalue = value.toDouble(), position = position)
|
||||
else -> throw FatalAstException("non numeric datatype")
|
||||
}
|
||||
}
|
||||
|
||||
fun optimalNumeric(value: Number, position: Position): LiteralValue {
|
||||
return if(value is Double) {
|
||||
LiteralValue(DataType.FLOAT, floatvalue = value, position = position)
|
||||
} else {
|
||||
when (val intval = value.toInt()) {
|
||||
in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = intval.toShort(), position = position)
|
||||
in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = intval.toShort(), position = position)
|
||||
in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = intval, position = position)
|
||||
in -32768..32767 -> LiteralValue(DataType.WORD, wordvalue = intval, position = position)
|
||||
else -> LiteralValue(DataType.FLOAT, floatvalue = intval.toDouble(), position = position)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun optimalInteger(value: Number, position: Position): LiteralValue {
|
||||
val intval = value.toInt()
|
||||
if(intval.toDouble() != value.toDouble())
|
||||
throw FatalAstException("value is not an integer: $value")
|
||||
return when (intval) {
|
||||
in 0..255 -> LiteralValue(DataType.UBYTE, bytevalue = value.toShort(), position = position)
|
||||
in -128..127 -> LiteralValue(DataType.BYTE, bytevalue = value.toShort(), position = position)
|
||||
in 0..65535 -> LiteralValue(DataType.UWORD, wordvalue = value.toInt(), position = position)
|
||||
else -> throw FatalAstException("integer overflow: $value")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
init {
|
||||
when(type){
|
||||
in ByteDatatypes -> if(bytevalue==null) throw FatalAstException("literal value missing bytevalue")
|
||||
in WordDatatypes -> if(wordvalue==null) throw FatalAstException("literal value missing wordvalue")
|
||||
DataType.FLOAT -> if(floatvalue==null) throw FatalAstException("literal value missing floatvalue")
|
||||
in StringDatatypes ->
|
||||
if(strvalue==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
|
||||
in ArrayDatatypes ->
|
||||
if(arrayvalue==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
|
||||
else -> throw FatalAstException("invalid type $type")
|
||||
}
|
||||
if(bytevalue==null && wordvalue==null && floatvalue==null && arrayvalue==null && strvalue==null && heapId==null)
|
||||
throw FatalAstException("literal value without actual value")
|
||||
}
|
||||
|
||||
val asNumericValue: Number? = when {
|
||||
bytevalue!=null -> bytevalue
|
||||
wordvalue!=null -> wordvalue
|
||||
floatvalue!=null -> floatvalue
|
||||
else -> null
|
||||
}
|
||||
|
||||
val asIntegerValue: Int? = when {
|
||||
bytevalue!=null -> bytevalue.toInt()
|
||||
wordvalue!=null -> wordvalue
|
||||
// don't round a float value, otherwise code will not detect that it's not an integer
|
||||
else -> null
|
||||
}
|
||||
|
||||
val asBooleanValue: Boolean =
|
||||
(floatvalue!=null && floatvalue != 0.0) ||
|
||||
(bytevalue!=null && bytevalue != 0.toShort()) ||
|
||||
(wordvalue!=null && wordvalue != 0) ||
|
||||
(strvalue!=null && strvalue.isNotEmpty()) ||
|
||||
(arrayvalue != null && arrayvalue.isNotEmpty())
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
arrayvalue?.forEach {it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
if(arrayvalue!=null) {
|
||||
for(v in arrayvalue) {
|
||||
if(v.constValue(program)==null) return null
|
||||
}
|
||||
}
|
||||
return this
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
val vstr = when(type) {
|
||||
DataType.UBYTE -> "ubyte:$bytevalue"
|
||||
DataType.BYTE -> "byte:$bytevalue"
|
||||
DataType.UWORD -> "uword:$wordvalue"
|
||||
DataType.WORD -> "word:$wordvalue"
|
||||
DataType.FLOAT -> "float:$floatvalue"
|
||||
in StringDatatypes -> "str:$strvalue"
|
||||
in ArrayDatatypes -> "array:$arrayvalue"
|
||||
else -> throw FatalAstException("weird datatype")
|
||||
}
|
||||
return "LiteralValue($vstr)"
|
||||
}
|
||||
|
||||
override fun inferType(program: Program) = type
|
||||
|
||||
override fun hashCode(): Int {
|
||||
val bh = bytevalue?.hashCode() ?: 0x10001234
|
||||
val wh = wordvalue?.hashCode() ?: 0x01002345
|
||||
val fh = floatvalue?.hashCode() ?: 0x00103456
|
||||
val sh = strvalue?.hashCode() ?: 0x00014567
|
||||
val ah = arrayvalue?.hashCode() ?: 0x11119876
|
||||
var hash = bh * 31 xor wh
|
||||
hash = hash*31 xor fh
|
||||
hash = hash*31 xor sh
|
||||
hash = hash*31 xor ah
|
||||
hash = hash*31 xor type.hashCode()
|
||||
return hash
|
||||
}
|
||||
|
||||
override fun equals(other: Any?): Boolean {
|
||||
if(other==null || other !is LiteralValue)
|
||||
return false
|
||||
if(isNumeric && other.isNumeric)
|
||||
return asNumericValue?.toDouble()==other.asNumericValue?.toDouble()
|
||||
if(isArray && other.isArray)
|
||||
return arrayvalue!!.contentEquals(other.arrayvalue!!) && heapId==other.heapId
|
||||
if(isString && other.isString)
|
||||
return strvalue==other.strvalue && heapId==other.heapId
|
||||
|
||||
if(type!=other.type)
|
||||
return false
|
||||
|
||||
return compareTo(other) == 0
|
||||
}
|
||||
|
||||
operator fun compareTo(other: LiteralValue): Int {
|
||||
val numLeft = asNumericValue?.toDouble()
|
||||
val numRight = other.asNumericValue?.toDouble()
|
||||
if(numLeft!=null && numRight!=null)
|
||||
return numLeft.compareTo(numRight)
|
||||
|
||||
if(strvalue!=null && other.strvalue!=null)
|
||||
return strvalue.compareTo(other.strvalue)
|
||||
|
||||
throw ExpressionError("cannot order compare type $type with ${other.type}", other.position)
|
||||
}
|
||||
|
||||
fun cast(targettype: DataType): LiteralValue? {
|
||||
if(type==targettype)
|
||||
return this
|
||||
when(type) {
|
||||
DataType.UBYTE -> {
|
||||
if(targettype== DataType.BYTE && bytevalue!! <= 127)
|
||||
return LiteralValue(targettype, bytevalue = bytevalue, position = position)
|
||||
if(targettype== DataType.WORD || targettype== DataType.UWORD)
|
||||
return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
|
||||
if(targettype== DataType.FLOAT)
|
||||
return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(targettype== DataType.UBYTE && bytevalue!! >= 0)
|
||||
return LiteralValue(targettype, bytevalue = bytevalue, position = position)
|
||||
if(targettype== DataType.UWORD && bytevalue!! >= 0)
|
||||
return LiteralValue(targettype, wordvalue = bytevalue.toInt(), position = position)
|
||||
if(targettype== DataType.WORD)
|
||||
return LiteralValue(targettype, wordvalue = bytevalue!!.toInt(), position = position)
|
||||
if(targettype== DataType.FLOAT)
|
||||
return LiteralValue(targettype, floatvalue = bytevalue!!.toDouble(), position = position)
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(targettype== DataType.BYTE && wordvalue!! <= 127)
|
||||
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
|
||||
if(targettype== DataType.UBYTE && wordvalue!! <= 255)
|
||||
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
|
||||
if(targettype== DataType.WORD && wordvalue!! <= 32767)
|
||||
return LiteralValue(targettype, wordvalue = wordvalue, position = position)
|
||||
if(targettype== DataType.FLOAT)
|
||||
return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(targettype== DataType.BYTE && wordvalue!! in -128..127)
|
||||
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
|
||||
if(targettype== DataType.UBYTE && wordvalue!! in 0..255)
|
||||
return LiteralValue(targettype, bytevalue = wordvalue.toShort(), position = position)
|
||||
if(targettype== DataType.UWORD && wordvalue!! >=0)
|
||||
return LiteralValue(targettype, wordvalue = wordvalue, position = position)
|
||||
if(targettype== DataType.FLOAT)
|
||||
return LiteralValue(targettype, floatvalue = wordvalue!!.toDouble(), position = position)
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(floor(floatvalue!!) ==floatvalue) {
|
||||
val value = floatvalue.toInt()
|
||||
if (targettype == DataType.BYTE && value in -128..127)
|
||||
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
|
||||
if (targettype == DataType.UBYTE && value in 0..255)
|
||||
return LiteralValue(targettype, bytevalue = value.toShort(), position = position)
|
||||
if (targettype == DataType.WORD && value in -32768..32767)
|
||||
return LiteralValue(targettype, wordvalue = value, position = position)
|
||||
if (targettype == DataType.UWORD && value in 0..65535)
|
||||
return LiteralValue(targettype, wordvalue = value, position = position)
|
||||
}
|
||||
}
|
||||
in StringDatatypes -> {
|
||||
if(targettype in StringDatatypes)
|
||||
return this
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
return null // invalid type conversion from $this to $targettype
|
||||
}
|
||||
|
||||
fun addToHeap(heap: HeapValues) {
|
||||
if(heapId==null) {
|
||||
if (strvalue != null) {
|
||||
heapId = heap.addString(type, strvalue)
|
||||
}
|
||||
else if (arrayvalue!=null) {
|
||||
if(arrayvalue.any {it is AddressOf }) {
|
||||
val intArrayWithAddressOfs = arrayvalue.map {
|
||||
when (it) {
|
||||
is AddressOf -> IntegerOrAddressOf(null, it)
|
||||
is LiteralValue -> IntegerOrAddressOf(it.asIntegerValue, null)
|
||||
else -> throw FatalAstException("invalid datatype in array")
|
||||
}
|
||||
}
|
||||
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
|
||||
} else {
|
||||
val valuesInArray = arrayvalue.map { (it as LiteralValue).asNumericValue!! }
|
||||
heapId = if(type== DataType.ARRAY_F) {
|
||||
val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
|
||||
heap.addDoublesArray(doubleArray)
|
||||
} else {
|
||||
val integerArray = valuesInArray.map { it.toInt() }
|
||||
heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RangeExpr(var from: IExpression,
|
||||
var to: IExpression,
|
||||
var step: IExpression,
|
||||
override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
from.linkParents(this)
|
||||
to.linkParents(this)
|
||||
step.linkParents(this)
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = from.referencesIdentifier(name) || to.referencesIdentifier(name)
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val fromDt=from.inferType(program)
|
||||
val toDt=to.inferType(program)
|
||||
return when {
|
||||
fromDt==null || toDt==null -> null
|
||||
fromDt== DataType.UBYTE && toDt== DataType.UBYTE -> DataType.UBYTE
|
||||
fromDt== DataType.UWORD && toDt== DataType.UWORD -> DataType.UWORD
|
||||
fromDt== DataType.STR && toDt== DataType.STR -> DataType.STR
|
||||
fromDt== DataType.STR_S && toDt== DataType.STR_S -> DataType.STR_S
|
||||
fromDt== DataType.WORD || toDt== DataType.WORD -> DataType.WORD
|
||||
fromDt== DataType.BYTE || toDt== DataType.BYTE -> DataType.BYTE
|
||||
else -> DataType.UBYTE
|
||||
}
|
||||
}
|
||||
override fun toString(): String {
|
||||
return "RangeExpr(from $from, to $to, step $step, pos=$position)"
|
||||
}
|
||||
|
||||
fun size(): Int? {
|
||||
val fromLv = (from as? LiteralValue)
|
||||
val toLv = (to as? LiteralValue)
|
||||
if(fromLv==null || toLv==null)
|
||||
return null
|
||||
return toConstantIntegerRange()?.count()
|
||||
}
|
||||
|
||||
fun toConstantIntegerRange(): IntProgression? {
|
||||
val fromLv = from as? LiteralValue
|
||||
val toLv = to as? LiteralValue
|
||||
if(fromLv==null || toLv==null)
|
||||
return null // non-constant range
|
||||
val fromVal: Int
|
||||
val toVal: Int
|
||||
if(fromLv.isString && toLv.isString) {
|
||||
// string range -> int range over petscii values
|
||||
fromVal = Petscii.encodePetscii(fromLv.strvalue!!, true)[0].toInt()
|
||||
toVal = Petscii.encodePetscii(toLv.strvalue!!, true)[0].toInt()
|
||||
} else {
|
||||
// integer range
|
||||
fromVal = (from as LiteralValue).asIntegerValue!!
|
||||
toVal = (to as LiteralValue).asIntegerValue!!
|
||||
}
|
||||
val stepVal = (step as? LiteralValue)?.asIntegerValue ?: 1
|
||||
return when {
|
||||
fromVal <= toVal -> when {
|
||||
stepVal <= 0 -> IntRange.EMPTY
|
||||
stepVal == 1 -> fromVal..toVal
|
||||
else -> fromVal..toVal step stepVal
|
||||
}
|
||||
else -> when {
|
||||
stepVal >= 0 -> IntRange.EMPTY
|
||||
stepVal == -1 -> fromVal downTo toVal
|
||||
else -> fromVal downTo toVal step abs(stepVal)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterExpr(val register: Register, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? = null
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = false
|
||||
override fun toString(): String {
|
||||
return "RegisterExpr(register=$register, pos=$position)"
|
||||
}
|
||||
|
||||
override fun inferType(program: Program) = DataType.UBYTE
|
||||
}
|
||||
|
||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : IExpression {
|
||||
override lateinit var parent: Node
|
||||
|
||||
fun targetStatement(namespace: INameScope) =
|
||||
if(nameInSource.size==1 && nameInSource[0] in BuiltinFunctions)
|
||||
BuiltinFunctionStatementPlaceholder(nameInSource[0], position)
|
||||
else
|
||||
namespace.lookup(nameInSource, this)
|
||||
|
||||
fun targetVarDecl(namespace: INameScope): VarDecl? = targetStatement(namespace) as? VarDecl
|
||||
fun targetSubroutine(namespace: INameScope): Subroutine? = targetStatement(namespace) as? Subroutine
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun constValue(program: Program): LiteralValue? {
|
||||
val node = program.namespace.lookup(nameInSource, this)
|
||||
?: throw UndefinedSymbolError(this)
|
||||
val vardecl = node as? VarDecl
|
||||
if(vardecl==null) {
|
||||
return null
|
||||
} else if(vardecl.type!= VarDeclType.CONST) {
|
||||
return null
|
||||
}
|
||||
return vardecl.value?.constValue(program)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "IdentifierRef($nameInSource)"
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = nameInSource.last() == name // @todo is this correct all the time?
|
||||
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val targetStmt = targetStatement(program.namespace)
|
||||
if(targetStmt is VarDecl) {
|
||||
return targetStmt.datatype
|
||||
} else {
|
||||
throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
|
||||
}
|
||||
}
|
||||
|
||||
fun heapId(namespace: INameScope): Int {
|
||||
val node = namespace.lookup(nameInSource, this) ?: throw UndefinedSymbolError(this)
|
||||
return ((node as? VarDecl)?.value as? LiteralValue)?.heapId ?: throw FatalAstException("identifier is not on the heap: $this")
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionCall(override var target: IdentifierReference,
|
||||
override var arglist: MutableList<IExpression>,
|
||||
override val position: Position) : IExpression, IFunctionCall {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
target.linkParents(this)
|
||||
arglist.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun constValue(program: Program) = constValue(program, true)
|
||||
|
||||
private fun constValue(program: Program, withDatatypeCheck: Boolean): LiteralValue? {
|
||||
// if the function is a built-in function and the args are consts, should try to const-evaluate!
|
||||
// lenghts of arrays and strings are constants that are determined at compile time!
|
||||
if(target.nameInSource.size>1) return null
|
||||
try {
|
||||
var resultValue: LiteralValue? = null
|
||||
val func = BuiltinFunctions[target.nameInSource[0]]
|
||||
if(func!=null) {
|
||||
val exprfunc = func.constExpressionFunc
|
||||
if(exprfunc!=null)
|
||||
resultValue = exprfunc(arglist, position, program)
|
||||
else if(func.returntype==null)
|
||||
throw ExpressionError("builtin function ${target.nameInSource[0]} can't be used here because it doesn't return a value", position)
|
||||
}
|
||||
|
||||
if(withDatatypeCheck) {
|
||||
val resultDt = this.inferType(program)
|
||||
if(resultValue==null || resultDt == resultValue.type)
|
||||
return resultValue
|
||||
throw FatalAstException("evaluated const expression result value doesn't match expected datatype $resultDt, pos=$position")
|
||||
} else {
|
||||
return resultValue
|
||||
}
|
||||
}
|
||||
catch(x: NotConstArgumentException) {
|
||||
// const-evaluating the builtin function call failed.
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "FunctionCall(target=$target, pos=$position)"
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun referencesIdentifier(name: String): Boolean = target.referencesIdentifier(name) || arglist.any{it.referencesIdentifier(name)}
|
||||
|
||||
override fun inferType(program: Program): DataType? {
|
||||
val constVal = constValue(program ,false)
|
||||
if(constVal!=null)
|
||||
return constVal.type
|
||||
val stmt = target.targetStatement(program.namespace) ?: return null
|
||||
when (stmt) {
|
||||
is BuiltinFunctionStatementPlaceholder -> {
|
||||
if(target.nameInSource[0] == "set_carry" || target.nameInSource[0]=="set_irqd" ||
|
||||
target.nameInSource[0] == "clear_carry" || target.nameInSource[0]=="clear_irqd") {
|
||||
return null // these have no return value
|
||||
}
|
||||
return builtinFunctionReturnType(target.nameInSource[0], this.arglist, program)
|
||||
}
|
||||
is Subroutine -> {
|
||||
if(stmt.returntypes.isEmpty())
|
||||
return null // no return value
|
||||
if(stmt.returntypes.size==1)
|
||||
return stmt.returntypes[0]
|
||||
return null // has multiple return types... so not a single resulting datatype possible
|
||||
}
|
||||
is Label -> return null
|
||||
}
|
||||
return null // calling something we don't recognise...
|
||||
}
|
||||
}
|
||||
+117
-129
@@ -1,58 +1,19 @@
|
||||
package prog8.ast
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.printWarning
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.CompilationOptions
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
|
||||
import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import prog8.parser.ParsingFailedError
|
||||
import java.io.File
|
||||
|
||||
/**
|
||||
* General checks on the Ast
|
||||
*/
|
||||
|
||||
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
||||
val checker = AstChecker(this, compilerOptions)
|
||||
checker.process(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
fun printErrors(errors: List<Any>, moduleName: String) {
|
||||
val reportedMessages = mutableSetOf<String>()
|
||||
print("\u001b[91m") // bright red
|
||||
errors.forEach {
|
||||
val msg = it.toString()
|
||||
if(msg !in reportedMessages) {
|
||||
System.err.println(msg)
|
||||
reportedMessages.add(msg)
|
||||
}
|
||||
}
|
||||
print("\u001b[0m") // reset color
|
||||
if(reportedMessages.isNotEmpty())
|
||||
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
|
||||
}
|
||||
|
||||
|
||||
fun printWarning(msg: String, position: Position, detailInfo: String?=null) {
|
||||
print("\u001b[93m") // bright yellow
|
||||
print("$position Warning: $msg")
|
||||
if(detailInfo==null)
|
||||
print("\n")
|
||||
else
|
||||
println(": $detailInfo\n")
|
||||
print("\u001b[0m") // normal
|
||||
}
|
||||
|
||||
fun printWarning(msg: String) {
|
||||
print("\u001b[93m") // bright yellow
|
||||
print("Warning: $msg")
|
||||
print("\u001b[0m\n") // normal
|
||||
}
|
||||
|
||||
private class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions) : IAstProcessor {
|
||||
internal class AstChecker(private val program: Program,
|
||||
private val compilerOptions: CompilationOptions) : IAstModifyingVisitor {
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
private val heapStringSentinel: Int
|
||||
init {
|
||||
@@ -64,7 +25,7 @@ private class AstChecker(private val program: Program,
|
||||
return checkResult
|
||||
}
|
||||
|
||||
override fun process(program: Program) {
|
||||
override fun visit(program: Program) {
|
||||
assert(program === this.program)
|
||||
// there must be a single 'main' block with a 'start' subroutine for the program entry point.
|
||||
val mainBlocks = program.modules.flatMap { it.statements }.filter { b -> b is Block && b.name=="main" }.map { it as Block }
|
||||
@@ -113,11 +74,11 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
super.process(program)
|
||||
super.visit(program)
|
||||
}
|
||||
|
||||
override fun process(module: Module) {
|
||||
super.process(module)
|
||||
override fun visit(module: Module) {
|
||||
super.visit(module)
|
||||
val directives = module.statements.filterIsInstance<Directive>().groupBy { it.directive }
|
||||
directives.filter { it.value.size > 1 }.forEach{ entry ->
|
||||
when(entry.key) {
|
||||
@@ -127,7 +88,7 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(returnStmt: Return): IStatement {
|
||||
override fun visit(returnStmt: Return): IStatement {
|
||||
val expectedReturnValues = returnStmt.definingSubroutine()?.returntypes ?: emptyList()
|
||||
if(expectedReturnValues.size != returnStmt.values.size) {
|
||||
// if the return value is a function call, check the result of that call instead
|
||||
@@ -142,12 +103,12 @@ private class AstChecker(private val program: Program,
|
||||
for (rv in expectedReturnValues.withIndex().zip(returnStmt.values)) {
|
||||
val valueDt=rv.second.inferType(program)
|
||||
if(rv.first.value!=valueDt)
|
||||
checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index+1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
|
||||
checkResult.add(ExpressionError("type $valueDt of return value #${rv.first.index + 1} doesn't match subroutine return type ${rv.first.value}", rv.second.position))
|
||||
}
|
||||
return super.process(returnStmt)
|
||||
return super.visit(returnStmt)
|
||||
}
|
||||
|
||||
override fun process(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
if(forLoop.body.containsNoCodeNorVars())
|
||||
printWarning("for loop body is empty", forLoop.position)
|
||||
|
||||
@@ -158,36 +119,36 @@ private class AstChecker(private val program: Program,
|
||||
if (forLoop.loopRegister != null) {
|
||||
printWarning("using a register as loop variable is risky (it could get clobbered in the body)", forLoop.position)
|
||||
// loop register
|
||||
if (iterableDt != DataType.UBYTE && iterableDt!=DataType.ARRAY_UB && iterableDt !in StringDatatypes)
|
||||
if (iterableDt != DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
|
||||
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
|
||||
} else {
|
||||
// loop variable
|
||||
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
|
||||
if(loopvar==null || loopvar.type==VarDeclType.CONST) {
|
||||
if(loopvar==null || loopvar.type== VarDeclType.CONST) {
|
||||
checkResult.add(SyntaxError("for loop requires a variable to loop with", forLoop.position))
|
||||
|
||||
} else {
|
||||
when (loopvar.datatype) {
|
||||
DataType.UBYTE -> {
|
||||
if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.ARRAY_UB && iterableDt !in StringDatatypes)
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
|
||||
checkResult.add(ExpressionError("ubyte loop variable can only loop over unsigned bytes or strings", forLoop.position))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(iterableDt!=DataType.UBYTE && iterableDt!=DataType.UWORD && iterableDt !in StringDatatypes &&
|
||||
iterableDt !=DataType.ARRAY_UB && iterableDt!=DataType.ARRAY_UW)
|
||||
if(iterableDt!= DataType.UBYTE && iterableDt!= DataType.UWORD && iterableDt !in StringDatatypes &&
|
||||
iterableDt != DataType.ARRAY_UB && iterableDt!= DataType.ARRAY_UW)
|
||||
checkResult.add(ExpressionError("uword loop variable can only loop over unsigned bytes, words or strings", forLoop.position))
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(iterableDt!=DataType.BYTE && iterableDt!=DataType.ARRAY_B)
|
||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.ARRAY_B)
|
||||
checkResult.add(ExpressionError("byte loop variable can only loop over bytes", forLoop.position))
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(iterableDt!=DataType.BYTE && iterableDt!=DataType.WORD &&
|
||||
iterableDt !=DataType.ARRAY_B && iterableDt!=DataType.ARRAY_W)
|
||||
if(iterableDt!= DataType.BYTE && iterableDt!= DataType.WORD &&
|
||||
iterableDt != DataType.ARRAY_B && iterableDt!= DataType.ARRAY_W)
|
||||
checkResult.add(ExpressionError("word loop variable can only loop over bytes or words", forLoop.position))
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(iterableDt!=DataType.FLOAT && iterableDt != DataType.ARRAY_F)
|
||||
if(iterableDt!= DataType.FLOAT && iterableDt != DataType.ARRAY_F)
|
||||
checkResult.add(ExpressionError("float loop variable can only loop over floats", forLoop.position))
|
||||
}
|
||||
else -> checkResult.add(ExpressionError("loop variable must be numeric type", forLoop.position))
|
||||
@@ -195,10 +156,10 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(forLoop)
|
||||
return super.visit(forLoop)
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
override fun visit(jump: Jump): IStatement {
|
||||
if(jump.identifier!=null) {
|
||||
val targetStatement = checkFunctionOrLabelExists(jump.identifier, jump)
|
||||
if(targetStatement!=null) {
|
||||
@@ -209,29 +170,29 @@ private class AstChecker(private val program: Program,
|
||||
|
||||
if(jump.address!=null && (jump.address < 0 || jump.address > 65535))
|
||||
checkResult.add(SyntaxError("jump address must be valid integer 0..\$ffff", jump.position))
|
||||
return super.process(jump)
|
||||
return super.visit(jump)
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
override fun visit(block: Block): IStatement {
|
||||
if(block.address!=null && (block.address<0 || block.address>65535)) {
|
||||
checkResult.add(SyntaxError("block memory address must be valid integer 0..\$ffff", block.position))
|
||||
}
|
||||
|
||||
return super.process(block)
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun process(label: Label): IStatement {
|
||||
override fun visit(label: Label): IStatement {
|
||||
// scope check
|
||||
if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) {
|
||||
checkResult.add(SyntaxError("Labels can only be defined in the scope of a block, a loop body, or within another subroutine", label.position))
|
||||
}
|
||||
return super.process(label)
|
||||
return super.visit(label)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check subroutine definition
|
||||
*/
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
fun err(msg: String) {
|
||||
checkResult.add(SyntaxError(msg, subroutine.position))
|
||||
}
|
||||
@@ -243,7 +204,7 @@ private class AstChecker(private val program: Program,
|
||||
if(uniqueNames.size!=subroutine.parameters.size)
|
||||
err("parameter names must be unique")
|
||||
|
||||
super.process(subroutine)
|
||||
super.visit(subroutine)
|
||||
|
||||
// user-defined subroutines can only have zero or one return type
|
||||
// (multiple return values are only allowed for asm subs)
|
||||
@@ -351,7 +312,7 @@ private class AstChecker(private val program: Program,
|
||||
// This is not easy to fix because strings and arrays are treated a bit simplistic (a "virtual" pointer to the value on the heap)
|
||||
// while passing them as subroutine parameters would require a "real" pointer OR copying the VALUE to the subroutine's parameter variable (which is very inefficient).
|
||||
// For now, don't pass strings and arrays as parameters and instead create the workaround as suggested in the error message below.
|
||||
if(!subroutine.parameters.all{it.type in NumericDatatypes}) {
|
||||
if(!subroutine.parameters.all{it.type in NumericDatatypes }) {
|
||||
err("Non-asm subroutine can only take numerical parameters (no str/array types) for now. Workaround (for nested subroutine): access the variable from the outer scope directly.")
|
||||
}
|
||||
}
|
||||
@@ -362,7 +323,7 @@ private class AstChecker(private val program: Program,
|
||||
* Assignment target must be register, or a variable name
|
||||
* Also check data type compatibility and number of values
|
||||
*/
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
|
||||
// assigning from a functioncall COULD return multiple values (from an asm subroutine)
|
||||
if(assignment.value is FunctionCall) {
|
||||
@@ -386,7 +347,7 @@ private class AstChecker(private val program: Program,
|
||||
for (target in assignment.targets) {
|
||||
resultingAssignment = processAssignmentTarget(resultingAssignment, target)
|
||||
}
|
||||
return super.process(resultingAssignment)
|
||||
return super.visit(resultingAssignment)
|
||||
}
|
||||
|
||||
private fun processAssignmentTarget(assignment: Assignment, target: AssignTarget): Assignment {
|
||||
@@ -467,7 +428,7 @@ private class AstChecker(private val program: Program,
|
||||
return assignment
|
||||
}
|
||||
|
||||
override fun process(addressOf: AddressOf): IExpression {
|
||||
override fun visit(addressOf: AddressOf): IExpression {
|
||||
val variable=addressOf.identifier.targetVarDecl(program.namespace)
|
||||
if(variable==null)
|
||||
checkResult.add(ExpressionError("pointer-of operand must be the name of a heap variable", addressOf.position))
|
||||
@@ -477,13 +438,13 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
if(addressOf.scopedname==null)
|
||||
throw FatalAstException("the scopedname of AddressOf should have been set by now $addressOf")
|
||||
return super.process(addressOf)
|
||||
return super.visit(addressOf)
|
||||
}
|
||||
|
||||
/**
|
||||
* Check the variable declarations (values within range etc)
|
||||
*/
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
fun err(msg: String, position: Position?=null) {
|
||||
checkResult.add(SyntaxError(msg, position ?: decl.position))
|
||||
}
|
||||
@@ -494,19 +455,19 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
// CONST can only occur on simple types (byte, word, float)
|
||||
if(decl.type==VarDeclType.CONST) {
|
||||
if(decl.type== VarDeclType.CONST) {
|
||||
if (decl.datatype !in NumericDatatypes)
|
||||
err("const modifier can only be used on numeric types (byte, word, float)")
|
||||
}
|
||||
|
||||
// FLOATS
|
||||
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!=VarDeclType.MEMORY) {
|
||||
if(!compilerOptions.floats && decl.datatype in setOf(DataType.FLOAT, DataType.ARRAY_F) && decl.type!= VarDeclType.MEMORY) {
|
||||
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", decl.position))
|
||||
}
|
||||
|
||||
// ARRAY without size specifier MUST have an iterable initializer value
|
||||
if(decl.isArray && decl.arraysize==null) {
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
if(decl.type== VarDeclType.MEMORY)
|
||||
checkResult.add(SyntaxError("memory mapped array must have a size specification", decl.position))
|
||||
if(decl.value==null) {
|
||||
checkResult.add(SyntaxError("array variable is missing a size specification or an initialization value", decl.position))
|
||||
@@ -528,22 +489,22 @@ private class AstChecker(private val program: Program,
|
||||
// initialize numeric var with value zero by default.
|
||||
val litVal =
|
||||
when {
|
||||
decl.datatype in ByteDatatypes -> LiteralValue(decl.datatype, bytevalue=0, position = decl.position)
|
||||
decl.datatype in WordDatatypes -> LiteralValue(decl.datatype, wordvalue=0, position = decl.position)
|
||||
else -> LiteralValue(decl.datatype, floatvalue=0.0, position = decl.position)
|
||||
decl.datatype in ByteDatatypes -> LiteralValue(decl.datatype, bytevalue = 0, position = decl.position)
|
||||
decl.datatype in WordDatatypes -> LiteralValue(decl.datatype, wordvalue = 0, position = decl.position)
|
||||
else -> LiteralValue(decl.datatype, floatvalue = 0.0, position = decl.position)
|
||||
}
|
||||
litVal.parent = decl
|
||||
decl.value = litVal
|
||||
}
|
||||
decl.type==VarDeclType.VAR -> {
|
||||
val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position=decl.position) // point to the sentinel heap value instead
|
||||
decl.type== VarDeclType.VAR -> {
|
||||
val litVal = LiteralValue(decl.datatype, initHeapId = heapStringSentinel, position = decl.position) // point to the sentinel heap value instead
|
||||
litVal.parent=decl
|
||||
decl.value = litVal
|
||||
}
|
||||
else -> err("var/const declaration needs a compile-time constant initializer value for type ${decl.datatype}")
|
||||
// const fold should have provided it!
|
||||
}
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
when {
|
||||
decl.value is RangeExpr -> {
|
||||
@@ -560,8 +521,8 @@ private class AstChecker(private val program: Program,
|
||||
checkValueTypeAndRange(decl.datatype, arraySpec, decl.value as LiteralValue, program.heap)
|
||||
}
|
||||
else -> {
|
||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!::class.simpleName}")
|
||||
return super.process(decl)
|
||||
err("var/const declaration needs a compile-time constant initializer value, or range, instead found: ${decl.value!!.javaClass.simpleName}")
|
||||
return super.visit(decl)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -583,7 +544,7 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(decl.value !is LiteralValue) {
|
||||
err("value of memory var decl is not a literal (it is a ${decl.value!!::class.simpleName}).", decl.value?.position)
|
||||
err("value of memory var decl is not a literal (it is a ${decl.value!!.javaClass.simpleName}).", decl.value?.position)
|
||||
} else {
|
||||
val value = decl.value as LiteralValue
|
||||
if (value.asIntegerValue == null || value.asIntegerValue< 0 || value.asIntegerValue > 65535) {
|
||||
@@ -593,13 +554,13 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
/**
|
||||
* check the arguments of the directive
|
||||
*/
|
||||
override fun process(directive: Directive): IStatement {
|
||||
override fun visit(directive: Directive): IStatement {
|
||||
fun err(msg: String) {
|
||||
checkResult.add(SyntaxError(msg, directive.position))
|
||||
}
|
||||
@@ -670,7 +631,7 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
else -> throw SyntaxError("invalid directive ${directive.directive}", directive.position)
|
||||
}
|
||||
return super.process(directive)
|
||||
return super.visit(directive)
|
||||
}
|
||||
|
||||
private fun checkFileExists(directive: Directive, filename: String) {
|
||||
@@ -681,7 +642,7 @@ private class AstChecker(private val program: Program,
|
||||
checkResult.add(NameError("included file not found: $filename", directive.position))
|
||||
}
|
||||
|
||||
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||
override fun visit(literalValue: LiteralValue): LiteralValue {
|
||||
if(!compilerOptions.floats && literalValue.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
checkResult.add(SyntaxError("floating point used, but that is not enabled via options", literalValue.position))
|
||||
}
|
||||
@@ -692,7 +653,7 @@ private class AstChecker(private val program: Program,
|
||||
ArrayIndex(LiteralValue.optimalInteger(-3, literalValue.position), literalValue.position)
|
||||
checkValueTypeAndRange(literalValue.type, arrayspec, literalValue, program.heap)
|
||||
|
||||
val lv = super.process(literalValue)
|
||||
val lv = super.visit(literalValue)
|
||||
when(lv.type) {
|
||||
in StringDatatypes -> {
|
||||
if(lv.heapId==null)
|
||||
@@ -707,17 +668,17 @@ private class AstChecker(private val program: Program,
|
||||
return lv
|
||||
}
|
||||
|
||||
override fun process(expr: PrefixExpression): IExpression {
|
||||
override fun visit(expr: PrefixExpression): IExpression {
|
||||
if(expr.operator=="-") {
|
||||
val dt = expr.inferType(program)
|
||||
if (dt != DataType.BYTE && dt != DataType.WORD && dt != DataType.FLOAT) {
|
||||
checkResult.add(ExpressionError("can only take negative of a signed number type", expr.position))
|
||||
}
|
||||
}
|
||||
return super.process(expr)
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
|
||||
@@ -728,7 +689,7 @@ private class AstChecker(private val program: Program,
|
||||
if(divisor==0.0)
|
||||
checkResult.add(ExpressionError("division by zero", expr.right.position))
|
||||
if(expr.operator=="%") {
|
||||
if ((rightDt != DataType.UBYTE && rightDt != DataType.UWORD) || (leftDt!=DataType.UBYTE && leftDt!=DataType.UWORD))
|
||||
if ((rightDt != DataType.UBYTE && rightDt != DataType.UWORD) || (leftDt!= DataType.UBYTE && leftDt!= DataType.UWORD))
|
||||
checkResult.add(ExpressionError("remainder can only be used on unsigned integer operands", expr.right.position))
|
||||
}
|
||||
}
|
||||
@@ -756,20 +717,20 @@ private class AstChecker(private val program: Program,
|
||||
checkResult.add(ExpressionError("left operand is not numeric", expr.left.position))
|
||||
if(rightDt!in NumericDatatypes)
|
||||
checkResult.add(ExpressionError("right operand is not numeric", expr.right.position))
|
||||
return super.process(expr)
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
override fun process(typecast: TypecastExpression): IExpression {
|
||||
override fun visit(typecast: TypecastExpression): IExpression {
|
||||
if(typecast.type in IterableDatatypes)
|
||||
checkResult.add(ExpressionError("cannot type cast to string or array type", typecast.position))
|
||||
return super.process(typecast)
|
||||
return super.visit(typecast)
|
||||
}
|
||||
|
||||
override fun process(range: RangeExpr): IExpression {
|
||||
override fun visit(range: RangeExpr): IExpression {
|
||||
fun err(msg: String) {
|
||||
checkResult.add(SyntaxError(msg, range.position))
|
||||
}
|
||||
super.process(range)
|
||||
super.visit(range)
|
||||
val from = range.from.constValue(program)
|
||||
val to = range.to.constValue(program)
|
||||
val stepLv = range.step.constValue(program) ?: LiteralValue(DataType.UBYTE, 1, position = range.position)
|
||||
@@ -806,7 +767,7 @@ private class AstChecker(private val program: Program,
|
||||
return range
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||
val stmtOfExpression = findParentNode<IStatement>(functionCall)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}")
|
||||
@@ -814,16 +775,16 @@ private class AstChecker(private val program: Program,
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
|
||||
if(targetStatement!=null)
|
||||
checkFunctionCall(targetStatement, functionCall.arglist, functionCall.position)
|
||||
return super.process(functionCall)
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCallStatement.target, functionCallStatement)
|
||||
if(targetStatement!=null)
|
||||
checkFunctionCall(targetStatement, functionCallStatement.arglist, functionCallStatement.position)
|
||||
if(targetStatement is Subroutine && targetStatement.returntypes.isNotEmpty())
|
||||
printWarning("result value of subroutine call is discarded", functionCallStatement.position)
|
||||
return super.process(functionCallStatement)
|
||||
return super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
private fun checkFunctionCall(target: IStatement, args: List<IExpression>, position: Position) {
|
||||
@@ -864,7 +825,7 @@ private class AstChecker(private val program: Program,
|
||||
val argDt = arg.first.value.inferType(program)
|
||||
if(argDt!=null && !(argDt isAssignableTo arg.second.type)) {
|
||||
// for asm subroutines having STR param it's okay to provide a UWORD too (pointer value)
|
||||
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt==DataType.UWORD))
|
||||
if(!(target.isAsmSubroutine && arg.second.type in StringDatatypes && argDt== DataType.UWORD))
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} has invalid type $argDt, expected ${arg.second.type}", position))
|
||||
}
|
||||
|
||||
@@ -878,13 +839,13 @@ private class AstChecker(private val program: Program,
|
||||
val asmParamReg = target.asmParameterRegisters[arg.first.index]
|
||||
if(asmParamReg.statusflag!=null) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for statusflag", position))
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for statusflag", position))
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.A, RegisterOrPair.X, RegisterOrPair.Y)) {
|
||||
if(argDt !in ByteDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be byte type for single register", position))
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be byte type for single register", position))
|
||||
} else if(asmParamReg.registerOrPair in setOf(RegisterOrPair.AX, RegisterOrPair.AY, RegisterOrPair.XY)) {
|
||||
if(argDt !in WordDatatypes+ IterableDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index+1} must be word type for register pair", position))
|
||||
if(argDt !in WordDatatypes + IterableDatatypes)
|
||||
checkResult.add(ExpressionError("subroutine '${target.name}' argument ${arg.first.index + 1} must be word type for register pair", position))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -892,14 +853,14 @@ private class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(postIncrDecr: PostIncrDecr): IStatement {
|
||||
override fun visit(postIncrDecr: PostIncrDecr): IStatement {
|
||||
if(postIncrDecr.target.identifier != null) {
|
||||
val targetName = postIncrDecr.target.identifier!!.nameInSource
|
||||
val target = program.namespace.lookup(targetName, postIncrDecr)
|
||||
if(target==null) {
|
||||
checkResult.add(SyntaxError("undefined symbol: ${targetName.joinToString(".")}", postIncrDecr.position))
|
||||
} else {
|
||||
if(target !is VarDecl || target.type==VarDeclType.CONST) {
|
||||
if(target !is VarDecl || target.type== VarDeclType.CONST) {
|
||||
checkResult.add(SyntaxError("can only increment or decrement a variable", postIncrDecr.position))
|
||||
} else if(target.datatype !in NumericDatatypes) {
|
||||
checkResult.add(SyntaxError("can only increment or decrement a byte/float/word variable", postIncrDecr.position))
|
||||
@@ -918,10 +879,10 @@ private class AstChecker(private val program: Program,
|
||||
} else if(postIncrDecr.target.memoryAddress != null) {
|
||||
// a memory location can always be ++/--
|
||||
}
|
||||
return super.process(postIncrDecr)
|
||||
return super.visit(postIncrDecr)
|
||||
}
|
||||
|
||||
override fun process(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
||||
val target = arrayIndexedExpression.identifier.targetStatement(program.namespace)
|
||||
if(target is VarDecl) {
|
||||
if(target.datatype !in IterableDatatypes)
|
||||
@@ -945,10 +906,37 @@ private class AstChecker(private val program: Program,
|
||||
|
||||
// check index value 0..255
|
||||
val dtx = arrayIndexedExpression.arrayspec.index.inferType(program)
|
||||
if(dtx!=DataType.UBYTE && dtx!=DataType.BYTE)
|
||||
if(dtx!= DataType.UBYTE && dtx!= DataType.BYTE)
|
||||
checkResult.add(SyntaxError("array indexing is limited to byte size 0..255", arrayIndexedExpression.position))
|
||||
|
||||
return super.process(arrayIndexedExpression)
|
||||
return super.visit(arrayIndexedExpression)
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
||||
val conditionType = whenStatement.condition.inferType(program)
|
||||
if(conditionType !in IntegerDatatypes)
|
||||
checkResult.add(SyntaxError("when condition must be an integer value", whenStatement.position))
|
||||
val choiceValues = whenStatement.choiceValues(program)
|
||||
val occurringValues = choiceValues.map {it.first}
|
||||
val tally = choiceValues.associate { it.second to occurringValues.count { ov->it.first==ov} }
|
||||
tally.filter { it.value>1 }.forEach {
|
||||
checkResult.add(SyntaxError("choice value occurs multiple times", it.key.position))
|
||||
}
|
||||
return super.visit(whenStatement)
|
||||
}
|
||||
|
||||
override fun visit(whenChoice: WhenChoice) {
|
||||
if(whenChoice.value!=null) {
|
||||
val constvalue = whenChoice.value.constValue(program)
|
||||
if (constvalue == null)
|
||||
checkResult.add(SyntaxError("value of a when choice must be a constant", whenChoice.position))
|
||||
else if (constvalue.type !in IntegerDatatypes)
|
||||
checkResult.add(SyntaxError("value of a when choice must be a byte or word", whenChoice.position))
|
||||
} else {
|
||||
if(whenChoice !== (whenChoice.parent as WhenStatement).choices.last())
|
||||
checkResult.add(SyntaxError("else choice must be the last one", whenChoice.position))
|
||||
}
|
||||
super.visit(whenChoice)
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
|
||||
@@ -1121,7 +1109,7 @@ private class AstChecker(private val program: Program,
|
||||
value.arrayvalue.map {it.constValue(program)?.asNumericValue!!.toDouble()}.toDoubleArray()
|
||||
else
|
||||
heap.get(value.heapId!!).doubleArray!!
|
||||
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE})
|
||||
if(doubles.any { it < FLOAT_MAX_NEGATIVE || it> FLOAT_MAX_POSITIVE })
|
||||
return err("floating point value overflow")
|
||||
return true
|
||||
}
|
||||
@@ -1190,26 +1178,26 @@ private class AstChecker(private val program: Program,
|
||||
checkResult.add(SyntaxError("can't assign a range value", position))
|
||||
|
||||
val result = when(targetDatatype) {
|
||||
DataType.BYTE -> sourceDatatype==DataType.BYTE
|
||||
DataType.UBYTE -> sourceDatatype==DataType.UBYTE
|
||||
DataType.WORD -> sourceDatatype==DataType.BYTE || sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.WORD
|
||||
DataType.UWORD -> sourceDatatype==DataType.UBYTE || sourceDatatype==DataType.UWORD
|
||||
DataType.BYTE -> sourceDatatype== DataType.BYTE
|
||||
DataType.UBYTE -> sourceDatatype== DataType.UBYTE
|
||||
DataType.WORD -> sourceDatatype== DataType.BYTE || sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.WORD
|
||||
DataType.UWORD -> sourceDatatype== DataType.UBYTE || sourceDatatype== DataType.UWORD
|
||||
DataType.FLOAT -> sourceDatatype in NumericDatatypes
|
||||
DataType.STR -> sourceDatatype==DataType.STR
|
||||
DataType.STR_S -> sourceDatatype==DataType.STR_S
|
||||
DataType.STR -> sourceDatatype== DataType.STR
|
||||
DataType.STR_S -> sourceDatatype== DataType.STR_S
|
||||
else -> checkResult.add(SyntaxError("cannot assign new value to variable of type $targetDatatype", position))
|
||||
}
|
||||
|
||||
if(result)
|
||||
return true
|
||||
|
||||
if((sourceDatatype==DataType.UWORD || sourceDatatype==DataType.WORD) && (targetDatatype==DataType.UBYTE || targetDatatype==DataType.BYTE)) {
|
||||
if((sourceDatatype== DataType.UWORD || sourceDatatype== DataType.WORD) && (targetDatatype== DataType.UBYTE || targetDatatype== DataType.BYTE)) {
|
||||
if(assignTargets.size==2 && assignTargets[0].register!=null && assignTargets[1].register!=null)
|
||||
return true // for asm subroutine calls that return a (U)WORD that's going to be stored into two BYTES (registers), we make an exception.
|
||||
else
|
||||
checkResult.add(ExpressionError("cannot assign word to byte, use msb() or lsb()?", position))
|
||||
}
|
||||
else if(sourceDatatype==DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
else if(sourceDatatype== DataType.FLOAT && targetDatatype in IntegerDatatypes)
|
||||
checkResult.add(ExpressionError("cannot assign float to ${targetDatatype.name.toLowerCase()}; possible loss of precision. Suggestion: round the value or revert to integer arithmetic", position))
|
||||
else
|
||||
checkResult.add(ExpressionError("cannot assign ${sourceDatatype.name.toLowerCase()} to ${targetDatatype.name.toLowerCase()}", position))
|
||||
+32
-74
@@ -1,54 +1,14 @@
|
||||
package prog8.ast
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.autoHeapValuePrefix
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
/**
|
||||
* Checks the validity of all identifiers (no conflicts)
|
||||
* Also makes sure that subroutine's parameters also become local variable decls in the subroutine's scope.
|
||||
* Finally, it also makes sure the datatype of all Var decls and sub Return values is set correctly.
|
||||
*/
|
||||
|
||||
internal fun Program.checkIdentifiers() {
|
||||
val checker = AstIdentifiersChecker(namespace)
|
||||
checker.process(this)
|
||||
|
||||
if(modules.map {it.name}.toSet().size != modules.size) {
|
||||
throw FatalAstException("modules should all be unique")
|
||||
}
|
||||
|
||||
// add any anonymous variables for heap values that are used,
|
||||
// and replace an iterable literalvalue by identifierref to new local variable
|
||||
for (variable in checker.anonymousVariablesFromHeap.values) {
|
||||
val scope = variable.first.definingScope()
|
||||
scope.statements.add(variable.second)
|
||||
val parent = variable.first.parent
|
||||
when {
|
||||
parent is Assignment && parent.value === variable.first -> {
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.value = idref
|
||||
}
|
||||
parent is IFunctionCall -> {
|
||||
val parameterPos = parent.arglist.indexOf(variable.first)
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.arglist[parameterPos] = idref
|
||||
}
|
||||
parent is ForLoop -> {
|
||||
val idref = IdentifierReference(listOf("$autoHeapValuePrefix${variable.first.heapId}"), variable.first.position)
|
||||
idref.linkParents(parent)
|
||||
parent.iterable = idref
|
||||
}
|
||||
else -> TODO("replace literalvalue by identifierref: $variable (in $parent)")
|
||||
}
|
||||
variable.second.linkParents(scope as Node)
|
||||
}
|
||||
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
private class AstIdentifiersChecker(private val namespace: INameScope) : IAstProcessor {
|
||||
internal class AstIdentifiersChecker(private val namespace: INameScope) : IAstModifyingVisitor {
|
||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
||||
|
||||
private var blocks: MutableMap<String, Block> = mutableMapOf()
|
||||
@@ -61,32 +21,32 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
||||
}
|
||||
|
||||
override fun process(module: Module) {
|
||||
override fun visit(module: Module) {
|
||||
blocks.clear() // blocks may be redefined within a different module
|
||||
super.process(module)
|
||||
super.visit(module)
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
override fun visit(block: Block): IStatement {
|
||||
val existing = blocks[block.name]
|
||||
if(existing!=null)
|
||||
nameError(block.name, block.position, existing)
|
||||
else
|
||||
blocks[block.name] = block
|
||||
|
||||
return super.process(block)
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||
val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position)
|
||||
typecast.linkParents(functionCall.parent)
|
||||
return super.process(typecast)
|
||||
return super.visit(typecast)
|
||||
}
|
||||
return super.process(functionCall)
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
// first, check if there are datatype errors on the vardecl
|
||||
decl.datatypeErrors.forEach { checkResult.add(it) }
|
||||
|
||||
@@ -99,10 +59,10 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
if (existing != null && existing !== decl)
|
||||
nameError(decl.name, decl.position, existing)
|
||||
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
||||
@@ -146,10 +106,10 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(subroutine)
|
||||
return super.visit(subroutine)
|
||||
}
|
||||
|
||||
override fun process(label: Label): IStatement {
|
||||
override fun visit(label: Label): IStatement {
|
||||
if(label.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
||||
@@ -158,10 +118,10 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
if (existing != null && existing !== label)
|
||||
nameError(label.name, label.position, existing)
|
||||
}
|
||||
return super.process(label)
|
||||
return super.visit(label)
|
||||
}
|
||||
|
||||
override fun process(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
||||
// rather than reusing an already declared loopvar from an outer scope.
|
||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
||||
@@ -198,16 +158,16 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(forLoop)
|
||||
return super.visit(forLoop)
|
||||
}
|
||||
|
||||
override fun process(assignTarget: AssignTarget): AssignTarget {
|
||||
if(assignTarget.register==Register.X)
|
||||
override fun visit(assignTarget: AssignTarget): AssignTarget {
|
||||
if(assignTarget.register== Register.X)
|
||||
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
|
||||
return super.process(assignTarget)
|
||||
return super.visit(assignTarget)
|
||||
}
|
||||
|
||||
override fun process(returnStmt: Return): IStatement {
|
||||
override fun visit(returnStmt: Return): IStatement {
|
||||
if(returnStmt.values.isNotEmpty()) {
|
||||
// possibly adjust any literal values returned, into the desired returning data type
|
||||
val subroutine = returnStmt.definingSubroutine()!!
|
||||
@@ -217,7 +177,7 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
for(returnvalue in returnStmt.values.zip(subroutine.returntypes)) {
|
||||
val lval = returnvalue.first as? LiteralValue
|
||||
if(lval!=null) {
|
||||
val adjusted = lval.intoDatatype(returnvalue.second)
|
||||
val adjusted = lval.cast(returnvalue.second)
|
||||
if(adjusted!=null && adjusted !== lval)
|
||||
newValues.add(adjusted)
|
||||
else
|
||||
@@ -228,14 +188,14 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
}
|
||||
returnStmt.values = newValues
|
||||
}
|
||||
return super.process(returnStmt)
|
||||
return super.visit(returnStmt)
|
||||
}
|
||||
|
||||
|
||||
internal val anonymousVariablesFromHeap = mutableMapOf<String, Pair<LiteralValue, VarDecl>>()
|
||||
|
||||
|
||||
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||
override fun visit(literalValue: LiteralValue): LiteralValue {
|
||||
if(literalValue.heapId!=null && literalValue.parent !is VarDecl) {
|
||||
// a literal value that's not declared as a variable, which refers to something on the heap.
|
||||
// we need to introduce an auto-generated variable for this to be able to refer to the value!
|
||||
@@ -243,16 +203,14 @@ private class AstIdentifiersChecker(private val namespace: INameScope) : IAstPro
|
||||
isArray = false, autoGenerated = false, position = literalValue.position)
|
||||
anonymousVariablesFromHeap[variable.name] = Pair(literalValue, variable)
|
||||
}
|
||||
return super.process(literalValue)
|
||||
return super.visit(literalValue)
|
||||
}
|
||||
|
||||
override fun process(addressOf: AddressOf): IExpression {
|
||||
override fun visit(addressOf: AddressOf): IExpression {
|
||||
// register the scoped name of the referenced identifier
|
||||
val variable= addressOf.identifier.targetVarDecl(namespace) ?: return addressOf
|
||||
addressOf.scopedname = variable.scopedname
|
||||
return super.process(addressOf)
|
||||
return super.visit(addressOf)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
internal const val autoHeapValuePrefix = "auto_heap_value_"
|
||||
@@ -0,0 +1,117 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.statements.FunctionCallStatement
|
||||
import prog8.ast.statements.Subroutine
|
||||
|
||||
|
||||
internal class AstRecursionChecker(private val namespace: INameScope) : IAstVisitor {
|
||||
private val callGraph = DirectedGraph<INameScope>()
|
||||
|
||||
internal fun result(): List<AstException> {
|
||||
val cycle = callGraph.checkForCycle()
|
||||
if(cycle.isEmpty())
|
||||
return emptyList()
|
||||
val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
||||
return listOf(AstException("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain"))
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val scope = functionCallStatement.definingScope()
|
||||
val targetStatement = functionCallStatement.target.targetStatement(namespace)
|
||||
if(targetStatement!=null) {
|
||||
val targetScope = when (targetStatement) {
|
||||
is Subroutine -> targetStatement
|
||||
else -> targetStatement.definingScope()
|
||||
}
|
||||
callGraph.add(scope, targetScope)
|
||||
}
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall) {
|
||||
val scope = functionCall.definingScope()
|
||||
val targetStatement = functionCall.target.targetStatement(namespace)
|
||||
if(targetStatement!=null) {
|
||||
val targetScope = when (targetStatement) {
|
||||
is Subroutine -> targetStatement
|
||||
else -> targetStatement.definingScope()
|
||||
}
|
||||
callGraph.add(scope, targetScope)
|
||||
}
|
||||
super.visit(functionCall)
|
||||
}
|
||||
|
||||
|
||||
private class DirectedGraph<VT> {
|
||||
private val graph = mutableMapOf<VT, MutableSet<VT>>()
|
||||
private var uniqueVertices = mutableSetOf<VT>()
|
||||
val numVertices : Int
|
||||
get() = uniqueVertices.size
|
||||
|
||||
fun add(from: VT, to: VT) {
|
||||
var targets = graph[from]
|
||||
if(targets==null) {
|
||||
targets = mutableSetOf()
|
||||
graph[from] = targets
|
||||
}
|
||||
targets.add(to)
|
||||
uniqueVertices.add(from)
|
||||
uniqueVertices.add(to)
|
||||
}
|
||||
|
||||
fun print() {
|
||||
println("#vertices: $numVertices")
|
||||
graph.forEach { (from, to) ->
|
||||
println("$from CALLS:")
|
||||
to.forEach { println(" $it") }
|
||||
}
|
||||
val cycle = checkForCycle()
|
||||
if(cycle.isNotEmpty()) {
|
||||
println("CYCLIC! $cycle")
|
||||
}
|
||||
}
|
||||
|
||||
fun checkForCycle(): MutableList<VT> {
|
||||
val visited = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val recStack = uniqueVertices.associateWith { false }.toMutableMap()
|
||||
val cycle = mutableListOf<VT>()
|
||||
for(node in uniqueVertices) {
|
||||
if(isCyclicUntil(node, visited, recStack, cycle))
|
||||
return cycle
|
||||
}
|
||||
return mutableListOf()
|
||||
}
|
||||
|
||||
private fun isCyclicUntil(node: VT,
|
||||
visited: MutableMap<VT, Boolean>,
|
||||
recStack: MutableMap<VT, Boolean>,
|
||||
cycleNodes: MutableList<VT>): Boolean {
|
||||
|
||||
if(recStack[node]==true) return true
|
||||
if(visited[node]==true) return false
|
||||
|
||||
// mark current node as visited and add to recursion stack
|
||||
visited[node] = true
|
||||
recStack[node] = true
|
||||
|
||||
// recurse for all neighbours
|
||||
val neighbors = graph[node]
|
||||
if(neighbors!=null) {
|
||||
for (neighbour in neighbors) {
|
||||
if (isCyclicUntil(neighbour, visited, recStack, cycleNodes)) {
|
||||
cycleNodes.add(node)
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// pop node from recursion stack
|
||||
recStack[node] = false
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
|
||||
interface IAstModifyingVisitor {
|
||||
fun visit(program: Program) {
|
||||
program.modules.forEach { visit(it) }
|
||||
}
|
||||
|
||||
fun visit(module: Module) {
|
||||
module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
}
|
||||
|
||||
fun visit(expr: PrefixExpression): IExpression {
|
||||
expr.expression = expr.expression.accept(this)
|
||||
return expr
|
||||
}
|
||||
|
||||
fun visit(expr: BinaryExpression): IExpression {
|
||||
expr.left = expr.left.accept(this)
|
||||
expr.right = expr.right.accept(this)
|
||||
return expr
|
||||
}
|
||||
|
||||
fun visit(directive: Directive): IStatement {
|
||||
return directive
|
||||
}
|
||||
|
||||
fun visit(block: Block): IStatement {
|
||||
block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return block
|
||||
}
|
||||
|
||||
fun visit(decl: VarDecl): IStatement {
|
||||
decl.value = decl.value?.accept(this)
|
||||
decl.arraysize?.accept(this)
|
||||
return decl
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine): IStatement {
|
||||
subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return subroutine
|
||||
}
|
||||
|
||||
fun visit(functionCall: FunctionCall): IExpression {
|
||||
val newtarget = functionCall.target.accept(this)
|
||||
if(newtarget is IdentifierReference)
|
||||
functionCall.target = newtarget
|
||||
functionCall.arglist = functionCall.arglist.map { it.accept(this) }.toMutableList()
|
||||
return functionCall
|
||||
}
|
||||
|
||||
fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val newtarget = functionCallStatement.target.accept(this)
|
||||
if(newtarget is IdentifierReference)
|
||||
functionCallStatement.target = newtarget
|
||||
functionCallStatement.arglist = functionCallStatement.arglist.map { it.accept(this) }.toMutableList()
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
fun visit(identifier: IdentifierReference): IExpression {
|
||||
// note: this is an identifier that is used in an expression.
|
||||
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
||||
return identifier
|
||||
}
|
||||
|
||||
fun visit(jump: Jump): IStatement {
|
||||
if(jump.identifier!=null) {
|
||||
val ident = jump.identifier.accept(this)
|
||||
if(ident is IdentifierReference && ident!==jump.identifier) {
|
||||
return Jump(null, ident, null, jump.position)
|
||||
}
|
||||
}
|
||||
return jump
|
||||
}
|
||||
|
||||
fun visit(ifStatement: IfStatement): IStatement {
|
||||
ifStatement.condition = ifStatement.condition.accept(this)
|
||||
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
||||
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
fun visit(branchStatement: BranchStatement): IStatement {
|
||||
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
||||
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
||||
return branchStatement
|
||||
}
|
||||
|
||||
fun visit(range: RangeExpr): IExpression {
|
||||
range.from = range.from.accept(this)
|
||||
range.to = range.to.accept(this)
|
||||
range.step = range.step.accept(this)
|
||||
return range
|
||||
}
|
||||
|
||||
fun visit(label: Label): IStatement {
|
||||
return label
|
||||
}
|
||||
|
||||
fun visit(literalValue: LiteralValue): LiteralValue {
|
||||
if(literalValue.arrayvalue!=null) {
|
||||
for(av in literalValue.arrayvalue.withIndex()) {
|
||||
val newvalue = av.value.accept(this)
|
||||
literalValue.arrayvalue[av.index] = newvalue
|
||||
}
|
||||
}
|
||||
return literalValue
|
||||
}
|
||||
|
||||
fun visit(assignment: Assignment): IStatement {
|
||||
assignment.targets = assignment.targets.map { it.accept(this) }
|
||||
assignment.value = assignment.value.accept(this)
|
||||
return assignment
|
||||
}
|
||||
|
||||
fun visit(postIncrDecr: PostIncrDecr): IStatement {
|
||||
postIncrDecr.target = postIncrDecr.target.accept(this)
|
||||
return postIncrDecr
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue): IStatement {
|
||||
return contStmt
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break): IStatement {
|
||||
return breakStmt
|
||||
}
|
||||
|
||||
fun visit(forLoop: ForLoop): IStatement {
|
||||
forLoop.loopVar?.accept(this)
|
||||
forLoop.iterable = forLoop.iterable.accept(this)
|
||||
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
||||
return forLoop
|
||||
}
|
||||
|
||||
fun visit(whileLoop: WhileLoop): IStatement {
|
||||
whileLoop.condition = whileLoop.condition.accept(this)
|
||||
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
||||
return whileLoop
|
||||
}
|
||||
|
||||
fun visit(repeatLoop: RepeatLoop): IStatement {
|
||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
fun visit(returnStmt: Return): IStatement {
|
||||
returnStmt.values = returnStmt.values.map { it.accept(this) }
|
||||
return returnStmt
|
||||
}
|
||||
|
||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
||||
arrayIndexedExpression.identifier.accept(this)
|
||||
arrayIndexedExpression.arrayspec.accept(this)
|
||||
return arrayIndexedExpression
|
||||
}
|
||||
|
||||
fun visit(assignTarget: AssignTarget): AssignTarget {
|
||||
assignTarget.arrayindexed?.accept(this)
|
||||
assignTarget.identifier?.accept(this)
|
||||
assignTarget.memoryAddress?.let { visit(it) }
|
||||
return assignTarget
|
||||
}
|
||||
|
||||
fun visit(scope: AnonymousScope): IStatement {
|
||||
scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return scope
|
||||
}
|
||||
|
||||
fun visit(typecast: TypecastExpression): IExpression {
|
||||
typecast.expression = typecast.expression.accept(this)
|
||||
return typecast
|
||||
}
|
||||
|
||||
fun visit(memread: DirectMemoryRead): IExpression {
|
||||
memread.addressExpression = memread.addressExpression.accept(this)
|
||||
return memread
|
||||
}
|
||||
|
||||
fun visit(memwrite: DirectMemoryWrite) {
|
||||
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(addressOf: AddressOf): IExpression {
|
||||
addressOf.identifier.accept(this)
|
||||
return addressOf
|
||||
}
|
||||
|
||||
fun visit(inlineAssembly: InlineAssembly): IStatement {
|
||||
return inlineAssembly
|
||||
}
|
||||
|
||||
fun visit(registerExpr: RegisterExpr): IExpression {
|
||||
return registerExpr
|
||||
}
|
||||
|
||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): IStatement {
|
||||
return builtinFunctionStatementPlaceholder
|
||||
}
|
||||
|
||||
fun visit(nopStatement: NopStatement): IStatement {
|
||||
return nopStatement
|
||||
}
|
||||
|
||||
fun visit(whenStatement: WhenStatement): IStatement {
|
||||
whenStatement.condition.accept(this)
|
||||
whenStatement.choices.forEach { it.accept(this) }
|
||||
return whenStatement
|
||||
}
|
||||
|
||||
fun visit(whenChoice: WhenChoice) {
|
||||
whenChoice.value?.accept(this)
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,169 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
|
||||
interface IAstVisitor {
|
||||
fun visit(program: Program) {
|
||||
program.modules.forEach { visit(it) }
|
||||
}
|
||||
|
||||
fun visit(module: Module) {
|
||||
module.statements.forEach{ it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(expr: PrefixExpression) {
|
||||
expr.expression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(expr: BinaryExpression) {
|
||||
expr.left.accept(this)
|
||||
expr.right.accept(this)
|
||||
}
|
||||
|
||||
fun visit(directive: Directive) {
|
||||
}
|
||||
|
||||
fun visit(block: Block) {
|
||||
block.statements.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(decl: VarDecl) {
|
||||
decl.value?.accept(this)
|
||||
decl.arraysize?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine) {
|
||||
subroutine.statements.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(functionCall: FunctionCall) {
|
||||
functionCall.target.accept(this)
|
||||
functionCall.arglist.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
functionCallStatement.target.accept(this)
|
||||
functionCallStatement.arglist.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(identifier: IdentifierReference) {
|
||||
}
|
||||
|
||||
fun visit(jump: Jump) {
|
||||
jump.identifier?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(ifStatement: IfStatement) {
|
||||
ifStatement.condition.accept(this)
|
||||
ifStatement.truepart.accept(this)
|
||||
ifStatement.elsepart.accept(this)
|
||||
}
|
||||
|
||||
fun visit(branchStatement: BranchStatement) {
|
||||
branchStatement.truepart.accept(this)
|
||||
branchStatement.elsepart.accept(this)
|
||||
}
|
||||
|
||||
fun visit(range: RangeExpr) {
|
||||
range.from.accept(this)
|
||||
range.to.accept(this)
|
||||
range.step.accept(this)
|
||||
}
|
||||
|
||||
fun visit(label: Label) {
|
||||
}
|
||||
|
||||
fun visit(literalValue: LiteralValue) {
|
||||
literalValue.arrayvalue?.let { it.forEach { v->v.accept(this) }}
|
||||
}
|
||||
|
||||
fun visit(assignment: Assignment) {
|
||||
assignment.targets.forEach { it.accept(this) }
|
||||
assignment.value.accept(this)
|
||||
}
|
||||
|
||||
fun visit(postIncrDecr: PostIncrDecr) {
|
||||
postIncrDecr.target.accept(this)
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue) {
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break) {
|
||||
}
|
||||
|
||||
fun visit(forLoop: ForLoop) {
|
||||
forLoop.loopVar?.accept(this)
|
||||
forLoop.iterable.accept(this)
|
||||
forLoop.body.accept(this)
|
||||
}
|
||||
|
||||
fun visit(whileLoop: WhileLoop) {
|
||||
whileLoop.condition.accept(this)
|
||||
whileLoop.body.accept(this)
|
||||
}
|
||||
|
||||
fun visit(repeatLoop: RepeatLoop) {
|
||||
repeatLoop.untilCondition.accept(this)
|
||||
repeatLoop.body.accept(this)
|
||||
}
|
||||
|
||||
fun visit(returnStmt: Return) {
|
||||
returnStmt.values.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
arrayIndexedExpression.identifier.accept(this)
|
||||
arrayIndexedExpression.arrayspec.accept(this)
|
||||
}
|
||||
|
||||
fun visit(assignTarget: AssignTarget) {
|
||||
assignTarget.arrayindexed?.accept(this)
|
||||
assignTarget.identifier?.accept(this)
|
||||
assignTarget.memoryAddress?.accept(this)
|
||||
}
|
||||
|
||||
fun visit(scope: AnonymousScope) {
|
||||
scope.statements.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(typecast: TypecastExpression) {
|
||||
typecast.expression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(memread: DirectMemoryRead) {
|
||||
memread.addressExpression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(memwrite: DirectMemoryWrite) {
|
||||
memwrite.addressExpression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(addressOf: AddressOf) {
|
||||
addressOf.identifier.accept(this)
|
||||
}
|
||||
|
||||
fun visit(inlineAssembly: InlineAssembly) {
|
||||
}
|
||||
|
||||
fun visit(registerExpr: RegisterExpr) {
|
||||
}
|
||||
|
||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||
}
|
||||
|
||||
fun visit(nopStatement: NopStatement) {
|
||||
}
|
||||
|
||||
fun visit(whenStatement: WhenStatement) {
|
||||
whenStatement.condition.accept(this)
|
||||
whenStatement.choices.forEach { it.accept(this) }
|
||||
}
|
||||
|
||||
fun visit(whenChoice: WhenChoice) {
|
||||
whenChoice.value?.accept(this)
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
}
|
||||
+10
-17
@@ -1,18 +1,11 @@
|
||||
package prog8.ast
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.base.printWarning
|
||||
import prog8.ast.statements.Directive
|
||||
|
||||
/**
|
||||
* Checks that are specific for imported modules.
|
||||
*/
|
||||
|
||||
internal fun Module.checkImportedValid() {
|
||||
val checker = ImportedAstChecker()
|
||||
checker.process(this)
|
||||
printErrors(checker.result(), name)
|
||||
}
|
||||
|
||||
|
||||
private class ImportedAstChecker : IAstProcessor {
|
||||
internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||
|
||||
internal fun result(): List<SyntaxError> {
|
||||
@@ -20,15 +13,15 @@ private class ImportedAstChecker : IAstProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Module check: most global directives don't apply for imported modules
|
||||
* Most global directives don't apply for imported modules, so remove them
|
||||
*/
|
||||
override fun process(module: Module) {
|
||||
super.process(module)
|
||||
override fun visit(module: Module) {
|
||||
super.visit(module)
|
||||
val newStatements : MutableList<IStatement> = mutableListOf()
|
||||
|
||||
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||
for (sourceStmt in module.statements) {
|
||||
val stmt = sourceStmt.process(this)
|
||||
val stmt = sourceStmt.accept(this)
|
||||
if(stmt is Directive && stmt.parent is Module) {
|
||||
if(stmt.directive in moduleLevelDirectives) {
|
||||
printWarning("ignoring module directive because it was imported", stmt.position, stmt.directive)
|
||||
+38
-183
@@ -1,19 +1,18 @@
|
||||
package prog8.ast
|
||||
package prog8.ast.processing
|
||||
|
||||
import kotlin.comparisons.nullsLast
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.initvarsSubName
|
||||
import prog8.ast.base.printWarning
|
||||
import prog8.ast.expressions.BinaryExpression
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.TypecastExpression
|
||||
import prog8.ast.statements.*
|
||||
import prog8.functions.BuiltinFunctions
|
||||
|
||||
internal fun Program.reorderStatements() {
|
||||
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace)
|
||||
initvalueCreator.process(this)
|
||||
|
||||
val checker = StatementReorderer(this)
|
||||
checker.process(this)
|
||||
}
|
||||
|
||||
internal const val initvarsSubName="prog8_init_vars" // the name of the subroutine that should be called for every block to initialize its variables
|
||||
|
||||
|
||||
private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
internal class StatementReorderer(private val program: Program): IAstModifyingVisitor {
|
||||
// Reorders the statements in a way the compiler needs.
|
||||
// - 'main' block must be the very first statement UNLESS it has an address set.
|
||||
// - blocks are ordered by address, where blocks without address are put at the end.
|
||||
@@ -24,14 +23,15 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
//
|
||||
// - the 'start' subroutine in the 'main' block will be moved to the top immediately following the directives.
|
||||
// - all other subroutines will be moved to the end of their block.
|
||||
// - sorts the choices in when statement.
|
||||
//
|
||||
// Also, makes sure any value assignments get the proper type casts if needed to cast them into the target variable's type.
|
||||
// (this includes function call arguments)
|
||||
|
||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||
|
||||
override fun process(module: Module) {
|
||||
super.process(module)
|
||||
override fun visit(module: Module) {
|
||||
super.visit(module)
|
||||
|
||||
val (blocks, other) = module.statements.partition { it is Block }
|
||||
module.statements = other.asSequence().plus(blocks.sortedBy { (it as Block).address ?: Int.MAX_VALUE }).toMutableList()
|
||||
@@ -58,11 +58,9 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||
module.statements.removeAll(directives)
|
||||
module.statements.addAll(0, directives)
|
||||
|
||||
sortConstantAssignments(module.statements)
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
override fun visit(block: Block): IStatement {
|
||||
|
||||
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
||||
var numSubroutinesAtEnd = 0
|
||||
@@ -108,8 +106,6 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
block.statements.addAll(0, directives)
|
||||
block.linkParents(block.parent)
|
||||
|
||||
sortConstantAssignments(block.statements)
|
||||
|
||||
// create subroutine that initializes the block's variables (if any)
|
||||
val varInits = block.statements.withIndex().filter { it.value is VariableInitializationAssignment }
|
||||
if(varInits.isNotEmpty()) {
|
||||
@@ -125,13 +121,11 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
block.statements.removeAt(index)
|
||||
}
|
||||
|
||||
return super.process(block)
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
super.process(subroutine)
|
||||
|
||||
sortConstantAssignments(subroutine.statements)
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
super.visit(subroutine)
|
||||
|
||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||
subroutine.statements.removeAll(varDecls)
|
||||
@@ -144,7 +138,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
// add the implicit return statement at the end (if it's not there yet), but only if it's not a kernel routine.
|
||||
// and if an assembly block doesn't contain a rts/rti
|
||||
if(subroutine.asmAddress==null && subroutine.amountOfRtsInAsm()==0) {
|
||||
if (subroutine.statements.lastOrNull {it !is VarDecl} !is Return) {
|
||||
if (subroutine.statements.lastOrNull {it !is VarDecl } !is Return) {
|
||||
val returnStmt = Return(emptyList(), subroutine.position)
|
||||
returnStmt.linkParents(subroutine)
|
||||
subroutine.statements.add(returnStmt)
|
||||
@@ -155,13 +149,7 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
return subroutine
|
||||
}
|
||||
|
||||
override fun process(scope: AnonymousScope): AnonymousScope {
|
||||
scope.statements = scope.statements.map { it.process(this)}.toMutableList()
|
||||
sortConstantAssignments(scope.statements)
|
||||
return scope
|
||||
}
|
||||
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
||||
@@ -181,33 +169,10 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(expr)
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
private fun sortConstantAssignments(statements: MutableList<IStatement>) {
|
||||
// sort assignments by datatype and value, so multiple initializations with the isSameAs value can be optimized (to load the value just once)
|
||||
val result = mutableListOf<IStatement>()
|
||||
val stmtIter = statements.iterator()
|
||||
for(stmt in stmtIter) {
|
||||
if(stmt is Assignment && !stmt.targets.any { it.isMemoryMapped(program.namespace) }) {
|
||||
val constval = stmt.value.constValue(program)
|
||||
if(constval!=null) {
|
||||
val (sorted, trailing) = sortConstantAssignmentSequence(stmt, stmtIter)
|
||||
result.addAll(sorted)
|
||||
if(trailing!=null)
|
||||
result.add(trailing)
|
||||
}
|
||||
else
|
||||
result.add(stmt)
|
||||
}
|
||||
else
|
||||
result.add(stmt)
|
||||
}
|
||||
statements.clear()
|
||||
statements.addAll(result)
|
||||
}
|
||||
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
val target=assignment.singleTarget
|
||||
if(target!=null) {
|
||||
// see if a typecast is needed to convert the value's type into the proper target type
|
||||
@@ -222,23 +187,22 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
}
|
||||
} else TODO("multi-target assign")
|
||||
|
||||
return super.process(assignment)
|
||||
return super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
||||
return super.process(functionCallStatement)
|
||||
return super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
||||
return super.process(functionCall)
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
|
||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||
val sub = call.target.targetStatement(scope)
|
||||
when(sub) {
|
||||
when(val sub = call.target.targetStatement(scope)) {
|
||||
is Subroutine -> {
|
||||
for(arg in sub.parameters.zip(call.arglist.withIndex())) {
|
||||
val argtype = arg.second.value.inferType(program)
|
||||
@@ -304,127 +268,18 @@ private class StatementReorderer(private val program: Program): IAstProcessor {
|
||||
return Pair(sorted, trailing)
|
||||
}
|
||||
|
||||
override fun process(typecast: TypecastExpression): IExpression {
|
||||
override fun visit(typecast: TypecastExpression): IExpression {
|
||||
// warn about any implicit type casts to Float, because that may not be intended
|
||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||
}
|
||||
return super.process(typecast)
|
||||
return super.visit(typecast)
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
||||
// sort the choices in low-to-high value order
|
||||
whenStatement.choices
|
||||
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.value?.constValue(program)?.asIntegerValue}))
|
||||
return super.visit(whenStatement)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstProcessor {
|
||||
// For VarDecls that declare an initialization value:
|
||||
// Replace the vardecl with an assignment (to set the initial value),
|
||||
// and add a new vardecl with the default constant value of that type (usually zero) to the scope.
|
||||
// This makes sure the variables get reset to the intended value on a next run of the program.
|
||||
// Variable decls without a value don't get this treatment, which means they retain the last
|
||||
// value they had when restarting the program.
|
||||
// This is done in a separate step because it interferes with the namespace lookup of symbols
|
||||
// in other ast processors.
|
||||
|
||||
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||
|
||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>()
|
||||
|
||||
override fun process(module: Module) {
|
||||
vardeclsToAdd.clear()
|
||||
super.process(module)
|
||||
|
||||
// add any new vardecls to the various scopes
|
||||
for(decl in vardeclsToAdd)
|
||||
for(d in decl.value) {
|
||||
d.value.linkParents(decl.key as Node)
|
||||
decl.key.statements.add(0, d.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
super.process(decl)
|
||||
if(decl.type!=VarDeclType.VAR || decl.value==null)
|
||||
return decl
|
||||
|
||||
if(decl.datatype in NumericDatatypes) {
|
||||
val scope = decl.definingScope()
|
||||
addVarDecl(scope, decl.asDefaultValueDecl(null))
|
||||
val declvalue = decl.value!!
|
||||
val value =
|
||||
if(declvalue is LiteralValue) {
|
||||
val converted = declvalue.intoDatatype(decl.datatype)
|
||||
converted ?: declvalue
|
||||
}
|
||||
else
|
||||
declvalue
|
||||
val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
|
||||
return VariableInitializationAssignment(
|
||||
AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
|
||||
null,
|
||||
value,
|
||||
decl.position
|
||||
)
|
||||
}
|
||||
return decl
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
val targetStatement = functionCall.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null) {
|
||||
var node: Node = functionCall
|
||||
while(node !is IStatement)
|
||||
node=node.parent
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
|
||||
}
|
||||
return functionCall
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null)
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<IExpression>, parent: IStatement) {
|
||||
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
||||
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
|
||||
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type in StringDatatypes) {
|
||||
if(argparam.second is AddressOf)
|
||||
continue
|
||||
val idref = argparam.second as? IdentifierReference
|
||||
val strvalue = argparam.second as? LiteralValue
|
||||
if(idref!=null) {
|
||||
val variable = idref.targetVarDecl(namespace)
|
||||
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
||||
val pointerExpr = AddressOf(idref, idref.position)
|
||||
pointerExpr.scopedname = parent.makeScopedName(idref.nameInSource.single())
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
}
|
||||
}
|
||||
else if(strvalue!=null) {
|
||||
if(strvalue.isString) {
|
||||
// replace the argument with &autovar
|
||||
val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
|
||||
val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
|
||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
||||
pointerExpr.scopedname = parent.makeScopedName(autoVarName)
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
// add a vardecl so that the autovar can be resolved in later lookups
|
||||
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue,
|
||||
isArray = false, autoGenerated = false, position=strvalue.position)
|
||||
addVarDecl(strvalue.definingScope(), variable)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
||||
if(scope !in vardeclsToAdd)
|
||||
vardeclsToAdd[scope] = mutableMapOf()
|
||||
vardeclsToAdd.getValue(scope)[variable.name]=variable
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,126 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.autoHeapValuePrefix
|
||||
import prog8.ast.expressions.AddressOf
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.*
|
||||
|
||||
internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope): IAstModifyingVisitor {
|
||||
// For VarDecls that declare an initialization value:
|
||||
// Replace the vardecl with an assignment (to set the initial value),
|
||||
// and add a new vardecl with the default constant value of that type (usually zero) to the scope.
|
||||
// This makes sure the variables get reset to the intended value on a next run of the program.
|
||||
// Variable decls without a value don't get this treatment, which means they retain the last
|
||||
// value they had when restarting the program.
|
||||
// This is done in a separate step because it interferes with the namespace lookup of symbols
|
||||
// in other ast processors.
|
||||
|
||||
// Also takes care to insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||
|
||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableMap<String, VarDecl>>()
|
||||
|
||||
override fun visit(module: Module) {
|
||||
vardeclsToAdd.clear()
|
||||
super.visit(module)
|
||||
|
||||
// add any new vardecls to the various scopes
|
||||
for(decl in vardeclsToAdd)
|
||||
for(d in decl.value) {
|
||||
d.value.linkParents(decl.key as Node)
|
||||
decl.key.statements.add(0, d.value)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
super.visit(decl)
|
||||
if(decl.type!= VarDeclType.VAR || decl.value==null)
|
||||
return decl
|
||||
|
||||
if(decl.datatype in NumericDatatypes) {
|
||||
val scope = decl.definingScope()
|
||||
addVarDecl(scope, decl.asDefaultValueDecl(null))
|
||||
val declvalue = decl.value!!
|
||||
val value =
|
||||
if(declvalue is LiteralValue) {
|
||||
val converted = declvalue.cast(decl.datatype)
|
||||
converted ?: declvalue
|
||||
}
|
||||
else
|
||||
declvalue
|
||||
val identifierName = listOf(decl.name) // // TODO this was: (scoped name) decl.scopedname.split(".")
|
||||
return VariableInitializationAssignment(
|
||||
AssignTarget(null, IdentifierReference(identifierName, decl.position), null, null, decl.position),
|
||||
null,
|
||||
value,
|
||||
decl.position
|
||||
)
|
||||
}
|
||||
return decl
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
val targetStatement = functionCall.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null) {
|
||||
var node: Node = functionCall
|
||||
while(node !is IStatement)
|
||||
node=node.parent
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
|
||||
}
|
||||
return functionCall
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null)
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<IExpression>, parent: IStatement) {
|
||||
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
||||
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
|
||||
if(argparam.first.value.type== DataType.UWORD || argparam.first.value.type in StringDatatypes) {
|
||||
if(argparam.second is AddressOf)
|
||||
continue
|
||||
val idref = argparam.second as? IdentifierReference
|
||||
val strvalue = argparam.second as? LiteralValue
|
||||
if(idref!=null) {
|
||||
val variable = idref.targetVarDecl(namespace)
|
||||
if(variable!=null && (variable.datatype in StringDatatypes || variable.datatype in ArrayDatatypes)) {
|
||||
val pointerExpr = AddressOf(idref, idref.position)
|
||||
pointerExpr.scopedname = parent.makeScopedName(idref.nameInSource.single())
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
}
|
||||
}
|
||||
else if(strvalue!=null) {
|
||||
if(strvalue.isString) {
|
||||
// replace the argument with &autovar
|
||||
val autoVarName = "$autoHeapValuePrefix${strvalue.heapId}"
|
||||
val autoHeapvarRef = IdentifierReference(listOf(autoVarName), strvalue.position)
|
||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
||||
pointerExpr.scopedname = parent.makeScopedName(autoVarName)
|
||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
||||
arglist[argparam.first.index] = pointerExpr
|
||||
// add a vardecl so that the autovar can be resolved in later lookups
|
||||
val variable = VarDecl(VarDeclType.VAR, strvalue.type, false, null, autoVarName, strvalue,
|
||||
isArray = false, autoGenerated = false, position = strvalue.position)
|
||||
addVarDecl(strvalue.definingScope(), variable)
|
||||
// println("MADE ANONVAR $variable") // XXX
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
||||
if(scope !in vardeclsToAdd)
|
||||
vardeclsToAdd[scope] = mutableMapOf()
|
||||
vardeclsToAdd.getValue(scope)[variable.name]=variable
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,733 @@
|
||||
package prog8.ast.statements
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.compiler.HeapValues
|
||||
import kotlin.math.max
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
|
||||
override var parent: Node = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
||||
override val expensiveToInline = false
|
||||
}
|
||||
|
||||
|
||||
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean?)
|
||||
|
||||
|
||||
class Block(override val name: String,
|
||||
val address: Int?,
|
||||
override var statements: MutableList<IStatement>,
|
||||
val isInLibrary: Boolean,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = statements.any { it.expensiveToInline }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach {it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Block(name=$name, address=$address, ${statements.size} statements)"
|
||||
}
|
||||
|
||||
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
|
||||
}
|
||||
|
||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
args.forEach{it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
data class Label(val name: String, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Label(name=$name, pos=$position)"
|
||||
}
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
}
|
||||
|
||||
open class Return(var values: List<IExpression>, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = values.any { it !is LiteralValue }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
values.forEach {it.linkParents(this)}
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Return(values: $values, pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class ReturnFromIrq(override val position: Position) : Return(emptyList(), position) {
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "ReturnFromIrq(pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class Continue(override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent=parent
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class Break(override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent=parent
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class VarDecl(val type: VarDeclType,
|
||||
private val declaredDatatype: DataType,
|
||||
val zeropage: Boolean,
|
||||
var arraysize: ArrayIndex?,
|
||||
val name: String,
|
||||
var value: IExpression?,
|
||||
val isArray: Boolean,
|
||||
val autoGenerated: Boolean,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = value!=null && value !is LiteralValue
|
||||
|
||||
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
||||
val datatype =
|
||||
if (!isArray) declaredDatatype
|
||||
else when (declaredDatatype) {
|
||||
DataType.UBYTE -> DataType.ARRAY_UB
|
||||
DataType.BYTE -> DataType.ARRAY_B
|
||||
DataType.UWORD -> DataType.ARRAY_UW
|
||||
DataType.WORD -> DataType.ARRAY_W
|
||||
DataType.FLOAT -> DataType.ARRAY_F
|
||||
else -> {
|
||||
datatypeErrors.add(SyntaxError("array can only contain bytes/words/floats", position))
|
||||
DataType.UBYTE
|
||||
}
|
||||
}
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
arraysize?.linkParents(this)
|
||||
value?.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
override fun toString(): String {
|
||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, array=$isArray, value=$value, pos=$position)"
|
||||
}
|
||||
|
||||
fun asDefaultValueDecl(parent: Node?): VarDecl {
|
||||
val constValue = when(declaredDatatype) {
|
||||
DataType.UBYTE -> LiteralValue(DataType.UBYTE, 0, position = position)
|
||||
DataType.BYTE -> LiteralValue(DataType.BYTE, 0, position = position)
|
||||
DataType.UWORD -> LiteralValue(DataType.UWORD, wordvalue = 0, position = position)
|
||||
DataType.WORD -> LiteralValue(DataType.WORD, wordvalue = 0, position = position)
|
||||
DataType.FLOAT -> LiteralValue(DataType.FLOAT, floatvalue = 0.0, position = position)
|
||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
||||
}
|
||||
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, constValue, isArray, true, position)
|
||||
if(parent!=null)
|
||||
decl.linkParents(parent)
|
||||
return decl
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayIndex(var index: IExpression, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
index.linkParents(this)
|
||||
}
|
||||
|
||||
companion object {
|
||||
fun forArray(v: LiteralValue, heap: HeapValues): ArrayIndex {
|
||||
val arraySize = v.arrayvalue?.size ?: heap.get(v.heapId!!).arraysize
|
||||
return ArrayIndex(LiteralValue.optimalNumeric(arraySize, v.position), v.position)
|
||||
}
|
||||
}
|
||||
|
||||
fun accept(visitor: IAstModifyingVisitor) {
|
||||
index = index.accept(visitor)
|
||||
}
|
||||
fun accept(visitor: IAstVisitor) {
|
||||
index.accept(visitor)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return("ArrayIndex($index, pos=$position)")
|
||||
}
|
||||
|
||||
fun size() = (index as? LiteralValue)?.asIntegerValue
|
||||
}
|
||||
|
||||
open class Assignment(var targets: List<AssignTarget>, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = value !is LiteralValue
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
targets.forEach { it.linkParents(this) }
|
||||
value.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return("Assignment(augop: $aug_op, targets: $targets, value: $value, pos=$position)")
|
||||
}
|
||||
|
||||
val singleTarget: AssignTarget?
|
||||
get() {
|
||||
return targets.singleOrNull() // common case
|
||||
}
|
||||
}
|
||||
|
||||
// This is a special class so the compiler can see if the assignments are for initializing the vars in the scope,
|
||||
// or just a regular assignment. It may optimize the initialization step from this.
|
||||
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: IExpression, position: Position)
|
||||
: Assignment(listOf(target), aug_op, value, position)
|
||||
|
||||
data class AssignTarget(val register: Register?,
|
||||
val identifier: IdentifierReference?,
|
||||
val arrayindexed: ArrayIndexedExpression?,
|
||||
var memoryAddress: DirectMemoryWrite?,
|
||||
override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
identifier?.linkParents(this)
|
||||
arrayindexed?.linkParents(this)
|
||||
memoryAddress?.linkParents(this)
|
||||
}
|
||||
|
||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
companion object {
|
||||
fun fromExpr(expr: IExpression): AssignTarget {
|
||||
return when (expr) {
|
||||
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
||||
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
||||
is ArrayIndexedExpression -> AssignTarget(null, null, expr, null, expr.position)
|
||||
is DirectMemoryRead -> AssignTarget(null, null, null, DirectMemoryWrite(expr.addressExpression, expr.position), expr.position)
|
||||
else -> throw FatalAstException("invalid expression object $expr")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun inferType(program: Program, stmt: IStatement): DataType? {
|
||||
if(register!=null)
|
||||
return DataType.UBYTE
|
||||
|
||||
if(identifier!=null) {
|
||||
val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
|
||||
if (symbol is VarDecl) return symbol.datatype
|
||||
}
|
||||
|
||||
if(arrayindexed!=null) {
|
||||
val dt = arrayindexed.inferType(program)
|
||||
if(dt!=null)
|
||||
return dt
|
||||
}
|
||||
|
||||
if(memoryAddress!=null)
|
||||
return DataType.UBYTE
|
||||
|
||||
return null
|
||||
}
|
||||
|
||||
fun shortString(withTypePrefix: Boolean=false): String {
|
||||
if(register!=null)
|
||||
return (if(withTypePrefix) "0register::" else "") + register.name
|
||||
if(identifier!=null)
|
||||
return (if(withTypePrefix) "3identifier::" else "") + identifier.nameInSource.last()
|
||||
if(arrayindexed!=null)
|
||||
return (if(withTypePrefix) "2arrayidx::" else "") + arrayindexed.identifier.nameInSource.last()
|
||||
val address = memoryAddress?.addressExpression
|
||||
if(address is LiteralValue)
|
||||
return (if(withTypePrefix) "1address::" else "") +address.asIntegerValue.toString()
|
||||
return if(withTypePrefix) "???::???" else "???"
|
||||
}
|
||||
|
||||
fun isMemoryMapped(namespace: INameScope): Boolean =
|
||||
memoryAddress!=null || (identifier?.targetVarDecl(namespace)?.type== VarDeclType.MEMORY)
|
||||
|
||||
infix fun isSameAs(value: IExpression): Boolean {
|
||||
return when {
|
||||
this.memoryAddress!=null -> false
|
||||
this.register!=null -> value is RegisterExpr && value.register==register
|
||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier.nameInSource
|
||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||
value.identifier.nameInSource==arrayindexed.identifier.nameInSource &&
|
||||
value.arrayspec.size()!=null &&
|
||||
arrayindexed.arrayspec.size()!=null &&
|
||||
value.arrayspec.size()==arrayindexed.arrayspec.size()
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
fun isSameAs(other: AssignTarget, program: Program): Boolean {
|
||||
if(this===other)
|
||||
return true
|
||||
if(this.register!=null && other.register!=null)
|
||||
return this.register==other.register
|
||||
if(this.identifier!=null && other.identifier!=null)
|
||||
return this.identifier.nameInSource==other.identifier.nameInSource
|
||||
if(this.memoryAddress!=null && other.memoryAddress!=null) {
|
||||
val addr1 = this.memoryAddress!!.addressExpression.constValue(program)
|
||||
val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
|
||||
return addr1!=null && addr2!=null && addr1==addr2
|
||||
}
|
||||
if(this.arrayindexed!=null && other.arrayindexed!=null) {
|
||||
if(this.arrayindexed.identifier.nameInSource == other.arrayindexed.identifier.nameInSource) {
|
||||
val x1 = this.arrayindexed.arrayspec.index.constValue(program)
|
||||
val x2 = other.arrayindexed.arrayspec.index.constValue(program)
|
||||
return x1!=null && x2!=null && x1==x2
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun isNotMemory(namespace: INameScope): Boolean {
|
||||
if(this.register!=null)
|
||||
return true
|
||||
if(this.memoryAddress!=null)
|
||||
return false
|
||||
if(this.arrayindexed!=null) {
|
||||
val targetStmt = this.arrayindexed.identifier.targetVarDecl(namespace)
|
||||
if(targetStmt!=null)
|
||||
return targetStmt.type!= VarDeclType.MEMORY
|
||||
}
|
||||
if(this.identifier!=null) {
|
||||
val targetStmt = this.identifier.targetVarDecl(namespace)
|
||||
if(targetStmt!=null)
|
||||
return targetStmt.type!= VarDeclType.MEMORY
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
target.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "PostIncrDecr(op: $operator, target: $target, pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class Jump(val address: Int?,
|
||||
val identifier: IdentifierReference?,
|
||||
val generatedLabel: String?, // used in code generation scenarios
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
identifier?.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class FunctionCallStatement(override var target: IdentifierReference,
|
||||
override var arglist: MutableList<IExpression>,
|
||||
override val position: Position) : IStatement, IFunctionCall {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = arglist.any { it !is LiteralValue }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
target.linkParents(this)
|
||||
arglist.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "FunctionCallStatement(target=$target, pos=$position)"
|
||||
}
|
||||
}
|
||||
|
||||
class InlineAssembly(val assembly: String, override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class AnonymousScope(override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : INameScope, IStatement {
|
||||
override val name: String
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = statements.any { it.expensiveToInline }
|
||||
|
||||
init {
|
||||
name = "<anon-$sequenceNumber>" // make sure it's an invalid soruce code identifier so user source code can never produce it
|
||||
sequenceNumber++
|
||||
}
|
||||
|
||||
companion object {
|
||||
private var sequenceNumber = 1
|
||||
}
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class NopStatement(override val position: Position): IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
// the subroutine class covers both the normal user-defined subroutines,
|
||||
// and also the predefined/ROM/register-based subroutines.
|
||||
// (multiple return types can only occur for the latter type)
|
||||
class Subroutine(override val name: String,
|
||||
val parameters: List<SubroutineParameter>,
|
||||
val returntypes: List<DataType>,
|
||||
val asmParameterRegisters: List<RegisterOrStatusflag>,
|
||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||
val asmClobbers: Set<Register>,
|
||||
val asmAddress: Int?,
|
||||
val isAsmSubroutine: Boolean,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
|
||||
var keepAlways: Boolean = false
|
||||
override val expensiveToInline
|
||||
get() = statements.any { it.expensiveToInline }
|
||||
|
||||
override lateinit var parent: Node
|
||||
val calledBy = mutableListOf<Node>()
|
||||
val calls = mutableSetOf<Subroutine>()
|
||||
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
parameters.forEach { it.linkParents(this) }
|
||||
statements.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
||||
}
|
||||
|
||||
fun amountOfRtsInAsm(): Int = statements
|
||||
.asSequence()
|
||||
.filter { it is InlineAssembly }
|
||||
.map { (it as InlineAssembly).assembly }
|
||||
.count { " rti" in it || "\trti" in it || " rts" in it || "\trts" in it || " jmp" in it || "\tjmp" in it }
|
||||
|
||||
val canBeAsmSubroutine =false // TODO disabled for now, see below about problem with converting to asm subroutine
|
||||
// !isAsmSubroutine
|
||||
// && ((parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE, DataType.WORD, DataType.UWORD))
|
||||
// || (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE }))
|
||||
|
||||
fun intoAsmSubroutine(): Subroutine {
|
||||
// TODO turn subroutine into asm calling convention. Requires rethinking of how parameters are handled (conflicts with local vardefs now, see AstIdentifierChecker...)
|
||||
return this // TODO
|
||||
|
||||
// println("TO ASM $this") // TODO
|
||||
// val paramregs = if (parameters.size == 1 && parameters[0].type in setOf(DataType.BYTE, DataType.UBYTE))
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
||||
// else if (parameters.size == 1 && parameters[0].type in setOf(DataType.WORD, DataType.UWORD))
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.AY, null, null))
|
||||
// else if (parameters.size == 2 && parameters.map { it.type }.all { it == DataType.BYTE || it == DataType.UBYTE })
|
||||
// listOf(RegisterOrStatusflag(RegisterOrPair.A, null, null), RegisterOrStatusflag(RegisterOrPair.Y, null, null))
|
||||
// else throw FatalAstException("cannot convert subroutine to asm parameters")
|
||||
//
|
||||
// val asmsub=Subroutine(
|
||||
// name,
|
||||
// parameters,
|
||||
// returntypes,
|
||||
// paramregs,
|
||||
// emptyList(),
|
||||
// emptySet(),
|
||||
// null,
|
||||
// true,
|
||||
// statements,
|
||||
// position
|
||||
// )
|
||||
// asmsub.linkParents(parent)
|
||||
// return asmsub
|
||||
}
|
||||
}
|
||||
|
||||
open class SubroutineParameter(val name: String,
|
||||
val type: DataType,
|
||||
override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
}
|
||||
}
|
||||
|
||||
class IfStatement(var condition: IExpression,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean
|
||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
condition.linkParents(this)
|
||||
truepart.linkParents(this)
|
||||
elsepart.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class BranchStatement(var condition: BranchCondition,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean
|
||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
truepart.linkParents(this)
|
||||
elsepart.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class ForLoop(val loopRegister: Register?,
|
||||
val decltype: DataType?,
|
||||
val zeropage: Boolean,
|
||||
val loopVar: IdentifierReference?,
|
||||
var iterable: IExpression,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent=parent
|
||||
loopVar?.linkParents(if(decltype==null) this else body)
|
||||
iterable.linkParents(this)
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
override fun toString(): String {
|
||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
||||
}
|
||||
|
||||
companion object {
|
||||
const val iteratorLoopcounterVarname = "prog8forloopcounter"
|
||||
}
|
||||
}
|
||||
|
||||
class WhileLoop(var condition: IExpression,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
condition.linkParents(this)
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class RepeatLoop(var body: AnonymousScope,
|
||||
var untilCondition: IExpression,
|
||||
override val position: Position) : IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
untilCondition.linkParents(this)
|
||||
body.linkParents(this)
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class WhenStatement(val condition: IExpression,
|
||||
val choices: MutableList<WhenChoice>,
|
||||
override val position: Position): IStatement {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean = true
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
condition.linkParents(this)
|
||||
choices.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
fun choiceValues(program: Program): List<Pair<Int?, WhenChoice>> {
|
||||
// only gives sensible results when the choices are all valid (constant integers)
|
||||
return choices
|
||||
.map {
|
||||
val cv = it.value?.constValue(program)
|
||||
if(cv==null)
|
||||
null to it
|
||||
else
|
||||
cv.asNumericValue!!.toInt() to it
|
||||
}
|
||||
}
|
||||
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class WhenChoice(val value: IExpression?, // if null, this is the 'else' part
|
||||
val statements: AnonymousScope,
|
||||
override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
value?.linkParents(this)
|
||||
statements.linkParents(this)
|
||||
this.parent = parent
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "Choice($value at $position)"
|
||||
}
|
||||
|
||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
|
||||
class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
this.addressExpression.linkParents(this)
|
||||
}
|
||||
|
||||
override fun toString(): String {
|
||||
return "DirectMemoryWrite($addressExpression)"
|
||||
}
|
||||
|
||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,416 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.antlr.escape
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
|
||||
class AstToSourceCode(val output: (text: String) -> Unit): IAstVisitor {
|
||||
var scopelevel = 0
|
||||
|
||||
fun indent(s: String) = " ".repeat(scopelevel) + s
|
||||
fun outputln(text: String) = output(text + "\n")
|
||||
fun outputlni(s: Any) = outputln(indent(s.toString()))
|
||||
fun outputi(s: Any) = output(indent(s.toString()))
|
||||
|
||||
override fun visit(program: Program) {
|
||||
outputln("============= PROGRAM ${program.name} (FROM AST) ===============")
|
||||
super.visit(program)
|
||||
outputln("============= END PROGRAM ${program.name} (FROM AST) ===========")
|
||||
}
|
||||
|
||||
override fun visit(module: Module) {
|
||||
if(!module.isLibraryModule) {
|
||||
outputln("; ----------- module: ${module.name} -----------")
|
||||
super.visit(module)
|
||||
}
|
||||
else outputln("; library module skipped: ${module.name}")
|
||||
}
|
||||
|
||||
override fun visit(block: Block) {
|
||||
val addr = if(block.address!=null) block.address.toHex() else ""
|
||||
outputln("~ ${block.name} $addr {")
|
||||
scopelevel++
|
||||
for(stmt in block.statements) {
|
||||
outputi("")
|
||||
stmt.accept(this)
|
||||
output("\n")
|
||||
}
|
||||
scopelevel--
|
||||
outputln("}\n")
|
||||
}
|
||||
|
||||
override fun visit(expr: PrefixExpression) {
|
||||
if(expr.operator.any { it.isLetter() })
|
||||
output(" ${expr.operator} ")
|
||||
else
|
||||
output(expr.operator)
|
||||
expr.expression.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(expr: BinaryExpression) {
|
||||
expr.left.accept(this)
|
||||
if(expr.operator.any { it.isLetter() })
|
||||
output(" ${expr.operator} ")
|
||||
else
|
||||
output(expr.operator)
|
||||
expr.right.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(directive: Directive) {
|
||||
output("${directive.directive} ")
|
||||
for(arg in directive.args) {
|
||||
when {
|
||||
arg.int!=null -> output(arg.int.toString())
|
||||
arg.name!=null -> output(arg.name)
|
||||
arg.str!=null -> output("\"${arg.str}\"")
|
||||
}
|
||||
if(arg!==directive.args.last())
|
||||
output(",")
|
||||
}
|
||||
output("\n")
|
||||
}
|
||||
|
||||
fun datatypeString(dt: DataType): String {
|
||||
return when(dt) {
|
||||
in NumericDatatypes -> dt.toString().toLowerCase()
|
||||
in StringDatatypes -> dt.toString().toLowerCase()
|
||||
DataType.ARRAY_UB -> "ubyte["
|
||||
DataType.ARRAY_B -> "byte["
|
||||
DataType.ARRAY_UW -> "uword["
|
||||
DataType.ARRAY_W -> "word["
|
||||
DataType.ARRAY_F -> "float["
|
||||
else -> "?????"
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl) {
|
||||
if(decl.autoGenerated) {
|
||||
// skip autogenerated vardecl
|
||||
return
|
||||
}
|
||||
|
||||
when(decl.type) {
|
||||
VarDeclType.VAR -> {}
|
||||
VarDeclType.CONST -> output("const ")
|
||||
VarDeclType.MEMORY -> output("&")
|
||||
}
|
||||
output(datatypeString(decl.datatype))
|
||||
if(decl.arraysize!=null) {
|
||||
decl.arraysize!!.index.accept(this)
|
||||
}
|
||||
if(decl.isArray)
|
||||
output("]")
|
||||
|
||||
if(decl.zeropage)
|
||||
output(" @zp")
|
||||
output(" ${decl.name} ")
|
||||
if(decl.value!=null) {
|
||||
output("= ")
|
||||
decl.value?.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
output("\n")
|
||||
if(subroutine.isAsmSubroutine) {
|
||||
outputi("asmsub ${subroutine.name} (")
|
||||
for(param in subroutine.parameters.zip(subroutine.asmParameterRegisters)) {
|
||||
val reg =
|
||||
when {
|
||||
true==param.second.stack -> "stack"
|
||||
param.second.registerOrPair!=null -> param.second.registerOrPair.toString()
|
||||
param.second.statusflag!=null -> param.second.statusflag.toString()
|
||||
else -> "?????"
|
||||
}
|
||||
output("${datatypeString(param.first.type)} ${param.first.name} @$reg")
|
||||
if(param.first!==subroutine.parameters.last())
|
||||
output(", ")
|
||||
}
|
||||
}
|
||||
else {
|
||||
outputi("sub ${subroutine.name} (")
|
||||
for(param in subroutine.parameters) {
|
||||
output("${datatypeString(param.type)} ${param.name}")
|
||||
if(param!==subroutine.parameters.last())
|
||||
output(", ")
|
||||
}
|
||||
}
|
||||
output(") ")
|
||||
if(subroutine.asmClobbers.isNotEmpty()) {
|
||||
output("-> clobbers (")
|
||||
val regs = subroutine.asmClobbers.toList().sorted()
|
||||
for(r in regs) {
|
||||
output(r.toString())
|
||||
if(r!==regs.last())
|
||||
output(",")
|
||||
}
|
||||
output(") ")
|
||||
}
|
||||
if(subroutine.returntypes.any()) {
|
||||
val rt = subroutine.returntypes.single()
|
||||
output("-> ${datatypeString(rt)} ")
|
||||
}
|
||||
if(subroutine.asmAddress!=null)
|
||||
outputln("= ${subroutine.asmAddress.toHex()}")
|
||||
else {
|
||||
outputln("{ ")
|
||||
scopelevel++
|
||||
outputStatements(subroutine.statements)
|
||||
scopelevel--
|
||||
outputi("}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStatements(statements: List<IStatement>) {
|
||||
for(stmt in statements) {
|
||||
if(stmt is VarDecl && stmt.autoGenerated)
|
||||
continue // skip autogenerated decls
|
||||
outputi("")
|
||||
stmt.accept(this)
|
||||
output("\n")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall) {
|
||||
printout(functionCall as IFunctionCall)
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
printout(functionCallStatement as IFunctionCall)
|
||||
}
|
||||
|
||||
private fun printout(call: IFunctionCall) {
|
||||
call.target.accept(this)
|
||||
output("(")
|
||||
for(arg in call.arglist) {
|
||||
arg.accept(this)
|
||||
if(arg!==call.arglist.last())
|
||||
output(", ")
|
||||
}
|
||||
output(")")
|
||||
}
|
||||
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
output(identifier.nameInSource.joinToString("."))
|
||||
}
|
||||
|
||||
override fun visit(jump: Jump) {
|
||||
output("goto ")
|
||||
when {
|
||||
jump.address!=null -> output(jump.address.toHex())
|
||||
jump.generatedLabel!=null -> output(jump.generatedLabel)
|
||||
jump.identifier!=null -> jump.identifier.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(ifStatement: IfStatement) {
|
||||
output("if ")
|
||||
ifStatement.condition.accept(this)
|
||||
output(" ")
|
||||
ifStatement.truepart.accept(this)
|
||||
if(ifStatement.elsepart.statements.isNotEmpty()) {
|
||||
output(" else ")
|
||||
ifStatement.elsepart.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(branchStatement: BranchStatement) {
|
||||
output("if_${branchStatement.condition.toString().toLowerCase()} ")
|
||||
branchStatement.truepart.accept(this)
|
||||
if(branchStatement.elsepart.statements.isNotEmpty()) {
|
||||
output(" else ")
|
||||
branchStatement.elsepart.accept(this)
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(range: RangeExpr) {
|
||||
range.from.accept(this)
|
||||
output(" to ")
|
||||
range.to.accept(this)
|
||||
output(" step ")
|
||||
range.step.accept(this)
|
||||
output(" ")
|
||||
}
|
||||
|
||||
override fun visit(label: Label) {
|
||||
output("\n")
|
||||
output("${label.name}:")
|
||||
}
|
||||
|
||||
override fun visit(literalValue: LiteralValue) {
|
||||
when {
|
||||
literalValue.isNumeric -> output(literalValue.asNumericValue.toString())
|
||||
literalValue.isString -> output("\"${escape(literalValue.strvalue!!)}\"")
|
||||
literalValue.isArray -> {
|
||||
if(literalValue.arrayvalue!=null) {
|
||||
output("[")
|
||||
for (v in literalValue.arrayvalue) {
|
||||
v.accept(this)
|
||||
if (v !== literalValue.arrayvalue.last())
|
||||
output(", ")
|
||||
}
|
||||
output("]")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(assignment: Assignment) {
|
||||
assignment.singleTarget!!.accept(this)
|
||||
if(assignment.aug_op!=null)
|
||||
output(" ${assignment.aug_op} ")
|
||||
else
|
||||
output(" = ")
|
||||
assignment.value.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(postIncrDecr: PostIncrDecr) {
|
||||
postIncrDecr.target.accept(this)
|
||||
output(postIncrDecr.operator)
|
||||
}
|
||||
|
||||
override fun visit(contStmt: Continue) {
|
||||
output("continue")
|
||||
}
|
||||
|
||||
override fun visit(breakStmt: Break) {
|
||||
output("break")
|
||||
}
|
||||
|
||||
override fun visit(forLoop: ForLoop) {
|
||||
output("for ")
|
||||
if(forLoop.decltype!=null) {
|
||||
output(datatypeString(forLoop.decltype))
|
||||
if (forLoop.zeropage)
|
||||
output(" @zp ")
|
||||
else
|
||||
output(" ")
|
||||
}
|
||||
if(forLoop.loopRegister!=null)
|
||||
output(forLoop.loopRegister.toString())
|
||||
else
|
||||
forLoop.loopVar!!.accept(this)
|
||||
output(" in ")
|
||||
forLoop.iterable.accept(this)
|
||||
output(" ")
|
||||
forLoop.body.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(whileLoop: WhileLoop) {
|
||||
output("while ")
|
||||
whileLoop.condition.accept(this)
|
||||
output(" ")
|
||||
whileLoop.body.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(repeatLoop: RepeatLoop) {
|
||||
outputln("repeat ")
|
||||
repeatLoop.body.accept(this)
|
||||
output(" until ")
|
||||
repeatLoop.untilCondition.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(returnStmt: Return) {
|
||||
output("return ")
|
||||
for(v in returnStmt.values) {
|
||||
v.accept(this)
|
||||
if(v!==returnStmt.values.last())
|
||||
output(", ")
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(arrayIndexedExpression: ArrayIndexedExpression) {
|
||||
arrayIndexedExpression.identifier.accept(this)
|
||||
output("[")
|
||||
arrayIndexedExpression.arrayspec.index.accept(this)
|
||||
output("]")
|
||||
}
|
||||
|
||||
override fun visit(assignTarget: AssignTarget) {
|
||||
if(assignTarget.register!=null)
|
||||
output(assignTarget.register.toString())
|
||||
else {
|
||||
assignTarget.memoryAddress?.accept(this)
|
||||
assignTarget.identifier?.accept(this)
|
||||
}
|
||||
assignTarget.arrayindexed?.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(scope: AnonymousScope) {
|
||||
outputln("{")
|
||||
scopelevel++
|
||||
outputStatements(scope.statements)
|
||||
scopelevel--
|
||||
outputi("}")
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression) {
|
||||
typecast.expression.accept(this)
|
||||
output(" as ${datatypeString(typecast.type)} ")
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead) {
|
||||
output("@(")
|
||||
memread.addressExpression.accept(this)
|
||||
output(")")
|
||||
}
|
||||
|
||||
override fun visit(memwrite: DirectMemoryWrite) {
|
||||
output("@(")
|
||||
memwrite.addressExpression.accept(this)
|
||||
output(")")
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf) {
|
||||
output("&")
|
||||
addressOf.identifier.accept(this)
|
||||
}
|
||||
|
||||
override fun visit(inlineAssembly: InlineAssembly) {
|
||||
outputlni("%asm {{")
|
||||
outputln(inlineAssembly.assembly)
|
||||
outputlni("}}")
|
||||
}
|
||||
|
||||
override fun visit(registerExpr: RegisterExpr) {
|
||||
output(registerExpr.register.toString())
|
||||
}
|
||||
|
||||
override fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder) {
|
||||
output(builtinFunctionStatementPlaceholder.name)
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement) {
|
||||
output("when ")
|
||||
whenStatement.condition.accept(this)
|
||||
outputln(" {")
|
||||
scopelevel++
|
||||
whenStatement.choices.forEach { it.accept(this) }
|
||||
scopelevel--
|
||||
outputlni("}")
|
||||
}
|
||||
|
||||
override fun visit(whenChoice: WhenChoice) {
|
||||
if(whenChoice.value==null)
|
||||
outputi("else -> ")
|
||||
else {
|
||||
outputi("")
|
||||
whenChoice.value.accept(this)
|
||||
output(" -> ")
|
||||
}
|
||||
if(whenChoice.statements.statements.size==1)
|
||||
whenChoice.statements.statements.single().accept(this)
|
||||
else
|
||||
whenChoice.statements.accept(this)
|
||||
outputln("")
|
||||
}
|
||||
override fun visit(nopStatement: NopStatement) {
|
||||
TODO("NOP???")
|
||||
}
|
||||
}
|
||||
@@ -1,13 +1,17 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.RegisterOrPair.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.RegisterOrPair.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.intermediate.IntermediateProgram
|
||||
import prog8.compiler.intermediate.Opcode
|
||||
import prog8.compiler.intermediate.branchOpcodes
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import prog8.parser.tryGetEmbeddedResource
|
||||
import prog8.stackvm.Syscall
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.vm.stackvm.Syscall
|
||||
import java.io.File
|
||||
import java.nio.file.Path
|
||||
import java.util.*
|
||||
@@ -143,7 +147,7 @@ data class CompilationOptions(val output: OutputType,
|
||||
val floats: Boolean)
|
||||
|
||||
|
||||
internal class Compiler(private val program: Program): IAstProcessor {
|
||||
internal class Compiler(private val program: Program) {
|
||||
|
||||
private val prog: IntermediateProgram = IntermediateProgram(program.name, program.loadAddress, program.heap, program.modules.first().source)
|
||||
private var generatedLabelSequenceNumber = 0
|
||||
@@ -153,17 +157,19 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
fun compile(options: CompilationOptions) : IntermediateProgram {
|
||||
println("Creating stackVM code...")
|
||||
program.modules.forEach {
|
||||
process(it)
|
||||
it.statements.forEach { stmt->
|
||||
if(stmt is Block)
|
||||
processBlock(stmt)
|
||||
}
|
||||
}
|
||||
return prog
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
private fun processBlock(block: Block) {
|
||||
prog.newBlock(block.name, block.address, block.options())
|
||||
processVariables(block)
|
||||
prog.line(block.position)
|
||||
translate(block.statements)
|
||||
return super.process(block)
|
||||
}
|
||||
|
||||
private fun processVariables(scope: INameScope) {
|
||||
@@ -173,27 +179,6 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
processVariables(subscope.value)
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
if(subroutine.asmAddress==null) {
|
||||
prog.label(subroutine.scopedname, true)
|
||||
prog.instr(Opcode.START_PROCDEF)
|
||||
prog.line(subroutine.position)
|
||||
// note: the caller has already written the arguments into the subroutine's parameter variables.
|
||||
// note2: don't separate normal and VariableInitializationAssignment here, because the order strictly matters
|
||||
translate(subroutine.statements)
|
||||
val r= super.process(subroutine)
|
||||
prog.instr(Opcode.END_PROCDEF)
|
||||
return r
|
||||
} else {
|
||||
// asmsub
|
||||
if(subroutine.containsCodeOrVars())
|
||||
throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine")
|
||||
|
||||
prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case
|
||||
return super.process(subroutine)
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(statements: List<IStatement>) {
|
||||
for (stmt: IStatement in statements) {
|
||||
generatedLabelSequenceNumber++
|
||||
@@ -223,14 +208,33 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
}
|
||||
}
|
||||
}
|
||||
is VarDecl, is Subroutine -> {} // skip this, already processed these.
|
||||
is VarDecl -> {} // skip this, already processed these.
|
||||
is Subroutine -> translate(stmt)
|
||||
is NopStatement -> {}
|
||||
is InlineAssembly -> translate(stmt)
|
||||
is WhenStatement -> translate(stmt)
|
||||
else -> TODO("translate statement $stmt to stackvm")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun translate(subroutine: Subroutine) {
|
||||
if(subroutine.asmAddress==null) {
|
||||
prog.label(subroutine.scopedname, true)
|
||||
prog.instr(Opcode.START_PROCDEF)
|
||||
prog.line(subroutine.position)
|
||||
// note: the caller has already written the arguments into the subroutine's parameter variables.
|
||||
// note2: don't separate normal and VariableInitializationAssignment here, because the order strictly matters
|
||||
translate(subroutine.statements)
|
||||
prog.instr(Opcode.END_PROCDEF)
|
||||
} else {
|
||||
// asmsub
|
||||
if(subroutine.containsCodeOrVars())
|
||||
throw CompilerException("kernel subroutines (with memory address) can't have a body: $subroutine")
|
||||
|
||||
prog.memoryPointer(subroutine.scopedname, subroutine.asmAddress, DataType.UBYTE) // the datatype is a bit of a dummy in this case
|
||||
}
|
||||
}
|
||||
private fun opcodePush(dt: DataType): Opcode {
|
||||
return when (dt) {
|
||||
in ByteDatatypes -> Opcode.PUSH_BYTE
|
||||
@@ -383,7 +387,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
// The compiler could then convert it to a special system call
|
||||
val sub = stmt.parent as? Subroutine
|
||||
val scopename =
|
||||
if(sub!=null && sub.statements.filter{it !is VarDecl}.size==1)
|
||||
if(sub!=null && sub.statements.filter{it !is VarDecl }.size==1)
|
||||
sub.scopedname
|
||||
else
|
||||
null
|
||||
@@ -586,7 +590,6 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
is RangeExpr -> throw CompilerException("it's not possible to just have a range expression that has to be translated")
|
||||
is TypecastExpression -> translate(expr)
|
||||
is DirectMemoryRead -> translate(expr)
|
||||
is DirectMemoryWrite -> translate(expr)
|
||||
is AddressOf -> translate(expr)
|
||||
else -> {
|
||||
val lv = expr.constValue(program) ?: throw CompilerException("constant expression required, not $expr")
|
||||
@@ -1413,7 +1416,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
// @todo use convertType()????
|
||||
when(targetDt) {
|
||||
in ByteDatatypes ->
|
||||
if(valueDt!=DataType.BYTE && valueDt!=DataType.UBYTE)
|
||||
if(valueDt!= DataType.BYTE && valueDt!= DataType.UBYTE)
|
||||
throw CompilerException("incompatible data types valueDt=$valueDt targetDt=$targetDt at $stmt")
|
||||
DataType.WORD -> {
|
||||
when (valueDt) {
|
||||
@@ -1534,7 +1537,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
}
|
||||
|
||||
private fun translate(stmt: Return) {
|
||||
// put the return values on the stack, in reversed order. The caller will process them.
|
||||
// put the return values on the stack, in reversed order. The caller will accept them.
|
||||
for(value in stmt.values.reversed()) {
|
||||
translate(value)
|
||||
}
|
||||
@@ -1619,11 +1622,11 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
}
|
||||
|
||||
private fun translateForOverIterableVar(loop: ForLoop, loopvarDt: DataType, iterableValue: LiteralValue) {
|
||||
if(loopvarDt==DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
|
||||
if(loopvarDt== DataType.UBYTE && iterableValue.type !in setOf(DataType.STR, DataType.STR_S, DataType.ARRAY_UB))
|
||||
throw CompilerException("loop variable type doesn't match iterableValue type")
|
||||
else if(loopvarDt==DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
|
||||
else if(loopvarDt== DataType.UWORD && iterableValue.type != DataType.ARRAY_UW)
|
||||
throw CompilerException("loop variable type doesn't match iterableValue type")
|
||||
else if(loopvarDt==DataType.FLOAT && iterableValue.type != DataType.ARRAY_F)
|
||||
else if(loopvarDt== DataType.FLOAT && iterableValue.type != DataType.ARRAY_F)
|
||||
throw CompilerException("loop variable type doesn't match iterableValue type")
|
||||
|
||||
val numElements: Int
|
||||
@@ -1645,7 +1648,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
else -> throw CompilerException("weird datatype")
|
||||
}
|
||||
|
||||
if(loop.loopRegister!=null && loop.loopRegister==Register.X)
|
||||
if(loop.loopRegister!=null && loop.loopRegister== Register.X)
|
||||
throw CompilerException("loopVar cannot use X register because that is used as internal stack pointer")
|
||||
|
||||
/**
|
||||
@@ -1848,10 +1851,10 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
val condition =
|
||||
if(literalStepValue > 0) {
|
||||
// if LV > last goto break
|
||||
BinaryExpression(loopVar,">", range.to, range.position)
|
||||
BinaryExpression(loopVar, ">", range.to, range.position)
|
||||
} else {
|
||||
// if LV < last goto break
|
||||
BinaryExpression(loopVar,"<", range.to, range.position)
|
||||
BinaryExpression(loopVar, "<", range.to, range.position)
|
||||
}
|
||||
val ifstmt = IfStatement(condition,
|
||||
AnonymousScope(mutableListOf(Jump(null, null, breakLabel, range.position)), range.position),
|
||||
@@ -1881,7 +1884,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
TODO("can't generate code for step other than 1 or -1 right now")
|
||||
|
||||
// LV++ / LV--
|
||||
val postIncr = PostIncrDecr(lvTarget, if(step==1) "++" else "--", range.position)
|
||||
val postIncr = PostIncrDecr(lvTarget, if (step == 1) "++" else "--", range.position)
|
||||
postIncr.linkParents(body)
|
||||
translate(postIncr)
|
||||
if(lvTarget.register!=null)
|
||||
@@ -2056,6 +2059,7 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
|
||||
private fun translate(memwrite: DirectMemoryWrite) {
|
||||
// for now, only a single memory location (ubyte) is written at a time.
|
||||
// TODO inline this function (it's only used once)
|
||||
val address = memwrite.addressExpression.constValue(program)?.asIntegerValue
|
||||
if(address!=null) {
|
||||
prog.instr(Opcode.POP_MEM_BYTE, arg = RuntimeValue(DataType.UWORD, address))
|
||||
@@ -2067,16 +2071,69 @@ internal class Compiler(private val program: Program): IAstProcessor {
|
||||
|
||||
private fun translate(addrof: AddressOf) {
|
||||
val target = addrof.identifier.targetVarDecl(program.namespace)!!
|
||||
if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes|| target.datatype==DataType.FLOAT) {
|
||||
if(target.datatype in ArrayDatatypes || target.datatype in StringDatatypes || target.datatype== DataType.FLOAT) {
|
||||
pushHeapVarAddress(addrof.identifier, false)
|
||||
}
|
||||
else if(target.datatype==DataType.FLOAT) {
|
||||
else if(target.datatype== DataType.FLOAT) {
|
||||
pushFloatAddress(addrof.identifier)
|
||||
}
|
||||
else
|
||||
throw CompilerException("cannot take memory pointer $addrof")
|
||||
}
|
||||
|
||||
private fun translate(whenstmt: WhenStatement) {
|
||||
val conditionDt = whenstmt.condition.inferType(program)
|
||||
if(conditionDt !in IntegerDatatypes)
|
||||
throw CompilerException("when condition must be integer")
|
||||
translate(whenstmt.condition)
|
||||
if(whenstmt.choices.isEmpty()) {
|
||||
if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE)
|
||||
else prog.instr(Opcode.DISCARD_WORD)
|
||||
return
|
||||
}
|
||||
|
||||
val endOfWhenLabel = makeLabel(whenstmt, "when_end")
|
||||
|
||||
val choiceLabels = mutableListOf<String>()
|
||||
var previousValue = 0
|
||||
for(choice in whenstmt.choiceValues(program)) {
|
||||
val choiceVal = choice.first
|
||||
if(choiceVal==null) {
|
||||
// the else clause
|
||||
translate(choice.second.statements)
|
||||
} else {
|
||||
val subtract = choiceVal-previousValue
|
||||
previousValue = choiceVal
|
||||
if (conditionDt in ByteDatatypes) {
|
||||
prog.instr(Opcode.DUP_B)
|
||||
prog.instr(Opcode.PUSH_BYTE, RuntimeValue(conditionDt!!, subtract))
|
||||
prog.instr(opcodeCompare(conditionDt))
|
||||
}
|
||||
else {
|
||||
prog.instr(Opcode.DUP_W)
|
||||
prog.instr(Opcode.PUSH_WORD, RuntimeValue(conditionDt!!, subtract))
|
||||
prog.instr(opcodeCompare(conditionDt))
|
||||
}
|
||||
val choiceLabel = makeLabel(whenstmt, "choice_$choiceVal")
|
||||
choiceLabels.add(choiceLabel)
|
||||
prog.instr(Opcode.BZ, callLabel = choiceLabel)
|
||||
}
|
||||
}
|
||||
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
||||
|
||||
for(choice in whenstmt.choices.zip(choiceLabels)) {
|
||||
// TODO the various code blocks here, don't forget to jump to the end label at their eind
|
||||
prog.label(choice.second)
|
||||
prog.instr(Opcode.NOP)
|
||||
prog.instr(Opcode.JUMP, callLabel = endOfWhenLabel)
|
||||
}
|
||||
|
||||
prog.label(endOfWhenLabel)
|
||||
|
||||
if (conditionDt in ByteDatatypes) prog.instr(Opcode.DISCARD_BYTE)
|
||||
else prog.instr(Opcode.DISCARD_WORD)
|
||||
}
|
||||
|
||||
private fun translateAsmInclude(args: List<DirectiveArg>, source: Path) {
|
||||
val scopeprefix = if(args[1].str!!.isNotBlank()) "${args[1].str}\t.proc\n" else ""
|
||||
val scopeprefixEnd = if(args[1].str!!.isNotBlank()) "\t.pend\n" else ""
|
||||
|
||||
@@ -0,0 +1,187 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.checkIdentifiers
|
||||
import prog8.ast.base.checkValid
|
||||
import prog8.ast.base.reorderStatements
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.compiler.target.c64.AsmGen
|
||||
import prog8.compiler.target.c64.C64Zeropage
|
||||
import prog8.optimizer.constantFold
|
||||
import prog8.optimizer.optimizeStatements
|
||||
import prog8.optimizer.simplifyExpressions
|
||||
import prog8.parser.ParsingFailedError
|
||||
import prog8.parser.importLibraryModule
|
||||
import prog8.parser.importModule
|
||||
import prog8.parser.moduleName
|
||||
import java.io.File
|
||||
import java.io.PrintStream
|
||||
import java.lang.Exception
|
||||
import java.nio.file.Path
|
||||
import kotlin.system.exitProcess
|
||||
import kotlin.system.measureTimeMillis
|
||||
|
||||
fun compileProgram(filepath: Path,
|
||||
optimize: Boolean, optimizeInlining: Boolean,
|
||||
generateVmCode: Boolean, writeVmCode: Boolean,
|
||||
writeAssembly: Boolean): Pair<Program, String?> {
|
||||
lateinit var programAst: Program
|
||||
var programName: String? = null
|
||||
|
||||
try {
|
||||
val totalTime = measureTimeMillis {
|
||||
// import main module and everything it needs
|
||||
println("Parsing...")
|
||||
programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||
importModule(programAst, filepath)
|
||||
|
||||
val compilerOptions = determineCompilationOptions(programAst)
|
||||
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
|
||||
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
|
||||
|
||||
// if we're producing a PRG or BASIC program, include the c64utils and c64lib libraries
|
||||
if (compilerOptions.launcher == LauncherType.BASIC || compilerOptions.output == OutputType.PRG) {
|
||||
importLibraryModule(programAst, "c64lib")
|
||||
importLibraryModule(programAst, "c64utils")
|
||||
}
|
||||
|
||||
// always import prog8lib and math
|
||||
importLibraryModule(programAst, "math")
|
||||
importLibraryModule(programAst, "prog8lib")
|
||||
|
||||
|
||||
// perform initial syntax checks and constant folding
|
||||
println("Syntax check...")
|
||||
val time1 = measureTimeMillis {
|
||||
programAst.checkIdentifiers()
|
||||
}
|
||||
|
||||
//println(" time1: $time1")
|
||||
val time2 = measureTimeMillis {
|
||||
programAst.constantFold()
|
||||
}
|
||||
//println(" time2: $time2")
|
||||
val time3 = measureTimeMillis {
|
||||
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
|
||||
}
|
||||
//println(" time3: $time3")
|
||||
val time4 = measureTimeMillis {
|
||||
programAst.checkValid(compilerOptions) // check if tree is valid
|
||||
}
|
||||
//println(" time4: $time4")
|
||||
|
||||
programAst.checkIdentifiers()
|
||||
if (optimize) {
|
||||
// optimize the parse tree
|
||||
println("Optimizing...")
|
||||
while (true) {
|
||||
// keep optimizing expressions and statements until no more steps remain
|
||||
val optsDone1 = programAst.simplifyExpressions()
|
||||
val optsDone2 = programAst.optimizeStatements(optimizeInlining)
|
||||
if (optsDone1 + optsDone2 == 0)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
programAst.checkValid(compilerOptions) // check if final tree is valid
|
||||
programAst.checkRecursion() // check if there are recursive subroutine calls
|
||||
|
||||
// printAst(programAst)
|
||||
// namespace.debugPrint()
|
||||
|
||||
if(generateVmCode) {
|
||||
// compile the syntax tree into stackvmProg form, and optimize that
|
||||
val compiler = Compiler(programAst)
|
||||
val intermediate = compiler.compile(compilerOptions)
|
||||
if (optimize)
|
||||
intermediate.optimize()
|
||||
|
||||
if (writeVmCode) {
|
||||
val stackVmFilename = intermediate.name + ".vm.txt"
|
||||
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
|
||||
intermediate.writeCode(stackvmFile)
|
||||
stackvmFile.close()
|
||||
println("StackVM program code written to '$stackVmFilename'")
|
||||
}
|
||||
|
||||
if (writeAssembly) {
|
||||
val zeropage = C64Zeropage(compilerOptions)
|
||||
intermediate.allocateZeropage(zeropage)
|
||||
val assembly = AsmGen(compilerOptions, intermediate, programAst.heap, zeropage).compileToAssembly(optimize)
|
||||
assembly.assemble(compilerOptions)
|
||||
programName = assembly.name
|
||||
}
|
||||
}
|
||||
}
|
||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||
|
||||
} catch (px: ParsingFailedError) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
System.err.println(px.message)
|
||||
System.err.print("\u001b[0m") // reset
|
||||
exitProcess(1)
|
||||
} catch (ax: AstException) {
|
||||
System.err.print("\u001b[91m") // bright red
|
||||
System.err.println(ax.toString())
|
||||
System.err.print("\u001b[0m") // reset
|
||||
exitProcess(1)
|
||||
} catch (x: Exception) {
|
||||
print("\u001b[91m") // bright red
|
||||
println("\n* internal error *")
|
||||
print("\u001b[0m") // reset
|
||||
System.out.flush()
|
||||
throw x
|
||||
} catch (x: NotImplementedError) {
|
||||
print("\u001b[91m") // bright red
|
||||
println("\n* internal error: missing feature/code *")
|
||||
print("\u001b[0m") // reset
|
||||
System.out.flush()
|
||||
throw x
|
||||
}
|
||||
return Pair(programAst, programName)
|
||||
}
|
||||
|
||||
fun printAst(programAst: Program) {
|
||||
println()
|
||||
val printer = AstToSourceCode(::print)
|
||||
printer.visit(programAst)
|
||||
println()
|
||||
}
|
||||
|
||||
|
||||
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||
val mainModule = program.modules.first()
|
||||
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val launcherType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%launcher" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
mainModule.loadAddress = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%address" }
|
||||
as? Directive)?.args?.single()?.int ?: 0
|
||||
val zpoption: String? = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%zeropage" }
|
||||
as? Directive)?.args?.single()?.name?.toUpperCase()
|
||||
val allOptions = program.modules.flatMap { it.statements }.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.toSet()
|
||||
val floatsEnabled = allOptions.any { it.name == "enable_floats" }
|
||||
val zpType: ZeropageType =
|
||||
if (zpoption == null)
|
||||
if(floatsEnabled) ZeropageType.FLOATSAFE else ZeropageType.KERNALSAFE
|
||||
else
|
||||
try {
|
||||
ZeropageType.valueOf(zpoption)
|
||||
} catch (x: IllegalArgumentException) {
|
||||
ZeropageType.KERNALSAFE
|
||||
// error will be printed by the astchecker
|
||||
}
|
||||
val zpReserved = mainModule.statements
|
||||
.asSequence()
|
||||
.filter { it is Directive && it.directive == "%zpreserved" }
|
||||
.map { (it as Directive).args }
|
||||
.map { it[0].int!!..it[1].int!! }
|
||||
.toList()
|
||||
|
||||
return CompilationOptions(
|
||||
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
|
||||
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
|
||||
zpType, zpReserved, floatsEnabled
|
||||
)
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.printWarning
|
||||
|
||||
|
||||
class ZeropageDepletedError(message: String) : Exception(message)
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package prog8.compiler.intermediate
|
||||
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.stackvm.Syscall
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.vm.stackvm.Syscall
|
||||
|
||||
open class Instruction(val opcode: Opcode,
|
||||
val arg: RuntimeValue? = null,
|
||||
|
||||
@@ -1,7 +1,11 @@
|
||||
package prog8.compiler.intermediate
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.antlr.escape
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.printWarning
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.Zeropage
|
||||
@@ -412,7 +416,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
|
||||
VarDeclType.MEMORY -> {
|
||||
// note that constants are all folded away, but assembly code may still refer to them
|
||||
val lv = decl.value as LiteralValue
|
||||
if(lv.type!=DataType.UWORD && lv.type!=DataType.UBYTE)
|
||||
if(lv.type!= DataType.UWORD && lv.type!= DataType.UBYTE)
|
||||
throw CompilerException("expected integer memory address $lv")
|
||||
currentBlock.memoryPointers[scopedname] = Pair(lv.asIntegerValue!!, decl.datatype)
|
||||
}
|
||||
|
||||
@@ -19,6 +19,8 @@ enum class Opcode {
|
||||
PUSH_REGAY_WORD, // push registers A/Y as a 16-bit word
|
||||
PUSH_REGXY_WORD, // push registers X/Y as a 16-bit word
|
||||
PUSH_ADDR_HEAPVAR, // push the address of the variable that's on the heap (string or array)
|
||||
DUP_B, // duplicate the top byte on the stack
|
||||
DUP_W, // duplicate the top word on the stack
|
||||
|
||||
// popping values off the (evaluation) stack, possibly storing them in another location
|
||||
DISCARD_BYTE, // discard top byte value
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
File diff suppressed because it is too large.
Load diff
@@ -0,0 +1,531 @@
|
||||
package prog8.compiler.target.c64
|
||||
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.compiler.intermediate.Instruction
|
||||
import prog8.compiler.intermediate.IntermediateProgram
|
||||
import prog8.compiler.intermediate.LabelInstr
|
||||
import prog8.compiler.intermediate.Opcode
|
||||
import prog8.compiler.toHex
|
||||
import prog8.vm.stackvm.Syscall
|
||||
import prog8.vm.stackvm.syscallsForStackVm
|
||||
|
||||
|
||||
private var breakpointCounter = 0
|
||||
|
||||
internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.ProgramBlock): String? {
|
||||
// a label 'instruction' is simply translated into a asm label
|
||||
if(ins is LabelInstr) {
|
||||
val labelresult =
|
||||
if(ins.name.startsWith("${block.name}."))
|
||||
ins.name.substring(block.name.length+1)
|
||||
else
|
||||
ins.name
|
||||
return if(ins.asmProc) labelresult+"\t\t.proc" else labelresult
|
||||
}
|
||||
|
||||
// simple opcodes that are translated directly into one or a few asm instructions
|
||||
return when(ins.opcode) {
|
||||
Opcode.LINE -> " ;\tsrc line: ${ins.callLabel}"
|
||||
Opcode.NOP -> " nop" // shouldn't be present anymore though
|
||||
Opcode.START_PROCDEF -> "" // is done as part of a label
|
||||
Opcode.END_PROCDEF -> " .pend"
|
||||
Opcode.TERMINATE -> " brk"
|
||||
Opcode.SEC -> " sec"
|
||||
Opcode.CLC -> " clc"
|
||||
Opcode.SEI -> " sei"
|
||||
Opcode.CLI -> " cli"
|
||||
Opcode.CARRY_TO_A -> " lda #0 | adc #0"
|
||||
Opcode.JUMP -> {
|
||||
if(ins.callLabel!=null)
|
||||
" jmp ${ins.callLabel}"
|
||||
else
|
||||
" jmp ${hexVal(ins)}"
|
||||
}
|
||||
Opcode.CALL -> {
|
||||
if(ins.callLabel!=null)
|
||||
" jsr ${ins.callLabel}"
|
||||
else
|
||||
" jsr ${hexVal(ins)}"
|
||||
}
|
||||
Opcode.RETURN -> " rts"
|
||||
Opcode.RSAVE -> {
|
||||
// save cpu status flag and all registers A, X, Y.
|
||||
// see http://6502.org/tutorials/register_preservation.html
|
||||
" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}"
|
||||
}
|
||||
Opcode.RRESTORE -> {
|
||||
// restore all registers and cpu status flag
|
||||
" pla | tay | pla | tax | pla | plp"
|
||||
}
|
||||
Opcode.RSAVEX -> " sta ${C64Zeropage.SCRATCH_REG} | txa | pha | lda ${C64Zeropage.SCRATCH_REG}"
|
||||
Opcode.RRESTOREX -> " sta ${C64Zeropage.SCRATCH_REG} | pla | tax | lda ${C64Zeropage.SCRATCH_REG}"
|
||||
Opcode.DISCARD_BYTE -> " inx"
|
||||
Opcode.DISCARD_WORD -> " inx"
|
||||
Opcode.DUP_B -> {
|
||||
" dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x"
|
||||
}
|
||||
Opcode.DUP_W -> {
|
||||
" dex | lda ${(ESTACK_LO+1).toHex()},x | sta ${ESTACK_LO.toHex()},x | lda ${(ESTACK_HI+1).toHex()},x | sta ${ESTACK_HI.toHex()},x "
|
||||
}
|
||||
Opcode.DISCARD_FLOAT -> " inx | inx | inx"
|
||||
Opcode.INLINE_ASSEMBLY -> "@inline@" + (ins.callLabel2 ?: "") // All of the inline assembly is stored in the calllabel2 property. the '@inline@' is a special marker to accept it.
|
||||
Opcode.INCLUDE_FILE -> {
|
||||
val offset = if(ins.arg==null) "" else ", ${ins.arg.integerValue()}"
|
||||
val length = if(ins.arg2==null) "" else ", ${ins.arg2.integerValue()}"
|
||||
" .binary \"${ins.callLabel}\" $offset $length"
|
||||
}
|
||||
Opcode.SYSCALL -> {
|
||||
if (ins.arg!!.numericValue() in syscallsForStackVm.map { it.callNr })
|
||||
throw CompilerException("cannot translate vm syscalls to real assembly calls - use *real* subroutine calls instead. Syscall ${ins.arg.numericValue()}")
|
||||
val call = Syscall.values().find { it.callNr==ins.arg.numericValue() }
|
||||
when(call) {
|
||||
Syscall.FUNC_SIN,
|
||||
Syscall.FUNC_COS,
|
||||
Syscall.FUNC_ABS,
|
||||
Syscall.FUNC_TAN,
|
||||
Syscall.FUNC_ATAN,
|
||||
Syscall.FUNC_LN,
|
||||
Syscall.FUNC_LOG2,
|
||||
Syscall.FUNC_SQRT,
|
||||
Syscall.FUNC_RAD,
|
||||
Syscall.FUNC_DEG,
|
||||
Syscall.FUNC_ROUND,
|
||||
Syscall.FUNC_FLOOR,
|
||||
Syscall.FUNC_CEIL,
|
||||
Syscall.FUNC_RNDF,
|
||||
Syscall.FUNC_ANY_F,
|
||||
Syscall.FUNC_ALL_F,
|
||||
Syscall.FUNC_MAX_F,
|
||||
Syscall.FUNC_MIN_F,
|
||||
Syscall.FUNC_SUM_F -> " jsr c64flt.${call.name.toLowerCase()}"
|
||||
null -> ""
|
||||
else -> " jsr prog8_lib.${call.name.toLowerCase()}"
|
||||
}
|
||||
}
|
||||
Opcode.BREAKPOINT -> {
|
||||
breakpointCounter++
|
||||
"_prog8_breakpoint_$breakpointCounter\tnop"
|
||||
}
|
||||
|
||||
Opcode.PUSH_BYTE -> {
|
||||
" lda #${hexVal(ins)} | sta ${ESTACK_LO.toHex()},x | dex"
|
||||
}
|
||||
Opcode.PUSH_WORD -> {
|
||||
val value = hexVal(ins)
|
||||
" lda #<$value | sta ${ESTACK_LO.toHex()},x | lda #>$value | sta ${ESTACK_HI.toHex()},x | dex"
|
||||
}
|
||||
Opcode.PUSH_FLOAT -> {
|
||||
val floatConst = getFloatConst(ins.arg!!)
|
||||
" lda #<$floatConst | ldy #>$floatConst | jsr c64flt.push_float"
|
||||
}
|
||||
Opcode.PUSH_VAR_BYTE -> {
|
||||
when(ins.callLabel) {
|
||||
"X" -> throw CompilerException("makes no sense to push X, it's used as a stack pointer itself. You should probably not use the X register (or only in trivial assignments)")
|
||||
"A" -> " sta ${ESTACK_LO.toHex()},x | dex"
|
||||
"Y" -> " tya | sta ${ESTACK_LO.toHex()},x | dex"
|
||||
else -> " lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | dex"
|
||||
}
|
||||
}
|
||||
Opcode.PUSH_VAR_WORD -> {
|
||||
" lda ${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda ${ins.callLabel}+1 | sta ${ESTACK_HI.toHex()},x | dex"
|
||||
}
|
||||
Opcode.PUSH_VAR_FLOAT -> " lda #<${ins.callLabel} | ldy #>${ins.callLabel}| jsr c64flt.push_float"
|
||||
Opcode.PUSH_MEM_B, Opcode.PUSH_MEM_UB -> {
|
||||
"""
|
||||
lda ${hexVal(ins)}
|
||||
sta ${ESTACK_LO.toHex()},x
|
||||
dex
|
||||
"""
|
||||
}
|
||||
Opcode.PUSH_MEM_W, Opcode.PUSH_MEM_UW -> {
|
||||
"""
|
||||
lda ${hexVal(ins)}
|
||||
sta ${ESTACK_LO.toHex()},x
|
||||
lda ${hexValPlusOne(ins)}
|
||||
sta ${ESTACK_HI.toHex()},x
|
||||
dex
|
||||
"""
|
||||
}
|
||||
Opcode.PUSH_MEM_FLOAT -> {
|
||||
" lda #<${hexVal(ins)} | ldy #>${hexVal(ins)}| jsr c64flt.push_float"
|
||||
}
|
||||
Opcode.PUSH_MEMREAD -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO+1).toHex()},x
|
||||
sta (+) +1
|
||||
lda ${(ESTACK_HI+1).toHex()},x
|
||||
sta (+) +2
|
||||
+ lda 65535 ; modified
|
||||
sta ${(ESTACK_LO+1).toHex()},x
|
||||
"""
|
||||
}
|
||||
|
||||
Opcode.PUSH_REGAY_WORD -> {
|
||||
" sta ${ESTACK_LO.toHex()},x | tya | sta ${ESTACK_HI.toHex()},x | dex "
|
||||
}
|
||||
Opcode.PUSH_ADDR_HEAPVAR -> {
|
||||
" lda #<${ins.callLabel} | sta ${ESTACK_LO.toHex()},x | lda #>${ins.callLabel} | sta ${ESTACK_HI.toHex()},x | dex"
|
||||
}
|
||||
Opcode.POP_REGAX_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||
Opcode.POP_REGXY_WORD -> throw AssemblyError("cannot load X register from stack because it's used as the stack pointer itself")
|
||||
Opcode.POP_REGAY_WORD -> {
|
||||
" inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x "
|
||||
}
|
||||
|
||||
Opcode.READ_INDEXED_VAR_BYTE -> {
|
||||
"""
|
||||
ldy ${(ESTACK_LO+1).toHex()},x
|
||||
lda ${ins.callLabel},y
|
||||
sta ${(ESTACK_LO+1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.READ_INDEXED_VAR_WORD -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO+1).toHex()},x
|
||||
asl a
|
||||
tay
|
||||
lda ${ins.callLabel},y
|
||||
sta ${(ESTACK_LO+1).toHex()},x
|
||||
lda ${ins.callLabel}+1,y
|
||||
sta ${(ESTACK_HI+1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.READ_INDEXED_VAR_FLOAT -> {
|
||||
"""
|
||||
lda #<${ins.callLabel}
|
||||
ldy #>${ins.callLabel}
|
||||
jsr c64flt.push_float_from_indexed_var
|
||||
"""
|
||||
}
|
||||
Opcode.WRITE_INDEXED_VAR_BYTE -> {
|
||||
"""
|
||||
inx
|
||||
ldy ${ESTACK_LO.toHex()},x
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta ${ins.callLabel},y
|
||||
"""
|
||||
}
|
||||
Opcode.WRITE_INDEXED_VAR_WORD -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
asl a
|
||||
tay
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta ${ins.callLabel},y
|
||||
lda ${ESTACK_HI.toHex()},x
|
||||
sta ${ins.callLabel}+1,y
|
||||
"""
|
||||
}
|
||||
Opcode.WRITE_INDEXED_VAR_FLOAT -> {
|
||||
"""
|
||||
lda #<${ins.callLabel}
|
||||
ldy #>${ins.callLabel}
|
||||
jsr c64flt.pop_float_to_indexed_var
|
||||
"""
|
||||
}
|
||||
Opcode.POP_MEM_BYTE -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta ${hexVal(ins)}
|
||||
"""
|
||||
}
|
||||
Opcode.POP_MEM_WORD -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta ${hexVal(ins)}
|
||||
lda ${ESTACK_HI.toHex()},x
|
||||
sta ${hexValPlusOne(ins)}
|
||||
"""
|
||||
}
|
||||
Opcode.POP_MEM_FLOAT -> {
|
||||
" lda ${hexVal(ins)} | ldy ${hexValPlusOne(ins)} | jsr c64flt.pop_float"
|
||||
}
|
||||
Opcode.POP_MEMWRITE -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta (+) +1
|
||||
lda ${ESTACK_HI.toHex()},x
|
||||
sta (+) +2
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
+ sta 65535 ; modified
|
||||
"""
|
||||
}
|
||||
|
||||
Opcode.POP_VAR_BYTE -> {
|
||||
when (ins.callLabel) {
|
||||
"X" -> throw CompilerException("makes no sense to pop X, it's used as a stack pointer itself")
|
||||
"A" -> " inx | lda ${ESTACK_LO.toHex()},x"
|
||||
"Y" -> " inx | ldy ${ESTACK_LO.toHex()},x"
|
||||
else -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${ins.callLabel}"
|
||||
}
|
||||
}
|
||||
Opcode.POP_VAR_WORD -> {
|
||||
" inx | lda ${ESTACK_LO.toHex()},x | ldy ${ESTACK_HI.toHex()},x | sta ${ins.callLabel} | sty ${ins.callLabel}+1"
|
||||
}
|
||||
Opcode.POP_VAR_FLOAT -> {
|
||||
" lda #<${ins.callLabel} | ldy #>${ins.callLabel} | jsr c64flt.pop_float"
|
||||
}
|
||||
|
||||
Opcode.INC_VAR_UB, Opcode.INC_VAR_B -> {
|
||||
when (ins.callLabel) {
|
||||
"A" -> " clc | adc #1"
|
||||
"X" -> " inx"
|
||||
"Y" -> " iny"
|
||||
else -> " inc ${ins.callLabel}"
|
||||
}
|
||||
}
|
||||
Opcode.INC_VAR_UW, Opcode.INC_VAR_W -> {
|
||||
" inc ${ins.callLabel} | bne + | inc ${ins.callLabel}+1 |+"
|
||||
}
|
||||
Opcode.INC_VAR_F -> {
|
||||
"""
|
||||
lda #<${ins.callLabel}
|
||||
ldy #>${ins.callLabel}
|
||||
jsr c64flt.inc_var_f
|
||||
"""
|
||||
}
|
||||
Opcode.POP_INC_MEMORY -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta (+) +1
|
||||
lda ${ESTACK_HI.toHex()},x
|
||||
sta (+) +2
|
||||
+ inc 65535 ; modified
|
||||
"""
|
||||
}
|
||||
Opcode.POP_DEC_MEMORY -> {
|
||||
"""
|
||||
inx
|
||||
lda ${ESTACK_LO.toHex()},x
|
||||
sta (+) +1
|
||||
lda ${ESTACK_HI.toHex()},x
|
||||
sta (+) +2
|
||||
+ dec 65535 ; modified
|
||||
"""
|
||||
}
|
||||
Opcode.DEC_VAR_UB, Opcode.DEC_VAR_B -> {
|
||||
when (ins.callLabel) {
|
||||
"A" -> " sec | sbc #1"
|
||||
"X" -> " dex"
|
||||
"Y" -> " dey"
|
||||
else -> " dec ${ins.callLabel}"
|
||||
}
|
||||
}
|
||||
Opcode.DEC_VAR_UW, Opcode.DEC_VAR_W -> {
|
||||
" lda ${ins.callLabel} | bne + | dec ${ins.callLabel}+1 |+ | dec ${ins.callLabel}"
|
||||
}
|
||||
Opcode.DEC_VAR_F -> {
|
||||
"""
|
||||
lda #<${ins.callLabel}
|
||||
ldy #>${ins.callLabel}
|
||||
jsr c64flt.dec_var_f
|
||||
"""
|
||||
}
|
||||
Opcode.INC_MEMORY -> " inc ${hexVal(ins)}"
|
||||
Opcode.DEC_MEMORY -> " dec ${hexVal(ins)}"
|
||||
Opcode.INC_INDEXED_VAR_B, Opcode.INC_INDEXED_VAR_UB -> " inx | txa | pha | lda ${ESTACK_LO.toHex()},x | tax | inc ${ins.callLabel},x | pla | tax"
|
||||
Opcode.DEC_INDEXED_VAR_B, Opcode.DEC_INDEXED_VAR_UB -> " inx | txa | pha | lda ${ESTACK_LO.toHex()},x | tax | dec ${ins.callLabel},x | pla | tax"
|
||||
|
||||
Opcode.NEG_B -> " jsr prog8_lib.neg_b"
|
||||
Opcode.NEG_W -> " jsr prog8_lib.neg_w"
|
||||
Opcode.NEG_F -> " jsr c64flt.neg_f"
|
||||
Opcode.ABS_B -> " jsr prog8_lib.abs_b"
|
||||
Opcode.ABS_W -> " jsr prog8_lib.abs_w"
|
||||
Opcode.ABS_F -> " jsr c64flt.abs_f"
|
||||
Opcode.POW_F -> " jsr c64flt.pow_f"
|
||||
Opcode.INV_BYTE -> {
|
||||
"""
|
||||
lda ${(ESTACK_LO + 1).toHex()},x
|
||||
eor #255
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.INV_WORD -> " jsr prog8_lib.inv_word"
|
||||
Opcode.NOT_BYTE -> " jsr prog8_lib.not_byte"
|
||||
Opcode.NOT_WORD -> " jsr prog8_lib.not_word"
|
||||
Opcode.BCS -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bcs $label"
|
||||
}
|
||||
Opcode.BCC -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bcc $label"
|
||||
}
|
||||
Opcode.BNEG -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bmi $label"
|
||||
}
|
||||
Opcode.BPOS -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bpl $label"
|
||||
}
|
||||
Opcode.BVC -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bvc $label"
|
||||
}
|
||||
Opcode.BVS -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bvs $label"
|
||||
}
|
||||
Opcode.BZ -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" beq $label"
|
||||
}
|
||||
Opcode.BNZ -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
" bne $label"
|
||||
}
|
||||
Opcode.JZ -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
"""
|
||||
inx
|
||||
lda ${(ESTACK_LO).toHex()},x
|
||||
beq $label
|
||||
"""
|
||||
}
|
||||
Opcode.JZW -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
"""
|
||||
inx
|
||||
lda ${(ESTACK_LO).toHex()},x
|
||||
beq $label
|
||||
lda ${(ESTACK_HI).toHex()},x
|
||||
beq $label
|
||||
"""
|
||||
}
|
||||
Opcode.JNZ -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
"""
|
||||
inx
|
||||
lda ${(ESTACK_LO).toHex()},x
|
||||
bne $label
|
||||
"""
|
||||
}
|
||||
Opcode.JNZW -> {
|
||||
val label = ins.callLabel ?: hexVal(ins)
|
||||
"""
|
||||
inx
|
||||
lda ${(ESTACK_LO).toHex()},x
|
||||
bne $label
|
||||
lda ${(ESTACK_HI).toHex()},x
|
||||
bne $label
|
||||
"""
|
||||
}
|
||||
Opcode.CAST_B_TO_UB -> "" // is a no-op, just carry on with the byte as-is
|
||||
Opcode.CAST_UB_TO_B -> "" // is a no-op, just carry on with the byte as-is
|
||||
Opcode.CAST_W_TO_UW -> "" // is a no-op, just carry on with the word as-is
|
||||
Opcode.CAST_UW_TO_W -> "" // is a no-op, just carry on with the word as-is
|
||||
Opcode.CAST_W_TO_UB -> "" // is a no-op, just carry on with the lsb of the word as-is
|
||||
Opcode.CAST_W_TO_B -> "" // is a no-op, just carry on with the lsb of the word as-is
|
||||
Opcode.CAST_UW_TO_UB -> "" // is a no-op, just carry on with the lsb of the uword as-is
|
||||
Opcode.CAST_UW_TO_B -> "" // is a no-op, just carry on with the lsb of the uword as-is
|
||||
Opcode.CAST_UB_TO_F -> " jsr c64flt.stack_ub2float"
|
||||
Opcode.CAST_B_TO_F -> " jsr c64flt.stack_b2float"
|
||||
Opcode.CAST_UW_TO_F -> " jsr c64flt.stack_uw2float"
|
||||
Opcode.CAST_W_TO_F -> " jsr c64flt.stack_w2float"
|
||||
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2uw"
|
||||
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2w"
|
||||
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
|
||||
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
|
||||
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta ${(ESTACK_HI+1).toHex()},x" // clear the msb
|
||||
Opcode.CAST_B_TO_UW, Opcode.CAST_B_TO_W -> " lda ${(ESTACK_LO+1).toHex()},x | ${signExtendA("${(ESTACK_HI+1).toHex()},x")}" // sign extend the lsb
|
||||
Opcode.MSB -> " lda ${(ESTACK_HI+1).toHex()},x | sta ${(ESTACK_LO+1).toHex()},x"
|
||||
Opcode.MKWORD -> " inx | lda ${ESTACK_LO.toHex()},x | sta ${(ESTACK_HI+1).toHex()},x "
|
||||
|
||||
Opcode.ADD_UB, Opcode.ADD_B -> { // TODO inline better (pattern with more opcodes)
|
||||
"""
|
||||
lda ${(ESTACK_LO + 2).toHex()},x
|
||||
clc
|
||||
adc ${(ESTACK_LO + 1).toHex()},x
|
||||
inx
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.SUB_UB, Opcode.SUB_B -> { // TODO inline better (pattern with more opcodes)
|
||||
"""
|
||||
lda ${(ESTACK_LO + 2).toHex()},x
|
||||
sec
|
||||
sbc ${(ESTACK_LO + 1).toHex()},x
|
||||
inx
|
||||
sta ${(ESTACK_LO + 1).toHex()},x
|
||||
"""
|
||||
}
|
||||
Opcode.ADD_W, Opcode.ADD_UW -> " jsr prog8_lib.add_w"
|
||||
Opcode.SUB_W, Opcode.SUB_UW -> " jsr prog8_lib.sub_w"
|
||||
Opcode.MUL_B, Opcode.MUL_UB -> " jsr prog8_lib.mul_byte"
|
||||
Opcode.MUL_W, Opcode.MUL_UW -> " jsr prog8_lib.mul_word"
|
||||
Opcode.MUL_F -> " jsr c64flt.mul_f"
|
||||
Opcode.ADD_F -> " jsr c64flt.add_f"
|
||||
Opcode.SUB_F -> " jsr c64flt.sub_f"
|
||||
Opcode.DIV_F -> " jsr c64flt.div_f"
|
||||
Opcode.IDIV_UB -> " jsr prog8_lib.idiv_ub"
|
||||
Opcode.IDIV_B -> " jsr prog8_lib.idiv_b"
|
||||
Opcode.IDIV_W -> " jsr prog8_lib.idiv_w"
|
||||
Opcode.IDIV_UW -> " jsr prog8_lib.idiv_uw"
|
||||
|
||||
Opcode.AND_BYTE -> " jsr prog8_lib.and_b"
|
||||
Opcode.OR_BYTE -> " jsr prog8_lib.or_b"
|
||||
Opcode.XOR_BYTE -> " jsr prog8_lib.xor_b"
|
||||
Opcode.AND_WORD -> " jsr prog8_lib.and_w"
|
||||
Opcode.OR_WORD -> " jsr prog8_lib.or_w"
|
||||
Opcode.XOR_WORD -> " jsr prog8_lib.xor_w"
|
||||
|
||||
Opcode.BITAND_BYTE -> " jsr prog8_lib.bitand_b"
|
||||
Opcode.BITOR_BYTE -> " jsr prog8_lib.bitor_b"
|
||||
Opcode.BITXOR_BYTE -> " jsr prog8_lib.bitxor_b"
|
||||
Opcode.BITAND_WORD -> " jsr prog8_lib.bitand_w"
|
||||
Opcode.BITOR_WORD -> " jsr prog8_lib.bitor_w"
|
||||
Opcode.BITXOR_WORD -> " jsr prog8_lib.bitxor_w"
|
||||
|
||||
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
|
||||
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"
|
||||
|
||||
Opcode.GREATER_B -> " jsr prog8_lib.greater_b"
|
||||
Opcode.GREATER_UB -> " jsr prog8_lib.greater_ub"
|
||||
Opcode.GREATER_W -> " jsr prog8_lib.greater_w"
|
||||
Opcode.GREATER_UW -> " jsr prog8_lib.greater_uw"
|
||||
Opcode.GREATER_F -> " jsr c64flt.greater_f"
|
||||
|
||||
Opcode.GREATEREQ_B -> " jsr prog8_lib.greatereq_b"
|
||||
Opcode.GREATEREQ_UB -> " jsr prog8_lib.greatereq_ub"
|
||||
Opcode.GREATEREQ_W -> " jsr prog8_lib.greatereq_w"
|
||||
Opcode.GREATEREQ_UW -> " jsr prog8_lib.greatereq_uw"
|
||||
Opcode.GREATEREQ_F -> " jsr c64flt.greatereq_f"
|
||||
|
||||
Opcode.EQUAL_BYTE -> " jsr prog8_lib.equal_b"
|
||||
Opcode.EQUAL_WORD -> " jsr prog8_lib.equal_w"
|
||||
Opcode.EQUAL_F -> " jsr c64flt.equal_f"
|
||||
Opcode.NOTEQUAL_BYTE -> " jsr prog8_lib.notequal_b"
|
||||
Opcode.NOTEQUAL_WORD -> " jsr prog8_lib.notequal_w"
|
||||
Opcode.NOTEQUAL_F -> " jsr c64flt.notequal_f"
|
||||
|
||||
Opcode.LESS_UB -> " jsr prog8_lib.less_ub"
|
||||
Opcode.LESS_B -> " jsr prog8_lib.less_b"
|
||||
Opcode.LESS_UW -> " jsr prog8_lib.less_uw"
|
||||
Opcode.LESS_W -> " jsr prog8_lib.less_w"
|
||||
Opcode.LESS_F -> " jsr c64flt.less_f"
|
||||
|
||||
Opcode.LESSEQ_UB -> " jsr prog8_lib.lesseq_ub"
|
||||
Opcode.LESSEQ_B -> " jsr prog8_lib.lesseq_b"
|
||||
Opcode.LESSEQ_UW -> " jsr prog8_lib.lesseq_uw"
|
||||
Opcode.LESSEQ_W -> " jsr prog8_lib.lesseq_w"
|
||||
Opcode.LESSEQ_F -> " jsr c64flt.lesseq_f"
|
||||
|
||||
Opcode.SHIFTEDL_BYTE -> " asl ${(ESTACK_LO+1).toHex()},x"
|
||||
Opcode.SHIFTEDL_WORD -> " asl ${(ESTACK_LO+1).toHex()},x | rol ${(ESTACK_HI+1).toHex()},x"
|
||||
Opcode.SHIFTEDR_SBYTE -> " lda ${(ESTACK_LO+1).toHex()},x | asl a | ror ${(ESTACK_LO+1).toHex()},x"
|
||||
Opcode.SHIFTEDR_UBYTE -> " lsr ${(ESTACK_LO+1).toHex()},x"
|
||||
Opcode.SHIFTEDR_SWORD -> " lda ${(ESTACK_HI+1).toHex()},x | asl a | ror ${(ESTACK_HI+1).toHex()},x | ror ${(ESTACK_LO+1).toHex()},x"
|
||||
Opcode.SHIFTEDR_UWORD -> " lsr ${(ESTACK_HI+1).toHex()},x | ror ${(ESTACK_LO+1).toHex()},x"
|
||||
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,11 @@
|
||||
package prog8.functions
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.DirectMemoryRead
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
|
||||
@@ -111,13 +116,13 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
||||
|
||||
fun datatypeFromIterableArg(arglist: IExpression): DataType {
|
||||
if(arglist is LiteralValue) {
|
||||
if(arglist.type==DataType.ARRAY_UB || arglist.type==DataType.ARRAY_UW || arglist.type==DataType.ARRAY_F) {
|
||||
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
|
||||
val dt = arglist.arrayvalue!!.map {it.inferType(program)}
|
||||
if(dt.any { it!=DataType.UBYTE && it!=DataType.UWORD && it!=DataType.FLOAT}) {
|
||||
if(dt.any { it!= DataType.UBYTE && it!= DataType.UWORD && it!= DataType.FLOAT}) {
|
||||
throw FatalAstException("fuction $function only accepts arraysize of numeric values")
|
||||
}
|
||||
if(dt.any { it==DataType.FLOAT }) return DataType.FLOAT
|
||||
if(dt.any { it==DataType.UWORD }) return DataType.UWORD
|
||||
if(dt.any { it== DataType.FLOAT }) return DataType.FLOAT
|
||||
if(dt.any { it== DataType.UWORD }) return DataType.UWORD
|
||||
return DataType.UBYTE
|
||||
}
|
||||
}
|
||||
@@ -186,27 +191,28 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position, program: P
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.FLOAT)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
|
||||
val float = constval.asNumericValue?.toDouble()!!
|
||||
val float = getFloatArg(constval, args[0].position)
|
||||
return numericLiteral(function(float), args[0].position)
|
||||
}
|
||||
|
||||
private fun getFloatArg(v: LiteralValue, position: Position): Double {
|
||||
val nv = v.asNumericValue ?: throw SyntaxError("numerical argument required", position)
|
||||
return nv.toDouble()
|
||||
}
|
||||
|
||||
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.FLOAT)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
return LiteralValue(DataType.WORD, wordvalue=function(constval.asNumericValue!!.toDouble()).toInt(), position=args[0].position)
|
||||
val float = getFloatArg(constval, args[0].position)
|
||||
return LiteralValue(DataType.WORD, wordvalue = function(float).toInt(), position = args[0].position)
|
||||
}
|
||||
|
||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): LiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
if(constval.type!=DataType.UBYTE && constval.type!=DataType.UWORD)
|
||||
if(constval.type!= DataType.UBYTE && constval.type!= DataType.UWORD)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
|
||||
val integer = constval.asNumericValue?.toInt()!!
|
||||
@@ -378,7 +384,7 @@ private fun builtinSin8(args: List<IExpression>, position: Position, program: Pr
|
||||
throw SyntaxError("sin8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0* sin(rad)).toShort(), position = position)
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * sin(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -386,7 +392,7 @@ private fun builtinSin8u(args: List<IExpression>, position: Position, program: P
|
||||
throw SyntaxError("sin8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0+127.5*sin(rad)).toShort(), position = position)
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * sin(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -394,7 +400,7 @@ private fun builtinCos8(args: List<IExpression>, position: Position, program: Pr
|
||||
throw SyntaxError("cos8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0* cos(rad)).toShort(), position = position)
|
||||
return LiteralValue(DataType.BYTE, bytevalue = (127.0 * cos(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -402,7 +408,7 @@ private fun builtinCos8u(args: List<IExpression>, position: Position, program: P
|
||||
throw SyntaxError("cos8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5*cos(rad)).toShort(), position = position)
|
||||
return LiteralValue(DataType.UBYTE, bytevalue = (128.0 + 127.5 * cos(rad)).toShort(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -410,7 +416,7 @@ private fun builtinSin16(args: List<IExpression>, position: Position, program: P
|
||||
throw SyntaxError("sin16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0* sin(rad)).toInt(), position = position)
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * sin(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -418,7 +424,7 @@ private fun builtinSin16u(args: List<IExpression>, position: Position, program:
|
||||
throw SyntaxError("sin16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5*sin(rad)).toInt(), position = position)
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * sin(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -426,7 +432,7 @@ private fun builtinCos16(args: List<IExpression>, position: Position, program: P
|
||||
throw SyntaxError("cos16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0* cos(rad)).toInt(), position = position)
|
||||
return LiteralValue(DataType.WORD, wordvalue = (32767.0 * cos(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): LiteralValue {
|
||||
@@ -434,7 +440,7 @@ private fun builtinCos16u(args: List<IExpression>, position: Position, program:
|
||||
throw SyntaxError("cos16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
val rad = constval.asNumericValue!!.toDouble() /256.0 * 2.0 * PI
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0+32767.5* cos(rad)).toInt(), position = position)
|
||||
return LiteralValue(DataType.UWORD, wordvalue = (32768.0 + 32767.5 * cos(rad)).toInt(), position = position)
|
||||
}
|
||||
|
||||
private fun numericLiteral(value: Number, position: Position): LiteralValue {
|
||||
|
||||
+31
-24
@@ -1,10 +1,17 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.base.initvarsSubName
|
||||
import prog8.ast.expressions.FunctionCall
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.loadAsmIncludeFile
|
||||
|
||||
|
||||
class CallGraph(private val program: Program): IAstProcessor {
|
||||
class CallGraph(private val program: Program): IAstVisitor {
|
||||
|
||||
val modulesImporting = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||
val modulesImportedBy = mutableMapOf<Module, List<Module>>().withDefault { mutableListOf() }
|
||||
@@ -13,7 +20,7 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
val usedSymbols = mutableSetOf<IStatement>()
|
||||
|
||||
init {
|
||||
process(program)
|
||||
visit(program)
|
||||
}
|
||||
|
||||
fun forAllSubroutines(scope: INameScope, sub: (s: Subroutine) -> Unit) {
|
||||
@@ -28,8 +35,8 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
findSubs(scope)
|
||||
}
|
||||
|
||||
override fun process(program: Program) {
|
||||
super.process(program)
|
||||
override fun visit(program: Program) {
|
||||
super.visit(program)
|
||||
|
||||
program.modules.forEach {
|
||||
it.importedBy.clear()
|
||||
@@ -52,16 +59,16 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
rootmodule.importedBy.add(rootmodule) // don't discard root module
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
override fun visit(block: Block) {
|
||||
if(block.definingModule().isLibraryModule) {
|
||||
// make sure the block is not removed
|
||||
addNodeAndParentScopes(block)
|
||||
}
|
||||
|
||||
return super.process(block)
|
||||
super.visit(block)
|
||||
}
|
||||
|
||||
override fun process(directive: Directive): IStatement {
|
||||
override fun visit(directive: Directive) {
|
||||
val thisModule = directive.definingModule()
|
||||
if(directive.directive=="%import") {
|
||||
val importedModule: Module = program.modules.single { it.name==directive.args[0].name }
|
||||
@@ -73,16 +80,16 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
scanAssemblyCode(asm, directive, scope)
|
||||
}
|
||||
|
||||
return super.process(directive)
|
||||
super.visit(directive)
|
||||
}
|
||||
|
||||
override fun process(identifier: IdentifierReference): IExpression {
|
||||
override fun visit(identifier: IdentifierReference) {
|
||||
// track symbol usage
|
||||
val target = identifier.targetStatement(this.program.namespace)
|
||||
if(target!=null) {
|
||||
addNodeAndParentScopes(target)
|
||||
}
|
||||
return super.process(identifier)
|
||||
super.visit(identifier)
|
||||
}
|
||||
|
||||
private fun addNodeAndParentScopes(stmt: IStatement) {
|
||||
@@ -96,24 +103,24 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
} while (node !is Module && node !is ParentSentinel)
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine) {
|
||||
if((subroutine.name=="start" && subroutine.definingScope().name=="main")
|
||||
|| subroutine.name==initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
||||
|| subroutine.name== initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
||||
// make sure the entrypoint is mentioned in the used symbols
|
||||
addNodeAndParentScopes(subroutine)
|
||||
}
|
||||
return super.process(subroutine)
|
||||
super.visit(subroutine)
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl) {
|
||||
if(decl.autoGenerated || (decl.definingModule().isLibraryModule && decl.type!=VarDeclType.VAR)) {
|
||||
// make sure autogenerated vardecls are in the used symbols
|
||||
addNodeAndParentScopes(decl)
|
||||
}
|
||||
return super.process(decl)
|
||||
super.visit(decl)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall) {
|
||||
val otherSub = functionCall.target.targetSubroutine(program.namespace)
|
||||
if(otherSub!=null) {
|
||||
functionCall.definingSubroutine()?.let { thisSub ->
|
||||
@@ -121,10 +128,10 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCall)
|
||||
}
|
||||
}
|
||||
return super.process(functionCall)
|
||||
super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||
val otherSub = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||
if(otherSub!=null) {
|
||||
functionCallStatement.definingSubroutine()?.let { thisSub ->
|
||||
@@ -132,10 +139,10 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(functionCallStatement)
|
||||
}
|
||||
}
|
||||
return super.process(functionCallStatement)
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
override fun visit(jump: Jump) {
|
||||
val otherSub = jump.identifier?.targetSubroutine(program.namespace)
|
||||
if(otherSub!=null) {
|
||||
jump.definingSubroutine()?.let { thisSub ->
|
||||
@@ -143,14 +150,14 @@ class CallGraph(private val program: Program): IAstProcessor {
|
||||
subroutinesCalledBy[otherSub] = subroutinesCalledBy.getValue(otherSub).plus(jump)
|
||||
}
|
||||
}
|
||||
return super.process(jump)
|
||||
super.visit(jump)
|
||||
}
|
||||
|
||||
override fun process(inlineAssembly: InlineAssembly): IStatement {
|
||||
override fun visit(inlineAssembly: InlineAssembly) {
|
||||
// parse inline asm for subroutine calls (jmp, jsr)
|
||||
val scope = inlineAssembly.definingScope()
|
||||
scanAssemblyCode(inlineAssembly.assembly, inlineAssembly, scope)
|
||||
return super.process(inlineAssembly)
|
||||
super.visit(inlineAssembly)
|
||||
}
|
||||
|
||||
private fun scanAssemblyCode(asm: String, context: IStatement, scope: INameScope) {
|
||||
+7
-6
@@ -1,13 +1,14 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.ExpressionError
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||
|
||||
|
||||
class ConstExprEvaluator {
|
||||
|
||||
fun evaluate(left: LiteralValue, operator: String, right: LiteralValue): IExpression {
|
||||
@@ -40,7 +41,7 @@ class ConstExprEvaluator {
|
||||
if(left.asIntegerValue==null || amount.asIntegerValue==null)
|
||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||
val result =
|
||||
if(left.type==DataType.UBYTE || left.type==DataType.UWORD)
|
||||
if(left.type== DataType.UBYTE || left.type== DataType.UWORD)
|
||||
left.asIntegerValue.ushr(amount.asIntegerValue)
|
||||
else
|
||||
left.asIntegerValue.shr(amount.asIntegerValue)
|
||||
+88
-86
@@ -1,7 +1,10 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.CompilerException
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.IntegerOrAddressOf
|
||||
import prog8.compiler.target.c64.FLOAT_MAX_NEGATIVE
|
||||
@@ -9,7 +12,7 @@ import prog8.compiler.target.c64.FLOAT_MAX_POSITIVE
|
||||
import kotlin.math.floor
|
||||
|
||||
|
||||
class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
var optimizationsDone: Int = 0
|
||||
var errors : MutableList<AstException> = mutableListOf()
|
||||
|
||||
@@ -23,7 +26,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
if(decl.value?.referencesIdentifier(decl.name) == true || decl.arraysize?.index?.referencesIdentifier(decl.name) == true) {
|
||||
errors.add(ExpressionError("recursive var declaration", decl.position))
|
||||
@@ -45,7 +48,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
else if(decl.arraysize?.size()==null) {
|
||||
val size = decl.arraysize!!.index.process(this)
|
||||
val size = decl.arraysize!!.index.accept(this)
|
||||
if(size is LiteralValue) {
|
||||
decl.arraysize = ArrayIndex(size, decl.position)
|
||||
optimizationsDone++
|
||||
@@ -75,19 +78,19 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
val eltType = rangeExpr.inferType(program)!!
|
||||
if(eltType in ByteDatatypes) {
|
||||
decl.value = LiteralValue(decl.datatype,
|
||||
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue=it.toShort(), position = decl.value!!.position ) }
|
||||
.toTypedArray(), position=decl.value!!.position)
|
||||
arrayvalue = constRange.map { LiteralValue(eltType, bytevalue = it.toShort(), position = decl.value!!.position) }
|
||||
.toTypedArray(), position = decl.value!!.position)
|
||||
} else {
|
||||
decl.value = LiteralValue(decl.datatype,
|
||||
arrayvalue = constRange.map { LiteralValue(eltType, wordvalue= it, position = decl.value!!.position ) }
|
||||
.toTypedArray(), position=decl.value!!.position)
|
||||
arrayvalue = constRange.map { LiteralValue(eltType, wordvalue = it, position = decl.value!!.position) }
|
||||
.toTypedArray(), position = decl.value!!.position)
|
||||
}
|
||||
decl.value!!.linkParents(decl)
|
||||
optimizationsDone++
|
||||
return decl
|
||||
}
|
||||
}
|
||||
if(litval?.type==DataType.FLOAT)
|
||||
if(litval?.type== DataType.FLOAT)
|
||||
errors.add(ExpressionError("arraysize requires only integers here", litval.position))
|
||||
val size = decl.arraysize?.size() ?: return decl
|
||||
if ((litval==null || !litval.isArray) && rangeExpr==null) {
|
||||
@@ -96,24 +99,29 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
when(decl.datatype){
|
||||
DataType.ARRAY_UB -> {
|
||||
if(fillvalue !in 0..255)
|
||||
errors.add(ExpressionError("ubyte value overflow", litval?.position ?: decl.position))
|
||||
errors.add(ExpressionError("ubyte value overflow", litval?.position
|
||||
?: decl.position))
|
||||
}
|
||||
DataType.ARRAY_B -> {
|
||||
if(fillvalue !in -128..127)
|
||||
errors.add(ExpressionError("byte value overflow", litval?.position ?: decl.position))
|
||||
errors.add(ExpressionError("byte value overflow", litval?.position
|
||||
?: decl.position))
|
||||
}
|
||||
DataType.ARRAY_UW -> {
|
||||
if(fillvalue !in 0..65535)
|
||||
errors.add(ExpressionError("uword value overflow", litval?.position ?: decl.position))
|
||||
errors.add(ExpressionError("uword value overflow", litval?.position
|
||||
?: decl.position))
|
||||
}
|
||||
DataType.ARRAY_W -> {
|
||||
if(fillvalue !in -32768..32767)
|
||||
errors.add(ExpressionError("word value overflow", litval?.position ?: decl.position))
|
||||
errors.add(ExpressionError("word value overflow", litval?.position
|
||||
?: decl.position))
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
|
||||
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position ?: decl.position)
|
||||
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval?.position
|
||||
?: decl.position)
|
||||
optimizationsDone++
|
||||
return decl
|
||||
}
|
||||
@@ -124,10 +132,12 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||
val fillvalue = if (litval == null) 0.0 else litval.asNumericValue?.toDouble() ?: 0.0
|
||||
if(fillvalue< FLOAT_MAX_NEGATIVE || fillvalue> FLOAT_MAX_POSITIVE)
|
||||
errors.add(ExpressionError("float value overflow", litval?.position ?: decl.position))
|
||||
errors.add(ExpressionError("float value overflow", litval?.position
|
||||
?: decl.position))
|
||||
else {
|
||||
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
|
||||
decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position ?: decl.position)
|
||||
decl.value = LiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval?.position
|
||||
?: decl.position)
|
||||
optimizationsDone++
|
||||
return decl
|
||||
}
|
||||
@@ -139,7 +149,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: LiteralValue) {
|
||||
@@ -154,7 +164,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
if(array.array!=null) {
|
||||
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
|
||||
decl.value = LiteralValue(decl.datatype, initHeapId=heapId, position = litval.position)
|
||||
decl.value = LiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
|
||||
}
|
||||
}
|
||||
DataType.ARRAY_F -> {
|
||||
@@ -172,7 +182,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
/**
|
||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
||||
*/
|
||||
override fun process(identifier: IdentifierReference): IExpression {
|
||||
override fun visit(identifier: IdentifierReference): IExpression {
|
||||
return try {
|
||||
val cval = identifier.constValue(program) ?: return identifier
|
||||
return if(cval.isNumeric) {
|
||||
@@ -187,9 +197,9 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
return try {
|
||||
super.process(functionCall)
|
||||
super.visit(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
functionCall.constValue(program) ?: functionCall
|
||||
} catch (ax: AstException) {
|
||||
@@ -198,8 +208,8 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
super.process(functionCallStatement)
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
super.visit(functionCallStatement)
|
||||
typeCastConstArguments(functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
@@ -212,7 +222,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
val expectedDt = arg.second.type
|
||||
val argConst = arg.first.value.constValue(program)
|
||||
if(argConst!=null && argConst.type!=expectedDt) {
|
||||
val convertedValue = argConst.intoDatatype(expectedDt)
|
||||
val convertedValue = argConst.cast(expectedDt)
|
||||
if(convertedValue!=null) {
|
||||
functionCall.arglist[arg.first.index] = convertedValue
|
||||
optimizationsDone++
|
||||
@@ -222,34 +232,26 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(memread: DirectMemoryRead): IExpression {
|
||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
return super.process(addrOf.identifier)
|
||||
return super.process(memread)
|
||||
}
|
||||
|
||||
override fun process(memwrite: DirectMemoryWrite): IExpression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memwrite.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
return super.process(addrOf.identifier)
|
||||
return super.process(memwrite)
|
||||
return super.visit(addrOf.identifier)
|
||||
return super.visit(memread)
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to process a unary prefix expression.
|
||||
* Try to accept a unary prefix expression.
|
||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||
*/
|
||||
override fun process(expr: PrefixExpression): IExpression {
|
||||
override fun visit(expr: PrefixExpression): IExpression {
|
||||
return try {
|
||||
super.process(expr)
|
||||
super.visit(expr)
|
||||
|
||||
val subexpr = expr.expression
|
||||
if (subexpr is LiteralValue) {
|
||||
// process prefixed literal values (such as -3, not true)
|
||||
// accept prefixed literal values (such as -3, not true)
|
||||
return when {
|
||||
expr.operator == "+" -> subexpr
|
||||
expr.operator == "-" -> when {
|
||||
@@ -292,7 +294,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
|
||||
/**
|
||||
* Try to process a binary expression.
|
||||
* Try to accept a binary expression.
|
||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||
* For instance, "9 * (4 + 2)" will be optimized into the integer literal 54.
|
||||
*
|
||||
@@ -308,9 +310,9 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
* (X / c1) * c2 -> X / (c2/c1)
|
||||
* (X + c1) - c2 -> X + (c1-c2)
|
||||
*/
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
return try {
|
||||
super.process(expr)
|
||||
super.visit(expr)
|
||||
val leftconst = expr.left.constValue(program)
|
||||
val rightconst = expr.right.constValue(program)
|
||||
|
||||
@@ -385,7 +387,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
expr
|
||||
} else
|
||||
BinaryExpression(
|
||||
BinaryExpression(expr.left, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position),
|
||||
BinaryExpression(expr.left, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||
expr.operator, subExpr.left, expr.position)
|
||||
} else {
|
||||
return if(subleftIsConst) {
|
||||
@@ -394,7 +396,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
} else
|
||||
BinaryExpression(
|
||||
subExpr.left, expr.operator,
|
||||
BinaryExpression(expr.right, if(expr.operator=="-") "+" else "*", subExpr.right, subExpr.position),
|
||||
BinaryExpression(expr.right, if (expr.operator == "-") "+" else "*", subExpr.right, subExpr.position),
|
||||
expr.position)
|
||||
}
|
||||
}
|
||||
@@ -537,21 +539,21 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
|
||||
fun adjustRangeDt(rangeFrom: LiteralValue, targetDt: DataType, rangeTo: LiteralValue, stepLiteral: LiteralValue?, range: RangeExpr): RangeExpr {
|
||||
val newFrom = rangeFrom.intoDatatype(targetDt)
|
||||
val newTo = rangeTo.intoDatatype(targetDt)
|
||||
val newFrom = rangeFrom.cast(targetDt)
|
||||
val newTo = rangeTo.cast(targetDt)
|
||||
if (newFrom != null && newTo != null) {
|
||||
val newStep: IExpression =
|
||||
if (stepLiteral != null) (stepLiteral.intoDatatype(targetDt) ?: stepLiteral) else range.step
|
||||
if (stepLiteral != null) (stepLiteral.cast(targetDt) ?: stepLiteral) else range.step
|
||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||
}
|
||||
return range
|
||||
}
|
||||
|
||||
// adjust the datatype of a range expression in for loops to the loop variable.
|
||||
val resultStmt = super.process(forLoop) as ForLoop
|
||||
val resultStmt = super.visit(forLoop) as ForLoop
|
||||
val iterableRange = resultStmt.iterable as? RangeExpr ?: return resultStmt
|
||||
val rangeFrom = iterableRange.from as? LiteralValue
|
||||
val rangeTo = iterableRange.to as? LiteralValue
|
||||
@@ -562,25 +564,25 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
val stepLiteral = iterableRange.step as? LiteralValue
|
||||
when(loopvar.datatype) {
|
||||
DataType.UBYTE -> {
|
||||
if(rangeFrom.type!=DataType.UBYTE) {
|
||||
if(rangeFrom.type!= DataType.UBYTE) {
|
||||
// attempt to translate the iterable into ubyte values
|
||||
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if(rangeFrom.type!=DataType.BYTE) {
|
||||
if(rangeFrom.type!= DataType.BYTE) {
|
||||
// attempt to translate the iterable into byte values
|
||||
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
}
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if(rangeFrom.type!=DataType.UWORD) {
|
||||
if(rangeFrom.type!= DataType.UWORD) {
|
||||
// attempt to translate the iterable into uword values
|
||||
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if(rangeFrom.type!=DataType.WORD) {
|
||||
if(rangeFrom.type!= DataType.WORD) {
|
||||
// attempt to translate the iterable into word values
|
||||
resultStmt.iterable = adjustRangeDt(rangeFrom, loopvar.datatype, rangeTo, stepLiteral, iterableRange)
|
||||
}
|
||||
@@ -591,8 +593,8 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
return resultStmt
|
||||
}
|
||||
|
||||
override fun process(literalValue: LiteralValue): LiteralValue {
|
||||
val litval = super.process(literalValue)
|
||||
override fun visit(literalValue: LiteralValue): LiteralValue {
|
||||
val litval = super.visit(literalValue)
|
||||
if(litval.isString) {
|
||||
// intern the string; move it into the heap
|
||||
if(litval.strvalue!!.length !in 1..255)
|
||||
@@ -614,7 +616,7 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
val typesInArray = array.mapNotNull { it.inferType(program) }.toSet()
|
||||
val arrayDt =
|
||||
when {
|
||||
array.any { it is AddressOf} -> DataType.ARRAY_UW
|
||||
array.any { it is AddressOf } -> DataType.ARRAY_UW
|
||||
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
|
||||
DataType.WORD in typesInArray -> DataType.ARRAY_W
|
||||
else -> {
|
||||
@@ -649,65 +651,65 @@ class ConstantFolding(private val program: Program) : IAstProcessor {
|
||||
return litval
|
||||
}
|
||||
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
super.process(assignment)
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
super.visit(assignment)
|
||||
val lv = assignment.value as? LiteralValue
|
||||
if(lv!=null) {
|
||||
// see if we can promote/convert a literal value to the required datatype
|
||||
when(assignment.singleTarget?.inferType(program, assignment)) {
|
||||
DataType.UWORD -> {
|
||||
// we can convert to UWORD: any UBYTE, BYTE/WORD that are >=0, FLOAT that's an integer 0..65535,
|
||||
if(lv.type==DataType.UBYTE)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
|
||||
else if(lv.type==DataType.BYTE && lv.bytevalue!!>=0)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
|
||||
else if(lv.type==DataType.WORD && lv.wordvalue!!>=0)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position=lv.position)
|
||||
else if(lv.type==DataType.FLOAT) {
|
||||
if(lv.type== DataType.UBYTE)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
||||
else if(lv.type== DataType.BYTE && lv.bytevalue!!>=0)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
||||
else if(lv.type== DataType.WORD && lv.wordvalue!!>=0)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = lv.asIntegerValue, position = lv.position)
|
||||
else if(lv.type== DataType.FLOAT) {
|
||||
val d = lv.floatvalue!!
|
||||
if(floor(d)==d && d>=0 && d<=65535)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue=floor(d).toInt(), position=lv.position)
|
||||
assignment.value = LiteralValue(DataType.UWORD, wordvalue = floor(d).toInt(), position = lv.position)
|
||||
}
|
||||
}
|
||||
DataType.UBYTE -> {
|
||||
// we can convert to UBYTE: UWORD <=255, BYTE >=0, FLOAT that's an integer 0..255,
|
||||
if(lv.type==DataType.UWORD && lv.wordvalue!! <= 255)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position=lv.position)
|
||||
else if(lv.type==DataType.BYTE && lv.bytevalue!! >=0)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position=lv.position)
|
||||
else if(lv.type==DataType.FLOAT) {
|
||||
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 255)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, lv.wordvalue.toShort(), position = lv.position)
|
||||
else if(lv.type== DataType.BYTE && lv.bytevalue!! >=0)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, lv.bytevalue.toShort(), position = lv.position)
|
||||
else if(lv.type== DataType.FLOAT) {
|
||||
val d = lv.floatvalue!!
|
||||
if(floor(d)==d && d >=0 && d<=255)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position=lv.position)
|
||||
assignment.value = LiteralValue(DataType.UBYTE, floor(d).toShort(), position = lv.position)
|
||||
}
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
// we can convert to BYTE: UWORD/UBYTE <= 127, FLOAT that's an integer 0..127
|
||||
if(lv.type==DataType.UWORD && lv.wordvalue!! <= 127)
|
||||
assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position=lv.position)
|
||||
else if(lv.type==DataType.UBYTE && lv.bytevalue!! <= 127)
|
||||
assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position=lv.position)
|
||||
else if(lv.type==DataType.FLOAT) {
|
||||
if(lv.type== DataType.UWORD && lv.wordvalue!! <= 127)
|
||||
assignment.value = LiteralValue(DataType.BYTE, lv.wordvalue.toShort(), position = lv.position)
|
||||
else if(lv.type== DataType.UBYTE && lv.bytevalue!! <= 127)
|
||||
assignment.value = LiteralValue(DataType.BYTE, lv.bytevalue, position = lv.position)
|
||||
else if(lv.type== DataType.FLOAT) {
|
||||
val d = lv.floatvalue!!
|
||||
if(floor(d)==d && d>=0 && d<=127)
|
||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position)
|
||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
// we can convert to WORD: any UBYTE/BYTE, UWORD <= 32767, FLOAT that's an integer -32768..32767,
|
||||
if(lv.type==DataType.UBYTE || lv.type==DataType.BYTE)
|
||||
assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.bytevalue!!.toInt(), position=lv.position)
|
||||
else if(lv.type==DataType.UWORD && lv.wordvalue!! <= 32767)
|
||||
assignment.value = LiteralValue(DataType.WORD, wordvalue=lv.wordvalue, position=lv.position)
|
||||
else if(lv.type==DataType.FLOAT) {
|
||||
if(lv.type== DataType.UBYTE || lv.type== DataType.BYTE)
|
||||
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.bytevalue!!.toInt(), position = lv.position)
|
||||
else if(lv.type== DataType.UWORD && lv.wordvalue!! <= 32767)
|
||||
assignment.value = LiteralValue(DataType.WORD, wordvalue = lv.wordvalue, position = lv.position)
|
||||
else if(lv.type== DataType.FLOAT) {
|
||||
val d = lv.floatvalue!!
|
||||
if(floor(d)==d && d>=-32768 && d<=32767)
|
||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position=lv.position)
|
||||
assignment.value = LiteralValue(DataType.BYTE, floor(d).toShort(), position = lv.position)
|
||||
}
|
||||
}
|
||||
DataType.FLOAT -> {
|
||||
if(lv.isNumeric)
|
||||
assignment.value = LiteralValue(DataType.FLOAT, floatvalue= lv.asNumericValue?.toDouble(), position=lv.position)
|
||||
assignment.value = LiteralValue(DataType.FLOAT, floatvalue = lv.asNumericValue?.toDouble(), position = lv.position)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
+7
-5
@@ -1,20 +1,22 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.statements.NopStatement
|
||||
import prog8.parser.ParsingFailedError
|
||||
|
||||
|
||||
internal fun Program.constantFold() {
|
||||
val optimizer = ConstantFolding(this)
|
||||
try {
|
||||
optimizer.process(this)
|
||||
optimizer.visit(this)
|
||||
} catch (ax: AstException) {
|
||||
optimizer.addError(ax)
|
||||
}
|
||||
|
||||
while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) {
|
||||
optimizer.optimizationsDone = 0
|
||||
optimizer.process(this)
|
||||
optimizer.visit(this)
|
||||
}
|
||||
|
||||
if(optimizer.errors.isNotEmpty()) {
|
||||
@@ -28,7 +30,7 @@ internal fun Program.constantFold() {
|
||||
|
||||
internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
||||
val optimizer = StatementOptimizer(this, optimizeInlining)
|
||||
optimizer.process(this)
|
||||
optimizer.visit(this)
|
||||
for(scope in optimizer.scopesToFlatten.reversed()) {
|
||||
val namescope = scope.parent as INameScope
|
||||
val idx = namescope.statements.indexOf(scope as IStatement)
|
||||
@@ -44,6 +46,6 @@ internal fun Program.optimizeStatements(optimizeInlining: Boolean): Int {
|
||||
|
||||
internal fun Program.simplifyExpressions() : Int {
|
||||
val optimizer = SimplifyExpressions(this)
|
||||
optimizer.process(this)
|
||||
optimizer.visit(this)
|
||||
return optimizer.optimizationsDone
|
||||
}
|
||||
+32
-33
@@ -1,6 +1,13 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.IntegerDatatypes
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.Assignment
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
|
||||
@@ -11,32 +18,24 @@ import kotlin.math.log2
|
||||
|
||||
*/
|
||||
|
||||
internal class SimplifyExpressions(private val program: Program) : IAstProcessor {
|
||||
internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor {
|
||||
var optimizationsDone: Int = 0
|
||||
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
if (assignment.aug_op != null)
|
||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||
return super.process(assignment)
|
||||
return super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun process(memread: DirectMemoryRead): IExpression {
|
||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
return super.process(addrOf.identifier)
|
||||
return super.process(memread)
|
||||
return super.visit(addrOf.identifier)
|
||||
return super.visit(memread)
|
||||
}
|
||||
|
||||
override fun process(memwrite: DirectMemoryWrite): IExpression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memwrite.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
return super.process(addrOf.identifier)
|
||||
return super.process(memwrite)
|
||||
}
|
||||
|
||||
override fun process(typecast: TypecastExpression): IExpression {
|
||||
override fun visit(typecast: TypecastExpression): IExpression {
|
||||
// remove redundant typecasts
|
||||
var tc = typecast
|
||||
while(true) {
|
||||
@@ -50,18 +49,18 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
|
||||
return tc.expression
|
||||
}
|
||||
}
|
||||
return super.process(tc)
|
||||
return super.visit(tc)
|
||||
}
|
||||
optimizationsDone++
|
||||
tc = expr
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(expr: PrefixExpression): IExpression {
|
||||
override fun visit(expr: PrefixExpression): IExpression {
|
||||
if (expr.operator == "+") {
|
||||
// +X --> X
|
||||
optimizationsDone++
|
||||
return expr.expression.process(this)
|
||||
return expr.expression.accept(this)
|
||||
} else if (expr.operator == "not") {
|
||||
(expr.expression as? BinaryExpression)?.let {
|
||||
// NOT (...) -> invert ...
|
||||
@@ -101,11 +100,11 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
|
||||
}
|
||||
}
|
||||
}
|
||||
return super.process(expr)
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
override fun process(expr: BinaryExpression): IExpression {
|
||||
super.process(expr)
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
super.visit(expr)
|
||||
val leftVal = expr.left.constValue(program)
|
||||
val rightVal = expr.right.constValue(program)
|
||||
val constTrue = LiteralValue.fromBoolean(true, expr.position)
|
||||
@@ -336,48 +335,48 @@ internal class SimplifyExpressions(private val program: Program) : IAstProcessor
|
||||
DataType.UBYTE -> {
|
||||
if (targetDt == DataType.BYTE) {
|
||||
if(value.bytevalue!! < 127)
|
||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
|
||||
}
|
||||
DataType.BYTE -> {
|
||||
if (targetDt == DataType.UBYTE) {
|
||||
if(value.bytevalue!! >= 0)
|
||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.bytevalue, position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.UWORD) {
|
||||
if(value.bytevalue!! >= 0)
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue=value.bytevalue.toInt(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue.toInt(), position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.WORD) return Pair(true, LiteralValue(targetDt, wordvalue=value.bytevalue!!.toInt(), position=value.position))
|
||||
else if (targetDt == DataType.WORD) return Pair(true, LiteralValue(targetDt, wordvalue = value.bytevalue!!.toInt(), position = value.position))
|
||||
}
|
||||
DataType.UWORD -> {
|
||||
if (targetDt == DataType.UBYTE) {
|
||||
if(value.wordvalue!! <= 255)
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.BYTE) {
|
||||
if(value.wordvalue!! <= 127)
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.WORD) {
|
||||
if(value.wordvalue!! <= 32767)
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue=value.wordvalue, position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, wordvalue = value.wordvalue, position = value.position))
|
||||
}
|
||||
}
|
||||
DataType.WORD -> {
|
||||
if (targetDt == DataType.UBYTE) {
|
||||
if(value.wordvalue!! in 0..255)
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.BYTE) {
|
||||
if(value.wordvalue!! in -128..127)
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
||||
}
|
||||
else if (targetDt == DataType.UWORD) {
|
||||
if(value.wordvalue!! >= 0)
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position=value.position))
|
||||
return Pair(true, LiteralValue(targetDt, value.wordvalue.toShort(), position = value.position))
|
||||
}
|
||||
}
|
||||
else -> {}
|
||||
+64
-37
@@ -1,6 +1,10 @@
|
||||
package prog8.optimizing
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import kotlin.math.floor
|
||||
@@ -11,10 +15,11 @@ import kotlin.math.floor
|
||||
todo analyse for unreachable code and remove that (f.i. code after goto or return that has no label so can never be jumped to) + print warning about this
|
||||
*/
|
||||
|
||||
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstProcessor {
|
||||
internal class StatementOptimizer(private val program: Program, private val optimizeInlining: Boolean) : IAstModifyingVisitor {
|
||||
var optimizationsDone: Int = 0
|
||||
private set
|
||||
var scopesToFlatten = mutableListOf<INameScope>()
|
||||
val nopStatements = mutableListOf<NopStatement>()
|
||||
|
||||
private val pureBuiltinFunctions = BuiltinFunctions.filter { it.value.pure }
|
||||
private val callgraph = CallGraph(program)
|
||||
@@ -23,12 +28,15 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
private var generatedLabelSequenceNumber = 0
|
||||
}
|
||||
|
||||
override fun process(program: Program) {
|
||||
override fun visit(program: Program) {
|
||||
removeUnusedCode(callgraph)
|
||||
if(optimizeInlining) {
|
||||
inlineSubroutines(callgraph)
|
||||
}
|
||||
super.process(program)
|
||||
super.visit(program)
|
||||
|
||||
// at the end, remove the encountered NOP statements
|
||||
this.nopStatements.forEach { it.definingScope().remove(it) }
|
||||
}
|
||||
|
||||
private fun inlineSubroutines(callgraph: CallGraph) {
|
||||
@@ -63,18 +71,23 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
val inlined = AnonymousScope(sub.statements.toMutableList(), caller.position)
|
||||
parent.statements[parent.statements.indexOf(caller)] = inlined
|
||||
// replace return statements in the inlined sub by a jump to the end of it
|
||||
var haveNewEndLabel = false
|
||||
var endLabelUsed = false
|
||||
var endlabel = inlined.statements.last() as? Label
|
||||
if(endlabel==null) {
|
||||
endlabel = makeLabel("_prog8_auto_sub_end", inlined.statements.last().position)
|
||||
inlined.statements.add(endlabel)
|
||||
endlabel.parent = inlined
|
||||
haveNewEndLabel = true
|
||||
}
|
||||
val returns = inlined.statements.withIndex().filter { iv -> iv.value is Return }.map { iv -> Pair(iv.index, iv.value as Return)}
|
||||
for(returnIdx in returns) {
|
||||
assert(returnIdx.second.values.isEmpty())
|
||||
val jump = Jump(null, IdentifierReference(listOf(endlabel.name), returnIdx.second.position), null, returnIdx.second.position)
|
||||
inlined.statements[returnIdx.first] = jump
|
||||
endLabelUsed = true
|
||||
}
|
||||
if(endLabelUsed && haveNewEndLabel)
|
||||
inlined.statements.add(endlabel)
|
||||
inlined.linkParents(caller.parent)
|
||||
sub.calledBy.remove(caller) // if there are no callers left, the sub will be removed automatically later
|
||||
optimizationsDone++
|
||||
@@ -127,7 +140,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
}
|
||||
}
|
||||
|
||||
override fun process(block: Block): IStatement {
|
||||
override fun visit(block: Block): IStatement {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars()) {
|
||||
optimizationsDone++
|
||||
@@ -142,11 +155,11 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(block)
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun process(subroutine: Subroutine): IStatement {
|
||||
super.process(subroutine)
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
super.visit(subroutine)
|
||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||
if(subroutine.asmAddress==null && !forceOutput) {
|
||||
if(subroutine.containsNoCodeNorVars()) {
|
||||
@@ -182,7 +195,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return subroutine
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||
if(decl.type!=VarDeclType.CONST)
|
||||
@@ -191,7 +204,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return NopStatement(decl.position) // remove unused variable
|
||||
}
|
||||
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
||||
@@ -219,7 +232,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return linesToRemove
|
||||
}
|
||||
|
||||
override fun process(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in pureBuiltinFunctions) {
|
||||
@@ -274,10 +287,10 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(functionCallStatement)
|
||||
return super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun process(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
||||
@@ -294,11 +307,11 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return constval
|
||||
}
|
||||
}
|
||||
return super.process(functionCall)
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun process(ifStatement: IfStatement): IStatement {
|
||||
super.process(ifStatement)
|
||||
override fun visit(ifStatement: IfStatement): IStatement {
|
||||
super.visit(ifStatement)
|
||||
|
||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
||||
optimizationsDone++
|
||||
@@ -331,8 +344,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
override fun process(forLoop: ForLoop): IStatement {
|
||||
super.process(forLoop)
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
super.visit(forLoop)
|
||||
if(forLoop.body.containsNoCodeNorVars()) {
|
||||
// remove empty for loop
|
||||
optimizationsDone++
|
||||
@@ -361,8 +374,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return forLoop
|
||||
}
|
||||
|
||||
override fun process(whileLoop: WhileLoop): IStatement {
|
||||
super.process(whileLoop)
|
||||
override fun visit(whileLoop: WhileLoop): IStatement {
|
||||
super.visit(whileLoop)
|
||||
val constvalue = whileLoop.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
@@ -387,8 +400,8 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return whileLoop
|
||||
}
|
||||
|
||||
override fun process(repeatLoop: RepeatLoop): IStatement {
|
||||
super.process(repeatLoop)
|
||||
override fun visit(repeatLoop: RepeatLoop): IStatement {
|
||||
super.visit(repeatLoop)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
return if(constvalue.asBooleanValue){
|
||||
@@ -417,32 +430,46 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
override fun visit(nopStatement: NopStatement): IStatement {
|
||||
this.nopStatements.add(nopStatement)
|
||||
return nopStatement
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
||||
val choices = whenStatement.choices.toList()
|
||||
for(choice in choices) {
|
||||
if(choice.statements.containsNoCodeNorVars())
|
||||
whenStatement.choices.remove(choice)
|
||||
}
|
||||
return super.visit(whenStatement)
|
||||
}
|
||||
|
||||
private fun hasContinueOrBreak(scope: INameScope): Boolean {
|
||||
|
||||
class Searcher:IAstProcessor
|
||||
class Searcher: IAstModifyingVisitor
|
||||
{
|
||||
var count=0
|
||||
|
||||
override fun process(breakStmt: Break): IStatement {
|
||||
override fun visit(breakStmt: Break): IStatement {
|
||||
count++
|
||||
return super.process(breakStmt)
|
||||
return super.visit(breakStmt)
|
||||
}
|
||||
|
||||
override fun process(contStmt: Continue): IStatement {
|
||||
override fun visit(contStmt: Continue): IStatement {
|
||||
count++
|
||||
return super.process(contStmt)
|
||||
return super.visit(contStmt)
|
||||
}
|
||||
}
|
||||
val s=Searcher()
|
||||
for(stmt in scope.statements) {
|
||||
stmt.process(s)
|
||||
stmt.accept(s)
|
||||
if(s.count>0)
|
||||
return true
|
||||
}
|
||||
return s.count > 0
|
||||
}
|
||||
|
||||
override fun process(jump: Jump): IStatement {
|
||||
override fun visit(jump: Jump): IStatement {
|
||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
||||
@@ -466,7 +493,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return jump
|
||||
}
|
||||
|
||||
override fun process(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
if(assignment.aug_op!=null)
|
||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||
|
||||
@@ -481,7 +508,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
if(bexpr!=null) {
|
||||
val cv = bexpr.right.constValue(program)?.asNumericValue?.toDouble()
|
||||
if(cv==null) {
|
||||
if(bexpr.operator=="+" && targetDt!=DataType.FLOAT) {
|
||||
if(bexpr.operator=="+" && targetDt!= DataType.FLOAT) {
|
||||
if (bexpr.left isSameAs bexpr.right && target isSameAs bexpr.left) {
|
||||
bexpr.operator = "*"
|
||||
bexpr.right = LiteralValue.optimalInteger(2, assignment.value.position)
|
||||
@@ -596,10 +623,10 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
}
|
||||
}
|
||||
|
||||
return super.process(assignment)
|
||||
return super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun process(scope: AnonymousScope): IStatement {
|
||||
override fun visit(scope: AnonymousScope): IStatement {
|
||||
val linesToRemove = deduplicateAssignments(scope.statements)
|
||||
if(linesToRemove.isNotEmpty()) {
|
||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
||||
@@ -609,17 +636,17 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
||||
}
|
||||
|
||||
return super.process(scope)
|
||||
return super.visit(scope)
|
||||
}
|
||||
|
||||
override fun process(label: Label): IStatement {
|
||||
override fun visit(label: Label): IStatement {
|
||||
// remove duplicate labels
|
||||
val stmts = label.definingScope().statements
|
||||
val startIdx = stmts.indexOf(label)
|
||||
if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label)
|
||||
return NopStatement(label.position)
|
||||
|
||||
return super.process(label)
|
||||
return super.visit(label)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,6 +2,12 @@ package prog8.parser
|
||||
|
||||
import org.antlr.v4.runtime.*
|
||||
import prog8.ast.*
|
||||
import prog8.ast.antlr.toAst
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.base.checkImportedValid
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.statements.DirectiveArg
|
||||
import java.io.InputStream
|
||||
import java.nio.file.Files
|
||||
import java.nio.file.Path
|
||||
@@ -40,13 +46,13 @@ internal fun importModule(program: Program, filePath: Path): Module {
|
||||
throw ParsingFailedError("No such file: $filePath")
|
||||
|
||||
val input = CharStreams.fromPath(filePath)
|
||||
return importModule(program, input, filePath, filePath.parent==null)
|
||||
return importModule(program, input, filePath, false)
|
||||
}
|
||||
|
||||
internal fun importLibraryModule(program: Program, name: String): Module? {
|
||||
val import = Directive("%import", listOf(
|
||||
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0 ,0))
|
||||
), Position("<<<implicit-import>>>", 0, 0 ,0))
|
||||
DirectiveArg("", name, 42, position = Position("<<<implicit-import>>>", 0, 0, 0))
|
||||
), Position("<<<implicit-import>>>", 0, 0, 0))
|
||||
return executeImportDirective(program, import, Paths.get(""))
|
||||
}
|
||||
|
||||
@@ -71,7 +77,7 @@ internal fun importModule(program: Program, stream: CharStream, modulePath: Path
|
||||
moduleAst.linkParents(program.namespace)
|
||||
program.modules.add(moduleAst)
|
||||
|
||||
// process additional imports
|
||||
// accept additional imports
|
||||
val lines = moduleAst.statements.toMutableList()
|
||||
lines.asSequence()
|
||||
.mapIndexed { i, it -> Pair(i, it) }
|
||||
|
||||
@@ -1,102 +0,0 @@
|
||||
package prog8.stackvm
|
||||
|
||||
import prog8.compiler.target.c64.Mflpt5
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import kotlin.math.abs
|
||||
|
||||
class Memory {
|
||||
private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255
|
||||
|
||||
fun getUByte(address: Int): Short {
|
||||
return mem[address]
|
||||
}
|
||||
|
||||
fun getSByte(address: Int): Short {
|
||||
val ubyte = getUByte(address)
|
||||
if(ubyte <= 127)
|
||||
return ubyte
|
||||
return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement
|
||||
}
|
||||
|
||||
fun setUByte(address: Int, value: Short) {
|
||||
if(value !in 0..255)
|
||||
throw VmExecutionException("ubyte value out of range")
|
||||
mem[address] = value
|
||||
}
|
||||
|
||||
fun setSByte(address: Int, value: Short) {
|
||||
if(value !in -128..127) throw VmExecutionException("byte value out of range")
|
||||
if(value>=0)
|
||||
mem[address] = value
|
||||
else
|
||||
mem[address] = ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement
|
||||
}
|
||||
|
||||
fun getUWord(address: Int): Int {
|
||||
return mem[address] + 256*mem[address+1]
|
||||
}
|
||||
|
||||
fun getSWord(address: Int): Int {
|
||||
val uword = getUWord(address)
|
||||
if(uword <= 32767)
|
||||
return uword
|
||||
return -((uword xor 65535)+1) // 2's complement
|
||||
}
|
||||
|
||||
fun setUWord(address: Int, value: Int) {
|
||||
if(value !in 0..65535)
|
||||
throw VmExecutionException("uword value out of range")
|
||||
mem[address] = value.and(255).toShort()
|
||||
mem[address+1] = (value / 256).toShort()
|
||||
}
|
||||
|
||||
fun setSWord(address: Int, value: Int) {
|
||||
if(value !in -32768..32767) throw VmExecutionException("word value out of range")
|
||||
if(value>=0)
|
||||
setUWord(address, value)
|
||||
else
|
||||
setUWord(address, (abs(value) xor 65535)+1) // 2's complement
|
||||
}
|
||||
|
||||
fun setFloat(address: Int, value: Double) {
|
||||
val mflpt5 = Mflpt5.fromNumber(value)
|
||||
mem[address] = mflpt5.b0
|
||||
mem[address+1] = mflpt5.b1
|
||||
mem[address+2] = mflpt5.b2
|
||||
mem[address+3] = mflpt5.b3
|
||||
mem[address+4] = mflpt5.b4
|
||||
}
|
||||
|
||||
fun getFloat(address: Int): Double {
|
||||
return Mflpt5(mem[address], mem[address + 1], mem[address + 2], mem[address + 3], mem[address + 4]).toDouble()
|
||||
}
|
||||
|
||||
fun setString(address: Int, str: String) {
|
||||
// lowercase PETSCII
|
||||
val petscii = Petscii.encodePetscii(str, true)
|
||||
var addr = address
|
||||
for (c in petscii) mem[addr++] = c
|
||||
mem[addr] = 0
|
||||
}
|
||||
|
||||
fun getString(strAddress: Int): String {
|
||||
// lowercase PETSCII
|
||||
val petscii = mutableListOf<Short>()
|
||||
var addr = strAddress
|
||||
while(true) {
|
||||
val byte = mem[addr++]
|
||||
if(byte==0.toShort()) break
|
||||
petscii.add(byte)
|
||||
}
|
||||
return Petscii.decodePetscii(petscii, true)
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
for(i in 0..65535) mem[i]=0
|
||||
}
|
||||
|
||||
fun copy(from: Int, to: Int, numbytes: Int) {
|
||||
for(i in 0 until numbytes)
|
||||
mem[to+i] = mem[from+i]
|
||||
}
|
||||
}
|
||||
@@ -1,188 +0,0 @@
|
||||
package prog8.stackvm
|
||||
|
||||
import prog8.compiler.target.c64.Charset
|
||||
import prog8.compiler.target.c64.Colors
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import java.awt.*
|
||||
import java.awt.event.KeyEvent
|
||||
import java.awt.event.KeyListener
|
||||
import java.awt.image.BufferedImage
|
||||
import javax.swing.JFrame
|
||||
import javax.swing.JPanel
|
||||
import javax.swing.Timer
|
||||
|
||||
|
||||
class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
|
||||
private val image = BufferedImage(SCREENWIDTH, SCREENHEIGHT, BufferedImage.TYPE_INT_ARGB)
|
||||
private val g2d = image.graphics as Graphics2D
|
||||
private var cursorX: Int=0
|
||||
private var cursorY: Int=0
|
||||
|
||||
init {
|
||||
val size = Dimension(image.width * SCALING, image.height * SCALING)
|
||||
minimumSize = size
|
||||
maximumSize = size
|
||||
preferredSize = size
|
||||
clearScreen(6)
|
||||
isFocusable = true
|
||||
requestFocusInWindow()
|
||||
addKeyListener(this)
|
||||
}
|
||||
|
||||
override fun keyTyped(p0: KeyEvent?) {}
|
||||
|
||||
override fun keyPressed(p0: KeyEvent?) {
|
||||
println("pressed: $p0.k")
|
||||
}
|
||||
|
||||
override fun keyReleased(p0: KeyEvent?) {
|
||||
println("released: $p0")
|
||||
}
|
||||
|
||||
override fun paint(graphics: Graphics?) {
|
||||
val g2d = graphics as Graphics2D?
|
||||
g2d!!.setRenderingHint(RenderingHints.KEY_ANTIALIASING, RenderingHints.VALUE_ANTIALIAS_OFF)
|
||||
g2d.setRenderingHint(RenderingHints.KEY_DITHERING, RenderingHints.VALUE_DITHER_DISABLE)
|
||||
g2d.setRenderingHint(RenderingHints.KEY_INTERPOLATION, RenderingHints.VALUE_INTERPOLATION_NEAREST_NEIGHBOR)
|
||||
g2d.drawImage(image, 0, 0, image.width * 3, image.height * 3, null)
|
||||
}
|
||||
|
||||
fun clearScreen(color: Short) {
|
||||
g2d.background = Colors.palette[color % Colors.palette.size]
|
||||
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
|
||||
cursorX = 0
|
||||
cursorY = 0
|
||||
}
|
||||
fun setPixel(x: Int, y: Int, color: Short) {
|
||||
image.setRGB(x, y, Colors.palette[color % Colors.palette.size].rgb)
|
||||
}
|
||||
fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) {
|
||||
g2d.color = Colors.palette[color % Colors.palette.size]
|
||||
g2d.drawLine(x1, y1, x2, y2)
|
||||
}
|
||||
fun printText(text: String, color: Short, lowercase: Boolean) {
|
||||
val lines = text.split('\n')
|
||||
for(line in lines.withIndex()) {
|
||||
printTextSingleLine(line.value, color, lowercase)
|
||||
if(line.index<lines.size-1) {
|
||||
cursorX=0
|
||||
cursorY++
|
||||
}
|
||||
}
|
||||
}
|
||||
private fun printTextSingleLine(text: String, color: Short, lowercase: Boolean) {
|
||||
for(clearx in cursorX until cursorX+text.length) {
|
||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||
}
|
||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||
setChar(cursorX, cursorY, sc, color)
|
||||
cursorX++
|
||||
if(cursorX>=(SCREENWIDTH/8)) {
|
||||
cursorY++
|
||||
cursorX=0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun printChar(char: Short) {
|
||||
if(char==13.toShort() || char==141.toShort()) {
|
||||
cursorX=0
|
||||
cursorY++
|
||||
} else {
|
||||
setChar(cursorX, cursorY, char, 1)
|
||||
cursorX++
|
||||
if (cursorX >= (SCREENWIDTH / 8)) {
|
||||
cursorY++
|
||||
cursorX = 0
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun setChar(x: Int, y: Int, screenCode: Short, color: Short) {
|
||||
g2d.clearRect(8*x, 8*y, 8, 8)
|
||||
val colorIdx = (color % Colors.palette.size).toShort()
|
||||
val coloredImage = Charset.getColoredChar(screenCode, colorIdx)
|
||||
g2d.drawImage(coloredImage, 8*x, 8*y , null)
|
||||
}
|
||||
|
||||
fun setCursorPos(x: Int, y: Int) {
|
||||
cursorX = x
|
||||
cursorY = y
|
||||
}
|
||||
|
||||
fun getCursorPos(): Pair<Int, Int> {
|
||||
return Pair(cursorX, cursorY)
|
||||
}
|
||||
|
||||
fun writeText(x: Int, y: Int, text: String, color: Short, lowercase: Boolean) {
|
||||
var xx=x
|
||||
for(clearx in xx until xx+text.length) {
|
||||
g2d.clearRect(8*clearx, 8*y, 8, 8)
|
||||
}
|
||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||
setChar(xx++, y, sc, color)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
companion object {
|
||||
const val SCREENWIDTH = 320
|
||||
const val SCREENHEIGHT = 200
|
||||
const val SCALING = 3
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class ScreenDialog : JFrame() {
|
||||
val canvas = BitmapScreenPanel()
|
||||
|
||||
init {
|
||||
val borderWidth = 16
|
||||
title = "StackVm graphics. Text I/O goes to console."
|
||||
layout = GridBagLayout()
|
||||
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
||||
isResizable = false
|
||||
|
||||
// the borders (top, left, right, bottom)
|
||||
val borderTop = JPanel().apply {
|
||||
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
val borderBottom = JPanel().apply {
|
||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
val borderLeft = JPanel().apply {
|
||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
val borderRight = JPanel().apply {
|
||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
var c = GridBagConstraints()
|
||||
c.gridx=0; c.gridy=1; c.gridwidth=3
|
||||
add(borderTop, c)
|
||||
c = GridBagConstraints()
|
||||
c.gridx=0; c.gridy=2
|
||||
add(borderLeft, c)
|
||||
c = GridBagConstraints()
|
||||
c.gridx=2; c.gridy=2
|
||||
add(borderRight, c)
|
||||
c = GridBagConstraints()
|
||||
c.gridx=0; c.gridy=3; c.gridwidth=3
|
||||
add(borderBottom, c)
|
||||
// the screen canvas(bitmap)
|
||||
c = GridBagConstraints()
|
||||
c.gridx = 1; c.gridy = 2
|
||||
add(canvas, c)
|
||||
|
||||
canvas.requestFocusInWindow()
|
||||
}
|
||||
|
||||
fun start() {
|
||||
val repaintTimer = Timer(1000 / 60) { repaint() }
|
||||
repaintTimer.start()
|
||||
}
|
||||
}
|
||||
@@ -1,6 +1,8 @@
|
||||
package prog8.compiler
|
||||
package prog8.vm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.pow
|
||||
@@ -174,7 +176,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
|
||||
DataType.UBYTE, DataType.UWORD -> {
|
||||
// storing a negative number in an unsigned one is done by storing the 2's complement instead
|
||||
val number = abs(result.toDouble().toInt())
|
||||
if(leftDt==DataType.UBYTE)
|
||||
if(leftDt== DataType.UBYTE)
|
||||
RuntimeValue(DataType.UBYTE, (number xor 255) + 1)
|
||||
else
|
||||
RuntimeValue(DataType.UBYTE, (number xor 65535) + 1)
|
||||
@@ -1,10 +1,18 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.compiler.RuntimeValueRange
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.initvarsSubName
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.statements.*
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.vm.RuntimeValueRange
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import java.awt.EventQueue
|
||||
import kotlin.NoSuchElementException
|
||||
import kotlin.concurrent.fixedRateTimer
|
||||
import kotlin.math.min
|
||||
|
||||
|
||||
class VmExecutionException(msg: String?) : Exception(msg)
|
||||
@@ -111,13 +119,19 @@ class RuntimeVariables {
|
||||
|
||||
|
||||
class AstVm(val program: Program) {
|
||||
val mem = Memory()
|
||||
|
||||
val mem = Memory(::memread, ::memwrite)
|
||||
val statusflags = StatusFlags()
|
||||
|
||||
private var dialog = ScreenDialog()
|
||||
private var dialog = ScreenDialog("AstVM")
|
||||
var instructionCounter = 0
|
||||
val bootTime = System.currentTimeMillis()
|
||||
var rtcOffset = bootTime
|
||||
|
||||
init {
|
||||
// observe the jiffyclock
|
||||
mem.observe(0xa0, 0xa1, 0xa2)
|
||||
|
||||
dialog.requestFocusInWindow()
|
||||
|
||||
EventQueue.invokeLater {
|
||||
@@ -125,12 +139,33 @@ class AstVm(val program: Program) {
|
||||
dialog.isVisible = true
|
||||
dialog.start()
|
||||
}
|
||||
|
||||
fixedRateTimer("60hz-irq", true, period=1000/60) {
|
||||
irq(this.scheduledExecutionTime())
|
||||
}
|
||||
}
|
||||
|
||||
fun memread(address: Int, value: Short): Short {
|
||||
// println("MEM READ $address -> $value")
|
||||
return value
|
||||
}
|
||||
|
||||
fun memwrite(address: Int, value: Short): Short {
|
||||
if(address==0xa0 || address==0xa1 || address==0xa2) {
|
||||
// a write to the jiffy clock, update the clock offset for the irq
|
||||
val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
|
||||
val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
|
||||
val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
|
||||
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo
|
||||
rtcOffset = bootTime - (jiffies*1000/60)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fun run() {
|
||||
try {
|
||||
val init = VariablesCreator(runtimeVariables, program.heap)
|
||||
init.process(program)
|
||||
init.visit(program)
|
||||
|
||||
// initialize all global variables
|
||||
for (m in program.modules) {
|
||||
@@ -188,6 +223,18 @@ class AstVm(val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun irq(timeStamp: Long) {
|
||||
// 60hz IRQ handling
|
||||
if(statusflags.irqd)
|
||||
return // interrupt is disabled
|
||||
|
||||
val jiffies = min((timeStamp-rtcOffset)*60/1000, 24*3600*60-1)
|
||||
// update the C-64 60hz jiffy clock in the ZP addresses:
|
||||
mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort())
|
||||
mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort())
|
||||
mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort())
|
||||
}
|
||||
|
||||
private val runtimeVariables = RuntimeVariables()
|
||||
private val functions = BuiltinFunctions()
|
||||
private val evalCtx = EvalContext(program, mem, statusflags, runtimeVariables, functions, ::executeSubroutine)
|
||||
@@ -244,7 +291,7 @@ class AstVm(val program: Program) {
|
||||
|
||||
private fun executeStatement(sub: INameScope, stmt: IStatement) {
|
||||
instructionCounter++
|
||||
if (instructionCounter % 100 == 0)
|
||||
if (instructionCounter % 200 == 0)
|
||||
Thread.sleep(1)
|
||||
when (stmt) {
|
||||
is NopStatement, is Label, is Subroutine -> {
|
||||
@@ -395,6 +442,23 @@ class AstVm(val program: Program) {
|
||||
}
|
||||
} while (!condition.asBoolean)
|
||||
}
|
||||
is WhenStatement -> {
|
||||
val condition=evaluate(stmt.condition, evalCtx)
|
||||
for(choice in stmt.choices) {
|
||||
if(choice.value==null) {
|
||||
// the 'else' choice
|
||||
executeAnonymousScope(choice.statements)
|
||||
break
|
||||
} else {
|
||||
val value = choice.value.constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
|
||||
val rtval = RuntimeValue.from(value, evalCtx.program.heap)
|
||||
if(condition==rtval) {
|
||||
executeAnonymousScope(choice.statements)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
TODO("implement $stmt")
|
||||
}
|
||||
@@ -509,12 +573,18 @@ class AstVm(val program: Program) {
|
||||
"c64scr.print_ub" -> {
|
||||
dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
|
||||
}
|
||||
"c64scr.print_ub0" -> {
|
||||
dialog.canvas.printText("%03d".format(args[0].byteval!!), 1, true)
|
||||
}
|
||||
"c64scr.print_b" -> {
|
||||
dialog.canvas.printText(args[0].byteval!!.toString(), 1, true)
|
||||
}
|
||||
"c64scr.print_uw" -> {
|
||||
dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
|
||||
}
|
||||
"c64scr.print_uw0" -> {
|
||||
dialog.canvas.printText("%05d".format(args[0].wordval!!), 1, true)
|
||||
}
|
||||
"c64scr.print_w" -> {
|
||||
dialog.canvas.printText(args[0].wordval!!.toString(), 1, true)
|
||||
}
|
||||
+4
-4
@@ -1,7 +1,7 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.DataType
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.vm.RuntimeValue
|
||||
import java.lang.Math.toDegrees
|
||||
import java.lang.Math.toRadians
|
||||
import java.util.*
|
||||
@@ -160,7 +160,7 @@ class BuiltinFunctions {
|
||||
null
|
||||
}
|
||||
"mkword" -> {
|
||||
val result = (args[0].integerValue() shl 8) or args[1].integerValue()
|
||||
val result = (args[1].integerValue() shl 8) or args[0].integerValue()
|
||||
RuntimeValue(DataType.UWORD, result)
|
||||
}
|
||||
"set_carry" -> {
|
||||
@@ -1,4 +1,4 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.INameScope
|
||||
import java.util.*
|
||||
+19
-14
@@ -1,8 +1,16 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.compiler.RuntimeValueRange
|
||||
import prog8.ast.base.ArrayElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||
import prog8.ast.statements.Label
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.vm.RuntimeValueRange
|
||||
import kotlin.math.abs
|
||||
|
||||
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
|
||||
@@ -24,7 +32,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
"~" -> evaluate(expr.expression, ctx).inv()
|
||||
"not" -> evaluate(expr.expression, ctx).not()
|
||||
// unary '+' should have been optimized away
|
||||
else -> TODO("prefixexpr ${expr.operator}")
|
||||
else -> throw VmExecutionException("unsupported prefix operator "+expr.operator)
|
||||
}
|
||||
}
|
||||
is BinaryExpression -> {
|
||||
@@ -59,7 +67,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
"and" -> left.and(right)
|
||||
"or" -> left.or(right)
|
||||
"xor" -> left.xor(right)
|
||||
else -> TODO("binexpression operator ${expr.operator}")
|
||||
else -> throw VmExecutionException("unsupported operator "+expr.operator)
|
||||
}
|
||||
}
|
||||
is ArrayIndexedExpression -> {
|
||||
@@ -80,9 +88,6 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
val address = evaluate(expr.addressExpression, ctx).wordval!!
|
||||
return RuntimeValue(DataType.UBYTE, ctx.mem.getUByte(address))
|
||||
}
|
||||
is DirectMemoryWrite -> {
|
||||
TODO("memorywrite $expr")
|
||||
}
|
||||
is RegisterExpr -> return ctx.runtimeVars.get(ctx.program.namespace, expr.register.name)
|
||||
is IdentifierReference -> {
|
||||
val scope = expr.definingScope()
|
||||
@@ -98,13 +103,13 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
DataType.UWORD -> RuntimeValue(DataType.UWORD, ctx.mem.getUWord(address))
|
||||
DataType.WORD -> RuntimeValue(DataType.WORD, ctx.mem.getSWord(address))
|
||||
DataType.FLOAT -> RuntimeValue(DataType.FLOAT, ctx.mem.getFloat(address))
|
||||
DataType.STR -> RuntimeValue(DataType.STR, str=ctx.mem.getString(address))
|
||||
DataType.STR_S -> RuntimeValue(DataType.STR_S, str=ctx.mem.getScreencodeString(address))
|
||||
else -> TODO("memvar $variable")
|
||||
DataType.STR -> RuntimeValue(DataType.STR, str = ctx.mem.getString(address))
|
||||
DataType.STR_S -> RuntimeValue(DataType.STR_S, str = ctx.mem.getScreencodeString(address))
|
||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
||||
}
|
||||
}
|
||||
} else
|
||||
TODO("weird ref $variable")
|
||||
throw VmExecutionException("weird identifier reference $variable")
|
||||
}
|
||||
is FunctionCall -> {
|
||||
val sub = expr.target.targetStatement(ctx.program.namespace)
|
||||
@@ -122,7 +127,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
result
|
||||
}
|
||||
else -> {
|
||||
TODO("call expr function ${expr.target}")
|
||||
throw VmExecutionException("unimplemented function call target $sub")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -148,7 +153,7 @@ fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
return RuntimeValueRange(expr.inferType(ctx.program)!!, range)
|
||||
}
|
||||
else -> {
|
||||
TODO("implement eval $expr")
|
||||
throw VmExecutionException("unimplemented expression node $expr")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,39 +1,60 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.compiler.target.c64.Mflpt5
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
import kotlin.math.abs
|
||||
|
||||
class Memory {
|
||||
class Memory(private val readObserver: (address: Int, value: Short) -> Short,
|
||||
private val writeObserver: (address: Int, value: Short) -> Short)
|
||||
{
|
||||
|
||||
private val mem = ShortArray(65536) // shorts because byte is signed and we store values 0..255
|
||||
private val observed = BooleanArray(65536) // what addresses are observed
|
||||
|
||||
|
||||
fun observe(vararg address: Int) {
|
||||
address.forEach { observed[it]=true }
|
||||
}
|
||||
|
||||
fun getUByte(address: Int): Short {
|
||||
return if(observed[address]) readObserver(address, mem[address])
|
||||
else mem[address]
|
||||
}
|
||||
|
||||
fun getUByte_DMA(address: Int): Short {
|
||||
return mem[address]
|
||||
}
|
||||
|
||||
fun getSByte(address: Int): Short {
|
||||
val ubyte = getUByte(address)
|
||||
if(ubyte <= 127)
|
||||
return ubyte
|
||||
return (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement
|
||||
return if(ubyte <= 127) ubyte
|
||||
else (-((ubyte.toInt() xor 255)+1)).toShort() // 2's complement
|
||||
}
|
||||
|
||||
fun setUByte(address: Int, value: Short) {
|
||||
if(value !in 0..255)
|
||||
throw VmExecutionException("ubyte value out of range")
|
||||
throw VmExecutionException("ubyte value out of range $value")
|
||||
mem[address] =
|
||||
if(observed[address]) writeObserver(address, value)
|
||||
else value
|
||||
}
|
||||
|
||||
fun setUByte_DMA(address: Int, value: Short) {
|
||||
if(value !in 0..255)
|
||||
throw VmExecutionException("ubyte value out of range $value")
|
||||
mem[address] = value
|
||||
}
|
||||
|
||||
fun setSByte(address: Int, value: Short) {
|
||||
if(value !in -128..127) throw VmExecutionException("byte value out of range")
|
||||
if(value>=0)
|
||||
mem[address] = value
|
||||
else
|
||||
mem[address] = ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement
|
||||
if(value !in -128..127) throw VmExecutionException("byte value out of range $value")
|
||||
val ubyte =
|
||||
if(value>=0) value
|
||||
else ((abs(value.toInt()) xor 255)+1).toShort() // 2's complement
|
||||
setUByte(address, ubyte)
|
||||
}
|
||||
|
||||
fun getUWord(address: Int): Int {
|
||||
return mem[address] + 256*mem[address+1]
|
||||
return getUByte(address) + 256*getUByte(address+1)
|
||||
}
|
||||
|
||||
fun getSWord(address: Int): Int {
|
||||
@@ -45,13 +66,13 @@ class Memory {
|
||||
|
||||
fun setUWord(address: Int, value: Int) {
|
||||
if(value !in 0..65535)
|
||||
throw VmExecutionException("uword value out of range")
|
||||
mem[address] = value.and(255).toShort()
|
||||
mem[address+1] = (value / 256).toShort()
|
||||
throw VmExecutionException("uword value out of range $value")
|
||||
setUByte(address, value.and(255).toShort())
|
||||
setUByte(address+1, (value / 256).toShort())
|
||||
}
|
||||
|
||||
fun setSWord(address: Int, value: Int) {
|
||||
if(value !in -32768..32767) throw VmExecutionException("word value out of range")
|
||||
if(value !in -32768..32767) throw VmExecutionException("word value out of range $value")
|
||||
if(value>=0)
|
||||
setUWord(address, value)
|
||||
else
|
||||
@@ -60,23 +81,24 @@ class Memory {
|
||||
|
||||
fun setFloat(address: Int, value: Double) {
|
||||
val mflpt5 = Mflpt5.fromNumber(value)
|
||||
mem[address] = mflpt5.b0
|
||||
mem[address+1] = mflpt5.b1
|
||||
mem[address+2] = mflpt5.b2
|
||||
mem[address+3] = mflpt5.b3
|
||||
mem[address+4] = mflpt5.b4
|
||||
setUByte(address, mflpt5.b0)
|
||||
setUByte(address+1, mflpt5.b1)
|
||||
setUByte(address+2, mflpt5.b2)
|
||||
setUByte(address+3, mflpt5.b3)
|
||||
setUByte(address+4, mflpt5.b4)
|
||||
}
|
||||
|
||||
fun getFloat(address: Int): Double {
|
||||
return Mflpt5(mem[address], mem[address + 1], mem[address + 2], mem[address + 3], mem[address + 4]).toDouble()
|
||||
return Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
|
||||
getUByte(address + 3), getUByte(address + 4)).toDouble()
|
||||
}
|
||||
|
||||
fun setString(address: Int, str: String) {
|
||||
// lowercase PETSCII
|
||||
val petscii = Petscii.encodePetscii(str, true)
|
||||
var addr = address
|
||||
for (c in petscii) mem[addr++] = c
|
||||
mem[addr] = 0
|
||||
for (c in petscii) setUByte(addr++, c)
|
||||
setUByte(addr, 0)
|
||||
}
|
||||
|
||||
fun getString(strAddress: Int): String {
|
||||
@@ -84,7 +106,7 @@ class Memory {
|
||||
val petscii = mutableListOf<Short>()
|
||||
var addr = strAddress
|
||||
while(true) {
|
||||
val byte = mem[addr++]
|
||||
val byte = getUByte(addr++)
|
||||
if(byte==0.toShort()) break
|
||||
petscii.add(byte)
|
||||
}
|
||||
@@ -92,12 +114,12 @@ class Memory {
|
||||
}
|
||||
|
||||
fun clear() {
|
||||
for(i in 0..65535) mem[i]=0
|
||||
for(i in 0..65535) setUByte(i, 0)
|
||||
}
|
||||
|
||||
fun copy(from: Int, to: Int, numbytes: Int) {
|
||||
for(i in 0 until numbytes)
|
||||
mem[to+i] = mem[from+i]
|
||||
setUByte(to+i, getUByte(from+i))
|
||||
}
|
||||
|
||||
fun getScreencodeString(strAddress: Int): String? {
|
||||
@@ -105,7 +127,7 @@ class Memory {
|
||||
val screencodes = mutableListOf<Short>()
|
||||
var addr = strAddress
|
||||
while(true) {
|
||||
val byte = mem[addr++]
|
||||
val byte = getUByte(addr++)
|
||||
if(byte==0.toShort()) break
|
||||
screencodes.add(byte)
|
||||
}
|
||||
@@ -116,7 +138,7 @@ class Memory {
|
||||
// lowercase screencodes
|
||||
val screencodes = Petscii.encodeScreencode(str, true)
|
||||
var addr = address
|
||||
for (c in screencodes) mem[addr++] = c
|
||||
mem[addr] = 0
|
||||
for (c in screencodes) setUByte(addr++, c)
|
||||
setUByte(addr, 0)
|
||||
}
|
||||
}
|
||||
+5
-6
@@ -1,4 +1,4 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.compiler.target.c64.Charset
|
||||
import prog8.compiler.target.c64.Colors
|
||||
@@ -79,7 +79,7 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
for(sc in Petscii.encodeScreencode(text, lowercase)) {
|
||||
setChar(cursorX, cursorY, sc, color)
|
||||
cursorX++
|
||||
if(cursorX>=(SCREENWIDTH/8)) {
|
||||
if(cursorX>=(SCREENWIDTH /8)) {
|
||||
cursorY++
|
||||
cursorX=0
|
||||
}
|
||||
@@ -138,23 +138,22 @@ class BitmapScreenPanel : KeyListener, JPanel() {
|
||||
}
|
||||
|
||||
|
||||
class ScreenDialog : JFrame() {
|
||||
class ScreenDialog(title: String) : JFrame(title) {
|
||||
val canvas = BitmapScreenPanel()
|
||||
|
||||
init {
|
||||
val borderWidth = 16
|
||||
title = "AstVm graphics. Text I/O goes to console."
|
||||
layout = GridBagLayout()
|
||||
defaultCloseOperation = JFrame.EXIT_ON_CLOSE
|
||||
isResizable = false
|
||||
|
||||
// the borders (top, left, right, bottom)
|
||||
val borderTop = JPanel().apply {
|
||||
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
preferredSize = Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
val borderBottom = JPanel().apply {
|
||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH+2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
||||
background = Colors.palette[14]
|
||||
}
|
||||
val borderLeft = JPanel().apply {
|
||||
+16
-32
@@ -1,12 +1,16 @@
|
||||
package prog8.astvm
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.vm.RuntimeValue
|
||||
|
||||
class VariablesCreator(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstProcessor {
|
||||
class VariablesCreator(private val runtimeVariables: RuntimeVariables, private val heap: HeapValues) : IAstModifyingVisitor {
|
||||
|
||||
override fun process(program: Program) {
|
||||
override fun visit(program: Program) {
|
||||
// define the three registers as global variables
|
||||
runtimeVariables.define(program.namespace, Register.A.name, RuntimeValue(DataType.UBYTE, 0))
|
||||
runtimeVariables.define(program.namespace, Register.X.name, RuntimeValue(DataType.UBYTE, 255))
|
||||
@@ -23,51 +27,31 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
||||
program.namespace.statements.add(vdX)
|
||||
program.namespace.statements.add(vdY)
|
||||
|
||||
super.process(program)
|
||||
super.visit(program)
|
||||
}
|
||||
|
||||
override fun process(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
when(decl.type) {
|
||||
// we can assume the value in the vardecl already has been converted into a constant LiteralValue here.
|
||||
VarDeclType.VAR -> {
|
||||
val value = when (decl.datatype) {
|
||||
in NumericDatatypes -> {
|
||||
if(decl.value !is LiteralValue) {
|
||||
TODO("evaluate vardecl expression $decl")
|
||||
//RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue())
|
||||
} else {
|
||||
RuntimeValue.from(decl.value as LiteralValue, heap)
|
||||
}
|
||||
}
|
||||
in StringDatatypes -> {
|
||||
RuntimeValue.from(decl.value as LiteralValue, heap)
|
||||
}
|
||||
in ArrayDatatypes -> {
|
||||
RuntimeValue.from(decl.value as LiteralValue, heap)
|
||||
}
|
||||
else -> throw VmExecutionException("weird type ${decl.datatype}")
|
||||
}
|
||||
val value = RuntimeValue.from(decl.value as LiteralValue, heap)
|
||||
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
||||
}
|
||||
VarDeclType.MEMORY -> {
|
||||
if(decl.value !is LiteralValue) {
|
||||
TODO("evaluate vardecl expression $decl")
|
||||
//RuntimeValue(decl.datatype, num = evaluate(decl.value!!, program, runtimeVariables, executeSubroutine).numericValue())
|
||||
} else {
|
||||
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!)
|
||||
}
|
||||
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as LiteralValue).asIntegerValue!!)
|
||||
}
|
||||
VarDeclType.CONST -> {
|
||||
// consts should have been const-folded away
|
||||
}
|
||||
}
|
||||
return super.process(decl)
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
// override fun process(assignment: Assignment): IStatement {
|
||||
// override fun accept(assignment: Assignment): IStatement {
|
||||
// if(assignment is VariableInitializationAssignment) {
|
||||
// println("INIT VAR $assignment")
|
||||
// }
|
||||
// return super.process(assignment)
|
||||
// return super.accept(assignment)
|
||||
// }
|
||||
|
||||
}
|
||||
@@ -1,6 +1,7 @@
|
||||
package prog8
|
||||
package prog8.vm.stackvm
|
||||
|
||||
import prog8.stackvm.*
|
||||
import prog8.printSoftwareHeader
|
||||
import prog8.vm.astvm.ScreenDialog
|
||||
import java.awt.EventQueue
|
||||
import javax.swing.Timer
|
||||
import kotlin.system.exitProcess
|
||||
@@ -19,7 +20,7 @@ fun stackVmMain(args: Array<String>) {
|
||||
|
||||
val program = Program.load(args.first())
|
||||
val vm = StackVm(traceOutputFile = null)
|
||||
val dialog = ScreenDialog()
|
||||
val dialog = ScreenDialog("StackVM")
|
||||
vm.load(program, dialog.canvas)
|
||||
EventQueue.invokeLater {
|
||||
dialog.pack()
|
||||
@@ -1,7 +1,10 @@
|
||||
package prog8.stackvm
|
||||
package prog8.vm.stackvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.antlr.unescape
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.AddressOf
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.IntegerOrAddressOf
|
||||
import prog8.compiler.intermediate.*
|
||||
@@ -89,7 +92,7 @@ class Program (val name: String,
|
||||
}
|
||||
heapvalues.sortedBy { it.first }.forEach {
|
||||
when(it.second) {
|
||||
DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length-1), Position("<stackvmsource>", 0, 0, 0)))
|
||||
DataType.STR, DataType.STR_S -> heap.addString(it.second, unescape(it.third.substring(1, it.third.length - 1), Position("<stackvmsource>", 0, 0, 0)))
|
||||
DataType.ARRAY_UB, DataType.ARRAY_B,
|
||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||
val numbers = it.third.substring(1, it.third.length-1).split(',')
|
||||
@@ -98,8 +101,8 @@ class Program (val name: String,
|
||||
if(num.startsWith("&")) {
|
||||
// it's AddressOf
|
||||
val scopedname = num.substring(1)
|
||||
val iref = IdentifierReference(scopedname.split('.'), Position("<intermediate>", 0,0,0))
|
||||
val addrOf = AddressOf(iref, Position("<intermediate>", 0,0,0))
|
||||
val iref = IdentifierReference(scopedname.split('.'), Position("<intermediate>", 0, 0, 0))
|
||||
val addrOf = AddressOf(iref, Position("<intermediate>", 0, 0, 0))
|
||||
addrOf.scopedname=scopedname
|
||||
IntegerOrAddressOf(null, addrOf)
|
||||
} else {
|
||||
@@ -1,7 +1,13 @@
|
||||
package prog8.stackvm
|
||||
package prog8.vm.stackvm
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.IterableDatatypes
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.base.Register
|
||||
import prog8.ast.base.initvarsSubName
|
||||
import prog8.vm.astvm.BitmapScreenPanel
|
||||
import prog8.vm.astvm.Memory
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.IntegerOrAddressOf
|
||||
import prog8.compiler.intermediate.Instruction
|
||||
@@ -85,7 +91,7 @@ enum class Syscall(val callNr: Short) {
|
||||
|
||||
// vm intercepts of system routines:
|
||||
SYSCALLSTUB(200),
|
||||
SYSASM_c64scr_PLOT(201),
|
||||
SYSASM_c64scr_plot(201),
|
||||
SYSASM_c64scr_print(202),
|
||||
SYSASM_c64scr_print_ub0(203),
|
||||
SYSASM_c64scr_print_ub(204),
|
||||
@@ -137,7 +143,7 @@ class MyStack<T> : Stack<T>() {
|
||||
|
||||
|
||||
class StackVm(private var traceOutputFile: String?) {
|
||||
val mem = Memory()
|
||||
val mem = Memory(::memread, ::memwrite)
|
||||
var P_carry: Boolean = false
|
||||
private set
|
||||
var P_zero: Boolean = true
|
||||
@@ -161,12 +167,29 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
private var canvas: BitmapScreenPanel? = null
|
||||
private val rnd = Random()
|
||||
private val bootTime = System.currentTimeMillis()
|
||||
private var rtcOffset = bootTime
|
||||
private var currentInstructionPtr: Int = -1
|
||||
private var irqStartInstructionPtr: Int = -1
|
||||
private var registerSaveX: RuntimeValue = RuntimeValue(DataType.UBYTE, 0)
|
||||
var sourceLine: String = ""
|
||||
private set
|
||||
|
||||
fun memread(address: Int, value: Short): Short {
|
||||
//println("MEM READ $address -> $value")
|
||||
return value
|
||||
}
|
||||
|
||||
fun memwrite(address: Int, value: Short): Short {
|
||||
if(address==0xa0 || address==0xa1 || address==0xa2) {
|
||||
// a write to the jiffy clock, update the clock offset for the irq
|
||||
val time_hi = if(address==0xa0) value else mem.getUByte_DMA(0xa0)
|
||||
val time_mid = if(address==0xa1) value else mem.getUByte_DMA(0xa1)
|
||||
val time_lo = if(address==0xa2) value else mem.getUByte_DMA(0xa2)
|
||||
val jiffies = (time_hi.toInt() shl 16) + (time_mid.toInt() shl 8) + time_lo
|
||||
rtcOffset = bootTime - (jiffies*1000/60)
|
||||
}
|
||||
return value
|
||||
}
|
||||
|
||||
fun load(program: Program, canvas: BitmapScreenPanel?) {
|
||||
this.program = program.program + Instruction(Opcode.RETURN) // append a RETURN for use in the IRQ handler
|
||||
@@ -241,6 +264,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
|
||||
private fun initMemory(memory: Map<Int, List<RuntimeValue>>) {
|
||||
mem.clear()
|
||||
|
||||
for (meminit in memory) {
|
||||
var address = meminit.key
|
||||
for (value in meminit.value) {
|
||||
@@ -274,6 +298,9 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// observe the jiffyclock
|
||||
mem.observe(0xa0, 0xa1, 0xa2)
|
||||
}
|
||||
|
||||
private fun checkDt(value: RuntimeValue?, vararg expected: DataType) {
|
||||
@@ -349,11 +376,21 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val value = evalstack.pop()
|
||||
checkDt(value, DataType.FLOAT)
|
||||
}
|
||||
Opcode.DUP_B -> {
|
||||
val value = evalstack.peek()
|
||||
checkDt(value, DataType.BYTE, DataType.UBYTE)
|
||||
evalstack.push(value)
|
||||
}
|
||||
Opcode.DUP_W -> {
|
||||
val value = evalstack.peek()
|
||||
checkDt(value, DataType.WORD, DataType.UWORD)
|
||||
evalstack.push(value)
|
||||
}
|
||||
Opcode.POP_MEM_BYTE -> {
|
||||
val value = evalstack.pop()
|
||||
checkDt(value, DataType.BYTE, DataType.UBYTE)
|
||||
val address = ins.arg!!.integerValue()
|
||||
if(value.type==DataType.BYTE)
|
||||
if(value.type== DataType.BYTE)
|
||||
mem.setSByte(address, value.integerValue().toShort())
|
||||
else
|
||||
mem.setUByte(address, value.integerValue().toShort())
|
||||
@@ -363,7 +400,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val value = evalstack.pop()
|
||||
checkDt(value, DataType.WORD, DataType.UWORD)
|
||||
val address = ins.arg!!.integerValue()
|
||||
if(value.type==DataType.WORD)
|
||||
if(value.type== DataType.WORD)
|
||||
mem.setSWord(address, value.integerValue())
|
||||
else
|
||||
mem.setUWord(address, value.integerValue())
|
||||
@@ -1650,7 +1687,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
} else {
|
||||
// normal variable
|
||||
val variable = getVar(ins.callLabel!!)
|
||||
if(variable.type==DataType.UWORD) {
|
||||
if(variable.type== DataType.UWORD) {
|
||||
// assume the variable is a pointer (address) and get the word value from that memory location
|
||||
RuntimeValue(DataType.UWORD, mem.getUWord(variable.integerValue()))
|
||||
} else {
|
||||
@@ -1685,7 +1722,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
if(ins.callLabel in memoryPointers) {
|
||||
val variable = memoryPointers.getValue(ins.callLabel!!)
|
||||
val address = variable.first + index*5
|
||||
if(variable.second==DataType.ARRAY_F)
|
||||
if(variable.second== DataType.ARRAY_F)
|
||||
RuntimeValue(DataType.FLOAT, mem.getFloat(address))
|
||||
else
|
||||
throw VmExecutionException("not a proper arraysize var with float elements")
|
||||
@@ -1714,13 +1751,13 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val memloc = memoryPointers[varname]
|
||||
if(memloc!=null) {
|
||||
// variable is the name of a pointer, write the byte value to that memory location
|
||||
if(value.type==DataType.UBYTE) {
|
||||
if(memloc.second!=DataType.ARRAY_UB)
|
||||
if(value.type== DataType.UBYTE) {
|
||||
if(memloc.second!= DataType.ARRAY_UB)
|
||||
throw VmExecutionException("invalid memory pointer type $memloc")
|
||||
mem.setUByte(memloc.first, value.integerValue().toShort())
|
||||
}
|
||||
else {
|
||||
if(memloc.second!=DataType.ARRAY_B)
|
||||
if(memloc.second!= DataType.ARRAY_B)
|
||||
throw VmExecutionException("invalid memory pointer type $memloc")
|
||||
mem.setSByte(memloc.first, value.integerValue().toShort())
|
||||
}
|
||||
@@ -1728,7 +1765,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val variable = getVar(varname)
|
||||
if (variable.type == DataType.UWORD) {
|
||||
// assume the variable is a pointer (address) and write the byte value to that memory location
|
||||
if(value.type==DataType.UBYTE)
|
||||
if(value.type== DataType.UBYTE)
|
||||
mem.setUByte(variable.integerValue(), value.integerValue().toShort())
|
||||
else
|
||||
mem.setSByte(variable.integerValue(), value.integerValue().toShort())
|
||||
@@ -1762,13 +1799,13 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val memloc = memoryPointers[varname]
|
||||
if(memloc!=null) {
|
||||
// variable is the name of a pointer, write the word value to that memory location
|
||||
if(value.type==DataType.UWORD) {
|
||||
if(memloc.second!=DataType.ARRAY_UW)
|
||||
if(value.type== DataType.UWORD) {
|
||||
if(memloc.second!= DataType.ARRAY_UW)
|
||||
throw VmExecutionException("invalid memory pointer type $memloc")
|
||||
mem.setUWord(memloc.first+index*2, value.integerValue())
|
||||
}
|
||||
else {
|
||||
if(memloc.second!=DataType.ARRAY_W)
|
||||
if(memloc.second!= DataType.ARRAY_W)
|
||||
throw VmExecutionException("invalid memory pointer type $memloc")
|
||||
mem.setSWord(memloc.first+index*2, value.integerValue())
|
||||
}
|
||||
@@ -1776,7 +1813,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val variable = getVar(varname)
|
||||
if (variable.type == DataType.UWORD) {
|
||||
// assume the variable is a pointer (address) and write the word value to that memory location
|
||||
if(value.type==DataType.UWORD)
|
||||
if(value.type== DataType.UWORD)
|
||||
mem.setUWord(variable.integerValue()+index*2, value.integerValue())
|
||||
else
|
||||
mem.setSWord(variable.integerValue()+index*2, value.integerValue())
|
||||
@@ -1801,7 +1838,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val memloc = memoryPointers[varname]
|
||||
if(memloc!=null) {
|
||||
// variable is the name of a pointer, write the float value to that memory location
|
||||
if(memloc.second!=DataType.ARRAY_F)
|
||||
if(memloc.second!= DataType.ARRAY_F)
|
||||
throw VmExecutionException("invalid memory pointer type $memloc")
|
||||
mem.setFloat(memloc.first+index*5, value.numericValue().toDouble())
|
||||
} else {
|
||||
@@ -2106,7 +2143,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
if(length!=value.array!!.size)
|
||||
throw VmExecutionException("iterable length mismatch")
|
||||
if(value.array.any {it.addressOf!=null})
|
||||
throw VmExecutionException("stackvm cannot process raw memory pointers")
|
||||
throw VmExecutionException("stackvm cannot accept raw memory pointers")
|
||||
evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.max() ?: 0))
|
||||
}
|
||||
Syscall.FUNC_MAX_W -> {
|
||||
@@ -2148,7 +2185,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
if(length!=value.array!!.size)
|
||||
throw VmExecutionException("iterable length mismatch")
|
||||
if(value.array.any {it.addressOf!=null})
|
||||
throw VmExecutionException("stackvm cannot process raw memory pointers")
|
||||
throw VmExecutionException("stackvm cannot accept raw memory pointers")
|
||||
evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.min() ?: 0))
|
||||
}
|
||||
Syscall.FUNC_MIN_W -> {
|
||||
@@ -2182,7 +2219,7 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
if(length!=value.array!!.size)
|
||||
throw VmExecutionException("iterable length mismatch")
|
||||
if(value.array.any {it.addressOf!=null})
|
||||
throw VmExecutionException("stackvm cannot process raw memory pointers")
|
||||
throw VmExecutionException("stackvm cannot accept raw memory pointers")
|
||||
evalstack.push(RuntimeValue(DataType.UWORD, value.array.map { it.integer!! }.sum()))
|
||||
}
|
||||
Syscall.FUNC_SUM_UB -> {
|
||||
@@ -2245,9 +2282,9 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val numbytes = evalstack.pop().integerValue()
|
||||
val bytevalue = value.integerValue().toShort()
|
||||
when {
|
||||
value.type==DataType.UBYTE -> for(addr in address until address+numbytes)
|
||||
value.type== DataType.UBYTE -> for(addr in address until address+numbytes)
|
||||
mem.setUByte(addr, bytevalue)
|
||||
value.type==DataType.BYTE -> for(addr in address until address+numbytes)
|
||||
value.type== DataType.BYTE -> for(addr in address until address+numbytes)
|
||||
mem.setSByte(addr, bytevalue)
|
||||
else -> throw VmExecutionException("(u)byte value expected")
|
||||
}
|
||||
@@ -2258,15 +2295,15 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val numwords = evalstack.pop().integerValue()
|
||||
val wordvalue = value.integerValue()
|
||||
when {
|
||||
value.type==DataType.UWORD -> for(addr in address until address+numwords*2 step 2)
|
||||
value.type== DataType.UWORD -> for(addr in address until address+numwords*2 step 2)
|
||||
mem.setUWord(addr, wordvalue)
|
||||
value.type==DataType.WORD -> for(addr in address until address+numwords*2 step 2)
|
||||
value.type== DataType.WORD -> for(addr in address until address+numwords*2 step 2)
|
||||
mem.setSWord(addr, wordvalue)
|
||||
else -> throw VmExecutionException("(u)word value expected")
|
||||
}
|
||||
}
|
||||
Syscall.SYSCALLSTUB -> throw VmExecutionException("unimplemented sysasm called: ${ins.callLabel} Create a Syscall enum for this and implement the vm intercept for it.")
|
||||
Syscall.SYSASM_c64scr_PLOT -> {
|
||||
Syscall.SYSASM_c64scr_plot -> {
|
||||
val x = variables.getValue("Y").integerValue()
|
||||
val y = variables.getValue("A").integerValue()
|
||||
canvas?.setCursorPos(x, y)
|
||||
@@ -2280,6 +2317,10 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val num = variables.getValue("A").integerValue()
|
||||
canvas?.printText(num.toString(), 1, true)
|
||||
}
|
||||
Syscall.SYSASM_c64scr_print_ub0 -> {
|
||||
val num = variables.getValue("A").integerValue()
|
||||
canvas?.printText("%03d".format(num), 1, true)
|
||||
}
|
||||
Syscall.SYSASM_c64scr_print_b -> {
|
||||
val num = variables.getValue("A").integerValue()
|
||||
if(num<=127)
|
||||
@@ -2293,6 +2334,12 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
val number = lo+256*hi
|
||||
canvas?.printText(number.toString(), 1, true)
|
||||
}
|
||||
Syscall.SYSASM_c64scr_print_uw0 -> {
|
||||
val lo = variables.getValue("A").integerValue()
|
||||
val hi = variables.getValue("Y").integerValue()
|
||||
val number = lo+256*hi
|
||||
canvas?.printText("%05d".format(number), 1, true)
|
||||
}
|
||||
Syscall.SYSASM_c64scr_print_uwhex -> {
|
||||
val prefix = if(this.P_carry) "$" else ""
|
||||
val lo = variables.getValue("A").integerValue()
|
||||
@@ -2332,11 +2379,11 @@ class StackVm(private var traceOutputFile: String?) {
|
||||
P_irqd=true
|
||||
swapIrqExecutionContexts(true)
|
||||
|
||||
val jiffies = min((timestamp-bootTime)*60/1000, 24*3600*60-1)
|
||||
val jiffies = min((timestamp-rtcOffset)*60/1000, 24*3600*60-1)
|
||||
// update the C-64 60hz jiffy clock in the ZP addresses:
|
||||
mem.setUByte(0x00a0, (jiffies ushr 16).toShort())
|
||||
mem.setUByte(0x00a1, (jiffies ushr 8 and 255).toShort())
|
||||
mem.setUByte(0x00a2, (jiffies and 255).toShort())
|
||||
mem.setUByte_DMA(0x00a0, (jiffies ushr 16).toShort())
|
||||
mem.setUByte_DMA(0x00a1, (jiffies ushr 8 and 255).toShort())
|
||||
mem.setUByte_DMA(0x00a2, (jiffies and 255).toShort())
|
||||
|
||||
if(irqStartInstructionPtr>=0) {
|
||||
try {
|
||||
@@ -2,9 +2,9 @@ package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.LiteralValue
|
||||
import prog8.ast.Position
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.ast.base.Position
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@@ -16,7 +16,7 @@ private fun sameValueAndType(lv1: LiteralValue, lv2: LiteralValue): Boolean {
|
||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||
class TestParserLiteralValue {
|
||||
|
||||
private val dummyPos = Position("test", 0,0,0)
|
||||
private val dummyPos = Position("test", 0, 0, 0)
|
||||
|
||||
@Test
|
||||
fun testIdentity() {
|
||||
@@ -33,99 +33,99 @@ class TestParserLiteralValue {
|
||||
|
||||
@Test
|
||||
fun testEqualsAndNotEquals() {
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=100, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos), LiteralValue(DataType.UBYTE, 254, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12345.0, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=22239.0, position=dummyPos), LiteralValue(DataType.UWORD,wordvalue=22239, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos))
|
||||
assertEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos))
|
||||
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=100, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos), LiteralValue(DataType.UBYTE, 254, position=dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12345.0, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos), LiteralValue(DataType.UBYTE, 100, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=22239.0, position=dummyPos), LiteralValue(DataType.UWORD,wordvalue=22239, position=dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 100, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos), LiteralValue(DataType.UBYTE, 254, position = dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12345.0, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos), LiteralValue(DataType.UBYTE, 100, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 22239.0, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 22239, position = dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos)))
|
||||
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 101, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=101, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=101.0, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=245, position=dummyPos), LiteralValue(DataType.UBYTE, 246, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12346, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12346.0, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UBYTE, 9, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=9, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.0, position=dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos))
|
||||
assertNotEquals(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos))
|
||||
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UBYTE, 101, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=101, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=101.0, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=245, position=dummyPos), LiteralValue(DataType.UBYTE, 246, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=12346, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue=12345, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=12346.0, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UBYTE, 9, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.UWORD, wordvalue=9, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue=9.99, position=dummyPos), LiteralValue(DataType.FLOAT, floatvalue=9.0, position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UBYTE, 101, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 101, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UBYTE, 100, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 101.0, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 245, position = dummyPos), LiteralValue(DataType.UBYTE, 246, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 12346, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.UWORD, wordvalue = 12345, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 12346.0, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UBYTE, 9, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.UWORD, wordvalue = 9, position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.FLOAT, floatvalue = 9.99, position = dummyPos), LiteralValue(DataType.FLOAT, floatvalue = 9.0, position = dummyPos)))
|
||||
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="hello", position=dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position=dummyPos), LiteralValue(DataType.STR, strvalue="bye", position=dummyPos)))
|
||||
assertTrue(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos)))
|
||||
assertFalse(sameValueAndType(LiteralValue(DataType.STR, strvalue = "hello", position = dummyPos), LiteralValue(DataType.STR, strvalue = "bye", position = dummyPos)))
|
||||
|
||||
val lvOne = LiteralValue(DataType.UBYTE, 1, position=dummyPos)
|
||||
val lvTwo = LiteralValue(DataType.UBYTE, 2, position=dummyPos)
|
||||
val lvThree = LiteralValue(DataType.UBYTE, 3, position=dummyPos)
|
||||
val lvOneR = LiteralValue(DataType.UBYTE, 1, position=dummyPos)
|
||||
val lvTwoR = LiteralValue(DataType.UBYTE, 2, position=dummyPos)
|
||||
val lvThreeR = LiteralValue(DataType.UBYTE, 3, position=dummyPos)
|
||||
val lvFour= LiteralValue(DataType.UBYTE, 4, position=dummyPos)
|
||||
val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position=dummyPos)
|
||||
val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position=dummyPos)
|
||||
val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position=dummyPos)
|
||||
val lvOne = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
|
||||
val lvTwo = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
|
||||
val lvThree = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
|
||||
val lvOneR = LiteralValue(DataType.UBYTE, 1, position = dummyPos)
|
||||
val lvTwoR = LiteralValue(DataType.UBYTE, 2, position = dummyPos)
|
||||
val lvThreeR = LiteralValue(DataType.UBYTE, 3, position = dummyPos)
|
||||
val lvFour= LiteralValue(DataType.UBYTE, 4, position = dummyPos)
|
||||
val lv1 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOne, lvTwo, lvThree), position = dummyPos)
|
||||
val lv2 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvThreeR), position = dummyPos)
|
||||
val lv3 = LiteralValue(DataType.ARRAY_UB, arrayvalue = arrayOf(lvOneR, lvTwoR, lvFour), position = dummyPos)
|
||||
assertEquals(lv1, lv2)
|
||||
assertNotEquals(lv1, lv3)
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testGreaterThan(){
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) > LiteralValue(DataType.UBYTE, 99, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.UWORD, wordvalue=253, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 99, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
|
||||
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) >= LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
||||
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) > LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) > LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) > LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) > LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) > LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) > LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
||||
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) >= LiteralValue(DataType.UBYTE, 101, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) >= LiteralValue(DataType.UWORD,wordvalue= 255, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) >= LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) >= LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) >= LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
|
||||
}
|
||||
|
||||
@Test
|
||||
fun testLessThan() {
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) < LiteralValue(DataType.UBYTE, 101, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.UWORD, wordvalue=255, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.1, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 101, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 255, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.1, position = dummyPos))
|
||||
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position=dummyPos) <= LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) <= LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
||||
assertTrue(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
||||
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) < LiteralValue(DataType.UBYTE, 100, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos) < LiteralValue(DataType.UWORD, wordvalue=254, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos) < LiteralValue(DataType.FLOAT, floatvalue=100.0, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) < LiteralValue(DataType.UBYTE, 100, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) < LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) < LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos))
|
||||
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position=dummyPos) <= LiteralValue(DataType.UBYTE, 99, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD,wordvalue= 254, position=dummyPos) <= LiteralValue(DataType.UWORD,wordvalue= 253, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT,floatvalue= 100.0, position=dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue=99.9, position=dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UBYTE, 100, position = dummyPos) <= LiteralValue(DataType.UBYTE, 99, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.UWORD, wordvalue = 254, position = dummyPos) <= LiteralValue(DataType.UWORD, wordvalue = 253, position = dummyPos))
|
||||
assertFalse(LiteralValue(DataType.FLOAT, floatvalue = 100.0, position = dummyPos) <= LiteralValue(DataType.FLOAT, floatvalue = 99.9, position = dummyPos))
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -2,8 +2,8 @@ package prog8tests
|
||||
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.DataType
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.vm.RuntimeValue
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
|
||||
@@ -4,15 +4,15 @@ import org.hamcrest.MatcherAssert.assertThat
|
||||
import org.hamcrest.Matchers.empty
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.ByteDatatypes
|
||||
import prog8.ast.DataType
|
||||
import prog8.ast.IterableDatatypes
|
||||
import prog8.ast.WordDatatypes
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.base.ByteDatatypes
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.IterableDatatypes
|
||||
import prog8.ast.base.WordDatatypes
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.intermediate.Instruction
|
||||
import prog8.compiler.intermediate.Opcode
|
||||
import prog8.stackvm.*
|
||||
import prog8.vm.stackvm.*
|
||||
import kotlin.test.*
|
||||
|
||||
|
||||
@@ -60,7 +60,8 @@ class TestStackVmOpcodes {
|
||||
labels: Map<String, Int>?=null,
|
||||
mem: Map<Int, List<RuntimeValue>>?=null) : Program {
|
||||
val heap = HeapValues()
|
||||
return Program("test", ins, vars ?: mapOf(), memoryPointers ?: mapOf(), labels ?: mapOf(), mem ?: mapOf(), heap)
|
||||
return Program("test", ins, vars ?: mapOf(), memoryPointers ?: mapOf(), labels ?: mapOf(), mem
|
||||
?: mapOf(), heap)
|
||||
}
|
||||
|
||||
@Test
|
||||
|
||||
@@ -5,8 +5,10 @@ import org.hamcrest.Matchers.closeTo
|
||||
import org.hamcrest.Matchers.equalTo
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.junit.jupiter.api.TestInstance
|
||||
import prog8.ast.*
|
||||
import prog8.compiler.RuntimeValue
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.expressions.LiteralValue
|
||||
import prog8.vm.RuntimeValue
|
||||
import prog8.compiler.*
|
||||
import prog8.compiler.target.c64.*
|
||||
import java.io.CharConversionException
|
||||
@@ -334,8 +336,8 @@ class TestPetscii {
|
||||
|
||||
@Test
|
||||
fun testLiteralValueComparisons() {
|
||||
val ten = LiteralValue(DataType.UWORD, wordvalue=10, position=Position("", 0 ,0 ,0))
|
||||
val nine = LiteralValue(DataType.UBYTE, bytevalue=9, position=Position("", 0 ,0 ,0))
|
||||
val ten = LiteralValue(DataType.UWORD, wordvalue = 10, position = Position("", 0, 0, 0))
|
||||
val nine = LiteralValue(DataType.UBYTE, bytevalue = 9, position = Position("", 0, 0, 0))
|
||||
assertEquals(ten, ten)
|
||||
assertNotEquals(ten, nine)
|
||||
assertFalse(ten != ten)
|
||||
@@ -351,8 +353,8 @@ class TestPetscii {
|
||||
assertTrue(ten <= ten)
|
||||
assertFalse(ten < ten)
|
||||
|
||||
val abc = LiteralValue(DataType.STR, strvalue = "abc", position=Position("", 0 ,0 ,0))
|
||||
val abd = LiteralValue(DataType.STR, strvalue = "abd", position=Position("", 0 ,0 ,0))
|
||||
val abc = LiteralValue(DataType.STR, strvalue = "abc", position = Position("", 0, 0, 0))
|
||||
val abd = LiteralValue(DataType.STR, strvalue = "abd", position = Position("", 0, 0, 0))
|
||||
assertEquals(abc, abc)
|
||||
assertTrue(abc!=abd)
|
||||
assertFalse(abc!=abc)
|
||||
|
||||
+16
-15
@@ -5,27 +5,28 @@ Writing and building a program
|
||||
First, getting a working compiler
|
||||
---------------------------------
|
||||
|
||||
Before you can compile Prog8 programs, you'll have to build the compiler itself.
|
||||
Before you can compile Prog8 programs, you'll have to download or build the compiler itself.
|
||||
First make sure you have installed the :ref:`requirements`.
|
||||
Then you can choose a few ways to create the compiler:
|
||||
Then you can choose a few ways to get a compiler:
|
||||
|
||||
**Using the shell script:**
|
||||
|
||||
#. run the "build_the_compiler.sh" shell script
|
||||
#. it will create a "prog8compiler.jar" file which contains everything.
|
||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
||||
|
||||
**using the Gradle build system:**
|
||||
|
||||
#. run the command "./gradlew installDist"
|
||||
#. it will create the commands and required libraries in the "./compiler/build/install/p8compile/" directory
|
||||
#. run the compiler with the "./compiler/build/install/p8compile/bin/p8compile" command to see how you can use it.
|
||||
|
||||
**download a precompiled version from github:**
|
||||
**Download a precompiled version from github:**
|
||||
|
||||
#. download a recent "prog8compiler.jar" from `the releases on Github <https://github.com/irmen/prog8/releases>`_
|
||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
||||
|
||||
**Using the shell scripts:**
|
||||
|
||||
#. run the "create_compiler_jar.sh" shell script and have a little patience while everything is built
|
||||
#. it will output "prog8compiler.jar" file which contains everything.
|
||||
#. run the compiler with "java -jar prog8compiler.jar" to see how you can use it.
|
||||
|
||||
**using the Gradle build system directly:**
|
||||
|
||||
#. run the command "./gradlew installDist" and have a little patience while everything is built
|
||||
#. it will create the commands and required libraries in the "./compiler/build/install/p8compile/" directory
|
||||
#. run the compiler with the "./compiler/build/install/p8compile/bin/p8compile" command to see how you can use it.
|
||||
|
||||
|
||||
|
||||
What is a Prog8 "Program" anyway?
|
||||
---------------------------------
|
||||
|
||||
+23
-13
@@ -136,29 +136,39 @@ For other platforms it is very easy to compile it yourself (make ; make install)
|
||||
|
||||
A **Java runtime (jre or jdk), version 8 or newer** is required to run the packaged compiler.
|
||||
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead
|
||||
and for Windows it's possible to get that as well: for instance,
|
||||
`Azul's Zulu <https://www.azul.com/downloads/zulu/>`_ is a certified OpenJDK
|
||||
implementation available for various platforms.
|
||||
and for Windows it's possible to get that as well. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ for
|
||||
downloads.
|
||||
|
||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) to run the programs on. The compiler assumes the presence
|
||||
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||
|
||||
.. hint::
|
||||
The compiler is almost completely written in Kotlin, but the packaged release version
|
||||
only requires a Java runtime. All other needed libraries and files are embedded in the
|
||||
packaged jar file.
|
||||
.. important::
|
||||
**Building the compiler itself:**
|
||||
|
||||
.. note::
|
||||
Building the compiler itself:
|
||||
|
||||
(re)building the compiler itself requires a Kotlin SDK version 1.3.
|
||||
(re)building the compiler itself requires a recent Kotlin SDK.
|
||||
The compiler is developed using the `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_
|
||||
IDE from Jetbrains, with the Kotlin plugin (free community edition of this IDE is available).
|
||||
But a bare Kotlin SDK installation should work just as well.
|
||||
A shell script (``build_the_compiler.sh``) is provided to build and package the compiler from the command line.
|
||||
|
||||
A shell script (``create_compiler_jar.sh``) is provided to build and package the compiler from the command line.
|
||||
If you have the 'fat-jar' you can run it with ``java -jar prog8compiler.jar``.
|
||||
You can also use the Gradle build system to build the compiler (it will take care of
|
||||
downloading all required libraries for you) by typing ``gradle installDist`` for instance.
|
||||
downloading all required libraries for you) by typing ``gradle build`` for instance.
|
||||
The output of this gradle build will appear in the "./compiler/build/install/p8compile/" directory.
|
||||
The most interesting gradle commands to run are probably:
|
||||
|
||||
``./gradlew check``
|
||||
Builds the compiler code and runs all available checks and unit-tests.
|
||||
``./gradlew installShadowDist``
|
||||
Creates a 'fat-jar' that contains the compiler and all dependencies,
|
||||
and a few start scripts to run it.
|
||||
The output can be found in ``.compiler/build/install/compiler-shadow/``
|
||||
and you can launch the compiler with the script
|
||||
``./compiler/build/install/compiler-shadow/bin/p8compile``.
|
||||
``./gradlew shadowDistZip``
|
||||
Creates a zipfile with the above in it, for easy distribution.
|
||||
This file can be found in ``./compiler/build/distributions/``
|
||||
|
||||
|
||||
.. note::
|
||||
Development and testing is done on Linux, but the compiler should run on most
|
||||
|
||||
@@ -54,7 +54,7 @@ Code
|
||||
|
||||
- value assignment
|
||||
- looping (for, while, repeat, unconditional jumps)
|
||||
- conditional execution (if - then - else, and conditional jumps)
|
||||
- conditional execution (if - then - else, when, and conditional jumps)
|
||||
- subroutine calls
|
||||
- label definition
|
||||
|
||||
@@ -364,6 +364,9 @@ You can also create loops by using the ``goto`` statement, but this should usual
|
||||
Conditional Execution
|
||||
---------------------
|
||||
|
||||
if statements
|
||||
^^^^^^^^^^^^^
|
||||
|
||||
Conditional execution means that the flow of execution changes based on certiain conditions,
|
||||
rather than having fixed gotos or subroutine calls::
|
||||
|
||||
@@ -413,6 +416,16 @@ So ``if_cc goto target`` will directly translate into the single CPU instruction
|
||||
Maybe in the future this will be a separate nested scope, but for now, that is
|
||||
only possible when defining a subroutine.
|
||||
|
||||
when statement (jumptable)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
.. attention::
|
||||
TODO: docs for this this must still be written.
|
||||
TODO: the code generator for this is not yet working.
|
||||
|
||||
Use a ``when`` statement if you have a set of choices that each should result in a certain
|
||||
action. It's more readable (and results in faster code) than using a lot of if / else statements.
|
||||
|
||||
|
||||
Assignments
|
||||
-----------
|
||||
|
||||
@@ -609,3 +609,29 @@ where <statements> can be just a single statement for instance just a ``goto``,
|
||||
The XX corresponds to one of the eigth branching instructions so the possibilities are:
|
||||
``if_cs``, ``if_cc``, ``if_eq``, ``if_ne``, ``if_pl``, ``if_mi``, ``if_vs`` and ``if_vc``.
|
||||
It can also be one of the four aliases that are easier to read: ``if_z``, ``if_nz``, ``if_pos`` and ``if_neg``.
|
||||
|
||||
when statement (jumptable)
|
||||
^^^^^^^^^^^^^^^^^^^^^^^^^^
|
||||
.. attention::
|
||||
TODO: docs for this this must still be written.
|
||||
TODO: the code generator for this is not yet working.
|
||||
|
||||
The condition value can only be an integer datatype.
|
||||
The choice values must be constant integer values.
|
||||
|
||||
code example::
|
||||
|
||||
when 4+A+Y {
|
||||
10 -> {
|
||||
c64scr.print("ten")
|
||||
}
|
||||
5 -> c64scr.print("five")
|
||||
30 -> c64scr.print("thirty")
|
||||
99 -> c64scr.print("nn")
|
||||
55 -> {
|
||||
; will be optimized away
|
||||
}
|
||||
else -> {
|
||||
c64scr.print("!??!\n")
|
||||
}
|
||||
}
|
||||
@@ -39,9 +39,9 @@
|
||||
}
|
||||
}
|
||||
|
||||
float duration = floor(((c64.TIME_LO as float)
|
||||
float duration = ((c64.TIME_LO as float)
|
||||
+ 256.0*(c64.TIME_MID as float)
|
||||
+ 65536.0*(c64.TIME_HI as float))/60.0)
|
||||
+ 65536.0*(c64.TIME_HI as float))/60.0
|
||||
c64scr.plot(0, 21)
|
||||
c64scr.print("finished in ")
|
||||
c64flt.print_f(duration)
|
||||
|
||||
+44
-45
@@ -1,57 +1,56 @@
|
||||
%import c64utils
|
||||
%zeropage basicsafe
|
||||
|
||||
%option enable_floats
|
||||
|
||||
~ main {
|
||||
|
||||
sub start() {
|
||||
A=10
|
||||
Y=22
|
||||
uword uw = A*Y
|
||||
|
||||
ubyte x = 99
|
||||
str teststring = "hello"
|
||||
c64scr.print(&teststring)
|
||||
|
||||
return
|
||||
when uw {
|
||||
12345 -> {
|
||||
A=44
|
||||
}
|
||||
12346 -> {
|
||||
A=44
|
||||
}
|
||||
12347 -> {
|
||||
A=44
|
||||
}
|
||||
else -> {
|
||||
A=0
|
||||
}
|
||||
}
|
||||
|
||||
startqqq:
|
||||
|
||||
sub startzzz() {
|
||||
if_cc goto startqqq
|
||||
c64.EXTCOL++
|
||||
when 4+A+Y {
|
||||
10 -> {
|
||||
c64scr.print("ten")
|
||||
}
|
||||
5 -> c64scr.print("five")
|
||||
30 -> c64scr.print("thirty")
|
||||
31 -> c64scr.print("thirty1")
|
||||
32 -> c64scr.print("thirty2")
|
||||
33 -> c64scr.print("thirty3")
|
||||
99 -> c64scr.print("nn")
|
||||
55 -> {
|
||||
; should be optimized away
|
||||
}
|
||||
56 -> {
|
||||
; should be optimized away
|
||||
}
|
||||
57243 -> {
|
||||
; should be optimized away
|
||||
}
|
||||
else -> {
|
||||
c64scr.print("!??!\n")
|
||||
c64scr.print("!??!!??!\n")
|
||||
c64scr.print("!??!!??!!?!\n")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
; for ubyte y in 0 to 3 {
|
||||
; for ubyte x in 0 to 10 {
|
||||
; ubyte product = x*y
|
||||
; c64scr.setcc(x, y, 160, product)
|
||||
; }
|
||||
; }
|
||||
; c64.CHROUT('\n')
|
||||
; c64.CHROUT('\n')
|
||||
;
|
||||
; for ubyte y in 12 to 15 {
|
||||
; for ubyte x in 0 to 10 {
|
||||
; ubyte sumv = x+y
|
||||
; c64scr.setcc(x, y, 160, sumv)
|
||||
; }
|
||||
; }
|
||||
|
||||
;ubyte bb = len(xcoor)
|
||||
|
||||
; storage for rotated coordinates
|
||||
; ubyte[len(xcoor)] xx = 2
|
||||
; float[len(xcoor)] rotatedx=0.0
|
||||
;
|
||||
; ubyte[4] x = 23
|
||||
; float[4] yy = 4.4
|
||||
|
||||
; c64flt.print_f(xcoor[1])
|
||||
; c64.CHROUT(',')
|
||||
; c64flt.print_f(xcoor[2])
|
||||
; c64.CHROUT('\n')
|
||||
; swap(xcoor[1], xcoor[2])
|
||||
; c64flt.print_f(xcoor[1])
|
||||
; c64.CHROUT(',')
|
||||
; c64flt.print_f(xcoor[2])
|
||||
; c64.CHROUT('\n')
|
||||
|
||||
}
|
||||
@@ -1,2 +1,7 @@
|
||||
org.gradle.caching=true
|
||||
org.gradle.console=rich
|
||||
org.gradle.parallel=true
|
||||
org.gradle.jvmargs=-Xmx2048M
|
||||
org.gradle.daemon=true
|
||||
|
||||
kotlinVersion=1.3.41
|
||||
@@ -4,6 +4,6 @@ set PROG8CLASSPATH=./compiler/build/classes/kotlin/main;./compiler/build/resourc
|
||||
set KOTLINPATH=%USERPROFILE%/.IdeaIC2019.1/config/plugins/Kotlin
|
||||
set LIBJARS=%KOTLINPATH%/lib/kotlin-stdlib.jar;%KOTLINPATH%/lib/kotlin-reflect.jar
|
||||
|
||||
java -cp %PROG8CLASSPATH%;%LIBJARS% prog8.StackVmMainKt %*
|
||||
java -cp %PROG8CLASSPATH%;%LIBJARS% prog8.vm.stackvm.MainKt %*
|
||||
|
||||
@REM if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar -vm
|
||||
@@ -4,6 +4,6 @@ PROG8CLASSPATH=./compiler/build/classes/kotlin/main:./compiler/build/resources/m
|
||||
KOTLINPATH=${HOME}/.IntelliJIdea2019.1/config/plugins/Kotlin
|
||||
LIBJARS=${KOTLINPATH}/lib/kotlin-stdlib.jar:${KOTLINPATH}/lib/kotlin-reflect.jar
|
||||
|
||||
java -cp ${PROG8CLASSPATH}:${LIBJARS} prog8.StackVmMainKt $*
|
||||
java -cp ${PROG8CLASSPATH}:${LIBJARS} prog8.vm.stackvm.MainKt $*
|
||||
|
||||
# if you have created a .jar file using the 'create_compiler_jar' script, you can simply do: java -jar prog8compiler.jar -vm
|
||||
+12
-5
@@ -90,6 +90,7 @@ statement :
|
||||
| forloop
|
||||
| whileloop
|
||||
| repeatloop
|
||||
| whenstmt
|
||||
| breakstmt
|
||||
| continuestmt
|
||||
| labeldef
|
||||
@@ -196,7 +197,7 @@ scoped_identifier : NAME ('.' NAME)* ;
|
||||
|
||||
register : 'A' | 'X' | 'Y' ;
|
||||
|
||||
registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // only used in subroutine params and returnvalues
|
||||
registerorpair : 'A' | 'X' | 'Y' | 'AX' | 'AY' | 'XY' ; // pairs can only be used in subroutine params and returnvalues
|
||||
|
||||
statusregister : 'Pc' | 'Pz' | 'Pn' | 'Pv' ;
|
||||
|
||||
@@ -244,19 +245,21 @@ sub_params : vardecl (',' EOL? vardecl)* ;
|
||||
sub_returns : datatype (',' EOL? datatype)* ;
|
||||
|
||||
asmsubroutine :
|
||||
'asmsub' identifier '(' asmsub_params? ')'
|
||||
'->' 'clobbers' '(' clobber? ')' '->' '(' asmsub_returns? ')' (asmsub_address | statement_block )
|
||||
'asmsub' identifier '(' asmsub_params? ')' EOL?
|
||||
asmsub_clobbers? asmsub_returns? (asmsub_address | statement_block )
|
||||
;
|
||||
|
||||
asmsub_address : '=' address=integerliteral ;
|
||||
|
||||
asmsub_params : asmsub_param (',' EOL? asmsub_param)* ;
|
||||
|
||||
asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack');
|
||||
asmsub_param : vardecl '@' (registerorpair | statusregister | stack='stack') ;
|
||||
|
||||
asmsub_clobbers : 'clobbers' '(' clobber? ')' ;
|
||||
|
||||
clobber : register (',' register)* ;
|
||||
|
||||
asmsub_returns : asmsub_return (',' EOL? asmsub_return)* ;
|
||||
asmsub_returns : '->' asmsub_return (',' EOL? asmsub_return)* ;
|
||||
|
||||
asmsub_return : datatype '@' (registerorpair | statusregister | stack='stack') ;
|
||||
|
||||
@@ -276,3 +279,7 @@ forloop : 'for' datatype? ZEROPAGE? (register | identifier) 'in' expression EOL
|
||||
whileloop: 'while' expression EOL? (statement | statement_block) ;
|
||||
|
||||
repeatloop: 'repeat' (statement | statement_block) EOL? 'until' expression ;
|
||||
|
||||
whenstmt: 'when' expression '{' EOL (when_choice | EOL) * '}' EOL? ;
|
||||
|
||||
when_choice: (expression | 'else' ) '->' (statement | statement_block ) ;
|
||||
+14
-3
@@ -7,12 +7,23 @@ repositories {
|
||||
mavenCentral()
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr('org.antlr:antlr4:4.7.2') {
|
||||
exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||
configurations {
|
||||
// strange antlr plugin issue, see https://github.com/gradle/gradle/issues/820
|
||||
// this avoids linking in the complete antlr binary jar
|
||||
compile {
|
||||
extendsFrom = extendsFrom.findAll { it != configurations.antlr }
|
||||
}
|
||||
}
|
||||
|
||||
dependencies {
|
||||
antlr 'org.antlr:antlr4:4.7.2'
|
||||
compile 'org.antlr:antlr4-runtime:4.7.2'
|
||||
|
||||
// antlr('org.antlr:antlr4:4.7.2') {
|
||||
// exclude group: 'com.ibm.icu', module: 'icu4j'
|
||||
// }
|
||||
}
|
||||
|
||||
compileJava {
|
||||
dependsOn tasks.withType(AntlrTask)
|
||||
}
|
||||
|
||||
@@ -34,9 +34,10 @@ public class prog8Lexer extends Lexer {
|
||||
T__87=88, T__88=89, T__89=90, T__90=91, T__91=92, T__92=93, T__93=94,
|
||||
T__94=95, T__95=96, T__96=97, T__97=98, T__98=99, T__99=100, T__100=101,
|
||||
T__101=102, T__102=103, T__103=104, T__104=105, T__105=106, T__106=107,
|
||||
T__107=108, LINECOMMENT=109, COMMENT=110, WS=111, EOL=112, NAME=113, DEC_INTEGER=114,
|
||||
HEX_INTEGER=115, BIN_INTEGER=116, ADDRESS_OF=117, FLOAT_NUMBER=118, STRING=119,
|
||||
INLINEASMBLOCK=120, SINGLECHAR=121, ZEROPAGE=122, ARRAYSIG=123;
|
||||
T__107=108, T__108=109, LINECOMMENT=110, COMMENT=111, WS=112, EOL=113,
|
||||
NAME=114, DEC_INTEGER=115, HEX_INTEGER=116, BIN_INTEGER=117, ADDRESS_OF=118,
|
||||
FLOAT_NUMBER=119, STRING=120, INLINEASMBLOCK=121, SINGLECHAR=122, ZEROPAGE=123,
|
||||
ARRAYSIG=124;
|
||||
public static String[] channelNames = {
|
||||
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
|
||||
};
|
||||
@@ -60,10 +61,10 @@ public class prog8Lexer extends Lexer {
|
||||
"T__81", "T__82", "T__83", "T__84", "T__85", "T__86", "T__87", "T__88",
|
||||
"T__89", "T__90", "T__91", "T__92", "T__93", "T__94", "T__95", "T__96",
|
||||
"T__97", "T__98", "T__99", "T__100", "T__101", "T__102", "T__103", "T__104",
|
||||
"T__105", "T__106", "T__107", "LINECOMMENT", "COMMENT", "WS", "EOL",
|
||||
"NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER",
|
||||
"FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK", "SINGLECHAR",
|
||||
"ZEROPAGE", "ARRAYSIG"
|
||||
"T__105", "T__106", "T__107", "T__108", "LINECOMMENT", "COMMENT", "WS",
|
||||
"EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF",
|
||||
"FLOAT_NUMBER", "FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK",
|
||||
"SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
|
||||
};
|
||||
}
|
||||
public static final String[] ruleNames = makeRuleNames();
|
||||
@@ -80,11 +81,11 @@ public class prog8Lexer extends Lexer {
|
||||
"'step'", "'and'", "'or'", "'xor'", "'not'", "'('", "')'", "'as'", "'@'",
|
||||
"'return'", "'break'", "'continue'", "'.'", "'A'", "'X'", "'Y'", "'AX'",
|
||||
"'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'", "'Pv'", "'.w'", "'true'", "'false'",
|
||||
"'%asm'", "'sub'", "'->'", "'{'", "'}'", "'asmsub'", "'clobbers'", "'stack'",
|
||||
"'%asm'", "'sub'", "'->'", "'{'", "'}'", "'asmsub'", "'stack'", "'clobbers'",
|
||||
"'if'", "'else'", "'if_cs'", "'if_cc'", "'if_eq'", "'if_z'", "'if_ne'",
|
||||
"'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'", "'if_neg'", "'if_vs'", "'if_vc'",
|
||||
"'for'", "'in'", "'while'", "'repeat'", "'until'", null, null, null,
|
||||
null, null, null, null, null, "'&'", null, null, null, null, "'@zp'",
|
||||
"'for'", "'in'", "'while'", "'repeat'", "'until'", "'when'", null, null,
|
||||
null, null, null, null, null, null, "'&'", null, null, null, null, "'@zp'",
|
||||
"'[]'"
|
||||
};
|
||||
}
|
||||
@@ -100,9 +101,9 @@ public class prog8Lexer extends Lexer {
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, null, null, null, null, null, null, null, null, null, null, null,
|
||||
null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER",
|
||||
"BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER", "STRING", "INLINEASMBLOCK",
|
||||
"SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
|
||||
null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER",
|
||||
"HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER", "STRING",
|
||||
"INLINEASMBLOCK", "SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
|
||||
};
|
||||
}
|
||||
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
|
||||
@@ -166,13 +167,13 @@ public class prog8Lexer extends Lexer {
|
||||
@Override
|
||||
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
|
||||
switch (ruleIndex) {
|
||||
case 120:
|
||||
case 121:
|
||||
STRING_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
case 121:
|
||||
case 122:
|
||||
INLINEASMBLOCK_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
case 122:
|
||||
case 123:
|
||||
SINGLECHAR_action((RuleContext)_localctx, actionIndex);
|
||||
break;
|
||||
}
|
||||
@@ -212,7 +213,7 @@ public class prog8Lexer extends Lexer {
|
||||
}
|
||||
|
||||
public static final String _serializedATN =
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2}\u035e\b\1\4\2\t"+
|
||||
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2~\u0365\b\1\4\2\t"+
|
||||
"\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4\13"+
|
||||
"\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22\t\22"+
|
||||
"\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31\t\31"+
|
||||
@@ -225,278 +226,281 @@ public class prog8Lexer extends Lexer {
|
||||
"\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_\4"+
|
||||
"`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k\t"+
|
||||
"k\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv\4"+
|
||||
"w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\3\2\3\2\3\3\3\3\3\4\3\4"+
|
||||
"\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3"+
|
||||
"\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b"+
|
||||
"\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3"+
|
||||
"\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13"+
|
||||
"\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3"+
|
||||
"\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16\3\16\3\16\3"+
|
||||
"\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3"+
|
||||
"\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\24\3\24\3\24\3\24\3"+
|
||||
"\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26\3\26\3\27\3"+
|
||||
"\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\32\3\32\3\33\3"+
|
||||
"\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3\36\3\37\3\37\3\37\3"+
|
||||
"\37\3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3$\3%\3%\3%\3%\3&"+
|
||||
"\3&\3&\3\'\3\'\3\'\3(\3(\3)\3)\3*\3*\3*\3+\3+\3,\3,\3-\3-\3.\3.\3.\3/"+
|
||||
"\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\62\3\63\3\63\3\63\3\64\3\64\3\64"+
|
||||
"\3\65\3\65\3\65\3\66\3\66\3\67\3\67\38\38\38\39\39\39\39\39\3:\3:\3:\3"+
|
||||
":\3;\3;\3;\3<\3<\3<\3<\3=\3=\3=\3=\3>\3>\3?\3?\3@\3@\3@\3A\3A\3B\3B\3"+
|
||||
"B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3D\3D\3D\3E\3E\3F\3"+
|
||||
"F\3G\3G\3H\3H\3I\3I\3I\3J\3J\3J\3K\3K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3"+
|
||||
"O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3S\3S\3S\3S\3S\3T\3"+
|
||||
"T\3T\3T\3U\3U\3U\3V\3V\3W\3W\3X\3X\3X\3X\3X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3"+
|
||||
"Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3\\\3\\\3\\\3\\\3\\\3]\3]\3]\3]\3]"+
|
||||
"\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3`\3`\3`\3`\3`\3a\3a\3a\3a\3a"+
|
||||
"\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3d\3d\3e\3e\3e"+
|
||||
"\3e\3e\3e\3f\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3g\3g\3h\3h\3h\3h\3h\3h\3i"+
|
||||
"\3i\3i\3i\3j\3j\3j\3k\3k\3k\3k\3k\3k\3l\3l\3l\3l\3l\3l\3l\3m\3m\3m\3m"+
|
||||
"\3m\3m\3n\3n\7n\u02e4\nn\fn\16n\u02e7\13n\3n\3n\3n\3n\3o\3o\7o\u02ef\n"+
|
||||
"o\fo\16o\u02f2\13o\3o\3o\3p\3p\3p\3p\3q\6q\u02fb\nq\rq\16q\u02fc\3r\3"+
|
||||
"r\7r\u0301\nr\fr\16r\u0304\13r\3s\3s\3s\6s\u0309\ns\rs\16s\u030a\5s\u030d"+
|
||||
"\ns\3t\3t\6t\u0311\nt\rt\16t\u0312\3u\3u\6u\u0317\nu\ru\16u\u0318\3v\3"+
|
||||
"v\3w\3w\3w\5w\u0320\nw\3w\5w\u0323\nw\3x\6x\u0326\nx\rx\16x\u0327\3x\3"+
|
||||
"x\6x\u032c\nx\rx\16x\u032d\5x\u0330\nx\3y\3y\3y\3y\5y\u0336\ny\3z\3z\3"+
|
||||
"z\7z\u033b\nz\fz\16z\u033e\13z\3z\3z\3z\3{\3{\3{\3{\6{\u0347\n{\r{\16"+
|
||||
"{\u0348\3{\3{\3{\3{\3{\3|\3|\3|\5|\u0353\n|\3|\3|\3|\3}\3}\3}\3}\3~\3"+
|
||||
"~\3~\3\u0348\2\177\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r"+
|
||||
"\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31\61\32\63\33"+
|
||||
"\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63"+
|
||||
"e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089"+
|
||||
"F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099N\u009bO\u009d"+
|
||||
"P\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1"+
|
||||
"Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5"+
|
||||
"d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9"+
|
||||
"n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9v\u00ebw\u00ed"+
|
||||
"x\u00ef\2\u00f1\2\u00f3y\u00f5z\u00f7{\u00f9|\u00fb}\3\2\n\4\2\f\f\17"+
|
||||
"\17\4\2\13\13\"\"\5\2C\\aac|\6\2\62;C\\aac|\5\2\62;CHch\4\2GGgg\4\2--"+
|
||||
"//\6\2\f\f\16\17$$^^\2\u036d\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3"+
|
||||
"\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2"+
|
||||
"\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37"+
|
||||
"\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3"+
|
||||
"\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2"+
|
||||
"\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C"+
|
||||
"\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2"+
|
||||
"\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2"+
|
||||
"\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i"+
|
||||
"\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2"+
|
||||
"\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081"+
|
||||
"\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2"+
|
||||
"\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093"+
|
||||
"\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2"+
|
||||
"\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5"+
|
||||
"\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2"+
|
||||
"\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7"+
|
||||
"\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2"+
|
||||
"\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9"+
|
||||
"\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2"+
|
||||
"\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db"+
|
||||
"\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2"+
|
||||
"\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed"+
|
||||
"\3\2\2\2\2\u00f3\3\2\2\2\2\u00f5\3\2\2\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2"+
|
||||
"\2\2\u00fb\3\2\2\2\3\u00fd\3\2\2\2\5\u00ff\3\2\2\2\7\u0101\3\2\2\2\t\u0106"+
|
||||
"\3\2\2\2\13\u010e\3\2\2\2\r\u0118\3\2\2\2\17\u0122\3\2\2\2\21\u012e\3"+
|
||||
"\2\2\2\23\u0137\3\2\2\2\25\u013f\3\2\2\2\27\u014b\3\2\2\2\31\u0157\3\2"+
|
||||
"\2\2\33\u0162\3\2\2\2\35\u016a\3\2\2\2\37\u016c\3\2\2\2!\u016e\3\2\2\2"+
|
||||
"#\u0174\3\2\2\2%\u017a\3\2\2\2\'\u017f\3\2\2\2)\u0185\3\2\2\2+\u018a\3"+
|
||||
"\2\2\2-\u0190\3\2\2\2/\u0194\3\2\2\2\61\u019a\3\2\2\2\63\u019c\3\2\2\2"+
|
||||
"\65\u019e\3\2\2\2\67\u01a1\3\2\2\29\u01a4\3\2\2\2;\u01a7\3\2\2\2=\u01aa"+
|
||||
"\3\2\2\2?\u01ae\3\2\2\2A\u01b1\3\2\2\2C\u01b4\3\2\2\2E\u01b7\3\2\2\2G"+
|
||||
"\u01ba\3\2\2\2I\u01be\3\2\2\2K\u01c2\3\2\2\2M\u01c5\3\2\2\2O\u01c8\3\2"+
|
||||
"\2\2Q\u01ca\3\2\2\2S\u01cc\3\2\2\2U\u01cf\3\2\2\2W\u01d1\3\2\2\2Y\u01d3"+
|
||||
"\3\2\2\2[\u01d5\3\2\2\2]\u01d8\3\2\2\2_\u01db\3\2\2\2a\u01dd\3\2\2\2c"+
|
||||
"\u01df\3\2\2\2e\u01e2\3\2\2\2g\u01e5\3\2\2\2i\u01e8\3\2\2\2k\u01eb\3\2"+
|
||||
"\2\2m\u01ed\3\2\2\2o\u01ef\3\2\2\2q\u01f2\3\2\2\2s\u01f7\3\2\2\2u\u01fb"+
|
||||
"\3\2\2\2w\u01fe\3\2\2\2y\u0202\3\2\2\2{\u0206\3\2\2\2}\u0208\3\2\2\2\177"+
|
||||
"\u020a\3\2\2\2\u0081\u020d\3\2\2\2\u0083\u020f\3\2\2\2\u0085\u0216\3\2"+
|
||||
"\2\2\u0087\u021c\3\2\2\2\u0089\u0225\3\2\2\2\u008b\u0227\3\2\2\2\u008d"+
|
||||
"\u0229\3\2\2\2\u008f\u022b\3\2\2\2\u0091\u022d\3\2\2\2\u0093\u0230\3\2"+
|
||||
"\2\2\u0095\u0233\3\2\2\2\u0097\u0236\3\2\2\2\u0099\u0239\3\2\2\2\u009b"+
|
||||
"\u023c\3\2\2\2\u009d\u023f\3\2\2\2\u009f\u0242\3\2\2\2\u00a1\u0245\3\2"+
|
||||
"\2\2\u00a3\u024a\3\2\2\2\u00a5\u0250\3\2\2\2\u00a7\u0255\3\2\2\2\u00a9"+
|
||||
"\u0259\3\2\2\2\u00ab\u025c\3\2\2\2\u00ad\u025e\3\2\2\2\u00af\u0260\3\2"+
|
||||
"\2\2\u00b1\u0267\3\2\2\2\u00b3\u0270\3\2\2\2\u00b5\u0276\3\2\2\2\u00b7"+
|
||||
"\u0279\3\2\2\2\u00b9\u027e\3\2\2\2\u00bb\u0284\3\2\2\2\u00bd\u028a\3\2"+
|
||||
"\2\2\u00bf\u0290\3\2\2\2\u00c1\u0295\3\2\2\2\u00c3\u029b\3\2\2\2\u00c5"+
|
||||
"\u02a1\3\2\2\2\u00c7\u02a7\3\2\2\2\u00c9\u02ae\3\2\2\2\u00cb\u02b4\3\2"+
|
||||
"\2\2\u00cd\u02bb\3\2\2\2\u00cf\u02c1\3\2\2\2\u00d1\u02c7\3\2\2\2\u00d3"+
|
||||
"\u02cb\3\2\2\2\u00d5\u02ce\3\2\2\2\u00d7\u02d4\3\2\2\2\u00d9\u02db\3\2"+
|
||||
"\2\2\u00db\u02e1\3\2\2\2\u00dd\u02ec\3\2\2\2\u00df\u02f5\3\2\2\2\u00e1"+
|
||||
"\u02fa\3\2\2\2\u00e3\u02fe\3\2\2\2\u00e5\u030c\3\2\2\2\u00e7\u030e\3\2"+
|
||||
"\2\2\u00e9\u0314\3\2\2\2\u00eb\u031a\3\2\2\2\u00ed\u031c\3\2\2\2\u00ef"+
|
||||
"\u0325\3\2\2\2\u00f1\u0335\3\2\2\2\u00f3\u0337\3\2\2\2\u00f5\u0342\3\2"+
|
||||
"\2\2\u00f7\u034f\3\2\2\2\u00f9\u0357\3\2\2\2\u00fb\u035b\3\2\2\2\u00fd"+
|
||||
"\u00fe\7\u0080\2\2\u00fe\4\3\2\2\2\u00ff\u0100\7<\2\2\u0100\6\3\2\2\2"+
|
||||
"\u0101\u0102\7i\2\2\u0102\u0103\7q\2\2\u0103\u0104\7v\2\2\u0104\u0105"+
|
||||
"\7q\2\2\u0105\b\3\2\2\2\u0106\u0107\7\'\2\2\u0107\u0108\7q\2\2\u0108\u0109"+
|
||||
"\7w\2\2\u0109\u010a\7v\2\2\u010a\u010b\7r\2\2\u010b\u010c\7w\2\2\u010c"+
|
||||
"\u010d\7v\2\2\u010d\n\3\2\2\2\u010e\u010f\7\'\2\2\u010f\u0110\7n\2\2\u0110"+
|
||||
"\u0111\7c\2\2\u0111\u0112\7w\2\2\u0112\u0113\7p\2\2\u0113\u0114\7e\2\2"+
|
||||
"\u0114\u0115\7j\2\2\u0115\u0116\7g\2\2\u0116\u0117\7t\2\2\u0117\f\3\2"+
|
||||
"\2\2\u0118\u0119\7\'\2\2\u0119\u011a\7|\2\2\u011a\u011b\7g\2\2\u011b\u011c"+
|
||||
"\7t\2\2\u011c\u011d\7q\2\2\u011d\u011e\7r\2\2\u011e\u011f\7c\2\2\u011f"+
|
||||
"\u0120\7i\2\2\u0120\u0121\7g\2\2\u0121\16\3\2\2\2\u0122\u0123\7\'\2\2"+
|
||||
"\u0123\u0124\7|\2\2\u0124\u0125\7r\2\2\u0125\u0126\7t\2\2\u0126\u0127"+
|
||||
"\7g\2\2\u0127\u0128\7u\2\2\u0128\u0129\7g\2\2\u0129\u012a\7t\2\2\u012a"+
|
||||
"\u012b\7x\2\2\u012b\u012c\7g\2\2\u012c\u012d\7f\2\2\u012d\20\3\2\2\2\u012e"+
|
||||
"\u012f\7\'\2\2\u012f\u0130\7c\2\2\u0130\u0131\7f\2\2\u0131\u0132\7f\2"+
|
||||
"\2\u0132\u0133\7t\2\2\u0133\u0134\7g\2\2\u0134\u0135\7u\2\2\u0135\u0136"+
|
||||
"\7u\2\2\u0136\22\3\2\2\2\u0137\u0138\7\'\2\2\u0138\u0139\7k\2\2\u0139"+
|
||||
"\u013a\7o\2\2\u013a\u013b\7r\2\2\u013b\u013c\7q\2\2\u013c\u013d\7t\2\2"+
|
||||
"\u013d\u013e\7v\2\2\u013e\24\3\2\2\2\u013f\u0140\7\'\2\2\u0140\u0141\7"+
|
||||
"d\2\2\u0141\u0142\7t\2\2\u0142\u0143\7g\2\2\u0143\u0144\7c\2\2\u0144\u0145"+
|
||||
"\7m\2\2\u0145\u0146\7r\2\2\u0146\u0147\7q\2\2\u0147\u0148\7k\2\2\u0148"+
|
||||
"\u0149\7p\2\2\u0149\u014a\7v\2\2\u014a\26\3\2\2\2\u014b\u014c\7\'\2\2"+
|
||||
"\u014c\u014d\7c\2\2\u014d\u014e\7u\2\2\u014e\u014f\7o\2\2\u014f\u0150"+
|
||||
"\7k\2\2\u0150\u0151\7p\2\2\u0151\u0152\7e\2\2\u0152\u0153\7n\2\2\u0153"+
|
||||
"\u0154\7w\2\2\u0154\u0155\7f\2\2\u0155\u0156\7g\2\2\u0156\30\3\2\2\2\u0157"+
|
||||
"\u0158\7\'\2\2\u0158\u0159\7c\2\2\u0159\u015a\7u\2\2\u015a\u015b\7o\2"+
|
||||
"\2\u015b\u015c\7d\2\2\u015c\u015d\7k\2\2\u015d\u015e\7p\2\2\u015e\u015f"+
|
||||
"\7c\2\2\u015f\u0160\7t\2\2\u0160\u0161\7{\2\2\u0161\32\3\2\2\2\u0162\u0163"+
|
||||
"\7\'\2\2\u0163\u0164\7q\2\2\u0164\u0165\7r\2\2\u0165\u0166\7v\2\2\u0166"+
|
||||
"\u0167\7k\2\2\u0167\u0168\7q\2\2\u0168\u0169\7p\2\2\u0169\34\3\2\2\2\u016a"+
|
||||
"\u016b\7.\2\2\u016b\36\3\2\2\2\u016c\u016d\7?\2\2\u016d \3\2\2\2\u016e"+
|
||||
"\u016f\7e\2\2\u016f\u0170\7q\2\2\u0170\u0171\7p\2\2\u0171\u0172\7u\2\2"+
|
||||
"\u0172\u0173\7v\2\2\u0173\"\3\2\2\2\u0174\u0175\7w\2\2\u0175\u0176\7d"+
|
||||
"\2\2\u0176\u0177\7{\2\2\u0177\u0178\7v\2\2\u0178\u0179\7g\2\2\u0179$\3"+
|
||||
"\2\2\2\u017a\u017b\7d\2\2\u017b\u017c\7{\2\2\u017c\u017d\7v\2\2\u017d"+
|
||||
"\u017e\7g\2\2\u017e&\3\2\2\2\u017f\u0180\7w\2\2\u0180\u0181\7y\2\2\u0181"+
|
||||
"\u0182\7q\2\2\u0182\u0183\7t\2\2\u0183\u0184\7f\2\2\u0184(\3\2\2\2\u0185"+
|
||||
"\u0186\7y\2\2\u0186\u0187\7q\2\2\u0187\u0188\7t\2\2\u0188\u0189\7f\2\2"+
|
||||
"\u0189*\3\2\2\2\u018a\u018b\7h\2\2\u018b\u018c\7n\2\2\u018c\u018d\7q\2"+
|
||||
"\2\u018d\u018e\7c\2\2\u018e\u018f\7v\2\2\u018f,\3\2\2\2\u0190\u0191\7"+
|
||||
"u\2\2\u0191\u0192\7v\2\2\u0192\u0193\7t\2\2\u0193.\3\2\2\2\u0194\u0195"+
|
||||
"\7u\2\2\u0195\u0196\7v\2\2\u0196\u0197\7t\2\2\u0197\u0198\7a\2\2\u0198"+
|
||||
"\u0199\7u\2\2\u0199\60\3\2\2\2\u019a\u019b\7]\2\2\u019b\62\3\2\2\2\u019c"+
|
||||
"\u019d\7_\2\2\u019d\64\3\2\2\2\u019e\u019f\7-\2\2\u019f\u01a0\7?\2\2\u01a0"+
|
||||
"\66\3\2\2\2\u01a1\u01a2\7/\2\2\u01a2\u01a3\7?\2\2\u01a38\3\2\2\2\u01a4"+
|
||||
"\u01a5\7\61\2\2\u01a5\u01a6\7?\2\2\u01a6:\3\2\2\2\u01a7\u01a8\7,\2\2\u01a8"+
|
||||
"\u01a9\7?\2\2\u01a9<\3\2\2\2\u01aa\u01ab\7,\2\2\u01ab\u01ac\7,\2\2\u01ac"+
|
||||
"\u01ad\7?\2\2\u01ad>\3\2\2\2\u01ae\u01af\7(\2\2\u01af\u01b0\7?\2\2\u01b0"+
|
||||
"@\3\2\2\2\u01b1\u01b2\7~\2\2\u01b2\u01b3\7?\2\2\u01b3B\3\2\2\2\u01b4\u01b5"+
|
||||
"\7`\2\2\u01b5\u01b6\7?\2\2\u01b6D\3\2\2\2\u01b7\u01b8\7\'\2\2\u01b8\u01b9"+
|
||||
"\7?\2\2\u01b9F\3\2\2\2\u01ba\u01bb\7>\2\2\u01bb\u01bc\7>\2\2\u01bc\u01bd"+
|
||||
"\7?\2\2\u01bdH\3\2\2\2\u01be\u01bf\7@\2\2\u01bf\u01c0\7@\2\2\u01c0\u01c1"+
|
||||
"\7?\2\2\u01c1J\3\2\2\2\u01c2\u01c3\7-\2\2\u01c3\u01c4\7-\2\2\u01c4L\3"+
|
||||
"\2\2\2\u01c5\u01c6\7/\2\2\u01c6\u01c7\7/\2\2\u01c7N\3\2\2\2\u01c8\u01c9"+
|
||||
"\7-\2\2\u01c9P\3\2\2\2\u01ca\u01cb\7/\2\2\u01cbR\3\2\2\2\u01cc\u01cd\7"+
|
||||
",\2\2\u01cd\u01ce\7,\2\2\u01ceT\3\2\2\2\u01cf\u01d0\7,\2\2\u01d0V\3\2"+
|
||||
"\2\2\u01d1\u01d2\7\61\2\2\u01d2X\3\2\2\2\u01d3\u01d4\7\'\2\2\u01d4Z\3"+
|
||||
"\2\2\2\u01d5\u01d6\7>\2\2\u01d6\u01d7\7>\2\2\u01d7\\\3\2\2\2\u01d8\u01d9"+
|
||||
"\7@\2\2\u01d9\u01da\7@\2\2\u01da^\3\2\2\2\u01db\u01dc\7>\2\2\u01dc`\3"+
|
||||
"\2\2\2\u01dd\u01de\7@\2\2\u01deb\3\2\2\2\u01df\u01e0\7>\2\2\u01e0\u01e1"+
|
||||
"\7?\2\2\u01e1d\3\2\2\2\u01e2\u01e3\7@\2\2\u01e3\u01e4\7?\2\2\u01e4f\3"+
|
||||
"\2\2\2\u01e5\u01e6\7?\2\2\u01e6\u01e7\7?\2\2\u01e7h\3\2\2\2\u01e8\u01e9"+
|
||||
"\7#\2\2\u01e9\u01ea\7?\2\2\u01eaj\3\2\2\2\u01eb\u01ec\7`\2\2\u01ecl\3"+
|
||||
"\2\2\2\u01ed\u01ee\7~\2\2\u01een\3\2\2\2\u01ef\u01f0\7v\2\2\u01f0\u01f1"+
|
||||
"\7q\2\2\u01f1p\3\2\2\2\u01f2\u01f3\7u\2\2\u01f3\u01f4\7v\2\2\u01f4\u01f5"+
|
||||
"\7g\2\2\u01f5\u01f6\7r\2\2\u01f6r\3\2\2\2\u01f7\u01f8\7c\2\2\u01f8\u01f9"+
|
||||
"\7p\2\2\u01f9\u01fa\7f\2\2\u01fat\3\2\2\2\u01fb\u01fc\7q\2\2\u01fc\u01fd"+
|
||||
"\7t\2\2\u01fdv\3\2\2\2\u01fe\u01ff\7z\2\2\u01ff\u0200\7q\2\2\u0200\u0201"+
|
||||
"\7t\2\2\u0201x\3\2\2\2\u0202\u0203\7p\2\2\u0203\u0204\7q\2\2\u0204\u0205"+
|
||||
"\7v\2\2\u0205z\3\2\2\2\u0206\u0207\7*\2\2\u0207|\3\2\2\2\u0208\u0209\7"+
|
||||
"+\2\2\u0209~\3\2\2\2\u020a\u020b\7c\2\2\u020b\u020c\7u\2\2\u020c\u0080"+
|
||||
"\3\2\2\2\u020d\u020e\7B\2\2\u020e\u0082\3\2\2\2\u020f\u0210\7t\2\2\u0210"+
|
||||
"\u0211\7g\2\2\u0211\u0212\7v\2\2\u0212\u0213\7w\2\2\u0213\u0214\7t\2\2"+
|
||||
"\u0214\u0215\7p\2\2\u0215\u0084\3\2\2\2\u0216\u0217\7d\2\2\u0217\u0218"+
|
||||
"\7t\2\2\u0218\u0219\7g\2\2\u0219\u021a\7c\2\2\u021a\u021b\7m\2\2\u021b"+
|
||||
"\u0086\3\2\2\2\u021c\u021d\7e\2\2\u021d\u021e\7q\2\2\u021e\u021f\7p\2"+
|
||||
"\2\u021f\u0220\7v\2\2\u0220\u0221\7k\2\2\u0221\u0222\7p\2\2\u0222\u0223"+
|
||||
"\7w\2\2\u0223\u0224\7g\2\2\u0224\u0088\3\2\2\2\u0225\u0226\7\60\2\2\u0226"+
|
||||
"\u008a\3\2\2\2\u0227\u0228\7C\2\2\u0228\u008c\3\2\2\2\u0229\u022a\7Z\2"+
|
||||
"\2\u022a\u008e\3\2\2\2\u022b\u022c\7[\2\2\u022c\u0090\3\2\2\2\u022d\u022e"+
|
||||
"\7C\2\2\u022e\u022f\7Z\2\2\u022f\u0092\3\2\2\2\u0230\u0231\7C\2\2\u0231"+
|
||||
"\u0232\7[\2\2\u0232\u0094\3\2\2\2\u0233\u0234\7Z\2\2\u0234\u0235\7[\2"+
|
||||
"\2\u0235\u0096\3\2\2\2\u0236\u0237\7R\2\2\u0237\u0238\7e\2\2\u0238\u0098"+
|
||||
"\3\2\2\2\u0239\u023a\7R\2\2\u023a\u023b\7|\2\2\u023b\u009a\3\2\2\2\u023c"+
|
||||
"\u023d\7R\2\2\u023d\u023e\7p\2\2\u023e\u009c\3\2\2\2\u023f\u0240\7R\2"+
|
||||
"\2\u0240\u0241\7x\2\2\u0241\u009e\3\2\2\2\u0242\u0243\7\60\2\2\u0243\u0244"+
|
||||
"\7y\2\2\u0244\u00a0\3\2\2\2\u0245\u0246\7v\2\2\u0246\u0247\7t\2\2\u0247"+
|
||||
"\u0248\7w\2\2\u0248\u0249\7g\2\2\u0249\u00a2\3\2\2\2\u024a\u024b\7h\2"+
|
||||
"\2\u024b\u024c\7c\2\2\u024c\u024d\7n\2\2\u024d\u024e\7u\2\2\u024e\u024f"+
|
||||
"\7g\2\2\u024f\u00a4\3\2\2\2\u0250\u0251\7\'\2\2\u0251\u0252\7c\2\2\u0252"+
|
||||
"\u0253\7u\2\2\u0253\u0254\7o\2\2\u0254\u00a6\3\2\2\2\u0255\u0256\7u\2"+
|
||||
"\2\u0256\u0257\7w\2\2\u0257\u0258\7d\2\2\u0258\u00a8\3\2\2\2\u0259\u025a"+
|
||||
"\7/\2\2\u025a\u025b\7@\2\2\u025b\u00aa\3\2\2\2\u025c\u025d\7}\2\2\u025d"+
|
||||
"\u00ac\3\2\2\2\u025e\u025f\7\177\2\2\u025f\u00ae\3\2\2\2\u0260\u0261\7"+
|
||||
"c\2\2\u0261\u0262\7u\2\2\u0262\u0263\7o\2\2\u0263\u0264\7u\2\2\u0264\u0265"+
|
||||
"\7w\2\2\u0265\u0266\7d\2\2\u0266\u00b0\3\2\2\2\u0267\u0268\7e\2\2\u0268"+
|
||||
"\u0269\7n\2\2\u0269\u026a\7q\2\2\u026a\u026b\7d\2\2\u026b\u026c\7d\2\2"+
|
||||
"\u026c\u026d\7g\2\2\u026d\u026e\7t\2\2\u026e\u026f\7u\2\2\u026f\u00b2"+
|
||||
"\3\2\2\2\u0270\u0271\7u\2\2\u0271\u0272\7v\2\2\u0272\u0273\7c\2\2\u0273"+
|
||||
"\u0274\7e\2\2\u0274\u0275\7m\2\2\u0275\u00b4\3\2\2\2\u0276\u0277\7k\2"+
|
||||
"\2\u0277\u0278\7h\2\2\u0278\u00b6\3\2\2\2\u0279\u027a\7g\2\2\u027a\u027b"+
|
||||
"\7n\2\2\u027b\u027c\7u\2\2\u027c\u027d\7g\2\2\u027d\u00b8\3\2\2\2\u027e"+
|
||||
"\u027f\7k\2\2\u027f\u0280\7h\2\2\u0280\u0281\7a\2\2\u0281\u0282\7e\2\2"+
|
||||
"\u0282\u0283\7u\2\2\u0283\u00ba\3\2\2\2\u0284\u0285\7k\2\2\u0285\u0286"+
|
||||
"\7h\2\2\u0286\u0287\7a\2\2\u0287\u0288\7e\2\2\u0288\u0289\7e\2\2\u0289"+
|
||||
"\u00bc\3\2\2\2\u028a\u028b\7k\2\2\u028b\u028c\7h\2\2\u028c\u028d\7a\2"+
|
||||
"\2\u028d\u028e\7g\2\2\u028e\u028f\7s\2\2\u028f\u00be\3\2\2\2\u0290\u0291"+
|
||||
"\7k\2\2\u0291\u0292\7h\2\2\u0292\u0293\7a\2\2\u0293\u0294\7|\2\2\u0294"+
|
||||
"\u00c0\3\2\2\2\u0295\u0296\7k\2\2\u0296\u0297\7h\2\2\u0297\u0298\7a\2"+
|
||||
"\2\u0298\u0299\7p\2\2\u0299\u029a\7g\2\2\u029a\u00c2\3\2\2\2\u029b\u029c"+
|
||||
"\7k\2\2\u029c\u029d\7h\2\2\u029d\u029e\7a\2\2\u029e\u029f\7p\2\2\u029f"+
|
||||
"\u02a0\7|\2\2\u02a0\u00c4\3\2\2\2\u02a1\u02a2\7k\2\2\u02a2\u02a3\7h\2"+
|
||||
"\2\u02a3\u02a4\7a\2\2\u02a4\u02a5\7r\2\2\u02a5\u02a6\7n\2\2\u02a6\u00c6"+
|
||||
"\3\2\2\2\u02a7\u02a8\7k\2\2\u02a8\u02a9\7h\2\2\u02a9\u02aa\7a\2\2\u02aa"+
|
||||
"\u02ab\7r\2\2\u02ab\u02ac\7q\2\2\u02ac\u02ad\7u\2\2\u02ad\u00c8\3\2\2"+
|
||||
"\2\u02ae\u02af\7k\2\2\u02af\u02b0\7h\2\2\u02b0\u02b1\7a\2\2\u02b1\u02b2"+
|
||||
"\7o\2\2\u02b2\u02b3\7k\2\2\u02b3\u00ca\3\2\2\2\u02b4\u02b5\7k\2\2\u02b5"+
|
||||
"\u02b6\7h\2\2\u02b6\u02b7\7a\2\2\u02b7\u02b8\7p\2\2\u02b8\u02b9\7g\2\2"+
|
||||
"\u02b9\u02ba\7i\2\2\u02ba\u00cc\3\2\2\2\u02bb\u02bc\7k\2\2\u02bc\u02bd"+
|
||||
"\7h\2\2\u02bd\u02be\7a\2\2\u02be\u02bf\7x\2\2\u02bf\u02c0\7u\2\2\u02c0"+
|
||||
"\u00ce\3\2\2\2\u02c1\u02c2\7k\2\2\u02c2\u02c3\7h\2\2\u02c3\u02c4\7a\2"+
|
||||
"\2\u02c4\u02c5\7x\2\2\u02c5\u02c6\7e\2\2\u02c6\u00d0\3\2\2\2\u02c7\u02c8"+
|
||||
"\7h\2\2\u02c8\u02c9\7q\2\2\u02c9\u02ca\7t\2\2\u02ca\u00d2\3\2\2\2\u02cb"+
|
||||
"\u02cc\7k\2\2\u02cc\u02cd\7p\2\2\u02cd\u00d4\3\2\2\2\u02ce\u02cf\7y\2"+
|
||||
"\2\u02cf\u02d0\7j\2\2\u02d0\u02d1\7k\2\2\u02d1\u02d2\7n\2\2\u02d2\u02d3"+
|
||||
"\7g\2\2\u02d3\u00d6\3\2\2\2\u02d4\u02d5\7t\2\2\u02d5\u02d6\7g\2\2\u02d6"+
|
||||
"\u02d7\7r\2\2\u02d7\u02d8\7g\2\2\u02d8\u02d9\7c\2\2\u02d9\u02da\7v\2\2"+
|
||||
"\u02da\u00d8\3\2\2\2\u02db\u02dc\7w\2\2\u02dc\u02dd\7p\2\2\u02dd\u02de"+
|
||||
"\7v\2\2\u02de\u02df\7k\2\2\u02df\u02e0\7n\2\2\u02e0\u00da\3\2\2\2\u02e1"+
|
||||
"\u02e5\t\2\2\2\u02e2\u02e4\t\3\2\2\u02e3\u02e2\3\2\2\2\u02e4\u02e7\3\2"+
|
||||
"\2\2\u02e5\u02e3\3\2\2\2\u02e5\u02e6\3\2\2\2\u02e6\u02e8\3\2\2\2\u02e7"+
|
||||
"\u02e5\3\2\2\2\u02e8\u02e9\5\u00ddo\2\u02e9\u02ea\3\2\2\2\u02ea\u02eb"+
|
||||
"\bn\2\2\u02eb\u00dc\3\2\2\2\u02ec\u02f0\7=\2\2\u02ed\u02ef\n\2\2\2\u02ee"+
|
||||
"\u02ed\3\2\2\2\u02ef\u02f2\3\2\2\2\u02f0\u02ee\3\2\2\2\u02f0\u02f1\3\2"+
|
||||
"\2\2\u02f1\u02f3\3\2\2\2\u02f2\u02f0\3\2\2\2\u02f3\u02f4\bo\2\2\u02f4"+
|
||||
"\u00de\3\2\2\2\u02f5\u02f6\t\3\2\2\u02f6\u02f7\3\2\2\2\u02f7\u02f8\bp"+
|
||||
"\3\2\u02f8\u00e0\3\2\2\2\u02f9\u02fb\t\2\2\2\u02fa\u02f9\3\2\2\2\u02fb"+
|
||||
"\u02fc\3\2\2\2\u02fc\u02fa\3\2\2\2\u02fc\u02fd\3\2\2\2\u02fd\u00e2\3\2"+
|
||||
"\2\2\u02fe\u0302\t\4\2\2\u02ff\u0301\t\5\2\2\u0300\u02ff\3\2\2\2\u0301"+
|
||||
"\u0304\3\2\2\2\u0302\u0300\3\2\2\2\u0302\u0303\3\2\2\2\u0303\u00e4\3\2"+
|
||||
"\2\2\u0304\u0302\3\2\2\2\u0305\u030d\4\62;\2\u0306\u0308\4\63;\2\u0307"+
|
||||
"\u0309\4\62;\2\u0308\u0307\3\2\2\2\u0309\u030a\3\2\2\2\u030a\u0308\3\2"+
|
||||
"\2\2\u030a\u030b\3\2\2\2\u030b\u030d\3\2\2\2\u030c\u0305\3\2\2\2\u030c"+
|
||||
"\u0306\3\2\2\2\u030d\u00e6\3\2\2\2\u030e\u0310\7&\2\2\u030f\u0311\t\6"+
|
||||
"\2\2\u0310\u030f\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u0310\3\2\2\2\u0312"+
|
||||
"\u0313\3\2\2\2\u0313\u00e8\3\2\2\2\u0314\u0316\7\'\2\2\u0315\u0317\4\62"+
|
||||
"\63\2\u0316\u0315\3\2\2\2\u0317\u0318\3\2\2\2\u0318\u0316\3\2\2\2\u0318"+
|
||||
"\u0319\3\2\2\2\u0319\u00ea\3\2\2\2\u031a\u031b\7(\2\2\u031b\u00ec\3\2"+
|
||||
"\2\2\u031c\u0322\5\u00efx\2\u031d\u031f\t\7\2\2\u031e\u0320\t\b\2\2\u031f"+
|
||||
"\u031e\3\2\2\2\u031f\u0320\3\2\2\2\u0320\u0321\3\2\2\2\u0321\u0323\5\u00ef"+
|
||||
"x\2\u0322\u031d\3\2\2\2\u0322\u0323\3\2\2\2\u0323\u00ee\3\2\2\2\u0324"+
|
||||
"\u0326\4\62;\2\u0325\u0324\3\2\2\2\u0326\u0327\3\2\2\2\u0327\u0325\3\2"+
|
||||
"\2\2\u0327\u0328\3\2\2\2\u0328\u032f\3\2\2\2\u0329\u032b\7\60\2\2\u032a"+
|
||||
"\u032c\4\62;\2\u032b\u032a\3\2\2\2\u032c\u032d\3\2\2\2\u032d\u032b\3\2"+
|
||||
"\2\2\u032d\u032e\3\2\2\2\u032e\u0330\3\2\2\2\u032f\u0329\3\2\2\2\u032f"+
|
||||
"\u0330\3\2\2\2\u0330\u00f0\3\2\2\2\u0331\u0332\7^\2\2\u0332\u0336\13\2"+
|
||||
"\2\2\u0333\u0334\7^\2\2\u0334\u0336\5\u00e1q\2\u0335\u0331\3\2\2\2\u0335"+
|
||||
"\u0333\3\2\2\2\u0336\u00f2\3\2\2\2\u0337\u033c\7$\2\2\u0338\u033b\5\u00f1"+
|
||||
"y\2\u0339\u033b\n\t\2\2\u033a\u0338\3\2\2\2\u033a\u0339\3\2\2\2\u033b"+
|
||||
"\u033e\3\2\2\2\u033c\u033a\3\2\2\2\u033c\u033d\3\2\2\2\u033d\u033f\3\2"+
|
||||
"\2\2\u033e\u033c\3\2\2\2\u033f\u0340\7$\2\2\u0340\u0341\bz\4\2\u0341\u00f4"+
|
||||
"\3\2\2\2\u0342\u0343\7}\2\2\u0343\u0344\7}\2\2\u0344\u0346\3\2\2\2\u0345"+
|
||||
"\u0347\13\2\2\2\u0346\u0345\3\2\2\2\u0347\u0348\3\2\2\2\u0348\u0349\3"+
|
||||
"\2\2\2\u0348\u0346\3\2\2\2\u0349\u034a\3\2\2\2\u034a\u034b\7\177\2\2\u034b"+
|
||||
"\u034c\7\177\2\2\u034c\u034d\3\2\2\2\u034d\u034e\b{\5\2\u034e\u00f6\3"+
|
||||
"\2\2\2\u034f\u0352\7)\2\2\u0350\u0353\5\u00f1y\2\u0351\u0353\n\t\2\2\u0352"+
|
||||
"\u0350\3\2\2\2\u0352\u0351\3\2\2\2\u0353\u0354\3\2\2\2\u0354\u0355\7)"+
|
||||
"\2\2\u0355\u0356\b|\6\2\u0356\u00f8\3\2\2\2\u0357\u0358\7B\2\2\u0358\u0359"+
|
||||
"\7|\2\2\u0359\u035a\7r\2\2\u035a\u00fa\3\2\2\2\u035b\u035c\7]\2\2\u035c"+
|
||||
"\u035d\7_\2\2\u035d\u00fc\3\2\2\2\26\2\u02e5\u02f0\u02fc\u0302\u030a\u030c"+
|
||||
"\u0310\u0312\u0318\u031f\u0322\u0327\u032d\u032f\u0335\u033a\u033c\u0348"+
|
||||
"\u0352\7\2\3\2\b\2\2\3z\2\3{\3\3|\4";
|
||||
"w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\3\2\3\2\3\3"+
|
||||
"\3\3\3\4\3\4\3\4\3\4\3\4\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3"+
|
||||
"\6\3\6\3\6\3\6\3\6\3\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b"+
|
||||
"\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3"+
|
||||
"\t\3\t\3\t\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3"+
|
||||
"\13\3\13\3\13\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f"+
|
||||
"\3\f\3\f\3\f\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\16"+
|
||||
"\3\16\3\16\3\16\3\16\3\16\3\17\3\17\3\20\3\20\3\21\3\21\3\21\3\21\3\21"+
|
||||
"\3\21\3\22\3\22\3\22\3\22\3\22\3\22\3\23\3\23\3\23\3\23\3\23\3\24\3\24"+
|
||||
"\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26"+
|
||||
"\3\26\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31\3\32"+
|
||||
"\3\32\3\33\3\33\3\33\3\34\3\34\3\34\3\35\3\35\3\35\3\36\3\36\3\36\3\37"+
|
||||
"\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3\"\3\"\3\"\3#\3#\3#\3$\3$\3$\3$\3%"+
|
||||
"\3%\3%\3%\3&\3&\3&\3\'\3\'\3\'\3(\3(\3)\3)\3*\3*\3*\3+\3+\3,\3,\3-\3-"+
|
||||
"\3.\3.\3.\3/\3/\3/\3\60\3\60\3\61\3\61\3\62\3\62\3\62\3\63\3\63\3\63\3"+
|
||||
"\64\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\67\3\67\38\38\38\39\39\39\39"+
|
||||
"\39\3:\3:\3:\3:\3;\3;\3;\3<\3<\3<\3<\3=\3=\3=\3=\3>\3>\3?\3?\3@\3@\3@"+
|
||||
"\3A\3A\3B\3B\3B\3B\3B\3B\3B\3C\3C\3C\3C\3C\3C\3D\3D\3D\3D\3D\3D\3D\3D"+
|
||||
"\3D\3E\3E\3F\3F\3G\3G\3H\3H\3I\3I\3I\3J\3J\3J\3K\3K\3K\3L\3L\3L\3M\3M"+
|
||||
"\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3Q\3Q\3R\3R\3R\3R\3R\3R\3S\3S"+
|
||||
"\3S\3S\3S\3T\3T\3T\3T\3U\3U\3U\3V\3V\3W\3W\3X\3X\3X\3X\3X\3X\3X\3Y\3Y"+
|
||||
"\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3\\\3\\\3\\\3\\\3\\\3"+
|
||||
"]\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3`\3`\3`\3`\3`\3"+
|
||||
"a\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3d\3d\3d\3d\3d\3"+
|
||||
"d\3d\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3g\3g\3h\3h\3"+
|
||||
"h\3h\3h\3h\3i\3i\3i\3i\3j\3j\3j\3k\3k\3k\3k\3k\3k\3l\3l\3l\3l\3l\3l\3"+
|
||||
"l\3m\3m\3m\3m\3m\3m\3n\3n\3n\3n\3n\3o\3o\7o\u02eb\no\fo\16o\u02ee\13o"+
|
||||
"\3o\3o\3o\3o\3p\3p\7p\u02f6\np\fp\16p\u02f9\13p\3p\3p\3q\3q\3q\3q\3r\6"+
|
||||
"r\u0302\nr\rr\16r\u0303\3s\3s\7s\u0308\ns\fs\16s\u030b\13s\3t\3t\3t\6"+
|
||||
"t\u0310\nt\rt\16t\u0311\5t\u0314\nt\3u\3u\6u\u0318\nu\ru\16u\u0319\3v"+
|
||||
"\3v\6v\u031e\nv\rv\16v\u031f\3w\3w\3x\3x\3x\5x\u0327\nx\3x\5x\u032a\n"+
|
||||
"x\3y\6y\u032d\ny\ry\16y\u032e\3y\3y\6y\u0333\ny\ry\16y\u0334\5y\u0337"+
|
||||
"\ny\3z\3z\3z\3z\5z\u033d\nz\3{\3{\3{\7{\u0342\n{\f{\16{\u0345\13{\3{\3"+
|
||||
"{\3{\3|\3|\3|\3|\6|\u034e\n|\r|\16|\u034f\3|\3|\3|\3|\3|\3}\3}\3}\5}\u035a"+
|
||||
"\n}\3}\3}\3}\3~\3~\3~\3~\3\177\3\177\3\177\3\u034f\2\u0080\3\3\5\4\7\5"+
|
||||
"\t\6\13\7\r\b\17\t\21\n\23\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23"+
|
||||
"%\24\'\25)\26+\27-\30/\31\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G"+
|
||||
"%I&K\'M(O)Q*S+U,W-Y.[/]\60_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{"+
|
||||
"?}@\177A\u0081B\u0083C\u0085D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091"+
|
||||
"J\u0093K\u0095L\u0097M\u0099N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5"+
|
||||
"T\u00a7U\u00a9V\u00abW\u00adX\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9"+
|
||||
"^\u00bb_\u00bd`\u00bfa\u00c1b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cd"+
|
||||
"h\u00cfi\u00d1j\u00d3k\u00d5l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1"+
|
||||
"r\u00e3s\u00e5t\u00e7u\u00e9v\u00ebw\u00edx\u00efy\u00f1\2\u00f3\2\u00f5"+
|
||||
"z\u00f7{\u00f9|\u00fb}\u00fd~\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\"+
|
||||
"aac|\6\2\62;C\\aac|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u0374"+
|
||||
"\2\3\3\2\2\2\2\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2"+
|
||||
"\2\2\2\17\3\2\2\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2"+
|
||||
"\2\31\3\2\2\2\2\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2"+
|
||||
"\2\2\2%\3\2\2\2\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2"+
|
||||
"\2\2\61\3\2\2\2\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3"+
|
||||
"\2\2\2\2=\3\2\2\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2"+
|
||||
"\2\2I\3\2\2\2\2K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2"+
|
||||
"U\3\2\2\2\2W\3\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3"+
|
||||
"\2\2\2\2c\3\2\2\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2"+
|
||||
"\2\2o\3\2\2\2\2q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2"+
|
||||
"{\3\2\2\2\2}\3\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085"+
|
||||
"\3\2\2\2\2\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2"+
|
||||
"\2\2\u008f\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097"+
|
||||
"\3\2\2\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2"+
|
||||
"\2\2\u00a1\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9"+
|
||||
"\3\2\2\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2"+
|
||||
"\2\2\u00b3\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb"+
|
||||
"\3\2\2\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2"+
|
||||
"\2\2\u00c5\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd"+
|
||||
"\3\2\2\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2"+
|
||||
"\2\2\u00d7\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df"+
|
||||
"\3\2\2\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2"+
|
||||
"\2\2\u00e9\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f5"+
|
||||
"\3\2\2\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\2\u00fd\3\2\2"+
|
||||
"\2\3\u00ff\3\2\2\2\5\u0101\3\2\2\2\7\u0103\3\2\2\2\t\u0108\3\2\2\2\13"+
|
||||
"\u0110\3\2\2\2\r\u011a\3\2\2\2\17\u0124\3\2\2\2\21\u0130\3\2\2\2\23\u0139"+
|
||||
"\3\2\2\2\25\u0141\3\2\2\2\27\u014d\3\2\2\2\31\u0159\3\2\2\2\33\u0164\3"+
|
||||
"\2\2\2\35\u016c\3\2\2\2\37\u016e\3\2\2\2!\u0170\3\2\2\2#\u0176\3\2\2\2"+
|
||||
"%\u017c\3\2\2\2\'\u0181\3\2\2\2)\u0187\3\2\2\2+\u018c\3\2\2\2-\u0192\3"+
|
||||
"\2\2\2/\u0196\3\2\2\2\61\u019c\3\2\2\2\63\u019e\3\2\2\2\65\u01a0\3\2\2"+
|
||||
"\2\67\u01a3\3\2\2\29\u01a6\3\2\2\2;\u01a9\3\2\2\2=\u01ac\3\2\2\2?\u01b0"+
|
||||
"\3\2\2\2A\u01b3\3\2\2\2C\u01b6\3\2\2\2E\u01b9\3\2\2\2G\u01bc\3\2\2\2I"+
|
||||
"\u01c0\3\2\2\2K\u01c4\3\2\2\2M\u01c7\3\2\2\2O\u01ca\3\2\2\2Q\u01cc\3\2"+
|
||||
"\2\2S\u01ce\3\2\2\2U\u01d1\3\2\2\2W\u01d3\3\2\2\2Y\u01d5\3\2\2\2[\u01d7"+
|
||||
"\3\2\2\2]\u01da\3\2\2\2_\u01dd\3\2\2\2a\u01df\3\2\2\2c\u01e1\3\2\2\2e"+
|
||||
"\u01e4\3\2\2\2g\u01e7\3\2\2\2i\u01ea\3\2\2\2k\u01ed\3\2\2\2m\u01ef\3\2"+
|
||||
"\2\2o\u01f1\3\2\2\2q\u01f4\3\2\2\2s\u01f9\3\2\2\2u\u01fd\3\2\2\2w\u0200"+
|
||||
"\3\2\2\2y\u0204\3\2\2\2{\u0208\3\2\2\2}\u020a\3\2\2\2\177\u020c\3\2\2"+
|
||||
"\2\u0081\u020f\3\2\2\2\u0083\u0211\3\2\2\2\u0085\u0218\3\2\2\2\u0087\u021e"+
|
||||
"\3\2\2\2\u0089\u0227\3\2\2\2\u008b\u0229\3\2\2\2\u008d\u022b\3\2\2\2\u008f"+
|
||||
"\u022d\3\2\2\2\u0091\u022f\3\2\2\2\u0093\u0232\3\2\2\2\u0095\u0235\3\2"+
|
||||
"\2\2\u0097\u0238\3\2\2\2\u0099\u023b\3\2\2\2\u009b\u023e\3\2\2\2\u009d"+
|
||||
"\u0241\3\2\2\2\u009f\u0244\3\2\2\2\u00a1\u0247\3\2\2\2\u00a3\u024c\3\2"+
|
||||
"\2\2\u00a5\u0252\3\2\2\2\u00a7\u0257\3\2\2\2\u00a9\u025b\3\2\2\2\u00ab"+
|
||||
"\u025e\3\2\2\2\u00ad\u0260\3\2\2\2\u00af\u0262\3\2\2\2\u00b1\u0269\3\2"+
|
||||
"\2\2\u00b3\u026f\3\2\2\2\u00b5\u0278\3\2\2\2\u00b7\u027b\3\2\2\2\u00b9"+
|
||||
"\u0280\3\2\2\2\u00bb\u0286\3\2\2\2\u00bd\u028c\3\2\2\2\u00bf\u0292\3\2"+
|
||||
"\2\2\u00c1\u0297\3\2\2\2\u00c3\u029d\3\2\2\2\u00c5\u02a3\3\2\2\2\u00c7"+
|
||||
"\u02a9\3\2\2\2\u00c9\u02b0\3\2\2\2\u00cb\u02b6\3\2\2\2\u00cd\u02bd\3\2"+
|
||||
"\2\2\u00cf\u02c3\3\2\2\2\u00d1\u02c9\3\2\2\2\u00d3\u02cd\3\2\2\2\u00d5"+
|
||||
"\u02d0\3\2\2\2\u00d7\u02d6\3\2\2\2\u00d9\u02dd\3\2\2\2\u00db\u02e3\3\2"+
|
||||
"\2\2\u00dd\u02e8\3\2\2\2\u00df\u02f3\3\2\2\2\u00e1\u02fc\3\2\2\2\u00e3"+
|
||||
"\u0301\3\2\2\2\u00e5\u0305\3\2\2\2\u00e7\u0313\3\2\2\2\u00e9\u0315\3\2"+
|
||||
"\2\2\u00eb\u031b\3\2\2\2\u00ed\u0321\3\2\2\2\u00ef\u0323\3\2\2\2\u00f1"+
|
||||
"\u032c\3\2\2\2\u00f3\u033c\3\2\2\2\u00f5\u033e\3\2\2\2\u00f7\u0349\3\2"+
|
||||
"\2\2\u00f9\u0356\3\2\2\2\u00fb\u035e\3\2\2\2\u00fd\u0362\3\2\2\2\u00ff"+
|
||||
"\u0100\7\u0080\2\2\u0100\4\3\2\2\2\u0101\u0102\7<\2\2\u0102\6\3\2\2\2"+
|
||||
"\u0103\u0104\7i\2\2\u0104\u0105\7q\2\2\u0105\u0106\7v\2\2\u0106\u0107"+
|
||||
"\7q\2\2\u0107\b\3\2\2\2\u0108\u0109\7\'\2\2\u0109\u010a\7q\2\2\u010a\u010b"+
|
||||
"\7w\2\2\u010b\u010c\7v\2\2\u010c\u010d\7r\2\2\u010d\u010e\7w\2\2\u010e"+
|
||||
"\u010f\7v\2\2\u010f\n\3\2\2\2\u0110\u0111\7\'\2\2\u0111\u0112\7n\2\2\u0112"+
|
||||
"\u0113\7c\2\2\u0113\u0114\7w\2\2\u0114\u0115\7p\2\2\u0115\u0116\7e\2\2"+
|
||||
"\u0116\u0117\7j\2\2\u0117\u0118\7g\2\2\u0118\u0119\7t\2\2\u0119\f\3\2"+
|
||||
"\2\2\u011a\u011b\7\'\2\2\u011b\u011c\7|\2\2\u011c\u011d\7g\2\2\u011d\u011e"+
|
||||
"\7t\2\2\u011e\u011f\7q\2\2\u011f\u0120\7r\2\2\u0120\u0121\7c\2\2\u0121"+
|
||||
"\u0122\7i\2\2\u0122\u0123\7g\2\2\u0123\16\3\2\2\2\u0124\u0125\7\'\2\2"+
|
||||
"\u0125\u0126\7|\2\2\u0126\u0127\7r\2\2\u0127\u0128\7t\2\2\u0128\u0129"+
|
||||
"\7g\2\2\u0129\u012a\7u\2\2\u012a\u012b\7g\2\2\u012b\u012c\7t\2\2\u012c"+
|
||||
"\u012d\7x\2\2\u012d\u012e\7g\2\2\u012e\u012f\7f\2\2\u012f\20\3\2\2\2\u0130"+
|
||||
"\u0131\7\'\2\2\u0131\u0132\7c\2\2\u0132\u0133\7f\2\2\u0133\u0134\7f\2"+
|
||||
"\2\u0134\u0135\7t\2\2\u0135\u0136\7g\2\2\u0136\u0137\7u\2\2\u0137\u0138"+
|
||||
"\7u\2\2\u0138\22\3\2\2\2\u0139\u013a\7\'\2\2\u013a\u013b\7k\2\2\u013b"+
|
||||
"\u013c\7o\2\2\u013c\u013d\7r\2\2\u013d\u013e\7q\2\2\u013e\u013f\7t\2\2"+
|
||||
"\u013f\u0140\7v\2\2\u0140\24\3\2\2\2\u0141\u0142\7\'\2\2\u0142\u0143\7"+
|
||||
"d\2\2\u0143\u0144\7t\2\2\u0144\u0145\7g\2\2\u0145\u0146\7c\2\2\u0146\u0147"+
|
||||
"\7m\2\2\u0147\u0148\7r\2\2\u0148\u0149\7q\2\2\u0149\u014a\7k\2\2\u014a"+
|
||||
"\u014b\7p\2\2\u014b\u014c\7v\2\2\u014c\26\3\2\2\2\u014d\u014e\7\'\2\2"+
|
||||
"\u014e\u014f\7c\2\2\u014f\u0150\7u\2\2\u0150\u0151\7o\2\2\u0151\u0152"+
|
||||
"\7k\2\2\u0152\u0153\7p\2\2\u0153\u0154\7e\2\2\u0154\u0155\7n\2\2\u0155"+
|
||||
"\u0156\7w\2\2\u0156\u0157\7f\2\2\u0157\u0158\7g\2\2\u0158\30\3\2\2\2\u0159"+
|
||||
"\u015a\7\'\2\2\u015a\u015b\7c\2\2\u015b\u015c\7u\2\2\u015c\u015d\7o\2"+
|
||||
"\2\u015d\u015e\7d\2\2\u015e\u015f\7k\2\2\u015f\u0160\7p\2\2\u0160\u0161"+
|
||||
"\7c\2\2\u0161\u0162\7t\2\2\u0162\u0163\7{\2\2\u0163\32\3\2\2\2\u0164\u0165"+
|
||||
"\7\'\2\2\u0165\u0166\7q\2\2\u0166\u0167\7r\2\2\u0167\u0168\7v\2\2\u0168"+
|
||||
"\u0169\7k\2\2\u0169\u016a\7q\2\2\u016a\u016b\7p\2\2\u016b\34\3\2\2\2\u016c"+
|
||||
"\u016d\7.\2\2\u016d\36\3\2\2\2\u016e\u016f\7?\2\2\u016f \3\2\2\2\u0170"+
|
||||
"\u0171\7e\2\2\u0171\u0172\7q\2\2\u0172\u0173\7p\2\2\u0173\u0174\7u\2\2"+
|
||||
"\u0174\u0175\7v\2\2\u0175\"\3\2\2\2\u0176\u0177\7w\2\2\u0177\u0178\7d"+
|
||||
"\2\2\u0178\u0179\7{\2\2\u0179\u017a\7v\2\2\u017a\u017b\7g\2\2\u017b$\3"+
|
||||
"\2\2\2\u017c\u017d\7d\2\2\u017d\u017e\7{\2\2\u017e\u017f\7v\2\2\u017f"+
|
||||
"\u0180\7g\2\2\u0180&\3\2\2\2\u0181\u0182\7w\2\2\u0182\u0183\7y\2\2\u0183"+
|
||||
"\u0184\7q\2\2\u0184\u0185\7t\2\2\u0185\u0186\7f\2\2\u0186(\3\2\2\2\u0187"+
|
||||
"\u0188\7y\2\2\u0188\u0189\7q\2\2\u0189\u018a\7t\2\2\u018a\u018b\7f\2\2"+
|
||||
"\u018b*\3\2\2\2\u018c\u018d\7h\2\2\u018d\u018e\7n\2\2\u018e\u018f\7q\2"+
|
||||
"\2\u018f\u0190\7c\2\2\u0190\u0191\7v\2\2\u0191,\3\2\2\2\u0192\u0193\7"+
|
||||
"u\2\2\u0193\u0194\7v\2\2\u0194\u0195\7t\2\2\u0195.\3\2\2\2\u0196\u0197"+
|
||||
"\7u\2\2\u0197\u0198\7v\2\2\u0198\u0199\7t\2\2\u0199\u019a\7a\2\2\u019a"+
|
||||
"\u019b\7u\2\2\u019b\60\3\2\2\2\u019c\u019d\7]\2\2\u019d\62\3\2\2\2\u019e"+
|
||||
"\u019f\7_\2\2\u019f\64\3\2\2\2\u01a0\u01a1\7-\2\2\u01a1\u01a2\7?\2\2\u01a2"+
|
||||
"\66\3\2\2\2\u01a3\u01a4\7/\2\2\u01a4\u01a5\7?\2\2\u01a58\3\2\2\2\u01a6"+
|
||||
"\u01a7\7\61\2\2\u01a7\u01a8\7?\2\2\u01a8:\3\2\2\2\u01a9\u01aa\7,\2\2\u01aa"+
|
||||
"\u01ab\7?\2\2\u01ab<\3\2\2\2\u01ac\u01ad\7,\2\2\u01ad\u01ae\7,\2\2\u01ae"+
|
||||
"\u01af\7?\2\2\u01af>\3\2\2\2\u01b0\u01b1\7(\2\2\u01b1\u01b2\7?\2\2\u01b2"+
|
||||
"@\3\2\2\2\u01b3\u01b4\7~\2\2\u01b4\u01b5\7?\2\2\u01b5B\3\2\2\2\u01b6\u01b7"+
|
||||
"\7`\2\2\u01b7\u01b8\7?\2\2\u01b8D\3\2\2\2\u01b9\u01ba\7\'\2\2\u01ba\u01bb"+
|
||||
"\7?\2\2\u01bbF\3\2\2\2\u01bc\u01bd\7>\2\2\u01bd\u01be\7>\2\2\u01be\u01bf"+
|
||||
"\7?\2\2\u01bfH\3\2\2\2\u01c0\u01c1\7@\2\2\u01c1\u01c2\7@\2\2\u01c2\u01c3"+
|
||||
"\7?\2\2\u01c3J\3\2\2\2\u01c4\u01c5\7-\2\2\u01c5\u01c6\7-\2\2\u01c6L\3"+
|
||||
"\2\2\2\u01c7\u01c8\7/\2\2\u01c8\u01c9\7/\2\2\u01c9N\3\2\2\2\u01ca\u01cb"+
|
||||
"\7-\2\2\u01cbP\3\2\2\2\u01cc\u01cd\7/\2\2\u01cdR\3\2\2\2\u01ce\u01cf\7"+
|
||||
",\2\2\u01cf\u01d0\7,\2\2\u01d0T\3\2\2\2\u01d1\u01d2\7,\2\2\u01d2V\3\2"+
|
||||
"\2\2\u01d3\u01d4\7\61\2\2\u01d4X\3\2\2\2\u01d5\u01d6\7\'\2\2\u01d6Z\3"+
|
||||
"\2\2\2\u01d7\u01d8\7>\2\2\u01d8\u01d9\7>\2\2\u01d9\\\3\2\2\2\u01da\u01db"+
|
||||
"\7@\2\2\u01db\u01dc\7@\2\2\u01dc^\3\2\2\2\u01dd\u01de\7>\2\2\u01de`\3"+
|
||||
"\2\2\2\u01df\u01e0\7@\2\2\u01e0b\3\2\2\2\u01e1\u01e2\7>\2\2\u01e2\u01e3"+
|
||||
"\7?\2\2\u01e3d\3\2\2\2\u01e4\u01e5\7@\2\2\u01e5\u01e6\7?\2\2\u01e6f\3"+
|
||||
"\2\2\2\u01e7\u01e8\7?\2\2\u01e8\u01e9\7?\2\2\u01e9h\3\2\2\2\u01ea\u01eb"+
|
||||
"\7#\2\2\u01eb\u01ec\7?\2\2\u01ecj\3\2\2\2\u01ed\u01ee\7`\2\2\u01eel\3"+
|
||||
"\2\2\2\u01ef\u01f0\7~\2\2\u01f0n\3\2\2\2\u01f1\u01f2\7v\2\2\u01f2\u01f3"+
|
||||
"\7q\2\2\u01f3p\3\2\2\2\u01f4\u01f5\7u\2\2\u01f5\u01f6\7v\2\2\u01f6\u01f7"+
|
||||
"\7g\2\2\u01f7\u01f8\7r\2\2\u01f8r\3\2\2\2\u01f9\u01fa\7c\2\2\u01fa\u01fb"+
|
||||
"\7p\2\2\u01fb\u01fc\7f\2\2\u01fct\3\2\2\2\u01fd\u01fe\7q\2\2\u01fe\u01ff"+
|
||||
"\7t\2\2\u01ffv\3\2\2\2\u0200\u0201\7z\2\2\u0201\u0202\7q\2\2\u0202\u0203"+
|
||||
"\7t\2\2\u0203x\3\2\2\2\u0204\u0205\7p\2\2\u0205\u0206\7q\2\2\u0206\u0207"+
|
||||
"\7v\2\2\u0207z\3\2\2\2\u0208\u0209\7*\2\2\u0209|\3\2\2\2\u020a\u020b\7"+
|
||||
"+\2\2\u020b~\3\2\2\2\u020c\u020d\7c\2\2\u020d\u020e\7u\2\2\u020e\u0080"+
|
||||
"\3\2\2\2\u020f\u0210\7B\2\2\u0210\u0082\3\2\2\2\u0211\u0212\7t\2\2\u0212"+
|
||||
"\u0213\7g\2\2\u0213\u0214\7v\2\2\u0214\u0215\7w\2\2\u0215\u0216\7t\2\2"+
|
||||
"\u0216\u0217\7p\2\2\u0217\u0084\3\2\2\2\u0218\u0219\7d\2\2\u0219\u021a"+
|
||||
"\7t\2\2\u021a\u021b\7g\2\2\u021b\u021c\7c\2\2\u021c\u021d\7m\2\2\u021d"+
|
||||
"\u0086\3\2\2\2\u021e\u021f\7e\2\2\u021f\u0220\7q\2\2\u0220\u0221\7p\2"+
|
||||
"\2\u0221\u0222\7v\2\2\u0222\u0223\7k\2\2\u0223\u0224\7p\2\2\u0224\u0225"+
|
||||
"\7w\2\2\u0225\u0226\7g\2\2\u0226\u0088\3\2\2\2\u0227\u0228\7\60\2\2\u0228"+
|
||||
"\u008a\3\2\2\2\u0229\u022a\7C\2\2\u022a\u008c\3\2\2\2\u022b\u022c\7Z\2"+
|
||||
"\2\u022c\u008e\3\2\2\2\u022d\u022e\7[\2\2\u022e\u0090\3\2\2\2\u022f\u0230"+
|
||||
"\7C\2\2\u0230\u0231\7Z\2\2\u0231\u0092\3\2\2\2\u0232\u0233\7C\2\2\u0233"+
|
||||
"\u0234\7[\2\2\u0234\u0094\3\2\2\2\u0235\u0236\7Z\2\2\u0236\u0237\7[\2"+
|
||||
"\2\u0237\u0096\3\2\2\2\u0238\u0239\7R\2\2\u0239\u023a\7e\2\2\u023a\u0098"+
|
||||
"\3\2\2\2\u023b\u023c\7R\2\2\u023c\u023d\7|\2\2\u023d\u009a\3\2\2\2\u023e"+
|
||||
"\u023f\7R\2\2\u023f\u0240\7p\2\2\u0240\u009c\3\2\2\2\u0241\u0242\7R\2"+
|
||||
"\2\u0242\u0243\7x\2\2\u0243\u009e\3\2\2\2\u0244\u0245\7\60\2\2\u0245\u0246"+
|
||||
"\7y\2\2\u0246\u00a0\3\2\2\2\u0247\u0248\7v\2\2\u0248\u0249\7t\2\2\u0249"+
|
||||
"\u024a\7w\2\2\u024a\u024b\7g\2\2\u024b\u00a2\3\2\2\2\u024c\u024d\7h\2"+
|
||||
"\2\u024d\u024e\7c\2\2\u024e\u024f\7n\2\2\u024f\u0250\7u\2\2\u0250\u0251"+
|
||||
"\7g\2\2\u0251\u00a4\3\2\2\2\u0252\u0253\7\'\2\2\u0253\u0254\7c\2\2\u0254"+
|
||||
"\u0255\7u\2\2\u0255\u0256\7o\2\2\u0256\u00a6\3\2\2\2\u0257\u0258\7u\2"+
|
||||
"\2\u0258\u0259\7w\2\2\u0259\u025a\7d\2\2\u025a\u00a8\3\2\2\2\u025b\u025c"+
|
||||
"\7/\2\2\u025c\u025d\7@\2\2\u025d\u00aa\3\2\2\2\u025e\u025f\7}\2\2\u025f"+
|
||||
"\u00ac\3\2\2\2\u0260\u0261\7\177\2\2\u0261\u00ae\3\2\2\2\u0262\u0263\7"+
|
||||
"c\2\2\u0263\u0264\7u\2\2\u0264\u0265\7o\2\2\u0265\u0266\7u\2\2\u0266\u0267"+
|
||||
"\7w\2\2\u0267\u0268\7d\2\2\u0268\u00b0\3\2\2\2\u0269\u026a\7u\2\2\u026a"+
|
||||
"\u026b\7v\2\2\u026b\u026c\7c\2\2\u026c\u026d\7e\2\2\u026d\u026e\7m\2\2"+
|
||||
"\u026e\u00b2\3\2\2\2\u026f\u0270\7e\2\2\u0270\u0271\7n\2\2\u0271\u0272"+
|
||||
"\7q\2\2\u0272\u0273\7d\2\2\u0273\u0274\7d\2\2\u0274\u0275\7g\2\2\u0275"+
|
||||
"\u0276\7t\2\2\u0276\u0277\7u\2\2\u0277\u00b4\3\2\2\2\u0278\u0279\7k\2"+
|
||||
"\2\u0279\u027a\7h\2\2\u027a\u00b6\3\2\2\2\u027b\u027c\7g\2\2\u027c\u027d"+
|
||||
"\7n\2\2\u027d\u027e\7u\2\2\u027e\u027f\7g\2\2\u027f\u00b8\3\2\2\2\u0280"+
|
||||
"\u0281\7k\2\2\u0281\u0282\7h\2\2\u0282\u0283\7a\2\2\u0283\u0284\7e\2\2"+
|
||||
"\u0284\u0285\7u\2\2\u0285\u00ba\3\2\2\2\u0286\u0287\7k\2\2\u0287\u0288"+
|
||||
"\7h\2\2\u0288\u0289\7a\2\2\u0289\u028a\7e\2\2\u028a\u028b\7e\2\2\u028b"+
|
||||
"\u00bc\3\2\2\2\u028c\u028d\7k\2\2\u028d\u028e\7h\2\2\u028e\u028f\7a\2"+
|
||||
"\2\u028f\u0290\7g\2\2\u0290\u0291\7s\2\2\u0291\u00be\3\2\2\2\u0292\u0293"+
|
||||
"\7k\2\2\u0293\u0294\7h\2\2\u0294\u0295\7a\2\2\u0295\u0296\7|\2\2\u0296"+
|
||||
"\u00c0\3\2\2\2\u0297\u0298\7k\2\2\u0298\u0299\7h\2\2\u0299\u029a\7a\2"+
|
||||
"\2\u029a\u029b\7p\2\2\u029b\u029c\7g\2\2\u029c\u00c2\3\2\2\2\u029d\u029e"+
|
||||
"\7k\2\2\u029e\u029f\7h\2\2\u029f\u02a0\7a\2\2\u02a0\u02a1\7p\2\2\u02a1"+
|
||||
"\u02a2\7|\2\2\u02a2\u00c4\3\2\2\2\u02a3\u02a4\7k\2\2\u02a4\u02a5\7h\2"+
|
||||
"\2\u02a5\u02a6\7a\2\2\u02a6\u02a7\7r\2\2\u02a7\u02a8\7n\2\2\u02a8\u00c6"+
|
||||
"\3\2\2\2\u02a9\u02aa\7k\2\2\u02aa\u02ab\7h\2\2\u02ab\u02ac\7a\2\2\u02ac"+
|
||||
"\u02ad\7r\2\2\u02ad\u02ae\7q\2\2\u02ae\u02af\7u\2\2\u02af\u00c8\3\2\2"+
|
||||
"\2\u02b0\u02b1\7k\2\2\u02b1\u02b2\7h\2\2\u02b2\u02b3\7a\2\2\u02b3\u02b4"+
|
||||
"\7o\2\2\u02b4\u02b5\7k\2\2\u02b5\u00ca\3\2\2\2\u02b6\u02b7\7k\2\2\u02b7"+
|
||||
"\u02b8\7h\2\2\u02b8\u02b9\7a\2\2\u02b9\u02ba\7p\2\2\u02ba\u02bb\7g\2\2"+
|
||||
"\u02bb\u02bc\7i\2\2\u02bc\u00cc\3\2\2\2\u02bd\u02be\7k\2\2\u02be\u02bf"+
|
||||
"\7h\2\2\u02bf\u02c0\7a\2\2\u02c0\u02c1\7x\2\2\u02c1\u02c2\7u\2\2\u02c2"+
|
||||
"\u00ce\3\2\2\2\u02c3\u02c4\7k\2\2\u02c4\u02c5\7h\2\2\u02c5\u02c6\7a\2"+
|
||||
"\2\u02c6\u02c7\7x\2\2\u02c7\u02c8\7e\2\2\u02c8\u00d0\3\2\2\2\u02c9\u02ca"+
|
||||
"\7h\2\2\u02ca\u02cb\7q\2\2\u02cb\u02cc\7t\2\2\u02cc\u00d2\3\2\2\2\u02cd"+
|
||||
"\u02ce\7k\2\2\u02ce\u02cf\7p\2\2\u02cf\u00d4\3\2\2\2\u02d0\u02d1\7y\2"+
|
||||
"\2\u02d1\u02d2\7j\2\2\u02d2\u02d3\7k\2\2\u02d3\u02d4\7n\2\2\u02d4\u02d5"+
|
||||
"\7g\2\2\u02d5\u00d6\3\2\2\2\u02d6\u02d7\7t\2\2\u02d7\u02d8\7g\2\2\u02d8"+
|
||||
"\u02d9\7r\2\2\u02d9\u02da\7g\2\2\u02da\u02db\7c\2\2\u02db\u02dc\7v\2\2"+
|
||||
"\u02dc\u00d8\3\2\2\2\u02dd\u02de\7w\2\2\u02de\u02df\7p\2\2\u02df\u02e0"+
|
||||
"\7v\2\2\u02e0\u02e1\7k\2\2\u02e1\u02e2\7n\2\2\u02e2\u00da\3\2\2\2\u02e3"+
|
||||
"\u02e4\7y\2\2\u02e4\u02e5\7j\2\2\u02e5\u02e6\7g\2\2\u02e6\u02e7\7p\2\2"+
|
||||
"\u02e7\u00dc\3\2\2\2\u02e8\u02ec\t\2\2\2\u02e9\u02eb\t\3\2\2\u02ea\u02e9"+
|
||||
"\3\2\2\2\u02eb\u02ee\3\2\2\2\u02ec\u02ea\3\2\2\2\u02ec\u02ed\3\2\2\2\u02ed"+
|
||||
"\u02ef\3\2\2\2\u02ee\u02ec\3\2\2\2\u02ef\u02f0\5\u00dfp\2\u02f0\u02f1"+
|
||||
"\3\2\2\2\u02f1\u02f2\bo\2\2\u02f2\u00de\3\2\2\2\u02f3\u02f7\7=\2\2\u02f4"+
|
||||
"\u02f6\n\2\2\2\u02f5\u02f4\3\2\2\2\u02f6\u02f9\3\2\2\2\u02f7\u02f5\3\2"+
|
||||
"\2\2\u02f7\u02f8\3\2\2\2\u02f8\u02fa\3\2\2\2\u02f9\u02f7\3\2\2\2\u02fa"+
|
||||
"\u02fb\bp\2\2\u02fb\u00e0\3\2\2\2\u02fc\u02fd\t\3\2\2\u02fd\u02fe\3\2"+
|
||||
"\2\2\u02fe\u02ff\bq\3\2\u02ff\u00e2\3\2\2\2\u0300\u0302\t\2\2\2\u0301"+
|
||||
"\u0300\3\2\2\2\u0302\u0303\3\2\2\2\u0303\u0301\3\2\2\2\u0303\u0304\3\2"+
|
||||
"\2\2\u0304\u00e4\3\2\2\2\u0305\u0309\t\4\2\2\u0306\u0308\t\5\2\2\u0307"+
|
||||
"\u0306\3\2\2\2\u0308\u030b\3\2\2\2\u0309\u0307\3\2\2\2\u0309\u030a\3\2"+
|
||||
"\2\2\u030a\u00e6\3\2\2\2\u030b\u0309\3\2\2\2\u030c\u0314\4\62;\2\u030d"+
|
||||
"\u030f\4\63;\2\u030e\u0310\4\62;\2\u030f\u030e\3\2\2\2\u0310\u0311\3\2"+
|
||||
"\2\2\u0311\u030f\3\2\2\2\u0311\u0312\3\2\2\2\u0312\u0314\3\2\2\2\u0313"+
|
||||
"\u030c\3\2\2\2\u0313\u030d\3\2\2\2\u0314\u00e8\3\2\2\2\u0315\u0317\7&"+
|
||||
"\2\2\u0316\u0318\t\6\2\2\u0317\u0316\3\2\2\2\u0318\u0319\3\2\2\2\u0319"+
|
||||
"\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u00ea\3\2\2\2\u031b\u031d\7\'"+
|
||||
"\2\2\u031c\u031e\4\62\63\2\u031d\u031c\3\2\2\2\u031e\u031f\3\2\2\2\u031f"+
|
||||
"\u031d\3\2\2\2\u031f\u0320\3\2\2\2\u0320\u00ec\3\2\2\2\u0321\u0322\7("+
|
||||
"\2\2\u0322\u00ee\3\2\2\2\u0323\u0329\5\u00f1y\2\u0324\u0326\t\7\2\2\u0325"+
|
||||
"\u0327\t\b\2\2\u0326\u0325\3\2\2\2\u0326\u0327\3\2\2\2\u0327\u0328\3\2"+
|
||||
"\2\2\u0328\u032a\5\u00f1y\2\u0329\u0324\3\2\2\2\u0329\u032a\3\2\2\2\u032a"+
|
||||
"\u00f0\3\2\2\2\u032b\u032d\4\62;\2\u032c\u032b\3\2\2\2\u032d\u032e\3\2"+
|
||||
"\2\2\u032e\u032c\3\2\2\2\u032e\u032f\3\2\2\2\u032f\u0336\3\2\2\2\u0330"+
|
||||
"\u0332\7\60\2\2\u0331\u0333\4\62;\2\u0332\u0331\3\2\2\2\u0333\u0334\3"+
|
||||
"\2\2\2\u0334\u0332\3\2\2\2\u0334\u0335\3\2\2\2\u0335\u0337\3\2\2\2\u0336"+
|
||||
"\u0330\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u00f2\3\2\2\2\u0338\u0339\7^"+
|
||||
"\2\2\u0339\u033d\13\2\2\2\u033a\u033b\7^\2\2\u033b\u033d\5\u00e3r\2\u033c"+
|
||||
"\u0338\3\2\2\2\u033c\u033a\3\2\2\2\u033d\u00f4\3\2\2\2\u033e\u0343\7$"+
|
||||
"\2\2\u033f\u0342\5\u00f3z\2\u0340\u0342\n\t\2\2\u0341\u033f\3\2\2\2\u0341"+
|
||||
"\u0340\3\2\2\2\u0342\u0345\3\2\2\2\u0343\u0341\3\2\2\2\u0343\u0344\3\2"+
|
||||
"\2\2\u0344\u0346\3\2\2\2\u0345\u0343\3\2\2\2\u0346\u0347\7$\2\2\u0347"+
|
||||
"\u0348\b{\4\2\u0348\u00f6\3\2\2\2\u0349\u034a\7}\2\2\u034a\u034b\7}\2"+
|
||||
"\2\u034b\u034d\3\2\2\2\u034c\u034e\13\2\2\2\u034d\u034c\3\2\2\2\u034e"+
|
||||
"\u034f\3\2\2\2\u034f\u0350\3\2\2\2\u034f\u034d\3\2\2\2\u0350\u0351\3\2"+
|
||||
"\2\2\u0351\u0352\7\177\2\2\u0352\u0353\7\177\2\2\u0353\u0354\3\2\2\2\u0354"+
|
||||
"\u0355\b|\5\2\u0355\u00f8\3\2\2\2\u0356\u0359\7)\2\2\u0357\u035a\5\u00f3"+
|
||||
"z\2\u0358\u035a\n\t\2\2\u0359\u0357\3\2\2\2\u0359\u0358\3\2\2\2\u035a"+
|
||||
"\u035b\3\2\2\2\u035b\u035c\7)\2\2\u035c\u035d\b}\6\2\u035d\u00fa\3\2\2"+
|
||||
"\2\u035e\u035f\7B\2\2\u035f\u0360\7|\2\2\u0360\u0361\7r\2\2\u0361\u00fc"+
|
||||
"\3\2\2\2\u0362\u0363\7]\2\2\u0363\u0364\7_\2\2\u0364\u00fe\3\2\2\2\26"+
|
||||
"\2\u02ec\u02f7\u0303\u0309\u0311\u0313\u0317\u0319\u031f\u0326\u0329\u032e"+
|
||||
"\u0334\u0336\u033c\u0341\u0343\u034f\u0359\7\2\3\2\b\2\2\3{\2\3|\3\3}"+
|
||||
"\4";
|
||||
public static final ATN _ATN =
|
||||
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
|
||||
static {
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
Reference in new issue
Block a user