mirror of
https://github.com/irmen/prog8.git
synced 2025-06-18 08:23:37 +00:00
Compare commits
97 Commits
Author | SHA1 | Date | |
---|---|---|---|
95e76058d3 | |||
a6bee6a860 | |||
d22780ee44 | |||
f8b0b9575d | |||
4274fd168e | |||
be7f5957f3 | |||
f2e5d987a9 | |||
f01173d8db | |||
15e8e0bf6d | |||
2c59cbdece | |||
b73da4ed02 | |||
267adb4612 | |||
05c73fa8bc | |||
bfe9f442e6 | |||
0deadb694b | |||
bed34378be | |||
5927cf2d43 | |||
fffe36e358 | |||
fac2a2d7cb | |||
0af5582ca7 | |||
582d31263c | |||
4108a528e1 | |||
ab7d7c2907 | |||
152888ee93 | |||
22f8f4f359 | |||
5f3a9e189a | |||
b734dc44fd | |||
fab224f509 | |||
2f05ebb966 | |||
a335ba519a | |||
8805693ed2 | |||
f2bb238e9b | |||
131fe670a4 | |||
11e9539416 | |||
3881ebe429 | |||
29d1b8802e | |||
bcc75732e9 | |||
50a85ee6b0 | |||
2c7424fd43 | |||
7426587c38 | |||
1f39749a5e | |||
ca63051c71 | |||
6dd44aaf0d | |||
f89457ba68 | |||
efef205fcf | |||
0c561d8528 | |||
8bfa2c4c02 | |||
f0d4c3aba9 | |||
3a99115070 | |||
7232134931 | |||
954e911eb3 | |||
63c073c93f | |||
78feef9d59 | |||
4fbdd6d570 | |||
4929c198ba | |||
9409f17372 | |||
43781c02d0 | |||
824f06e17f | |||
21dbc6da97 | |||
270ea54ff7 | |||
771ac7aba7 | |||
97d36243f2 | |||
511b47bac4 | |||
f265199fbe | |||
a191ec71a4 | |||
82dce2dd53 | |||
29ac160811 | |||
5e50ea14f8 | |||
40e6091506 | |||
0ee4d420b1 | |||
66acce9e8e | |||
6c23ae14ab | |||
6f000d0d26 | |||
9d7eb3be5a | |||
835555171e | |||
68ce4a1bf0 | |||
a995867deb | |||
6bd99d63b4 | |||
baf5d3041a | |||
a326ffa00a | |||
d28dd92b47 | |||
1de328b2e8 | |||
51bb902162 | |||
4fd14f1366 | |||
91d9559f79 | |||
3245a9b157 | |||
2b28493bba | |||
1382728bd2 | |||
0422ad080a | |||
64d682bfde | |||
b182f7e693 | |||
e6be428589 | |||
85c7f8314b | |||
796d07a7f8 | |||
2af86a10b2 | |||
7fbe486dff | |||
87e5a9859a |
2
.gitignore
vendored
2
.gitignore
vendored
@ -14,7 +14,7 @@ docs/build
|
|||||||
out/
|
out/
|
||||||
parser/**/*.interp
|
parser/**/*.interp
|
||||||
parser/**/*.tokens
|
parser/**/*.tokens
|
||||||
|
parser/**/*.java
|
||||||
*.py[cod]
|
*.py[cod]
|
||||||
*.egg
|
*.egg
|
||||||
*.egg-info
|
*.egg-info
|
||||||
|
@ -1,5 +1,6 @@
|
|||||||
[](https://saythanks.io/to/irmen)
|
[](https://saythanks.io/to/irmen)
|
||||||
[](https://travis-ci.org/irmen/prog8)
|
[](https://travis-ci.org/irmen/prog8)
|
||||||
|
[](https://prog8.readthedocs.io/)
|
||||||
|
|
||||||
Prog8 - Structured Programming Language for 8-bit 6502/6510 microprocessors
|
Prog8 - Structured Programming Language for 8-bit 6502/6510 microprocessors
|
||||||
===========================================================================
|
===========================================================================
|
||||||
@ -35,15 +36,13 @@ Rapid edit-compile-run-debug cycle:
|
|||||||
- option to automatically run the program in the Vice emulator
|
- option to automatically run the program in the Vice emulator
|
||||||
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
- breakpoints, that let the Vice emulator drop into the monitor if execution hits them
|
||||||
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
- source code labels automatically loaded in Vice emulator so it can show them in disassembly
|
||||||
- virtual machine that can execute compiled code directy on the host system,
|
|
||||||
without having to actually convert it to assembly to run on a real 6502
|
|
||||||
|
|
||||||
It is mainly targeted at the Commodore-64 machine at this time.
|
It is mainly targeted at the Commodore-64 machine at this time.
|
||||||
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
Contributions to add support for other 8-bit (or other?!) machines are welcome.
|
||||||
|
|
||||||
Documentation/manual
|
Documentation/manual
|
||||||
--------------------
|
--------------------
|
||||||
This describes the language, but also how to build and run the compiler. See https://prog8.readthedocs.io/
|
https://prog8.readthedocs.io/
|
||||||
|
|
||||||
Required tools
|
Required tools
|
||||||
--------------
|
--------------
|
||||||
|
@ -34,6 +34,7 @@ dependencies {
|
|||||||
implementation 'org.antlr:antlr4-runtime:4.8'
|
implementation 'org.antlr:antlr4-runtime:4.8'
|
||||||
implementation 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
|
implementation 'org.jetbrains.kotlinx:kotlinx-cli-jvm:0.1.0-dev-5'
|
||||||
// implementation 'net.razorvine:ksim65:1.6'
|
// implementation 'net.razorvine:ksim65:1.6'
|
||||||
|
// implementation "com.github.hypfvieh:dbus-java:3.2.0"
|
||||||
implementation project(':parser')
|
implementation project(':parser')
|
||||||
|
|
||||||
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
testImplementation "org.jetbrains.kotlin:kotlin-test-junit5"
|
||||||
@ -100,7 +101,7 @@ test {
|
|||||||
|
|
||||||
// Show test results.
|
// Show test results.
|
||||||
testLogging {
|
testLogging {
|
||||||
events "passed", "skipped", "failed"
|
events "skipped", "failed"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
BIN
compiler/lib/dbus-java-3.2.0.jar
Normal file
BIN
compiler/lib/dbus-java-3.2.0.jar
Normal file
Binary file not shown.
@ -739,3 +739,45 @@ sign_f .proc
|
|||||||
dex
|
dex
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
set_0_array_float .proc
|
||||||
|
; -- set a float in an array to zero (index on stack, array in SCRATCH_ZPWORD1)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.ESTACK_LO,x
|
||||||
|
tay
|
||||||
|
lda #0
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
set_array_float .proc
|
||||||
|
; -- set a float in an array to a value (index on stack, float in SCRATCH_ZPWORD1, array in SCRATCH_ZPWORD2)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
asl a
|
||||||
|
clc
|
||||||
|
adc c64.ESTACK_LO,x
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPWORD2
|
||||||
|
ldy c64.SCRATCH_ZPWORD2+1
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
+ jmp copy_float
|
||||||
|
; -- copies the 5 bytes of the mflt value pointed to by SCRATCH_ZPWORD1,
|
||||||
|
; into the 5 bytes pointed to by A/Y. Clobbers A,Y.
|
||||||
|
.pend
|
||||||
|
@ -34,6 +34,7 @@ c64flt {
|
|||||||
&float FL_PIHALF = $e2e0 ; PI / 2
|
&float FL_PIHALF = $e2e0 ; PI / 2
|
||||||
&float FL_TWOPI = $e2e5 ; 2 * PI
|
&float FL_TWOPI = $e2e5 ; 2 * PI
|
||||||
&float FL_FR4 = $e2ea ; .25
|
&float FL_FR4 = $e2ea ; .25
|
||||||
|
; oddly enough, 0.0 isn't available in the kernel.
|
||||||
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
|
float FL_ZERO = 0.0 ; oddly enough 0.0 isn't available in the kernel
|
||||||
|
|
||||||
|
|
||||||
@ -209,8 +210,8 @@ sub print_fln (float value) {
|
|||||||
; ---- prints the floating point value (with a newline at the end) using basic rom routines
|
; ---- prints the floating point value (with a newline at the end) using basic rom routines
|
||||||
%asm {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
lda #<print_fln_value
|
lda #<value
|
||||||
ldy #>print_fln_value
|
ldy #>value
|
||||||
jsr MOVFM ; load float into fac1
|
jsr MOVFM ; load float into fac1
|
||||||
jsr FPRINTLN ; print fac1 with newline
|
jsr FPRINTLN ; print fac1 with newline
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
@ -565,14 +565,9 @@ asmsub clear_screenchars (ubyte char @ A) clobbers(Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
ldy #0
|
ldy #0
|
||||||
_loop sta c64.Screen,y
|
_loop sta c64.Screen,y
|
||||||
sta c64.Screen+1,y
|
|
||||||
sta c64.Screen+$0100,y
|
sta c64.Screen+$0100,y
|
||||||
sta c64.Screen+$0101,y
|
|
||||||
sta c64.Screen+$0200,y
|
sta c64.Screen+$0200,y
|
||||||
sta c64.Screen+$0201,y
|
|
||||||
sta c64.Screen+$02e8,y
|
sta c64.Screen+$02e8,y
|
||||||
sta c64.Screen+$02e9,y
|
|
||||||
iny
|
|
||||||
iny
|
iny
|
||||||
bne _loop
|
bne _loop
|
||||||
rts
|
rts
|
||||||
@ -585,14 +580,9 @@ asmsub clear_screencolors (ubyte color @ A) clobbers(Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
ldy #0
|
ldy #0
|
||||||
_loop sta c64.Colors,y
|
_loop sta c64.Colors,y
|
||||||
sta c64.Colors+1,y
|
|
||||||
sta c64.Colors+$0100,y
|
sta c64.Colors+$0100,y
|
||||||
sta c64.Colors+$0101,y
|
|
||||||
sta c64.Colors+$0200,y
|
sta c64.Colors+$0200,y
|
||||||
sta c64.Colors+$0201,y
|
|
||||||
sta c64.Colors+$02e8,y
|
sta c64.Colors+$02e8,y
|
||||||
sta c64.Colors+$02e9,y
|
|
||||||
iny
|
|
||||||
iny
|
iny
|
||||||
bne _loop
|
bne _loop
|
||||||
rts
|
rts
|
||||||
@ -603,6 +593,7 @@ asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
|||||||
; ---- scroll the whole screen 1 character to the left
|
; ---- scroll the whole screen 1 character to the left
|
||||||
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
; contents of the rightmost column are unchanged, you should clear/refill this yourself
|
||||||
; Carry flag determines if screen color data must be scrolled too
|
; Carry flag determines if screen color data must be scrolled too
|
||||||
|
|
||||||
%asm {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
bcs +
|
bcs +
|
||||||
@ -612,18 +603,7 @@ asmsub scroll_left_full (ubyte alsocolors @ Pc) clobbers(A, Y) {
|
|||||||
ldx #0
|
ldx #0
|
||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=12, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Colors + 40*row + 1,x
|
|
||||||
sta c64.Colors + 40*row,x
|
|
||||||
.next
|
|
||||||
inx
|
|
||||||
dey
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #0
|
|
||||||
ldy #38
|
|
||||||
-
|
|
||||||
.for row=13, row<=24, row+=1
|
|
||||||
lda c64.Colors + 40*row + 1,x
|
lda c64.Colors + 40*row + 1,x
|
||||||
sta c64.Colors + 40*row,x
|
sta c64.Colors + 40*row,x
|
||||||
.next
|
.next
|
||||||
@ -635,18 +615,7 @@ _scroll_screen ; scroll the screen memory
|
|||||||
ldx #0
|
ldx #0
|
||||||
ldy #38
|
ldy #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=12, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 1,x
|
|
||||||
sta c64.Screen + 40*row,x
|
|
||||||
.next
|
|
||||||
inx
|
|
||||||
dey
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #0
|
|
||||||
ldy #38
|
|
||||||
-
|
|
||||||
.for row=13, row<=24, row+=1
|
|
||||||
lda c64.Screen + 40*row + 1,x
|
lda c64.Screen + 40*row + 1,x
|
||||||
sta c64.Screen + 40*row,x
|
sta c64.Screen + 40*row,x
|
||||||
.next
|
.next
|
||||||
@ -671,41 +640,23 @@ asmsub scroll_right_full (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
+ ; scroll the color memory
|
+ ; scroll the color memory
|
||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=12, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Colors + 40*row + 0,x
|
lda c64.Colors + 40*row + 0,x
|
||||||
sta c64.Colors + 40*row + 1,x
|
sta c64.Colors + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
|
|
||||||
ldx #38
|
|
||||||
-
|
|
||||||
.for row=13, row<=24, row+=1
|
|
||||||
lda c64.Colors + 40*row,x
|
|
||||||
sta c64.Colors + 40*row + 1,x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
_scroll_screen ; scroll the screen memory
|
_scroll_screen ; scroll the screen memory
|
||||||
ldx #38
|
ldx #38
|
||||||
-
|
-
|
||||||
.for row=0, row<=12, row+=1
|
.for row=0, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row + 0,x
|
lda c64.Screen + 40*row + 0,x
|
||||||
sta c64.Screen + 40*row + 1,x
|
sta c64.Screen + 40*row + 1,x
|
||||||
.next
|
.next
|
||||||
dex
|
dex
|
||||||
bpl -
|
bpl -
|
||||||
|
|
||||||
ldx #38
|
|
||||||
-
|
|
||||||
.for row=13, row<=24, row+=1
|
|
||||||
lda c64.Screen + 40*row,x
|
|
||||||
sta c64.Screen + 40*row + 1,x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx c64.SCRATCH_ZPREGX
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
@ -723,16 +674,7 @@ asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
+ ; scroll the color memory
|
+ ; scroll the color memory
|
||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=11, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Colors + 40*row,x
|
|
||||||
sta c64.Colors + 40*(row-1),x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #39
|
|
||||||
-
|
|
||||||
.for row=12, row<=24, row+=1
|
|
||||||
lda c64.Colors + 40*row,x
|
lda c64.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row-1),x
|
sta c64.Colors + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
@ -742,16 +684,7 @@ asmsub scroll_up_full (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
_scroll_screen ; scroll the screen memory
|
_scroll_screen ; scroll the screen memory
|
||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=1, row<=11, row+=1
|
.for row=1, row<=24, row+=1
|
||||||
lda c64.Screen + 40*row,x
|
|
||||||
sta c64.Screen + 40*(row-1),x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #39
|
|
||||||
-
|
|
||||||
.for row=12, row<=24, row+=1
|
|
||||||
lda c64.Screen + 40*row,x
|
lda c64.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row-1),x
|
sta c64.Screen + 40*(row-1),x
|
||||||
.next
|
.next
|
||||||
@ -775,16 +708,7 @@ asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
+ ; scroll the color memory
|
+ ; scroll the color memory
|
||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=12, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Colors + 40*row,x
|
|
||||||
sta c64.Colors + 40*(row+1),x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #39
|
|
||||||
-
|
|
||||||
.for row=11, row>=0, row-=1
|
|
||||||
lda c64.Colors + 40*row,x
|
lda c64.Colors + 40*row,x
|
||||||
sta c64.Colors + 40*(row+1),x
|
sta c64.Colors + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
@ -794,16 +718,7 @@ asmsub scroll_down_full (ubyte alsocolors @ Pc) clobbers(A) {
|
|||||||
_scroll_screen ; scroll the screen memory
|
_scroll_screen ; scroll the screen memory
|
||||||
ldx #39
|
ldx #39
|
||||||
-
|
-
|
||||||
.for row=23, row>=12, row-=1
|
.for row=23, row>=0, row-=1
|
||||||
lda c64.Screen + 40*row,x
|
|
||||||
sta c64.Screen + 40*(row+1),x
|
|
||||||
.next
|
|
||||||
dex
|
|
||||||
bpl -
|
|
||||||
|
|
||||||
ldx #39
|
|
||||||
-
|
|
||||||
.for row=11, row>=0, row-=1
|
|
||||||
lda c64.Screen + 40*row,x
|
lda c64.Screen + 40*row,x
|
||||||
sta c64.Screen + 40*(row+1),x
|
sta c64.Screen + 40*(row+1),x
|
||||||
.next
|
.next
|
||||||
@ -862,11 +777,14 @@ _print_byte_digits
|
|||||||
beq +
|
beq +
|
||||||
tya
|
tya
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
|
pla
|
||||||
|
jsr c64.CHROUT
|
||||||
|
jmp _ones
|
||||||
+ pla
|
+ pla
|
||||||
cmp #'0'
|
cmp #'0'
|
||||||
beq +
|
beq _ones
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
+ txa
|
_ones txa
|
||||||
jsr c64.CHROUT
|
jsr c64.CHROUT
|
||||||
ldx c64.SCRATCH_ZPREGX
|
ldx c64.SCRATCH_ZPREGX
|
||||||
rts
|
rts
|
||||||
@ -975,6 +893,7 @@ asmsub print_uw (uword value @ AY) clobbers(A,Y) {
|
|||||||
%asm {{
|
%asm {{
|
||||||
stx c64.SCRATCH_ZPREGX
|
stx c64.SCRATCH_ZPREGX
|
||||||
jsr c64utils.uword2decimal
|
jsr c64utils.uword2decimal
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
ldy #0
|
ldy #0
|
||||||
- lda c64utils.uword2decimal.decTenThousands,y
|
- lda c64utils.uword2decimal.decTenThousands,y
|
||||||
beq _allzero
|
beq _allzero
|
||||||
|
@ -680,3 +680,192 @@ _sign_possibly_zero lda c64.ESTACK_LO+1,x
|
|||||||
sta c64.ESTACK_LO+1,x
|
sta c64.ESTACK_LO+1,x
|
||||||
rts
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
; bit shifts.
|
||||||
|
; anything below 3 is done inline. anything above 7 is done via other optimizations.
|
||||||
|
|
||||||
|
shift_left_w_7 .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
|
||||||
|
asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
_shift6 asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
_shift5 asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
_shift4 asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
_shift3 asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
asl a
|
||||||
|
rol c64.SCRATCH_ZPB1
|
||||||
|
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
lda c64.SCRATCH_ZPB1
|
||||||
|
sta c64.ESTACK_HI+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_left_w_6 .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
jmp shift_left_w_7._shift6
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_left_w_5 .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
jmp shift_left_w_7._shift5
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_left_w_4 .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
jmp shift_left_w_7._shift4
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_left_w_3 .proc
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
jmp shift_left_w_7._shift3
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_uw_7 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
|
||||||
|
lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
_shift6 lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
_shift5 lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
_shift4 lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
_shift3 lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
lsr a
|
||||||
|
ror c64.SCRATCH_ZPB1
|
||||||
|
|
||||||
|
sta c64.ESTACK_HI+1,x
|
||||||
|
lda c64.SCRATCH_ZPB1
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_uw_6 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
jmp shift_right_uw_7._shift6
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_uw_5 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
jmp shift_right_uw_7._shift5
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_uw_4 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
jmp shift_right_uw_7._shift4
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_uw_3 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPB1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
jmp shift_right_uw_7._shift3
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
shift_right_w_7 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
|
||||||
|
asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
_shift6 asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
_shift5 asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
_shift4 asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
_shift3 asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
asl a
|
||||||
|
ror c64.SCRATCH_ZPWORD1+1
|
||||||
|
ror c64.SCRATCH_ZPWORD1
|
||||||
|
|
||||||
|
lda c64.SCRATCH_ZPWORD1
|
||||||
|
sta c64.ESTACK_LO+1,x
|
||||||
|
lda c64.SCRATCH_ZPWORD1+1
|
||||||
|
sta c64.ESTACK_HI+1,x
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_w_6 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp shift_right_w_7._shift6
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_w_5 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp shift_right_w_7._shift5
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_w_4 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp shift_right_w_7._shift4
|
||||||
|
.pend
|
||||||
|
|
||||||
|
shift_right_w_3 .proc
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI+1,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
jmp shift_right_w_7._shift3
|
||||||
|
.pend
|
||||||
|
|
||||||
|
@ -651,6 +651,18 @@ greatereq_w .proc
|
|||||||
bmi equal_b._equal_b_false
|
bmi equal_b._equal_b_false
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
|
|
||||||
|
orig_stackpointer .byte 0 ; stores the Stack pointer register at program start
|
||||||
|
|
||||||
|
func_exit .proc
|
||||||
|
; -- immediately exit the program with a return code in the A register
|
||||||
|
lda c64.ESTACK_LO+1,x
|
||||||
|
ldx orig_stackpointer
|
||||||
|
txs
|
||||||
|
rts ; return to original caller
|
||||||
|
.pend
|
||||||
|
|
||||||
|
|
||||||
func_read_flags .proc
|
func_read_flags .proc
|
||||||
; -- put the processor status register on the stack
|
; -- put the processor status register on the stack
|
||||||
php
|
php
|
||||||
@ -1776,7 +1788,6 @@ ror2_mem_ub .proc
|
|||||||
|
|
||||||
rol2_mem_ub .proc
|
rol2_mem_ub .proc
|
||||||
; -- in-place 8-bit rol of byte at memory location on stack
|
; -- in-place 8-bit rol of byte at memory location on stack
|
||||||
;" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}"
|
|
||||||
inx
|
inx
|
||||||
lda c64.ESTACK_LO,x
|
lda c64.ESTACK_LO,x
|
||||||
sta c64.SCRATCH_ZPWORD1
|
sta c64.SCRATCH_ZPWORD1
|
||||||
@ -1791,57 +1802,279 @@ rol2_mem_ub .proc
|
|||||||
.pend
|
.pend
|
||||||
|
|
||||||
lsl_array_b .proc
|
lsl_array_b .proc
|
||||||
.warn "lsl_array_b" ; TODO
|
; -- lsl a (u)byte in an array (index and array address on stack)
|
||||||
.pend
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
lsl_array_w .proc
|
inx
|
||||||
.warn "lsl_array_w" ; TODO
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
asl a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
lsr_array_ub .proc
|
lsr_array_ub .proc
|
||||||
.warn "lsr_array_ub" ; TODO
|
; -- lsr a ubyte in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
lsr a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
lsr_array_b .proc
|
lsr_array_b .proc
|
||||||
.warn "lsr_array_b" ; TODO
|
; -- lsr a byte in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
asl a
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
lsl_array_w .proc
|
||||||
|
; -- lsl a (u)word in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
asl a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
lsr_array_uw .proc
|
lsr_array_uw .proc
|
||||||
.warn "lsr_array_uw" ; TODO
|
; -- lsr a uword in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
lsr a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
lsr_array_w .proc
|
lsr_array_w .proc
|
||||||
.warn "lsr_array_w" ; TODO
|
; -- lsr a uword in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
asl a
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
rol_array_ub .proc
|
rol_array_ub .proc
|
||||||
.warn "rol_array_ub" ; TODO
|
; -- rol a ubyte in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
rol_array_uw .proc
|
|
||||||
.warn "rol_array_uw" ; TODO
|
|
||||||
.pend
|
|
||||||
|
|
||||||
rol2_array_ub .proc
|
|
||||||
.warn "rol2_array_ub" ; TODO
|
|
||||||
.pend
|
|
||||||
|
|
||||||
rol2_array_uw .proc
|
|
||||||
.warn "rol2_array_uw" ; TODO
|
|
||||||
.pend
|
|
||||||
|
|
||||||
ror_array_ub .proc
|
ror_array_ub .proc
|
||||||
.warn "ror_array_ub" ; TODO
|
; -- ror a ubyte in an array (index and array address on stack)
|
||||||
.pend
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
ror_array_uw .proc
|
inx
|
||||||
.warn "ror_array_uw" ; TODO
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
ror2_array_ub .proc
|
ror2_array_ub .proc
|
||||||
.warn "ror2_array_ub" ; TODO
|
; -- ror2 (8-bit ror) a ubyte in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
lsr a
|
||||||
|
bcc +
|
||||||
|
ora #$80
|
||||||
|
+ sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
rol2_array_ub .proc
|
||||||
|
; -- rol2 (8-bit rol) a ubyte in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
ldy c64.ESTACK_LO,x
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
cmp #$80
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
ror_array_uw .proc
|
||||||
|
; -- ror a uword in an array (index and array address on stack)
|
||||||
|
php
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
plp
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
rol_array_uw .proc
|
||||||
|
; -- rol a uword in an array (index and array address on stack)
|
||||||
|
php
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
plp
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rts
|
||||||
|
.pend
|
||||||
|
|
||||||
|
rol2_array_uw .proc
|
||||||
|
; -- rol2 (16-bit rol) a uword in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
asl a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
rol a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
bcc +
|
||||||
|
dey
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
adc #0
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
+ rts
|
||||||
.pend
|
.pend
|
||||||
|
|
||||||
ror2_array_uw .proc
|
ror2_array_uw .proc
|
||||||
.warn "ror2_array_uw" ; TODO
|
; -- ror2 (16-bit ror) a uword in an array (index and array address on stack)
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
asl a
|
||||||
|
tay
|
||||||
|
inx
|
||||||
|
lda c64.ESTACK_LO,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1
|
||||||
|
lda c64.ESTACK_HI,x
|
||||||
|
sta c64.SCRATCH_ZPWORD1+1
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
lsr a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
dey
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ror a
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
bcc +
|
||||||
|
iny
|
||||||
|
lda (c64.SCRATCH_ZPWORD1),y
|
||||||
|
ora #$80
|
||||||
|
sta (c64.SCRATCH_ZPWORD1),y
|
||||||
|
+ rts
|
||||||
.pend
|
.pend
|
||||||
|
@ -1 +1 @@
|
|||||||
1.80
|
1.91
|
||||||
|
@ -2,13 +2,13 @@ package prog8
|
|||||||
|
|
||||||
import kotlinx.cli.*
|
import kotlinx.cli.*
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.AstException
|
||||||
import prog8.compiler.*
|
import prog8.compiler.CompilationResult
|
||||||
|
import prog8.compiler.compileProgram
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.compiler.target.c64.codegen.AsmGen
|
import prog8.compiler.target.c64.codegen.AsmGen
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import prog8.vm.astvm.AstVm
|
|
||||||
import java.io.IOException
|
import java.io.IOException
|
||||||
import java.nio.file.FileSystems
|
import java.nio.file.FileSystems
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -39,7 +39,6 @@ private fun compileMain(args: Array<String>) {
|
|||||||
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
val outputDir by cli.flagValueArgument("-out", "directory", "directory for output files instead of current directory", ".")
|
||||||
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
val dontWriteAssembly by cli.flagArgument("-noasm", "don't create assembly code")
|
||||||
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
val dontOptimize by cli.flagArgument("-noopt", "don't perform any optimizations")
|
||||||
val launchSimulator by cli.flagArgument("-sim", "launch the builtin execution simulator after compilation")
|
|
||||||
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
val watchMode by cli.flagArgument("-watch", "continuous compilation mode (watches for file changes), greatly increases compilation speed")
|
||||||
val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64")
|
val compilationTarget by cli.flagValueArgument("-target", "compilertarget", "target output of the compiler, currently only 'c64' (C64 6502 assembly) available", "c64")
|
||||||
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
val moduleFiles by cli.positionalArgumentsList("modules", "main module file(s) to compile", minArgs = 1)
|
||||||
@ -118,18 +117,6 @@ private fun compileMain(args: Array<String>) {
|
|||||||
exitProcess(1)
|
exitProcess(1)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (launchSimulator) {
|
|
||||||
// val c64 = razorvine.c64emu.C64Machine("C64 emulator launched from Prog8 compiler")
|
|
||||||
// c64.cpu.addBreakpoint(0xea31) { cpu, address ->
|
|
||||||
// println("zz")
|
|
||||||
// Cpu6502.BreakpointResultAction()
|
|
||||||
// }
|
|
||||||
// c64.start()
|
|
||||||
println("\nLaunching AST-based simulator...")
|
|
||||||
val vm = AstVm(compilationResult.programAst, compilationTarget)
|
|
||||||
vm.run()
|
|
||||||
}
|
|
||||||
|
|
||||||
if (startEmulator) {
|
if (startEmulator) {
|
||||||
if (compilationResult.programName.isEmpty())
|
if (compilationResult.programName.isEmpty())
|
||||||
println("\nCan't start emulator because no program was assembled.")
|
println("\nCan't start emulator because no program was assembled.")
|
||||||
|
@ -283,14 +283,6 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment) {
|
override fun visit(assignment: Assignment) {
|
||||||
if(assignment is VariableInitializationAssignment) {
|
|
||||||
val targetVar = assignment.target.identifier?.targetVarDecl(program.namespace)
|
|
||||||
if(targetVar?.struct != null) {
|
|
||||||
// skip STRUCT init assignments
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
assignment.target.accept(this)
|
assignment.target.accept(this)
|
||||||
if (assignment.aug_op != null)
|
if (assignment.aug_op != null)
|
||||||
output(" ${assignment.aug_op} ")
|
output(" ${assignment.aug_op} ")
|
||||||
@ -331,6 +323,11 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun visit(foreverLoop: ForeverLoop) {
|
||||||
|
output("forever ")
|
||||||
|
foreverLoop.body.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
override fun visit(repeatLoop: RepeatLoop) {
|
override fun visit(repeatLoop: RepeatLoop) {
|
||||||
output("repeat ")
|
output("repeat ")
|
||||||
repeatLoop.body.accept(this)
|
repeatLoop.body.accept(this)
|
||||||
|
@ -3,6 +3,9 @@ package prog8.ast
|
|||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
@ -33,6 +36,8 @@ interface Node {
|
|||||||
return this
|
return this
|
||||||
throw FatalAstException("scope missing from $this")
|
throw FatalAstException("scope missing from $this")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun replaceChildNode(node: Node, replacement: Node)
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IFunctionCall {
|
interface IFunctionCall {
|
||||||
@ -158,6 +163,31 @@ interface INameScope {
|
|||||||
if(!statements.remove(stmt))
|
if(!statements.remove(stmt))
|
||||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun getAllLabels(label: String): List<Label> {
|
||||||
|
val result = mutableListOf<Label>()
|
||||||
|
|
||||||
|
fun find(scope: INameScope) {
|
||||||
|
scope.statements.forEach {
|
||||||
|
when(it) {
|
||||||
|
is Label -> result.add(it)
|
||||||
|
is INameScope -> find(it)
|
||||||
|
is IfStatement -> {
|
||||||
|
find(it.truepart)
|
||||||
|
find(it.elsepart)
|
||||||
|
}
|
||||||
|
is RepeatLoop -> find(it.body)
|
||||||
|
is ForeverLoop -> find(it.body)
|
||||||
|
is WhileLoop -> find(it.body)
|
||||||
|
is WhenStatement -> it.choices.forEach { choice->find(choice.statements) }
|
||||||
|
else -> { /* do nothing */ }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
find(this)
|
||||||
|
return result
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
interface IAssignable {
|
interface IAssignable {
|
||||||
@ -167,7 +197,7 @@ interface IAssignable {
|
|||||||
|
|
||||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||||
|
|
||||||
class Program(val name: String, val modules: MutableList<Module>) {
|
class Program(val name: String, val modules: MutableList<Module>): Node {
|
||||||
val namespace = GlobalNamespace(modules)
|
val namespace = GlobalNamespace(modules)
|
||||||
|
|
||||||
val definedLoadAddress: Int
|
val definedLoadAddress: Int
|
||||||
@ -187,6 +217,23 @@ class Program(val name: String, val modules: MutableList<Module>) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
|
||||||
|
|
||||||
|
override val position: Position = Position.DUMMY
|
||||||
|
override var parent: Node
|
||||||
|
get() = throw FatalAstException("program has no parent")
|
||||||
|
set(value) = throw FatalAstException("can't set parent of program")
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
modules.forEach {
|
||||||
|
it.linkParents(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(node is Module && replacement is Module)
|
||||||
|
val idx = modules.indexOf(node)
|
||||||
|
modules[idx] = replacement
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class Module(override val name: String,
|
class Module(override val name: String,
|
||||||
@ -194,6 +241,7 @@ class Module(override val name: String,
|
|||||||
override val position: Position,
|
override val position: Position,
|
||||||
val isLibraryModule: Boolean,
|
val isLibraryModule: Boolean,
|
||||||
val source: Path) : Node, INameScope {
|
val source: Path) : Node, INameScope {
|
||||||
|
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
lateinit var program: Program
|
lateinit var program: Program
|
||||||
val importedBy = mutableListOf<Module>()
|
val importedBy = mutableListOf<Module>()
|
||||||
@ -207,10 +255,20 @@ class Module(override val name: String,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun definingScope(): INameScope = program.namespace
|
override fun definingScope(): INameScope = program.namespace
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(node is Statement && replacement is Statement)
|
||||||
|
val idx = statements.indexOf(node)
|
||||||
|
statements[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
override fun toString() = "Module(name=$name, pos=$position, lib=$isLibraryModule)"
|
||||||
|
|
||||||
|
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||||
override val name = "<<<global>>>"
|
override val name = "<<<global>>>"
|
||||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||||
@ -221,6 +279,10 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
modules.forEach { it.linkParents(this) }
|
modules.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("cannot replace anything in the namespace")
|
||||||
|
}
|
||||||
|
|
||||||
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
||||||
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
||||||
// builtin functions always exist, return a dummy localContext for them
|
// builtin functions always exist, return a dummy localContext for them
|
||||||
@ -246,7 +308,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
|||||||
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
return when (val stmt = localContext.definingModule().lookup(scopedName, localContext)) {
|
||||||
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
is Label, is VarDecl, is Block, is Subroutine -> stmt
|
||||||
null -> null
|
null -> null
|
||||||
else -> throw NameError("wrong identifier target: $stmt", stmt.position)
|
else -> throw SyntaxError("wrong identifier target for $scopedName: $stmt", stmt.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -19,13 +19,13 @@ import java.nio.file.Path
|
|||||||
|
|
||||||
private data class NumericLiteral(val number: Number, val datatype: DataType)
|
private data class NumericLiteral(val number: Number, val datatype: DataType)
|
||||||
|
|
||||||
|
internal fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module {
|
||||||
fun prog8Parser.ModuleContext.toAst(name: String, isLibrary: Boolean, source: Path) : Module {
|
|
||||||
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
|
val nameWithoutSuffix = if(name.endsWith(".p8")) name.substringBeforeLast('.') else name
|
||||||
return Module(nameWithoutSuffix, modulestatement().asSequence().map { it.toAst(isLibrary) }.toMutableList(), toPosition(), isLibrary, source)
|
val directives = this.directive().map { it.toAst() }
|
||||||
|
val blocks = this.block().map { it.toAst(isLibrary) }
|
||||||
|
return Module(nameWithoutSuffix, (directives + blocks).toMutableList(), toPosition(), isLibrary, source)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun ParserRuleContext.toPosition() : Position {
|
private fun ParserRuleContext.toPosition() : Position {
|
||||||
val customTokensource = this.start.tokenSource as? CustomLexer
|
val customTokensource = this.start.tokenSource as? CustomLexer
|
||||||
val filename =
|
val filename =
|
||||||
@ -38,27 +38,23 @@ private fun ParserRuleContext.toPosition() : Position {
|
|||||||
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
return Position(filename, start.line, start.charPositionInLine, stop.charPositionInLine + stop.text.length)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement {
|
||||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : Statement {
|
val blockstatements = block_statement().map {
|
||||||
val directive = directive()?.toAst()
|
when {
|
||||||
if(directive!=null) return directive
|
it.variabledeclaration()!=null -> it.variabledeclaration().toAst()
|
||||||
|
it.subroutinedeclaration()!=null -> it.subroutinedeclaration().toAst()
|
||||||
val block = block()?.toAst(isInLibrary)
|
it.directive()!=null -> it.directive().toAst()
|
||||||
if(block!=null) return block
|
it.inlineasm()!=null -> it.inlineasm().toAst()
|
||||||
|
else -> throw FatalAstException("weird block statement $it")
|
||||||
throw FatalAstException(text)
|
}
|
||||||
|
}
|
||||||
|
return Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), blockstatements.toMutableList(), isInLibrary, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement =
|
|
||||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<Statement> =
|
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<Statement> =
|
||||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||||
|
|
||||||
|
private fun prog8Parser.VariabledeclarationContext.toAst() : Statement {
|
||||||
private fun prog8Parser.StatementContext.toAst() : Statement {
|
|
||||||
vardecl()?.let { return it.toAst() }
|
vardecl()?.let { return it.toAst() }
|
||||||
|
|
||||||
varinitializer()?.let {
|
varinitializer()?.let {
|
||||||
@ -142,6 +138,28 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
structdecl()?.let {
|
||||||
|
return StructDecl(it.identifier().text,
|
||||||
|
it.vardecl().map { vd->vd.toAst() }.toMutableList(),
|
||||||
|
toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
|
throw FatalAstException("weird variable decl $this")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.SubroutinedeclarationContext.toAst() : Subroutine {
|
||||||
|
return when {
|
||||||
|
subroutine()!=null -> subroutine().toAst()
|
||||||
|
asmsubroutine()!=null -> asmsubroutine().toAst()
|
||||||
|
romsubroutine()!=null -> romsubroutine().toAst()
|
||||||
|
else -> throw FatalAstException("weird subroutine decl $this")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||||
|
val vardecl = variabledeclaration()?.toAst()
|
||||||
|
if(vardecl!=null) return vardecl
|
||||||
|
|
||||||
assignment()?.let {
|
assignment()?.let {
|
||||||
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
|
return Assignment(it.assign_target().toAst(), null, it.expression().toAst(), it.toPosition())
|
||||||
}
|
}
|
||||||
@ -175,8 +193,8 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
val returnstmt = returnstmt()?.toAst()
|
val returnstmt = returnstmt()?.toAst()
|
||||||
if(returnstmt!=null) return returnstmt
|
if(returnstmt!=null) return returnstmt
|
||||||
|
|
||||||
val sub = subroutine()?.toAst()
|
val subroutine = subroutinedeclaration()?.toAst()
|
||||||
if(sub!=null) return sub
|
if(subroutine!=null) return subroutine
|
||||||
|
|
||||||
val asm = inlineasm()?.toAst()
|
val asm = inlineasm()?.toAst()
|
||||||
if(asm!=null) return asm
|
if(asm!=null) return asm
|
||||||
@ -193,31 +211,22 @@ private fun prog8Parser.StatementContext.toAst() : Statement {
|
|||||||
val whileloop = whileloop()?.toAst()
|
val whileloop = whileloop()?.toAst()
|
||||||
if(whileloop!=null) return whileloop
|
if(whileloop!=null) return whileloop
|
||||||
|
|
||||||
|
val foreverloop = foreverloop()?.toAst()
|
||||||
|
if(foreverloop!=null) return foreverloop
|
||||||
|
|
||||||
val breakstmt = breakstmt()?.toAst()
|
val breakstmt = breakstmt()?.toAst()
|
||||||
if(breakstmt!=null) return breakstmt
|
if(breakstmt!=null) return breakstmt
|
||||||
|
|
||||||
val continuestmt = continuestmt()?.toAst()
|
val continuestmt = continuestmt()?.toAst()
|
||||||
if(continuestmt!=null) return continuestmt
|
if(continuestmt!=null) return continuestmt
|
||||||
|
|
||||||
val asmsubstmt = asmsubroutine()?.toAst()
|
|
||||||
if(asmsubstmt!=null) return asmsubstmt
|
|
||||||
|
|
||||||
val romsubstmt = romsubroutine()?.toAst()
|
|
||||||
if(romsubstmt!=null) return romsubstmt
|
|
||||||
|
|
||||||
val whenstmt = whenstmt()?.toAst()
|
val whenstmt = whenstmt()?.toAst()
|
||||||
if(whenstmt!=null) return whenstmt
|
if(whenstmt!=null) return whenstmt
|
||||||
|
|
||||||
structdecl()?.let {
|
|
||||||
return StructDecl(it.identifier().text,
|
|
||||||
it.vardecl().map { vd->vd.toAst() }.toMutableList(),
|
|
||||||
toPosition())
|
|
||||||
}
|
|
||||||
|
|
||||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
private fun prog8Parser.AsmsubroutineContext.toAst(): Subroutine {
|
||||||
val subdecl = asmsub_decl().toAst()
|
val subdecl = asmsub_decl().toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf()
|
val statements = statement_block()?.toAst() ?: mutableListOf()
|
||||||
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
||||||
@ -225,7 +234,7 @@ private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
|||||||
subdecl.asmClobbers, null, true, statements, toPosition())
|
subdecl.asmClobbers, null, true, statements, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RomsubroutineContext.toAst(): Statement {
|
private fun prog8Parser.RomsubroutineContext.toAst(): Subroutine {
|
||||||
val subdecl = asmsub_decl().toAst()
|
val subdecl = asmsub_decl().toAst()
|
||||||
val address = integerliteral().toAst().number.toInt()
|
val address = integerliteral().toAst().number.toInt()
|
||||||
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
return Subroutine(subdecl.name, subdecl.parameters, subdecl.returntypes,
|
||||||
@ -233,7 +242,6 @@ private fun prog8Parser.RomsubroutineContext.toAst(): Statement {
|
|||||||
subdecl.asmClobbers, address, true, mutableListOf(), toPosition())
|
subdecl.asmClobbers, address, true, mutableListOf(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class AsmsubDecl(val name: String,
|
private class AsmsubDecl(val name: String,
|
||||||
val parameters: List<SubroutineParameter>,
|
val parameters: List<SubroutineParameter>,
|
||||||
val returntypes: List<DataType>,
|
val returntypes: List<DataType>,
|
||||||
@ -241,7 +249,6 @@ private class AsmsubDecl(val name: String,
|
|||||||
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
val asmReturnvaluesRegisters: List<RegisterOrStatusflag>,
|
||||||
val asmClobbers: Set<Register>)
|
val asmClobbers: Set<Register>)
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
||||||
val name = identifier().text
|
val name = identifier().text
|
||||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||||
@ -254,7 +261,6 @@ private fun prog8Parser.Asmsub_declContext.toAst(): AsmsubDecl {
|
|||||||
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
return AsmsubDecl(name, normalParameters, normalReturntypes, paramRegisters, returnRegisters, clobbers)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class AsmSubroutineParameter(name: String,
|
private class AsmSubroutineParameter(name: String,
|
||||||
type: DataType,
|
type: DataType,
|
||||||
val registerOrPair: RegisterOrPair?,
|
val registerOrPair: RegisterOrPair?,
|
||||||
@ -271,7 +277,6 @@ private class AsmSubroutineReturn(val type: DataType,
|
|||||||
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
|
private fun prog8Parser.ClobberContext.toAst(): Set<Register>
|
||||||
= this.register().asSequence().map { it.toAst() }.toSet()
|
= this.register().asSequence().map { it.toAst() }.toSet()
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Asmsub_returnsContext.toAst(): List<AsmSubroutineReturn>
|
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()) }
|
= asmsub_return().map { AsmSubroutineReturn(it.datatype().toAst(), it.registerorpair()?.toAst(), it.statusregister()?.toAst(), !it.stack?.text.isNullOrEmpty(), toPosition()) }
|
||||||
|
|
||||||
@ -285,10 +290,8 @@ private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParamete
|
|||||||
!it.stack?.text.isNullOrEmpty(), toPosition())
|
!it.stack?.text.isNullOrEmpty(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||||
val void = this.VOID() != null
|
val void = this.VOID() != null
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
@ -298,7 +301,6 @@ private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
|||||||
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
|
FunctionCallStatement(location, expression_list().toAst().toMutableList(), void, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
|
private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
|
||||||
val location = scoped_identifier().toAst()
|
val location = scoped_identifier().toAst()
|
||||||
return if(expression_list() == null)
|
return if(expression_list() == null)
|
||||||
@ -307,11 +309,9 @@ private fun prog8Parser.FunctioncallContext.toAst(): FunctionCall {
|
|||||||
FunctionCall(location, expression_list().toAst().toMutableList(), toPosition())
|
FunctionCall(location, expression_list().toAst().toMutableList(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.InlineasmContext.toAst() =
|
private fun prog8Parser.InlineasmContext.toAst() =
|
||||||
InlineAssembly(INLINEASMBLOCK().text, toPosition())
|
InlineAssembly(INLINEASMBLOCK().text, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ReturnstmtContext.toAst() : Return {
|
private fun prog8Parser.ReturnstmtContext.toAst() : Return {
|
||||||
return Return(expression()?.toAst(), toPosition())
|
return Return(expression()?.toAst(), toPosition())
|
||||||
}
|
}
|
||||||
@ -322,11 +322,9 @@ private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
|||||||
return Jump(address, identifier, null, toPosition())
|
return Jump(address, identifier, null, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.LabeldefContext.toAst(): Statement =
|
private fun prog8Parser.LabeldefContext.toAst(): Statement =
|
||||||
Label(children[0].text, toPosition())
|
Label(children[0].text, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
|
private fun prog8Parser.SubroutineContext.toAst() : Subroutine {
|
||||||
return Subroutine(identifier().text,
|
return Subroutine(identifier().text,
|
||||||
sub_params()?.toAst() ?: emptyList(),
|
sub_params()?.toAst() ?: emptyList(),
|
||||||
@ -345,14 +343,12 @@ private fun prog8Parser.Sub_return_partContext.toAst(): List<DataType> {
|
|||||||
return returns.datatype().map { it.toAst() }
|
return returns.datatype().map { it.toAst() }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
private fun prog8Parser.Sub_paramsContext.toAst(): List<SubroutineParameter> =
|
||||||
vardecl().map {
|
vardecl().map {
|
||||||
val datatype = it.datatype()?.toAst() ?: DataType.STRUCT
|
val datatype = it.datatype()?.toAst() ?: DataType.STRUCT
|
||||||
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
SubroutineParameter(it.varname.text, datatype, it.toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
private fun prog8Parser.Assign_targetContext.toAst() : AssignTarget {
|
||||||
val register = register()?.toAst()
|
val register = register()?.toAst()
|
||||||
val identifier = scoped_identifier()
|
val identifier = scoped_identifier()
|
||||||
@ -371,15 +367,12 @@ private fun prog8Parser.DatatypeContext.toAst() = DataType.valueOf(text.toUpperC
|
|||||||
|
|
||||||
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
|
private fun prog8Parser.RegisterorpairContext.toAst() = RegisterOrPair.valueOf(text.toUpperCase())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
private fun prog8Parser.ArrayindexContext.toAst() : ArrayIndex =
|
||||||
ArrayIndex(expression().toAst(), toPosition())
|
ArrayIndex(expression().toAst(), toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
private fun prog8Parser.DirectiveContext.toAst() : Directive =
|
||||||
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
Directive(directivename.text, directivearg().map { it.toAst() }, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
||||||
val str = stringliteral()
|
val str = stringliteral()
|
||||||
if(str?.ALT_STRING_ENCODING() != null)
|
if(str?.ALT_STRING_ENCODING() != null)
|
||||||
@ -388,7 +381,6 @@ private fun prog8Parser.DirectiveargContext.toAst() : DirectiveArg {
|
|||||||
return DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
return DirectiveArg(stringliteral()?.text, identifier()?.text, integerliteral()?.toAst()?.number?.toInt(), toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||||
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
fun makeLiteral(text: String, radix: Int, forceWord: Boolean): NumericLiteral {
|
||||||
val integer: Int
|
val integer: Int
|
||||||
@ -440,7 +432,6 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
||||||
|
|
||||||
val litval = literalvalue()
|
val litval = literalvalue()
|
||||||
@ -526,39 +517,31 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
|||||||
throw FatalAstException(text)
|
throw FatalAstException(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.StringliteralContext.toAst(): StringLiteralValue =
|
private fun prog8Parser.StringliteralContext.toAst(): StringLiteralValue =
|
||||||
StringLiteralValue(unescape(this.STRING().text, toPosition()), ALT_STRING_ENCODING()!=null, toPosition())
|
StringLiteralValue(unescape(this.STRING().text, toPosition()), ALT_STRING_ENCODING()!=null, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
private fun prog8Parser.ArrayindexedContext.toAst(): ArrayIndexedExpression {
|
||||||
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
return ArrayIndexedExpression(scoped_identifier().toAst(),
|
||||||
arrayindex().toAst(),
|
arrayindex().toAst(),
|
||||||
toPosition())
|
toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() }
|
private fun prog8Parser.Expression_listContext.toAst() = expression().map{ it.toAst() }
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
|
private fun prog8Parser.IdentifierContext.toAst() : IdentifierReference =
|
||||||
IdentifierReference(listOf(text), toPosition())
|
IdentifierReference(listOf(text), toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Scoped_identifierContext.toAst() : IdentifierReference =
|
private fun prog8Parser.Scoped_identifierContext.toAst() : IdentifierReference =
|
||||||
IdentifierReference(NAME().map { it.text }, toPosition())
|
IdentifierReference(NAME().map { it.text }, toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.FloatliteralContext.toAst() = text.toDouble()
|
private fun prog8Parser.FloatliteralContext.toAst() = text.toDouble()
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
||||||
"true" -> true
|
"true" -> true
|
||||||
"false" -> false
|
"false" -> false
|
||||||
else -> throw FatalAstException(text)
|
else -> throw FatalAstException(text)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<Expression> =
|
private fun prog8Parser.ArrayliteralContext.toAst() : Array<Expression> =
|
||||||
expression().map { it.toAst() }.toTypedArray()
|
expression().map { it.toAst() }.toTypedArray()
|
||||||
|
|
||||||
@ -576,7 +559,6 @@ private fun prog8Parser.Else_partContext.toAst(): MutableList<Statement> {
|
|||||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
||||||
val branchcondition = branchcondition().toAst()
|
val branchcondition = branchcondition().toAst()
|
||||||
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val trueStatements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
@ -589,7 +571,6 @@ private fun prog8Parser.Branch_stmtContext.toAst(): BranchStatement {
|
|||||||
|
|
||||||
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
private fun prog8Parser.BranchconditionContext.toAst() = BranchCondition.valueOf(text.substringAfter('_').toUpperCase())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
||||||
val loopregister = register()?.toAst()
|
val loopregister = register()?.toAst()
|
||||||
val loopvar = identifier()?.toAst()
|
val loopvar = identifier()?.toAst()
|
||||||
@ -602,12 +583,10 @@ private fun prog8Parser.ForloopContext.toAst(): ForLoop {
|
|||||||
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
|
return ForLoop(loopregister, loopvar, iterable, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
private fun prog8Parser.ContinuestmtContext.toAst() = Continue(toPosition())
|
||||||
|
|
||||||
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
private fun prog8Parser.BreakstmtContext.toAst() = Break(toPosition())
|
||||||
|
|
||||||
|
|
||||||
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
||||||
val condition = expression().toAst()
|
val condition = expression().toAst()
|
||||||
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
@ -616,6 +595,12 @@ private fun prog8Parser.WhileloopContext.toAst(): WhileLoop {
|
|||||||
return WhileLoop(condition, scope, toPosition())
|
return WhileLoop(condition, scope, toPosition())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun prog8Parser.ForeverloopContext.toAst(): ForeverLoop {
|
||||||
|
val statements = statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||||
|
val scope = AnonymousScope(statements, statement_block()?.toPosition()
|
||||||
|
?: statement().toPosition())
|
||||||
|
return ForeverLoop(scope, toPosition())
|
||||||
|
}
|
||||||
|
|
||||||
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
private fun prog8Parser.RepeatloopContext.toAst(): RepeatLoop {
|
||||||
val untilCondition = expression().toAst()
|
val untilCondition = expression().toAst()
|
||||||
@ -681,4 +666,3 @@ internal fun unescape(str: String, position: Position): String {
|
|||||||
}
|
}
|
||||||
return result.joinToString("")
|
return result.joinToString("")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -150,8 +150,13 @@ object ParentSentinel : Node {
|
|||||||
override val position = Position("<<sentinel>>", 0, 0, 0)
|
override val position = Position("<<sentinel>>", 0, 0, 0)
|
||||||
override var parent: Node = this
|
override var parent: Node = this
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {}
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Position(val file: String, val line: Int, val startCol: Int, val endCol: Int) {
|
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}]"
|
override fun toString(): String = "[$file: line $line col ${startCol+1}-${endCol+1}]"
|
||||||
|
|
||||||
|
companion object {
|
||||||
|
val DUMMY = Position("<dummy>", 0, 0, 0)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -3,35 +3,42 @@ package prog8.ast.base
|
|||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
|
|
||||||
|
|
||||||
fun printErrors(errors: List<Any>, moduleName: String) {
|
class ErrorReporter {
|
||||||
val reportedMessages = mutableSetOf<String>()
|
private enum class MessageSeverity {
|
||||||
System.err.print("\u001b[91m") // bright red
|
WARNING,
|
||||||
errors.forEach {
|
ERROR
|
||||||
val msg = it.toString()
|
}
|
||||||
if(msg !in reportedMessages) {
|
private class CompilerMessage(val severity: MessageSeverity, val message: String, val position: Position)
|
||||||
|
|
||||||
|
private val messages = mutableListOf<CompilerMessage>()
|
||||||
|
private val alreadyReportedMessages = mutableSetOf<String>()
|
||||||
|
|
||||||
|
fun err(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.ERROR, msg, position))
|
||||||
|
fun warn(msg: String, position: Position) = messages.add(CompilerMessage(MessageSeverity.WARNING, msg, position))
|
||||||
|
|
||||||
|
fun handle() {
|
||||||
|
var numErrors = 0
|
||||||
|
var numWarnings = 0
|
||||||
|
messages.forEach {
|
||||||
|
when(it.severity) {
|
||||||
|
MessageSeverity.ERROR -> System.err.print("\u001b[91m") // bright red
|
||||||
|
MessageSeverity.WARNING -> System.err.print("\u001b[93m") // bright yellow
|
||||||
|
}
|
||||||
|
val msg = "${it.position} ${it.severity} ${it.message}".trim()
|
||||||
|
if(msg !in alreadyReportedMessages) {
|
||||||
System.err.println(msg)
|
System.err.println(msg)
|
||||||
reportedMessages.add(msg)
|
alreadyReportedMessages.add(msg)
|
||||||
|
when(it.severity) {
|
||||||
|
MessageSeverity.WARNING -> numWarnings++
|
||||||
|
MessageSeverity.ERROR -> numErrors++
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
System.err.print("\u001b[0m") // reset color
|
System.err.print("\u001b[0m") // reset color
|
||||||
if(reportedMessages.isNotEmpty())
|
}
|
||||||
throw ParsingFailedError("There are ${reportedMessages.size} errors in module '$moduleName'.")
|
messages.clear()
|
||||||
|
if(numErrors>0)
|
||||||
|
throw ParsingFailedError("There are $numErrors errors and $numWarnings warnings.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun isEmpty() = messages.isEmpty()
|
||||||
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
|
|
||||||
}
|
}
|
||||||
|
@ -6,17 +6,13 @@ class FatalAstException (override var message: String) : Exception(message)
|
|||||||
|
|
||||||
open class AstException (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) {
|
open class SyntaxError(override var message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Syntax error: $message"
|
override fun toString() = "$position Syntax error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
open class NameError(override var message: String, val position: Position) : AstException(message) {
|
|
||||||
override fun toString() = "$position Name error: $message"
|
|
||||||
}
|
|
||||||
|
|
||||||
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
class ExpressionError(message: String, val position: Position) : AstException(message) {
|
||||||
override fun toString() = "$position Error: $message"
|
override fun toString() = "$position Error: $message"
|
||||||
}
|
}
|
||||||
|
|
||||||
class UndefinedSymbolError(symbol: IdentifierReference)
|
class UndefinedSymbolError(symbol: IdentifierReference)
|
||||||
: NameError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
: SyntaxError("undefined symbol: ${symbol.nameInSource.joinToString(".")}", symbol.position)
|
||||||
|
@ -4,66 +4,64 @@ import prog8.ast.Module
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.processing.*
|
import prog8.ast.processing.*
|
||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
|
import prog8.compiler.target.AsmVariableAndReturnsPreparer
|
||||||
|
import prog8.optimizer.FlattenAnonymousScopesAndNopRemover
|
||||||
|
|
||||||
|
|
||||||
// the name of the subroutine that should be called for every block to initialize its variables
|
internal fun Program.checkValid(compilerOptions: CompilationOptions, errors: ErrorReporter) {
|
||||||
internal const val initvarsSubName="prog8_init_vars"
|
val checker = AstChecker(this, compilerOptions, errors)
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.removeNopsFlattenAnonScopes() {
|
|
||||||
val flattener = FlattenAnonymousScopesAndRemoveNops()
|
|
||||||
flattener.visit(this)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.checkValid(compilerOptions: CompilationOptions) {
|
|
||||||
val checker = AstChecker(this, compilerOptions)
|
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
printErrors(checker.result(), name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.prepareAsmVariablesAndReturns(errors: ErrorReporter) {
|
||||||
internal fun Program.anonscopeVarsCleanup() {
|
val fixer = AsmVariableAndReturnsPreparer(this, errors)
|
||||||
val mover = AnonymousScopeVarsCleanup(this)
|
fixer.visit(this)
|
||||||
mover.visit(this)
|
fixer.applyModifications()
|
||||||
printErrors(mover.result(), name)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.reorderStatements() {
|
internal fun Program.reorderStatements() {
|
||||||
val initvalueCreator = VarInitValueAndAddressOfCreator(this)
|
val initvalueCreator = AddressOfInserter(this)
|
||||||
initvalueCreator.visit(this)
|
initvalueCreator.visit(this)
|
||||||
|
initvalueCreator.applyModifications()
|
||||||
|
|
||||||
val checker = StatementReorderer(this)
|
val checker = StatementReorderer(this)
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.addTypecasts() {
|
internal fun Program.addTypecasts(errors: ErrorReporter) {
|
||||||
val caster = TypecastsAdder(this)
|
val caster = TypecastsAdder(this, errors)
|
||||||
caster.visit(this)
|
caster.visit(this)
|
||||||
|
caster.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Module.checkImportedValid() {
|
internal fun Module.checkImportedValid() {
|
||||||
val checker = ImportedModuleDirectiveRemover()
|
val imr = ImportedModuleDirectiveRemover()
|
||||||
checker.visit(this)
|
imr.visit(this, this.parent)
|
||||||
printErrors(checker.result(), name)
|
imr.applyModifications()
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.checkRecursion() {
|
internal fun Program.checkRecursion(errors: ErrorReporter) {
|
||||||
val checker = AstRecursionChecker(namespace)
|
val checker = AstRecursionChecker(namespace, errors)
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
printErrors(checker.result(), name)
|
checker.processMessages(name)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal fun Program.checkIdentifiers(errors: ErrorReporter) {
|
||||||
internal fun Program.checkIdentifiers() {
|
val checker = AstIdentifiersChecker(this, errors)
|
||||||
val checker = AstIdentifiersChecker(this)
|
|
||||||
checker.visit(this)
|
checker.visit(this)
|
||||||
|
|
||||||
if (modules.map { it.name }.toSet().size != modules.size) {
|
if (modules.map { it.name }.toSet().size != modules.size) {
|
||||||
throw FatalAstException("modules should all be unique")
|
throw FatalAstException("modules should all be unique")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
printErrors(checker.result(), name)
|
|
||||||
|
internal fun Program.makeForeverLoops() {
|
||||||
|
val checker = ForeverLoopsMaker()
|
||||||
|
checker.visit(this)
|
||||||
|
checker.applyModifications()
|
||||||
|
}
|
||||||
|
|
||||||
|
internal fun Program.removeNopsFlattenAnonScopes() {
|
||||||
|
val flattener = FlattenAnonymousScopesAndNopRemover()
|
||||||
|
flattener.visit(this)
|
||||||
}
|
}
|
||||||
|
@ -3,6 +3,7 @@ package prog8.ast.expressions
|
|||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.antlr.escape
|
import prog8.ast.antlr.escape
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
@ -10,7 +11,7 @@ import prog8.compiler.target.CompilationTarget
|
|||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.NotConstArgumentException
|
import prog8.functions.NotConstArgumentException
|
||||||
import prog8.functions.builtinFunctionReturnType
|
import prog8.functions.builtinFunctionReturnType
|
||||||
import java.util.Objects
|
import java.util.*
|
||||||
import kotlin.math.abs
|
import kotlin.math.abs
|
||||||
|
|
||||||
|
|
||||||
@ -21,6 +22,7 @@ sealed class Expression: Node {
|
|||||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove this and add identifier usage tracking into CallGraph instead
|
||||||
abstract fun inferType(program: Program): InferredTypes.InferredType
|
abstract fun inferType(program: Program): InferredTypes.InferredType
|
||||||
|
|
||||||
@ -56,9 +58,16 @@ class PrefixExpression(val operator: String, var expression: Expression, overrid
|
|||||||
expression.linkParents(this)
|
expression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(node === expression && replacement is Expression)
|
||||||
|
expression = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val inferred = expression.inferType(program)
|
val inferred = expression.inferType(program)
|
||||||
@ -96,6 +105,15 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
right.linkParents(this)
|
right.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression)
|
||||||
|
when {
|
||||||
|
node===left -> left = replacement
|
||||||
|
node===right -> right = replacement
|
||||||
|
else -> throw FatalAstException("invalid replace, no child $node")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "[$left $operator $right]"
|
return "[$left $operator $right]"
|
||||||
}
|
}
|
||||||
@ -105,6 +123,8 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = left.referencesIdentifiers(*name) || right.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val leftDt = left.inferType(program)
|
val leftDt = left.inferType(program)
|
||||||
@ -205,9 +225,19 @@ class ArrayIndexedExpression(var identifier: IdentifierReference,
|
|||||||
arrayspec.linkParents(this)
|
arrayspec.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===identifier -> identifier = replacement as IdentifierReference
|
||||||
|
node===arrayspec.index -> arrayspec.index = replacement as Expression
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = identifier.referencesIdentifiers(*name)
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
@ -235,8 +265,15 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
|
|||||||
expression.linkParents(this)
|
expression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression && node===expression)
|
||||||
|
expression = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String) = expression.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(type)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
@ -259,11 +296,17 @@ data class AddressOf(var identifier: IdentifierReference, override val position:
|
|||||||
identifier.parent=this
|
identifier.parent=this
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is IdentifierReference && node===identifier)
|
||||||
|
identifier = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UWORD)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression(), IAssignable {
|
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression(), IAssignable {
|
||||||
@ -274,8 +317,15 @@ class DirectMemoryRead(var addressExpression: Expression, override val position:
|
|||||||
this.addressExpression.linkParents(this)
|
this.addressExpression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression && node===addressExpression)
|
||||||
|
addressExpression = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.UBYTE)
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
@ -325,11 +375,16 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace here")
|
||||||
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun constValue(program: Program) = this
|
override fun constValue(program: Program) = this
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
|
override fun toString(): String = "NumericLiteral(${type.name}:$number)"
|
||||||
|
|
||||||
@ -413,9 +468,15 @@ class StructLiteralValue(var values: List<Expression>,
|
|||||||
values.forEach { it.linkParents(this) }
|
values.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace here")
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
override fun referencesIdentifiers(vararg name: String) = values.any { it.referencesIdentifiers(*name) }
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STRUCT)
|
||||||
|
|
||||||
@ -436,10 +497,17 @@ class StringLiteralValue(val value: String,
|
|||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace here")
|
||||||
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = false
|
override fun referencesIdentifiers(vararg name: String) = false
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String = "'${escape(value)}'"
|
override fun toString(): String = "'${escape(value)}'"
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STR)
|
override fun inferType(program: Program): InferredTypes.InferredType = InferredTypes.knownFor(DataType.STR)
|
||||||
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
|
operator fun compareTo(other: StringLiteralValue): Int = value.compareTo(other.value)
|
||||||
@ -462,10 +530,19 @@ class ArrayLiteralValue(val type: InferredTypes.InferredType, // inferred be
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
value.forEach {it.linkParents(this)}
|
value.forEach {it.linkParents(this)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression)
|
||||||
|
val idx = value.indexOf(node)
|
||||||
|
value[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
override fun referencesIdentifiers(vararg name: String) = value.any { it.referencesIdentifiers(*name) }
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String = "$value"
|
override fun toString(): String = "$value"
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isUnknown) type else guessDatatype(program)
|
override fun inferType(program: Program): InferredTypes.InferredType = if(type.isUnknown) type else guessDatatype(program)
|
||||||
|
|
||||||
@ -544,9 +621,21 @@ class RangeExpr(var from: Expression,
|
|||||||
step.linkParents(this)
|
step.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression)
|
||||||
|
when {
|
||||||
|
from===node -> from=replacement
|
||||||
|
to===node -> to=replacement
|
||||||
|
step===node -> step=replacement
|
||||||
|
else -> throw FatalAstException("invalid replacement")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
override fun referencesIdentifiers(vararg name: String): Boolean = from.referencesIdentifiers(*name) || to.referencesIdentifiers(*name)
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val fromDt=from.inferType(program)
|
val fromDt=from.inferType(program)
|
||||||
@ -618,9 +707,15 @@ class RegisterExpr(val register: Register, override val position: Position) : Ex
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace here")
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? = null
|
override fun constValue(program: Program): NumericLiteralValue? = null
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
|
override fun referencesIdentifiers(vararg name: String): Boolean = register.name in name
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "RegisterExpr(register=$register, pos=$position)"
|
return "RegisterExpr(register=$register, pos=$position)"
|
||||||
@ -645,6 +740,10 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace here")
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program): NumericLiteralValue? {
|
override fun constValue(program: Program): NumericLiteralValue? {
|
||||||
val node = program.namespace.lookup(nameInSource, this)
|
val node = program.namespace.lookup(nameInSource, this)
|
||||||
?: throw UndefinedSymbolError(this)
|
?: throw UndefinedSymbolError(this)
|
||||||
@ -663,14 +762,16 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
override fun referencesIdentifiers(vararg name: String): Boolean = nameInSource.last() in name
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
val targetStmt = targetStatement(program.namespace)
|
val targetStmt = targetStatement(program.namespace)
|
||||||
if(targetStmt is VarDecl) {
|
return if(targetStmt is VarDecl) {
|
||||||
return InferredTypes.knownFor(targetStmt.datatype)
|
InferredTypes.knownFor(targetStmt.datatype)
|
||||||
} else {
|
} else {
|
||||||
throw FatalAstException("cannot get datatype from identifier reference ${this}, pos=$position")
|
InferredTypes.InferredType.unknown()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -699,6 +800,15 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
args.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
if(node===target)
|
||||||
|
target=replacement as IdentifierReference
|
||||||
|
else {
|
||||||
|
val idx = args.indexOf(node)
|
||||||
|
args[idx] = replacement as Expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun constValue(program: Program) = constValue(program, true)
|
override fun constValue(program: Program) = constValue(program, true)
|
||||||
|
|
||||||
private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? {
|
private fun constValue(program: Program, withDatatypeCheck: Boolean): NumericLiteralValue? {
|
||||||
@ -737,6 +847,8 @@ class FunctionCall(override var target: IdentifierReference,
|
|||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node)= visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || args.any{it.referencesIdentifiers(*name)}
|
override fun referencesIdentifiers(vararg name: String): Boolean = target.referencesIdentifiers(*name) || args.any{it.referencesIdentifiers(*name)}
|
||||||
|
|
||||||
override fun inferType(program: Program): InferredTypes.InferredType {
|
override fun inferType(program: Program): InferredTypes.InferredType {
|
||||||
|
@ -1,7 +1,7 @@
|
|||||||
package prog8.ast.expressions
|
package prog8.ast.expressions
|
||||||
|
|
||||||
import java.util.Objects
|
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import java.util.*
|
||||||
|
|
||||||
|
|
||||||
object InferredTypes {
|
object InferredTypes {
|
||||||
|
93
compiler/src/prog8/ast/processing/AddressOfInserter.kt
Normal file
93
compiler/src/prog8/ast/processing/AddressOfInserter.kt
Normal file
@ -0,0 +1,93 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.IFunctionCall
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.IterableDatatypes
|
||||||
|
import prog8.ast.base.PassByReferenceDatatypes
|
||||||
|
import prog8.ast.expressions.AddressOf
|
||||||
|
import prog8.ast.expressions.Expression
|
||||||
|
import prog8.ast.expressions.FunctionCall
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.ast.statements.Statement
|
||||||
|
import prog8.ast.statements.Subroutine
|
||||||
|
import prog8.compiler.CompilerException
|
||||||
|
import prog8.functions.BuiltinFunctions
|
||||||
|
import prog8.functions.FSignature
|
||||||
|
|
||||||
|
|
||||||
|
internal class AddressOfInserter(val program: Program): AstWalker() {
|
||||||
|
// Insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
// TODO join this into the StatementReorderer?
|
||||||
|
|
||||||
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
|
// insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
var parentStatement: Node = functionCall
|
||||||
|
while(parentStatement !is Statement)
|
||||||
|
parentStatement = parentStatement.parent
|
||||||
|
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
|
||||||
|
if(targetStatement!=null) {
|
||||||
|
return addAddressOfExprIfNeeded(targetStatement, functionCall.args, functionCall)
|
||||||
|
} else {
|
||||||
|
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
|
||||||
|
if(builtinFunc!=null)
|
||||||
|
return addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.args, functionCall)
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
|
// insert AddressOf (&) expression where required (string params to a UWORD function param etc).
|
||||||
|
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
|
||||||
|
if(targetStatement!=null) {
|
||||||
|
return addAddressOfExprIfNeeded(targetStatement, functionCallStatement.args, functionCallStatement)
|
||||||
|
} else {
|
||||||
|
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
|
||||||
|
if(builtinFunc!=null)
|
||||||
|
return addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.args, functionCallStatement)
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, args: MutableList<Expression>, parent: IFunctionCall): Iterable<IAstModification> {
|
||||||
|
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
||||||
|
val replacements = mutableListOf<IAstModification>()
|
||||||
|
for(argparam in subroutine.parameters.withIndex().zip(args)) {
|
||||||
|
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type == DataType.STR) {
|
||||||
|
if(argparam.second is AddressOf)
|
||||||
|
continue
|
||||||
|
val idref = argparam.second as? IdentifierReference
|
||||||
|
if(idref!=null) {
|
||||||
|
val variable = idref.targetVarDecl(program.namespace)
|
||||||
|
if(variable!=null && variable.datatype in IterableDatatypes) {
|
||||||
|
replacements += IAstModification.ReplaceNode(
|
||||||
|
args[argparam.first.index],
|
||||||
|
AddressOf(idref, idref.position),
|
||||||
|
parent as Node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replacements
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun addAddressOfExprIfNeededForBuiltinFuncs(signature: FSignature, args: MutableList<Expression>, parent: IFunctionCall): Iterable<IAstModification> {
|
||||||
|
// val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
||||||
|
val replacements = mutableListOf<IAstModification>()
|
||||||
|
for(arg in args.withIndex().zip(signature.parameters)) {
|
||||||
|
val argvalue = arg.first.value
|
||||||
|
val argDt = argvalue.inferType(program)
|
||||||
|
if(argDt.typeOrElse(DataType.UBYTE) in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) {
|
||||||
|
if(argvalue !is IdentifierReference)
|
||||||
|
throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue")
|
||||||
|
replacements += IAstModification.ReplaceNode(
|
||||||
|
args[arg.first.index],
|
||||||
|
AddressOf(argvalue, argvalue.position),
|
||||||
|
parent as Node)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return replacements
|
||||||
|
}
|
||||||
|
}
|
@ -1,47 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.AstException
|
|
||||||
import prog8.ast.base.NameError
|
|
||||||
import prog8.ast.statements.AnonymousScope
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
|
|
||||||
class AnonymousScopeVarsCleanup(val program: Program): IAstModifyingVisitor {
|
|
||||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
|
||||||
private val varsToMove: MutableMap<AnonymousScope, List<VarDecl>> = mutableMapOf()
|
|
||||||
|
|
||||||
fun result(): List<AstException> {
|
|
||||||
return checkResult
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
varsToMove.clear()
|
|
||||||
super.visit(program)
|
|
||||||
for((scope, decls) in varsToMove) {
|
|
||||||
val sub = scope.definingSubroutine()!!
|
|
||||||
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
|
|
||||||
var conflicts = false
|
|
||||||
decls.forEach {
|
|
||||||
val existing = existingVariables[it.name]
|
|
||||||
if (existing!=null) {
|
|
||||||
checkResult.add(NameError("variable ${it.name} already defined in subroutine ${sub.name} at ${existing.position}", it.position))
|
|
||||||
conflicts = true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (!conflicts) {
|
|
||||||
decls.forEach { scope.remove(it) }
|
|
||||||
sub.statements.addAll(0, decls)
|
|
||||||
decls.forEach { it.parent = sub }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope): Statement {
|
|
||||||
val scope2 = super.visit(scope) as AnonymousScope
|
|
||||||
val vardecls = scope2.statements.filterIsInstance<VarDecl>()
|
|
||||||
varsToMove[scope2] = vardecls
|
|
||||||
return scope2
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
File diff suppressed because it is too large
Load Diff
@ -11,19 +11,13 @@ import prog8.compiler.target.CompilationTarget
|
|||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
internal class AstIdentifiersChecker(private val program: Program) : IAstModifyingVisitor {
|
internal class AstIdentifiersChecker(private val program: Program,
|
||||||
|
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
||||||
private val checkResult: MutableList<AstException> = mutableListOf()
|
|
||||||
|
|
||||||
private var blocks = mutableMapOf<String, Block>()
|
private var blocks = mutableMapOf<String, Block>()
|
||||||
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
private val vardeclsToAdd = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||||
|
|
||||||
internal fun result(): List<AstException> {
|
|
||||||
return checkResult
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||||
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
errors.err("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
override fun visit(module: Module) {
|
||||||
@ -59,15 +53,15 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
// first, check if there are datatype errors on the vardecl
|
// first, check if there are datatype errors on the vardecl
|
||||||
decl.datatypeErrors.forEach { checkResult.add(it) }
|
decl.datatypeErrors.forEach { errors.err(it.message, it.position) }
|
||||||
|
|
||||||
// now check the identifier
|
// now check the identifier
|
||||||
if(decl.name in BuiltinFunctions)
|
if(decl.name in BuiltinFunctions)
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
|
errors.err("builtin function cannot be redefined", decl.position)
|
||||||
|
|
||||||
if(decl.name in CompilationTarget.machine.opcodeNames)
|
if(decl.name in CompilationTarget.machine.opcodeNames)
|
||||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position))
|
errors.err("can't use a cpu opcode name as a symbol: '${decl.name}'", decl.position)
|
||||||
|
|
||||||
// is it a struct variable? then define all its struct members as mangled names,
|
// is it a struct variable? then define all its struct members as mangled names,
|
||||||
// and include the original decl as well.
|
// and include the original decl as well.
|
||||||
@ -76,7 +70,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
return super.visit(decl) // don't do this multiple times
|
return super.visit(decl) // don't do this multiple times
|
||||||
|
|
||||||
if(decl.struct==null) {
|
if(decl.struct==null) {
|
||||||
checkResult.add(NameError("undefined struct type", decl.position))
|
errors.err("undefined struct type", decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -84,7 +78,12 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
return super.visit(decl) // a non-numeric member, not supported. proper error is given by AstChecker later
|
||||||
|
|
||||||
if(decl.value is NumericLiteralValue) {
|
if(decl.value is NumericLiteralValue) {
|
||||||
checkResult.add(ExpressionError("you cannot initialize a struct using a single value", decl.position))
|
errors.err("you cannot initialize a struct using a single value", decl.position)
|
||||||
|
return super.visit(decl)
|
||||||
|
}
|
||||||
|
|
||||||
|
if(decl.value != null && decl.value !is StructLiteralValue) {
|
||||||
|
errors.err("initializing requires struct literal value", decl.value?.position ?: decl.position)
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,10 +103,10 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
if(subroutine.name in CompilationTarget.machine.opcodeNames) {
|
||||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position))
|
errors.err("can't use a cpu opcode name as a symbol: '${subroutine.name}'", subroutine.position)
|
||||||
} else if(subroutine.name in BuiltinFunctions) {
|
} else if(subroutine.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
errors.err("builtin function cannot be redefined", subroutine.position)
|
||||||
} else {
|
} else {
|
||||||
// already reported elsewhere:
|
// already reported elsewhere:
|
||||||
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
|
||||||
@ -148,8 +147,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
subroutine.parameters
|
subroutine.parameters
|
||||||
.filter { it.name !in namesInSub }
|
.filter { it.name !in namesInSub }
|
||||||
.forEach {
|
.forEach {
|
||||||
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.NOT_IN_ZEROPAGE, null, it.name, null, null,
|
val vardecl = ParameterVarDecl(it.name, it.type, subroutine.position)
|
||||||
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
|
|
||||||
vardecl.linkParents(subroutine)
|
vardecl.linkParents(subroutine)
|
||||||
subroutine.statements.add(0, vardecl)
|
subroutine.statements.add(0, vardecl)
|
||||||
}
|
}
|
||||||
@ -157,7 +155,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
if(subroutine.isAsmSubroutine && subroutine.statements.any{it !is InlineAssembly}) {
|
||||||
checkResult.add(SyntaxError("asmsub can only contain inline assembly (%asm)", subroutine.position))
|
errors.err("asmsub can only contain inline assembly (%asm)", subroutine.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return super.visit(subroutine)
|
return super.visit(subroutine)
|
||||||
@ -165,15 +163,21 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
override fun visit(label: Label): Statement {
|
override fun visit(label: Label): Statement {
|
||||||
if(label.name in CompilationTarget.machine.opcodeNames)
|
if(label.name in CompilationTarget.machine.opcodeNames)
|
||||||
checkResult.add(NameError("can't use a cpu opcode name as a symbol: '${label.name}'", label.position))
|
errors.err("can't use a cpu opcode name as a symbol: '${label.name}'", label.position)
|
||||||
|
|
||||||
if(label.name in BuiltinFunctions) {
|
if(label.name in BuiltinFunctions) {
|
||||||
// the builtin functions can't be redefined
|
// the builtin functions can't be redefined
|
||||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
errors.err("builtin function cannot be redefined", label.position)
|
||||||
} else {
|
} else {
|
||||||
val existing = program.namespace.lookup(listOf(label.name), label)
|
val existing = label.definingSubroutine()?.getAllLabels(label.name) ?: emptyList()
|
||||||
if (existing != null && existing !== label)
|
for(el in existing) {
|
||||||
nameError(label.name, label.position, existing)
|
if(el === label || el.name != label.name)
|
||||||
|
continue
|
||||||
|
else {
|
||||||
|
nameError(label.name, label.position, el)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return super.visit(label)
|
return super.visit(label)
|
||||||
}
|
}
|
||||||
@ -185,7 +189,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
// additional interation count variable in their scope.
|
// additional interation count variable in their scope.
|
||||||
if(forLoop.loopRegister!=null) {
|
if(forLoop.loopRegister!=null) {
|
||||||
if(forLoop.loopRegister == Register.X)
|
if(forLoop.loopRegister == Register.X)
|
||||||
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
|
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
|
||||||
} else {
|
} else {
|
||||||
val loopVar = forLoop.loopVar
|
val loopVar = forLoop.loopVar
|
||||||
if (loopVar != null) {
|
if (loopVar != null) {
|
||||||
@ -209,23 +213,10 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
|
|
||||||
override fun visit(assignTarget: AssignTarget): AssignTarget {
|
override fun visit(assignTarget: AssignTarget): AssignTarget {
|
||||||
if(assignTarget.register== Register.X)
|
if(assignTarget.register== Register.X)
|
||||||
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
|
errors.warn("writing to the X register is dangerous, because it's used as an internal pointer", assignTarget.position)
|
||||||
return super.visit(assignTarget)
|
return super.visit(assignTarget)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(returnStmt: Return): Statement {
|
|
||||||
if(returnStmt.value!=null) {
|
|
||||||
// possibly adjust any literal values returned, into the desired returning data type
|
|
||||||
val subroutine = returnStmt.definingSubroutine()!!
|
|
||||||
if(subroutine.returntypes.size!=1)
|
|
||||||
return returnStmt // mismatch in number of return values, error will be printed later.
|
|
||||||
val lval = returnStmt.value as? NumericLiteralValue
|
|
||||||
returnStmt.value = lval?.cast(subroutine.returntypes.single()) ?: returnStmt.value!!
|
|
||||||
}
|
|
||||||
return super.visit(returnStmt)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
override fun visit(arrayLiteral: ArrayLiteralValue): Expression {
|
||||||
val array = super.visit(arrayLiteral)
|
val array = super.visit(arrayLiteral)
|
||||||
if(array is ArrayLiteralValue) {
|
if(array is ArrayLiteralValue) {
|
||||||
@ -264,7 +255,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
val vardecl = string.parent as? VarDecl
|
val vardecl = string.parent as? VarDecl
|
||||||
// intern the string; move it into the heap
|
// intern the string; move it into the heap
|
||||||
if (string.value.length !in 1..255)
|
if (string.value.length !in 1..255)
|
||||||
checkResult.add(ExpressionError("string literal length must be between 1 and 255", string.position))
|
errors.err("string literal length must be between 1 and 255", string.position)
|
||||||
return if (vardecl != null)
|
return if (vardecl != null)
|
||||||
string
|
string
|
||||||
else
|
else
|
||||||
@ -303,7 +294,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
|||||||
for(member in structDecl.statements){
|
for(member in structDecl.statements){
|
||||||
val decl = member as? VarDecl
|
val decl = member as? VarDecl
|
||||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||||
checkResult.add(SyntaxError("structs can only contain numerical types", decl.position))
|
errors.err("structs can only contain numerical types", decl.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
return super.visit(structDecl)
|
return super.visit(structDecl)
|
||||||
|
@ -1,21 +1,23 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.ErrorReporter
|
||||||
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
|
|
||||||
|
|
||||||
internal class AstRecursionChecker(private val namespace: INameScope) : IAstVisitor {
|
internal class AstRecursionChecker(private val namespace: INameScope,
|
||||||
|
private val errors: ErrorReporter) : IAstVisitor {
|
||||||
private val callGraph = DirectedGraph<INameScope>()
|
private val callGraph = DirectedGraph<INameScope>()
|
||||||
|
|
||||||
internal fun result(): List<AstException> {
|
fun processMessages(modulename: String) {
|
||||||
val cycle = callGraph.checkForCycle()
|
val cycle = callGraph.checkForCycle()
|
||||||
if(cycle.isEmpty())
|
if(cycle.isEmpty())
|
||||||
return emptyList()
|
return
|
||||||
val chain = cycle.joinToString(" <-- ") { "${it.name} at ${it.position}" }
|
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"))
|
errors.err("Program contains recursive subroutine calls, this is not supported. Recursive chain:\n (a subroutine call in) $chain", Position.DUMMY)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement) {
|
override fun visit(functionCallStatement: FunctionCallStatement) {
|
||||||
@ -44,7 +46,6 @@ internal class AstRecursionChecker(private val namespace: INameScope) : IAstVisi
|
|||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
private class DirectedGraph<VT> {
|
private class DirectedGraph<VT> {
|
||||||
private val graph = mutableMapOf<VT, MutableSet<VT>>()
|
private val graph = mutableMapOf<VT, MutableSet<VT>>()
|
||||||
private var uniqueVertices = mutableSetOf<VT>()
|
private var uniqueVertices = mutableSetOf<VT>()
|
||||||
|
443
compiler/src/prog8/ast/processing/AstWalker.kt
Normal file
443
compiler/src/prog8/ast/processing/AstWalker.kt
Normal file
@ -0,0 +1,443 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Module
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.FatalAstException
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
interface IAstModification {
|
||||||
|
fun perform()
|
||||||
|
|
||||||
|
class Remove(val node: Node, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
if(parent is INameScope) {
|
||||||
|
if (!parent.statements.remove(node))
|
||||||
|
throw FatalAstException("attempt to remove non-existing node $node")
|
||||||
|
} else {
|
||||||
|
throw FatalAstException("parent of a remove modification is not an INameScope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SetExpression(val setter: (newExpr: Expression) -> Unit, val newExpr: Expression, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
setter(newExpr)
|
||||||
|
newExpr.linkParents(parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InsertFirst(val stmt: Statement, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
if(parent is INameScope) {
|
||||||
|
parent.statements.add(0, stmt)
|
||||||
|
stmt.linkParents(parent)
|
||||||
|
} else {
|
||||||
|
throw FatalAstException("parent of an insert modification is not an INameScope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class InsertAfter(val after: Statement, val stmt: Statement, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
if(parent is INameScope) {
|
||||||
|
val idx = parent.statements.indexOf(after)+1
|
||||||
|
parent.statements.add(idx, stmt)
|
||||||
|
stmt.linkParents(parent)
|
||||||
|
} else {
|
||||||
|
throw FatalAstException("parent of an insert modification is not an INameScope")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class ReplaceNode(val node: Node, val replacement: Node, val parent: Node) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
parent.replaceChildNode(node, replacement)
|
||||||
|
replacement.parent = parent
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
class SwapOperands(val expr: BinaryExpression): IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
val tmp = expr.left
|
||||||
|
expr.left = expr.right
|
||||||
|
expr.right = tmp
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
abstract class AstWalker {
|
||||||
|
open fun before(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(block: Block, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(label: Label, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(module: Module, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
|
||||||
|
open fun after(addressOf: AddressOf, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(array: ArrayLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(arrayIndexedExpression: ArrayIndexedExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(assignTarget: AssignTarget, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(block: Block, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(branchStatement: BranchStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(breakStmt: Break, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(contStmt: Continue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(directive: Directive, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(expr: PrefixExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(forLoop: ForLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(foreverLoop: ForeverLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(ifStatement: IfStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(label: Label, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(module: Module, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(nopStatement: NopStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(numLiteral: NumericLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(postIncrDecr: PostIncrDecr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(program: Program, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(range: RangeExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(registerExpr: RegisterExpr, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(structDecl: StructDecl, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(whenChoice: WhenChoice, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(whenStatement: WhenStatement, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
open fun after(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> = emptyList()
|
||||||
|
|
||||||
|
private val modifications = mutableListOf<Triple<IAstModification, Node, Node>>()
|
||||||
|
|
||||||
|
private fun track(mods: Iterable<IAstModification>, node: Node, parent: Node) {
|
||||||
|
for (it in mods) modifications += Triple(it, node, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun applyModifications(): Int {
|
||||||
|
modifications.forEach {
|
||||||
|
it.first.perform()
|
||||||
|
}
|
||||||
|
val amount = modifications.size
|
||||||
|
modifications.clear()
|
||||||
|
return amount
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(program: Program) {
|
||||||
|
track(before(program, program), program, program)
|
||||||
|
program.modules.forEach { it.accept(this, program) }
|
||||||
|
track(after(program, program), program, program)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(module: Module, parent: Node) {
|
||||||
|
track(before(module, parent), module, parent)
|
||||||
|
module.statements.forEach{ it.accept(this, module) }
|
||||||
|
track(after(module, parent), module, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(expr: PrefixExpression, parent: Node) {
|
||||||
|
track(before(expr, parent), expr, parent)
|
||||||
|
expr.expression.accept(this, expr)
|
||||||
|
track(after(expr, parent), expr, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(expr: BinaryExpression, parent: Node) {
|
||||||
|
track(before(expr, parent), expr, parent)
|
||||||
|
expr.left.accept(this, expr)
|
||||||
|
expr.right.accept(this, expr)
|
||||||
|
track(after(expr, parent), expr, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(directive: Directive, parent: Node) {
|
||||||
|
track(before(directive, parent), directive, parent)
|
||||||
|
track(after(directive, parent), directive, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(block: Block, parent: Node) {
|
||||||
|
track(before(block, parent), block, parent)
|
||||||
|
block.statements.forEach { it.accept(this, block) }
|
||||||
|
track(after(block, parent), block, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(decl: VarDecl, parent: Node) {
|
||||||
|
track(before(decl, parent), decl, parent)
|
||||||
|
decl.value?.accept(this, decl)
|
||||||
|
decl.arraysize?.accept(this, decl)
|
||||||
|
track(after(decl, parent), decl, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(subroutine: Subroutine, parent: Node) {
|
||||||
|
track(before(subroutine, parent), subroutine, parent)
|
||||||
|
subroutine.statements.forEach { it.accept(this, subroutine) }
|
||||||
|
track(after(subroutine, parent), subroutine, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(functionCall: FunctionCall, parent: Node) {
|
||||||
|
track(before(functionCall, parent), functionCall, parent)
|
||||||
|
functionCall.target.accept(this, functionCall)
|
||||||
|
functionCall.args.forEach { it.accept(this, functionCall) }
|
||||||
|
track(after(functionCall, parent), functionCall, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(functionCallStatement: FunctionCallStatement, parent: Node) {
|
||||||
|
track(before(functionCallStatement, parent), functionCallStatement, parent)
|
||||||
|
functionCallStatement.target.accept(this, functionCallStatement)
|
||||||
|
functionCallStatement.args.forEach { it.accept(this, functionCallStatement) }
|
||||||
|
track(after(functionCallStatement, parent), functionCallStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(identifier: IdentifierReference, parent: Node) {
|
||||||
|
track(before(identifier, parent), identifier, parent)
|
||||||
|
track(after(identifier, parent), identifier, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(jump: Jump, parent: Node) {
|
||||||
|
track(before(jump, parent), jump, parent)
|
||||||
|
jump.identifier?.accept(this, jump)
|
||||||
|
track(after(jump, parent), jump, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(ifStatement: IfStatement, parent: Node) {
|
||||||
|
track(before(ifStatement, parent), ifStatement, parent)
|
||||||
|
ifStatement.condition.accept(this, ifStatement)
|
||||||
|
ifStatement.truepart.accept(this, ifStatement)
|
||||||
|
ifStatement.elsepart.accept(this, ifStatement)
|
||||||
|
track(after(ifStatement, parent), ifStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(branchStatement: BranchStatement, parent: Node) {
|
||||||
|
track(before(branchStatement, parent), branchStatement, parent)
|
||||||
|
branchStatement.truepart.accept(this, branchStatement)
|
||||||
|
branchStatement.elsepart.accept(this, branchStatement)
|
||||||
|
track(after(branchStatement, parent), branchStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(range: RangeExpr, parent: Node) {
|
||||||
|
track(before(range, parent), range, parent)
|
||||||
|
range.from.accept(this, range)
|
||||||
|
range.to.accept(this, range)
|
||||||
|
range.step.accept(this, range)
|
||||||
|
track(after(range, parent), range, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(label: Label, parent: Node) {
|
||||||
|
track(before(label, parent), label, parent)
|
||||||
|
track(after(label, parent), label, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(numLiteral: NumericLiteralValue, parent: Node) {
|
||||||
|
track(before(numLiteral, parent), numLiteral, parent)
|
||||||
|
track(after(numLiteral, parent), numLiteral, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(string: StringLiteralValue, parent: Node) {
|
||||||
|
track(before(string, parent), string, parent)
|
||||||
|
track(after(string, parent), string, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(array: ArrayLiteralValue, parent: Node) {
|
||||||
|
track(before(array, parent), array, parent)
|
||||||
|
array.value.forEach { v->v.accept(this, array) }
|
||||||
|
track(after(array, parent), array, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(assignment: Assignment, parent: Node) {
|
||||||
|
track(before(assignment, parent), assignment, parent)
|
||||||
|
assignment.target.accept(this, assignment)
|
||||||
|
assignment.value.accept(this, assignment)
|
||||||
|
track(after(assignment, parent), assignment, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(postIncrDecr: PostIncrDecr, parent: Node) {
|
||||||
|
track(before(postIncrDecr, parent), postIncrDecr, parent)
|
||||||
|
postIncrDecr.target.accept(this, postIncrDecr)
|
||||||
|
track(after(postIncrDecr, parent), postIncrDecr, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(contStmt: Continue, parent: Node) {
|
||||||
|
track(before(contStmt, parent), contStmt, parent)
|
||||||
|
track(after(contStmt, parent), contStmt, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(breakStmt: Break, parent: Node) {
|
||||||
|
track(before(breakStmt, parent), breakStmt, parent)
|
||||||
|
track(after(breakStmt, parent), breakStmt, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(forLoop: ForLoop, parent: Node) {
|
||||||
|
track(before(forLoop, parent), forLoop, parent)
|
||||||
|
forLoop.loopVar?.accept(this, forLoop)
|
||||||
|
forLoop.iterable.accept(this, forLoop)
|
||||||
|
forLoop.body.accept(this, forLoop)
|
||||||
|
track(after(forLoop, parent), forLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(whileLoop: WhileLoop, parent: Node) {
|
||||||
|
track(before(whileLoop, parent), whileLoop, parent)
|
||||||
|
whileLoop.condition.accept(this, whileLoop)
|
||||||
|
whileLoop.body.accept(this, whileLoop)
|
||||||
|
track(after(whileLoop, parent), whileLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(foreverLoop: ForeverLoop, parent: Node) {
|
||||||
|
track(before(foreverLoop, parent), foreverLoop, parent)
|
||||||
|
foreverLoop.body.accept(this, foreverLoop)
|
||||||
|
track(after(foreverLoop, parent), foreverLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(repeatLoop: RepeatLoop, parent: Node) {
|
||||||
|
track(before(repeatLoop, parent), repeatLoop, parent)
|
||||||
|
repeatLoop.untilCondition.accept(this, repeatLoop)
|
||||||
|
repeatLoop.body.accept(this, repeatLoop)
|
||||||
|
track(after(repeatLoop, parent), repeatLoop, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(returnStmt: Return, parent: Node) {
|
||||||
|
track(before(returnStmt, parent), returnStmt, parent)
|
||||||
|
returnStmt.value?.accept(this, returnStmt)
|
||||||
|
track(after(returnStmt, parent), returnStmt, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(arrayIndexedExpression: ArrayIndexedExpression, parent: Node) {
|
||||||
|
track(before(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
||||||
|
arrayIndexedExpression.identifier.accept(this, arrayIndexedExpression)
|
||||||
|
arrayIndexedExpression.arrayspec.accept(this, arrayIndexedExpression)
|
||||||
|
track(after(arrayIndexedExpression, parent), arrayIndexedExpression, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(assignTarget: AssignTarget, parent: Node) {
|
||||||
|
track(before(assignTarget, parent), assignTarget, parent)
|
||||||
|
assignTarget.arrayindexed?.accept(this, assignTarget)
|
||||||
|
assignTarget.identifier?.accept(this, assignTarget)
|
||||||
|
assignTarget.memoryAddress?.accept(this, assignTarget)
|
||||||
|
track(after(assignTarget, parent), assignTarget, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(scope: AnonymousScope, parent: Node) {
|
||||||
|
track(before(scope, parent), scope, parent)
|
||||||
|
scope.statements.forEach { it.accept(this, scope) }
|
||||||
|
track(after(scope, parent), scope, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(typecast: TypecastExpression, parent: Node) {
|
||||||
|
track(before(typecast, parent), typecast, parent)
|
||||||
|
typecast.expression.accept(this, typecast)
|
||||||
|
track(after(typecast, parent), typecast, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(memread: DirectMemoryRead, parent: Node) {
|
||||||
|
track(before(memread, parent), memread, parent)
|
||||||
|
memread.addressExpression.accept(this, memread)
|
||||||
|
track(after(memread, parent), memread, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(memwrite: DirectMemoryWrite, parent: Node) {
|
||||||
|
track(before(memwrite, parent), memwrite, parent)
|
||||||
|
memwrite.addressExpression.accept(this, memwrite)
|
||||||
|
track(after(memwrite, parent), memwrite, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(addressOf: AddressOf, parent: Node) {
|
||||||
|
track(before(addressOf, parent), addressOf, parent)
|
||||||
|
addressOf.identifier.accept(this, addressOf)
|
||||||
|
track(after(addressOf, parent), addressOf, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(inlineAssembly: InlineAssembly, parent: Node) {
|
||||||
|
track(before(inlineAssembly, parent), inlineAssembly, parent)
|
||||||
|
track(after(inlineAssembly, parent), inlineAssembly, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(registerExpr: RegisterExpr, parent: Node) {
|
||||||
|
track(before(registerExpr, parent), registerExpr, parent)
|
||||||
|
track(after(registerExpr, parent), registerExpr, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder, parent: Node) {
|
||||||
|
track(before(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
|
track(after(builtinFunctionStatementPlaceholder, parent), builtinFunctionStatementPlaceholder, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(nopStatement: NopStatement, parent: Node) {
|
||||||
|
track(before(nopStatement, parent), nopStatement, parent)
|
||||||
|
track(after(nopStatement, parent), nopStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(whenStatement: WhenStatement, parent: Node) {
|
||||||
|
track(before(whenStatement, parent), whenStatement, parent)
|
||||||
|
whenStatement.condition.accept(this, whenStatement)
|
||||||
|
whenStatement.choices.forEach { it.accept(this, whenStatement) }
|
||||||
|
track(after(whenStatement, parent), whenStatement, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(whenChoice: WhenChoice, parent: Node) {
|
||||||
|
track(before(whenChoice, parent), whenChoice, parent)
|
||||||
|
whenChoice.values?.forEach { it.accept(this, whenChoice) }
|
||||||
|
whenChoice.statements.accept(this, whenChoice)
|
||||||
|
track(after(whenChoice, parent), whenChoice, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(structDecl: StructDecl, parent: Node) {
|
||||||
|
track(before(structDecl, parent), structDecl, parent)
|
||||||
|
structDecl.statements.forEach { it.accept(this, structDecl) }
|
||||||
|
track(after(structDecl, parent), structDecl, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
fun visit(structLv: StructLiteralValue, parent: Node) {
|
||||||
|
track(before(structLv, parent), structLv, parent)
|
||||||
|
structLv.values.forEach { it.accept(this, structLv) }
|
||||||
|
track(after(structLv, parent), structLv, parent)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
28
compiler/src/prog8/ast/processing/ForeverLoopsMaker.kt
Normal file
28
compiler/src/prog8/ast/processing/ForeverLoopsMaker.kt
Normal file
@ -0,0 +1,28 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
import prog8.ast.statements.ForeverLoop
|
||||||
|
import prog8.ast.statements.RepeatLoop
|
||||||
|
import prog8.ast.statements.WhileLoop
|
||||||
|
|
||||||
|
|
||||||
|
internal class ForeverLoopsMaker: AstWalker() {
|
||||||
|
override fun before(repeatLoop: RepeatLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val numeric = repeatLoop.untilCondition as? NumericLiteralValue
|
||||||
|
if(numeric!=null && numeric.number.toInt() == 0) {
|
||||||
|
val forever = ForeverLoop(repeatLoop.body, repeatLoop.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(repeatLoop, forever, parent))
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(whileLoop: WhileLoop, parent: Node): Iterable<IAstModification> {
|
||||||
|
val numeric = whileLoop.condition as? NumericLiteralValue
|
||||||
|
if(numeric!=null && numeric.number.toInt() != 0) {
|
||||||
|
val forever = ForeverLoop(whileLoop.body, whileLoop.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(whileLoop, forever, parent))
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
@ -6,9 +6,10 @@ import prog8.ast.base.FatalAstException
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
interface IAstModifyingVisitor {
|
interface IAstModifyingVisitor {
|
||||||
fun visit(program: Program) {
|
fun visit(program: Program) {
|
||||||
program.modules.forEach { visit(it) }
|
program.modules.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(module: Module) {
|
fun visit(module: Module) {
|
||||||
@ -158,6 +159,11 @@ interface IAstModifyingVisitor {
|
|||||||
return whileLoop
|
return whileLoop
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(foreverLoop: ForeverLoop): Statement {
|
||||||
|
foreverLoop.body = foreverLoop.body.accept(this) as AnonymousScope
|
||||||
|
return foreverLoop
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop): Statement {
|
fun visit(repeatLoop: RepeatLoop): Statement {
|
||||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
||||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
||||||
|
@ -7,7 +7,7 @@ import prog8.ast.statements.*
|
|||||||
|
|
||||||
interface IAstVisitor {
|
interface IAstVisitor {
|
||||||
fun visit(program: Program) {
|
fun visit(program: Program) {
|
||||||
program.modules.forEach { visit(it) }
|
program.modules.forEach { it.accept(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
fun visit(module: Module) {
|
fun visit(module: Module) {
|
||||||
@ -112,6 +112,10 @@ interface IAstVisitor {
|
|||||||
whileLoop.body.accept(this)
|
whileLoop.body.accept(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun visit(foreverLoop: ForeverLoop) {
|
||||||
|
foreverLoop.body.accept(this)
|
||||||
|
}
|
||||||
|
|
||||||
fun visit(repeatLoop: RepeatLoop) {
|
fun visit(repeatLoop: RepeatLoop) {
|
||||||
repeatLoop.untilCondition.accept(this)
|
repeatLoop.untilCondition.accept(this)
|
||||||
repeatLoop.body.accept(this)
|
repeatLoop.body.accept(this)
|
||||||
|
@ -1,36 +1,20 @@
|
|||||||
package prog8.ast.processing
|
package prog8.ast.processing
|
||||||
|
|
||||||
import prog8.ast.Module
|
import prog8.ast.Node
|
||||||
import prog8.ast.base.SyntaxError
|
|
||||||
import prog8.ast.base.printWarning
|
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
|
|
||||||
internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
|
||||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
|
||||||
|
|
||||||
internal fun result(): List<SyntaxError> {
|
|
||||||
return checkResult
|
|
||||||
}
|
|
||||||
|
|
||||||
|
internal class ImportedModuleDirectiveRemover: AstWalker() {
|
||||||
/**
|
/**
|
||||||
* Most global directives don't apply for imported modules, so remove them
|
* Most global directives don't apply for imported modules, so remove them
|
||||||
*/
|
*/
|
||||||
override fun visit(module: Module) {
|
|
||||||
super.visit(module)
|
|
||||||
val newStatements : MutableList<Statement> = mutableListOf()
|
|
||||||
|
|
||||||
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
private val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||||
for (sourceStmt in module.statements) {
|
|
||||||
val stmt = sourceStmt.accept(this)
|
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
|
||||||
if(stmt is Directive && stmt.parent is Module) {
|
if(directive.directive in moduleLevelDirectives) {
|
||||||
if(stmt.directive in moduleLevelDirectives) {
|
return listOf(IAstModification.Remove(directive, parent))
|
||||||
printWarning("ignoring module directive because it was imported", stmt.position, stmt.directive)
|
}
|
||||||
continue
|
return emptyList()
|
||||||
}
|
|
||||||
}
|
|
||||||
newStatements.add(stmt)
|
|
||||||
}
|
|
||||||
module.statements = newStatements
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
71
compiler/src/prog8/ast/processing/ReflectionAstWalker.kt
Normal file
71
compiler/src/prog8/ast/processing/ReflectionAstWalker.kt
Normal file
@ -0,0 +1,71 @@
|
|||||||
|
package prog8.ast.processing
|
||||||
|
|
||||||
|
|
||||||
|
/*
|
||||||
|
This is here for reference only, reflection based ast walking is very slow
|
||||||
|
when compared to the more verbose visitor pattern interfaces.
|
||||||
|
Too bad, because the code is very small
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
//import prog8.ast.NoAstWalk
|
||||||
|
//import prog8.ast.Node
|
||||||
|
//import prog8.ast.Program
|
||||||
|
//import prog8.ast.base.Position
|
||||||
|
//import prog8.ast.expressions.BinaryExpression
|
||||||
|
//import prog8.ast.expressions.NumericLiteralValue
|
||||||
|
//import kotlin.reflect.KClass
|
||||||
|
//import kotlin.reflect.KVisibility
|
||||||
|
//import kotlin.reflect.full.declaredMemberProperties
|
||||||
|
//import kotlin.reflect.full.isSubtypeOf
|
||||||
|
//import kotlin.reflect.full.starProjectedType
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//class ReflectionAstWalker {
|
||||||
|
// private val nodeType = Node::class.starProjectedType
|
||||||
|
// private val collectionType = Collection::class.starProjectedType
|
||||||
|
//
|
||||||
|
//
|
||||||
|
// fun walk(node: Node, nesting: Int) {
|
||||||
|
// val nodetype: KClass<out Node> = node::class
|
||||||
|
// val indent = " ".repeat(nesting)
|
||||||
|
// //println("$indent VISITING ${nodetype.simpleName}")
|
||||||
|
// val visibleAstMembers = nodetype.declaredMemberProperties.filter {
|
||||||
|
// it.visibility!=KVisibility.PRIVATE && !it.isLateinit &&
|
||||||
|
// !(it.annotations.any{a->a is NoAstWalk})
|
||||||
|
// }
|
||||||
|
// for(prop in visibleAstMembers) {
|
||||||
|
// if(prop.returnType.isSubtypeOf(nodeType)) {
|
||||||
|
// // println("$indent +PROP: ${prop.name}")
|
||||||
|
// walk(prop.call(node) as Node, nesting + 1)
|
||||||
|
// }
|
||||||
|
// else if(prop.returnType.isSubtypeOf(collectionType)) {
|
||||||
|
// val elementType = prop.returnType.arguments.single().type
|
||||||
|
// if(elementType!=null && elementType.isSubtypeOf(nodeType)) {
|
||||||
|
// val nodes = prop.call(node) as Collection<Node>
|
||||||
|
// nodes.forEach { walk(it, nesting+1) }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
// fun walk(program: Program) {
|
||||||
|
// for(module in program.modules) {
|
||||||
|
// println("---MODULE $module---")
|
||||||
|
// walk(module, 0)
|
||||||
|
// }
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//fun main() {
|
||||||
|
// val ast = BinaryExpression(
|
||||||
|
// NumericLiteralValue.optimalInteger(100, Position.DUMMY),
|
||||||
|
// "+",
|
||||||
|
// NumericLiteralValue.optimalInteger(200, Position.DUMMY),
|
||||||
|
// Position.DUMMY
|
||||||
|
// )
|
||||||
|
//
|
||||||
|
// val walker = ReflectionAstWalker()
|
||||||
|
// walker.walk(ast,0)
|
||||||
|
//
|
||||||
|
//}
|
@ -3,11 +3,216 @@ package prog8.ast.processing
|
|||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.initvarsSubName
|
import prog8.ast.base.NumericDatatypes
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
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.
|
||||||
|
// - in every scope:
|
||||||
|
// -- the directives '%output', '%launcher', '%zeropage', '%zpreserved', '%address' and '%option' will come first.
|
||||||
|
// -- all vardecls then follow.
|
||||||
|
// -- the remaining statements then follow in their original order.
|
||||||
|
//
|
||||||
|
// - 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.
|
||||||
|
// - a vardecl with a non-const initializer value is split into a regular vardecl and an assignment statement.
|
||||||
|
|
||||||
|
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
||||||
|
|
||||||
|
private val addVardecls = mutableMapOf<INameScope, MutableList<VarDecl>>()
|
||||||
|
|
||||||
|
override fun visit(module: Module) {
|
||||||
|
addVardecls.clear()
|
||||||
|
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()
|
||||||
|
|
||||||
|
// make sure user-defined blocks come BEFORE library blocks, and move the "main" block to the top of everything
|
||||||
|
val nonLibraryBlocks = module.statements.withIndex()
|
||||||
|
.filter { it.value is Block && !(it.value as Block).isInLibrary }
|
||||||
|
.map { it.index to it.value }
|
||||||
|
.reversed()
|
||||||
|
for(nonLibBlock in nonLibraryBlocks)
|
||||||
|
module.statements.removeAt(nonLibBlock.first)
|
||||||
|
for(nonLibBlock in nonLibraryBlocks)
|
||||||
|
module.statements.add(0, nonLibBlock.second)
|
||||||
|
val mainBlock = module.statements.singleOrNull { it is Block && it.name=="main" }
|
||||||
|
if(mainBlock!=null && (mainBlock as Block).address==null) {
|
||||||
|
module.remove(mainBlock)
|
||||||
|
module.statements.add(0, mainBlock)
|
||||||
|
}
|
||||||
|
|
||||||
|
val varDecls = module.statements.filterIsInstance<VarDecl>()
|
||||||
|
module.statements.removeAll(varDecls)
|
||||||
|
module.statements.addAll(0, varDecls)
|
||||||
|
|
||||||
|
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||||
|
module.statements.removeAll(directives)
|
||||||
|
module.statements.addAll(0, directives)
|
||||||
|
|
||||||
|
for((where, decls) in addVardecls) {
|
||||||
|
where.statements.addAll(0, decls)
|
||||||
|
decls.forEach { it.linkParents(where as Node) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(block: Block): Statement {
|
||||||
|
|
||||||
|
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
||||||
|
var numSubroutinesAtEnd = 0
|
||||||
|
// move all subroutines to the end of the block
|
||||||
|
for (subroutine in subroutines) {
|
||||||
|
if(subroutine.name!="start" || block.name!="main") {
|
||||||
|
block.remove(subroutine)
|
||||||
|
block.statements.add(subroutine)
|
||||||
|
}
|
||||||
|
numSubroutinesAtEnd++
|
||||||
|
}
|
||||||
|
// move the "start" subroutine to the top
|
||||||
|
if(block.name=="main") {
|
||||||
|
block.statements.singleOrNull { it is Subroutine && it.name == "start" } ?.let {
|
||||||
|
block.remove(it)
|
||||||
|
block.statements.add(0, it)
|
||||||
|
numSubroutinesAtEnd--
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val varDecls = block.statements.filterIsInstance<VarDecl>()
|
||||||
|
block.statements.removeAll(varDecls)
|
||||||
|
block.statements.addAll(0, varDecls)
|
||||||
|
val directives = block.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||||
|
block.statements.removeAll(directives)
|
||||||
|
block.statements.addAll(0, directives)
|
||||||
|
block.linkParents(block.parent)
|
||||||
|
|
||||||
|
return super.visit(block)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(subroutine: Subroutine): Statement {
|
||||||
|
super.visit(subroutine)
|
||||||
|
|
||||||
|
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||||
|
subroutine.statements.removeAll(varDecls)
|
||||||
|
subroutine.statements.addAll(0, varDecls)
|
||||||
|
val directives = subroutine.statements.filter {it is Directive && it.directive in directivesToMove}
|
||||||
|
subroutine.statements.removeAll(directives)
|
||||||
|
subroutine.statements.addAll(0, directives)
|
||||||
|
|
||||||
|
return subroutine
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
private fun addVarDecl(scope: INameScope, variable: VarDecl): VarDecl {
|
||||||
|
if(scope !in addVardecls)
|
||||||
|
addVardecls[scope] = mutableListOf()
|
||||||
|
val declList = addVardecls.getValue(scope)
|
||||||
|
val existing = declList.singleOrNull { it.name==variable.name }
|
||||||
|
return if(existing!=null) {
|
||||||
|
existing
|
||||||
|
} else {
|
||||||
|
declList.add(variable)
|
||||||
|
variable
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(decl: VarDecl): Statement {
|
||||||
|
val declValue = decl.value
|
||||||
|
if(declValue!=null && decl.type== VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
val declConstValue = declValue.constValue(program)
|
||||||
|
if(declConstValue==null) {
|
||||||
|
// move the vardecl (without value) to the scope and replace this with a regular assignment
|
||||||
|
val target = AssignTarget(null, IdentifierReference(listOf(decl.name), decl.position), null, null, decl.position)
|
||||||
|
val assign = Assignment(target, null, declValue, decl.position)
|
||||||
|
assign.linkParents(decl.parent)
|
||||||
|
decl.value = null
|
||||||
|
addVarDecl(decl.definingScope(), decl)
|
||||||
|
return assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return super.visit(decl)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(assignment: Assignment): Statement {
|
||||||
|
val assg = super.visit(assignment)
|
||||||
|
if(assg !is Assignment)
|
||||||
|
return assg
|
||||||
|
|
||||||
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
|
val valueItype = assg.value.inferType(program)
|
||||||
|
val targetItype = assg.target.inferType(program, assg)
|
||||||
|
|
||||||
|
if(targetItype.isKnown && valueItype.isKnown) {
|
||||||
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
|
// struct assignments will be flattened (if it's not a struct literal)
|
||||||
|
if (valuetype == DataType.STRUCT && targettype == DataType.STRUCT) {
|
||||||
|
val assignments = if (assg.value is StructLiteralValue) {
|
||||||
|
flattenStructAssignmentFromStructLiteral(assg, program) // 'structvar = { ..... } '
|
||||||
|
} else {
|
||||||
|
flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2'
|
||||||
|
}
|
||||||
|
return if (assignments.isEmpty()) {
|
||||||
|
// something went wrong (probably incompatible struct types)
|
||||||
|
// we'll get an error later from the AstChecker
|
||||||
|
assg
|
||||||
|
} else {
|
||||||
|
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
||||||
|
scope.linkParents(assg.parent)
|
||||||
|
scope
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(assg.aug_op!=null) {
|
||||||
|
// transform augmented assg into normal assg so we have one case less to deal with later
|
||||||
|
val newTarget: Expression =
|
||||||
|
when {
|
||||||
|
assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position)
|
||||||
|
assg.target.identifier != null -> assg.target.identifier!!
|
||||||
|
assg.target.arrayindexed != null -> assg.target.arrayindexed!!
|
||||||
|
assg.target.memoryAddress != null -> DirectMemoryRead(assg.target.memoryAddress!!.addressExpression, assg.value.position)
|
||||||
|
else -> throw FatalAstException("strange assg")
|
||||||
|
}
|
||||||
|
|
||||||
|
val expression = BinaryExpression(newTarget, assg.aug_op.substringBeforeLast('='), assg.value, assg.position)
|
||||||
|
expression.linkParents(assg.parent)
|
||||||
|
val convertedAssignment = Assignment(assg.target, null, expression, assg.position)
|
||||||
|
convertedAssignment.linkParents(assg.parent)
|
||||||
|
return super.visit(convertedAssignment)
|
||||||
|
}
|
||||||
|
|
||||||
|
return assg
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun flattenStructAssignmentFromStructLiteral(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
|
val identifier = structAssignment.target.identifier!!
|
||||||
|
val identifierName = identifier.nameInSource.single()
|
||||||
|
val targetVar = identifier.targetVarDecl(program.namespace)!!
|
||||||
|
val struct = targetVar.struct!!
|
||||||
|
|
||||||
|
val slv = structAssignment.value as? StructLiteralValue
|
||||||
|
if(slv==null || slv.values.size != struct.numberOfElements)
|
||||||
|
throw FatalAstException("element count mismatch")
|
||||||
|
|
||||||
|
return struct.statements.zip(slv.values).map { (targetDecl, sourceValue) ->
|
||||||
|
targetDecl as VarDecl
|
||||||
|
val mangled = mangledStructMemberName(identifierName, targetDecl.name)
|
||||||
|
val idref = IdentifierReference(listOf(mangled), structAssignment.position)
|
||||||
|
val assign = Assignment(AssignTarget(null, idref, null, null, structAssignment.position),
|
||||||
|
null, sourceValue, sourceValue.position)
|
||||||
|
assign.linkParents(structAssignment)
|
||||||
|
assign
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment, program: Program): List<Assignment> {
|
||||||
val identifier = structAssignment.target.identifier!!
|
val identifier = structAssignment.target.identifier!!
|
||||||
val identifierName = identifier.nameInSource.single()
|
val identifierName = identifier.nameInSource.single()
|
||||||
@ -46,213 +251,4 @@ private fun flattenStructAssignmentFromIdentifier(structAssignment: Assignment,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
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.
|
|
||||||
// - in every scope:
|
|
||||||
// -- the directives '%output', '%launcher', '%zeropage', '%zpreserved', '%address' and '%option' will come first.
|
|
||||||
// -- all vardecls then follow.
|
|
||||||
// -- the remaining statements then follow in their original order.
|
|
||||||
//
|
|
||||||
// - 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.
|
|
||||||
|
|
||||||
private val directivesToMove = setOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address", "%option")
|
|
||||||
|
|
||||||
private val addReturns = mutableListOf<Pair<INameScope, Int>>()
|
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
|
||||||
addReturns.clear()
|
|
||||||
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()
|
|
||||||
|
|
||||||
// make sure user-defined blocks come BEFORE library blocks, and move the "main" block to the top of everything
|
|
||||||
val nonLibraryBlocks = module.statements.withIndex()
|
|
||||||
.filter { it.value is Block && !(it.value as Block).isInLibrary }
|
|
||||||
.map { it.index to it.value }
|
|
||||||
.reversed()
|
|
||||||
for(nonLibBlock in nonLibraryBlocks)
|
|
||||||
module.statements.removeAt(nonLibBlock.first)
|
|
||||||
for(nonLibBlock in nonLibraryBlocks)
|
|
||||||
module.statements.add(0, nonLibBlock.second)
|
|
||||||
val mainBlock = module.statements.singleOrNull { it is Block && it.name=="main" }
|
|
||||||
if(mainBlock!=null && (mainBlock as Block).address==null) {
|
|
||||||
module.remove(mainBlock)
|
|
||||||
module.statements.add(0, mainBlock)
|
|
||||||
}
|
|
||||||
|
|
||||||
val varDecls = module.statements.filterIsInstance<VarDecl>()
|
|
||||||
module.statements.removeAll(varDecls)
|
|
||||||
module.statements.addAll(0, varDecls)
|
|
||||||
|
|
||||||
val directives = module.statements.filter {it is Directive && it.directive in directivesToMove}
|
|
||||||
module.statements.removeAll(directives)
|
|
||||||
module.statements.addAll(0, directives)
|
|
||||||
|
|
||||||
for(pos in addReturns) {
|
|
||||||
println(pos)
|
|
||||||
val returnStmt = Return(null, pos.first.position)
|
|
||||||
returnStmt.linkParents(pos.first as Node)
|
|
||||||
pos.first.statements.add(pos.second, returnStmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(block: Block): Statement {
|
|
||||||
|
|
||||||
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
|
||||||
var numSubroutinesAtEnd = 0
|
|
||||||
// move all subroutines to the end of the block
|
|
||||||
for (subroutine in subroutines) {
|
|
||||||
if(subroutine.name!="start" || block.name!="main") {
|
|
||||||
block.remove(subroutine)
|
|
||||||
block.statements.add(subroutine)
|
|
||||||
}
|
|
||||||
numSubroutinesAtEnd++
|
|
||||||
}
|
|
||||||
// move the "start" subroutine to the top
|
|
||||||
if(block.name=="main") {
|
|
||||||
block.statements.singleOrNull { it is Subroutine && it.name == "start" } ?.let {
|
|
||||||
block.remove(it)
|
|
||||||
block.statements.add(0, it)
|
|
||||||
numSubroutinesAtEnd--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// make sure there is a 'return' in front of the first subroutine
|
|
||||||
// (if it isn't the first statement in the block itself, and isn't the program's entrypoint)
|
|
||||||
if(numSubroutinesAtEnd>0 && block.statements.size > (numSubroutinesAtEnd+1)) {
|
|
||||||
val firstSub = block.statements[block.statements.size - numSubroutinesAtEnd] as Subroutine
|
|
||||||
if(firstSub.name != "start" && block.name != "main") {
|
|
||||||
val stmtBeforeFirstSub = block.statements[block.statements.size - numSubroutinesAtEnd - 1]
|
|
||||||
if (stmtBeforeFirstSub !is Return
|
|
||||||
&& stmtBeforeFirstSub !is Jump
|
|
||||||
&& stmtBeforeFirstSub !is Subroutine
|
|
||||||
&& stmtBeforeFirstSub !is BuiltinFunctionStatementPlaceholder) {
|
|
||||||
val ret = Return(null, stmtBeforeFirstSub.position)
|
|
||||||
ret.linkParents(block)
|
|
||||||
block.statements.add(block.statements.size - numSubroutinesAtEnd, ret)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val varDecls = block.statements.filterIsInstance<VarDecl>()
|
|
||||||
block.statements.removeAll(varDecls)
|
|
||||||
block.statements.addAll(0, varDecls)
|
|
||||||
val directives = block.statements.filter {it is Directive && it.directive in directivesToMove}
|
|
||||||
block.statements.removeAll(directives)
|
|
||||||
block.statements.addAll(0, directives)
|
|
||||||
block.linkParents(block.parent)
|
|
||||||
|
|
||||||
// create subroutine that initializes the block's variables (if any)
|
|
||||||
val varInits = block.statements.withIndex().filter { it.value is VariableInitializationAssignment }
|
|
||||||
if(varInits.isNotEmpty()) {
|
|
||||||
val statements = varInits.map{it.value}.toMutableList()
|
|
||||||
val varInitSub = Subroutine(initvarsSubName, emptyList(), emptyList(), emptyList(), emptyList(),
|
|
||||||
emptySet(), null, false, statements, block.position)
|
|
||||||
varInitSub.keepAlways = true
|
|
||||||
varInitSub.linkParents(block)
|
|
||||||
block.statements.add(varInitSub)
|
|
||||||
|
|
||||||
// remove the varinits from the block's statements
|
|
||||||
for(index in varInits.map{it.index}.reversed())
|
|
||||||
block.statements.removeAt(index)
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(block)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(subroutine: Subroutine): Statement {
|
|
||||||
super.visit(subroutine)
|
|
||||||
|
|
||||||
val scope = subroutine.definingScope()
|
|
||||||
if(scope is Subroutine) {
|
|
||||||
for(stmt in scope.statements.withIndex()) {
|
|
||||||
if(stmt.index>0 && stmt.value===subroutine) {
|
|
||||||
val precedingStmt = scope.statements[stmt.index-1]
|
|
||||||
if(precedingStmt !is Jump && precedingStmt !is Subroutine) {
|
|
||||||
// insert a return statement before a nested subroutine, to avoid falling trough inside the subroutine
|
|
||||||
addReturns.add(Pair(scope, stmt.index))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
|
||||||
subroutine.statements.removeAll(varDecls)
|
|
||||||
subroutine.statements.addAll(0, varDecls)
|
|
||||||
val directives = subroutine.statements.filter {it is Directive && it.directive in directivesToMove}
|
|
||||||
subroutine.statements.removeAll(directives)
|
|
||||||
subroutine.statements.addAll(0, directives)
|
|
||||||
|
|
||||||
if(subroutine.returntypes.isEmpty()) {
|
|
||||||
// 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) {
|
|
||||||
val returnStmt = Return(null, subroutine.position)
|
|
||||||
returnStmt.linkParents(subroutine)
|
|
||||||
subroutine.statements.add(returnStmt)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return subroutine
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
val assg = super.visit(assignment)
|
|
||||||
if(assg !is Assignment)
|
|
||||||
return assg
|
|
||||||
|
|
||||||
// see if a typecast is needed to convert the value's type into the proper target type
|
|
||||||
val valueItype = assg.value.inferType(program)
|
|
||||||
val targetItype = assg.target.inferType(program, assg)
|
|
||||||
|
|
||||||
if(targetItype.isKnown && valueItype.isKnown) {
|
|
||||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
|
||||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
|
||||||
|
|
||||||
// struct assignments will be flattened (if it's not a struct literal)
|
|
||||||
if (valuetype == DataType.STRUCT && targettype == DataType.STRUCT) {
|
|
||||||
if (assg.value is StructLiteralValue)
|
|
||||||
return assg // do NOT flatten it at this point!! (the compiler will take care if it, later, if needed)
|
|
||||||
|
|
||||||
val assignments = flattenStructAssignmentFromIdentifier(assg, program) // 'structvar1 = structvar2'
|
|
||||||
return if (assignments.isEmpty()) {
|
|
||||||
// something went wrong (probably incompatible struct types)
|
|
||||||
// we'll get an error later from the AstChecker
|
|
||||||
assg
|
|
||||||
} else {
|
|
||||||
val scope = AnonymousScope(assignments.toMutableList(), assg.position)
|
|
||||||
scope.linkParents(assg.parent)
|
|
||||||
scope
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(assg.aug_op!=null) {
|
|
||||||
// transform augmented assg into normal assg so we have one case less to deal with later
|
|
||||||
val newTarget: Expression =
|
|
||||||
when {
|
|
||||||
assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position)
|
|
||||||
assg.target.identifier != null -> assg.target.identifier!!
|
|
||||||
assg.target.arrayindexed != null -> assg.target.arrayindexed!!
|
|
||||||
assg.target.memoryAddress != null -> DirectMemoryRead(assg.target.memoryAddress!!.addressExpression, assg.value.position)
|
|
||||||
else -> throw FatalAstException("strange assg")
|
|
||||||
}
|
|
||||||
|
|
||||||
val expression = BinaryExpression(newTarget, assg.aug_op.substringBeforeLast('='), assg.value, assg.position)
|
|
||||||
expression.linkParents(assg.parent)
|
|
||||||
val convertedAssignment = Assignment(assg.target, null, expression, assg.position)
|
|
||||||
convertedAssignment.linkParents(assg.parent)
|
|
||||||
return super.visit(convertedAssignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
return assg
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -2,81 +2,69 @@ package prog8.ast.processing
|
|||||||
|
|
||||||
import prog8.ast.IFunctionCall
|
import prog8.ast.IFunctionCall
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.ast.base.FatalAstException
|
import prog8.ast.base.FatalAstException
|
||||||
import prog8.ast.base.printWarning
|
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
|
|
||||||
|
|
||||||
internal class TypecastsAdder(private val program: Program): IAstModifyingVisitor {
|
class TypecastsAdder(val program: Program, val errors: ErrorReporter) : AstWalker() {
|
||||||
// Make 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)
|
* Make 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)
|
||||||
|
*/
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
val expr2 = super.visit(expr)
|
val leftDt = expr.left.inferType(program)
|
||||||
if(expr2 !is BinaryExpression)
|
val rightDt = expr.right.inferType(program)
|
||||||
return expr2
|
|
||||||
val leftDt = expr2.left.inferType(program)
|
|
||||||
val rightDt = expr2.right.inferType(program)
|
|
||||||
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
if(leftDt.isKnown && rightDt.isKnown && leftDt!=rightDt) {
|
||||||
// determine common datatype and add typecast as required to make left and right equal types
|
// determine common datatype and add typecast as required to make left and right equal types
|
||||||
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.STRUCT), rightDt.typeOrElse(DataType.STRUCT), expr2.left, expr2.right)
|
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt.typeOrElse(DataType.STRUCT), rightDt.typeOrElse(DataType.STRUCT), expr.left, expr.right)
|
||||||
if(toFix!=null) {
|
if(toFix!=null) {
|
||||||
when {
|
return when {
|
||||||
toFix===expr2.left -> {
|
toFix===expr.left -> listOf(IAstModification.ReplaceNode(
|
||||||
expr2.left = TypecastExpression(expr2.left, commonDt, true, expr2.left.position)
|
expr.left, TypecastExpression(expr.left, commonDt, true, expr.left.position), expr))
|
||||||
expr2.left.linkParents(expr2)
|
toFix===expr.right -> listOf(IAstModification.ReplaceNode(
|
||||||
}
|
expr.right, TypecastExpression(expr.right, commonDt, true, expr.right.position), expr))
|
||||||
toFix===expr2.right -> {
|
|
||||||
expr2.right = TypecastExpression(expr2.right, commonDt, true, expr2.right.position)
|
|
||||||
expr2.right.linkParents(expr2)
|
|
||||||
}
|
|
||||||
else -> throw FatalAstException("confused binary expression side")
|
else -> throw FatalAstException("confused binary expression side")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return expr2
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
val assg = super.visit(assignment)
|
|
||||||
if(assg !is Assignment)
|
|
||||||
return assg
|
|
||||||
|
|
||||||
// see if a typecast is needed to convert the value's type into the proper target type
|
// see if a typecast is needed to convert the value's type into the proper target type
|
||||||
val valueItype = assg.value.inferType(program)
|
val valueItype = assignment.value.inferType(program)
|
||||||
val targetItype = assg.target.inferType(program, assg)
|
val targetItype = assignment.target.inferType(program, assignment)
|
||||||
|
|
||||||
if(targetItype.isKnown && valueItype.isKnown) {
|
if(targetItype.isKnown && valueItype.isKnown) {
|
||||||
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
val targettype = targetItype.typeOrElse(DataType.STRUCT)
|
||||||
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
val valuetype = valueItype.typeOrElse(DataType.STRUCT)
|
||||||
if (valuetype != targettype) {
|
if (valuetype != targettype) {
|
||||||
if (valuetype isAssignableTo targettype) {
|
return listOf(IAstModification.ReplaceNode(
|
||||||
assg.value = TypecastExpression(assg.value, targettype, true, assg.value.position)
|
assignment.value,
|
||||||
assg.value.linkParents(assg)
|
TypecastExpression(assignment.value, targettype, true, assignment.value.position),
|
||||||
}
|
assignment))
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return assg
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
override fun after(functionCallStatement: FunctionCallStatement, parent: Node): Iterable<IAstModification> {
|
||||||
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
return afterFunctionCallArgs(functionCallStatement, functionCallStatement.definingScope())
|
||||||
return super.visit(functionCallStatement)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun after(functionCall: FunctionCall, parent: Node): Iterable<IAstModification> {
|
||||||
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
return afterFunctionCallArgs(functionCall, functionCall.definingScope())
|
||||||
return super.visit(functionCall)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun checkFunctionCallArguments(call: IFunctionCall, scope: INameScope) {
|
private fun afterFunctionCallArgs(call: IFunctionCall, scope: INameScope): Iterable<IAstModification> {
|
||||||
// see if a typecast is needed to convert the arguments into the required parameter's type
|
// see if a typecast is needed to convert the arguments into the required parameter's type
|
||||||
when(val sub = call.target.targetStatement(scope)) {
|
return when(val sub = call.target.targetStatement(scope)) {
|
||||||
is Subroutine -> {
|
is Subroutine -> {
|
||||||
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
for(arg in sub.parameters.zip(call.args.withIndex())) {
|
||||||
val argItype = arg.second.value.inferType(program)
|
val argItype = arg.second.value.inferType(program)
|
||||||
@ -85,15 +73,16 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
|
|||||||
val requiredType = arg.first.type
|
val requiredType = arg.first.type
|
||||||
if (requiredType != argtype) {
|
if (requiredType != argtype) {
|
||||||
if (argtype isAssignableTo requiredType) {
|
if (argtype isAssignableTo requiredType) {
|
||||||
val typecasted = TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position)
|
return listOf(IAstModification.ReplaceNode(
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
call.args[arg.second.index],
|
||||||
call.args[arg.second.index] = typecasted
|
TypecastExpression(arg.second.value, requiredType, true, arg.second.value.position),
|
||||||
}
|
call as Node))
|
||||||
// if they're not assignable, we'll get a proper error later from the AstChecker
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
is BuiltinFunctionStatementPlaceholder -> {
|
||||||
val func = BuiltinFunctions.getValue(sub.name)
|
val func = BuiltinFunctions.getValue(sub.name)
|
||||||
if(func.pure) {
|
if(func.pure) {
|
||||||
@ -106,93 +95,116 @@ internal class TypecastsAdder(private val program: Program): IAstModifyingVisito
|
|||||||
continue
|
continue
|
||||||
for (possibleType in arg.first.possibleDatatypes) {
|
for (possibleType in arg.first.possibleDatatypes) {
|
||||||
if (argtype isAssignableTo possibleType) {
|
if (argtype isAssignableTo possibleType) {
|
||||||
val typecasted = TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position)
|
return listOf(IAstModification.ReplaceNode(
|
||||||
typecasted.linkParents(arg.second.value.parent)
|
call.args[arg.second.index],
|
||||||
call.args[arg.second.index] = typecasted
|
TypecastExpression(arg.second.value, possibleType, true, arg.second.value.position),
|
||||||
break
|
call as Node))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
emptyList()
|
||||||
}
|
}
|
||||||
null -> {}
|
null -> emptyList()
|
||||||
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
else -> throw FatalAstException("call to something weird $sub ${call.target}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression): Expression {
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
// warn about any implicit type casts to Float, because that may not be intended
|
// 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)) {
|
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)
|
errors.warn("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.visit(typecast)
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): Expression {
|
override fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> {
|
||||||
// make sure the memory address is an uword
|
// make sure the memory address is an uword
|
||||||
val dt = memread.addressExpression.inferType(program)
|
val dt = memread.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val literaladdr = memread.addressExpression as? NumericLiteralValue
|
val typecast = (memread.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||||
if(literaladdr!=null) {
|
?: TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
||||||
memread.addressExpression = literaladdr.cast(DataType.UWORD)
|
return listOf(IAstModification.ReplaceNode(memread.addressExpression, typecast, memread))
|
||||||
} else {
|
|
||||||
memread.addressExpression = TypecastExpression(memread.addressExpression, DataType.UWORD, true, memread.addressExpression.position)
|
|
||||||
memread.addressExpression.parent = memread
|
|
||||||
}
|
}
|
||||||
}
|
return emptyList()
|
||||||
return super.visit(memread)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(memwrite: DirectMemoryWrite) {
|
override fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> {
|
||||||
|
// make sure the memory address is an uword
|
||||||
val dt = memwrite.addressExpression.inferType(program)
|
val dt = memwrite.addressExpression.inferType(program)
|
||||||
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
if(dt.isKnown && dt.typeOrElse(DataType.UWORD)!=DataType.UWORD) {
|
||||||
val literaladdr = memwrite.addressExpression as? NumericLiteralValue
|
val typecast = (memwrite.addressExpression as? NumericLiteralValue)?.cast(DataType.UWORD)
|
||||||
if(literaladdr!=null) {
|
?: TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
||||||
memwrite.addressExpression = literaladdr.cast(DataType.UWORD)
|
return listOf(IAstModification.ReplaceNode(memwrite.addressExpression, typecast, memwrite))
|
||||||
} else {
|
|
||||||
memwrite.addressExpression = TypecastExpression(memwrite.addressExpression, DataType.UWORD, true, memwrite.addressExpression.position)
|
|
||||||
memwrite.addressExpression.parent = memwrite
|
|
||||||
}
|
}
|
||||||
}
|
return emptyList()
|
||||||
super.visit(memwrite)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(structLv: StructLiteralValue): Expression {
|
override fun after(structLv: StructLiteralValue, parent: Node): Iterable<IAstModification> {
|
||||||
val litval = super.visit(structLv)
|
// assignment of a struct literal value, some member values may need proper typecast
|
||||||
if(litval !is StructLiteralValue)
|
|
||||||
return litval
|
|
||||||
|
|
||||||
val decl = litval.parent as? VarDecl
|
fun addTypecastsIfNeeded(struct: StructDecl): Iterable<IAstModification> {
|
||||||
|
val newValues = struct.statements.zip(structLv.values).map { (structMemberDecl, memberValue) ->
|
||||||
|
val memberDt = (structMemberDecl as VarDecl).datatype
|
||||||
|
val valueDt = memberValue.inferType(program)
|
||||||
|
if (valueDt.typeOrElse(memberDt) != memberDt)
|
||||||
|
TypecastExpression(memberValue, memberDt, true, memberValue.position)
|
||||||
|
else
|
||||||
|
memberValue
|
||||||
|
}
|
||||||
|
|
||||||
|
class StructLvValueReplacer(val targetStructLv: StructLiteralValue, val typecastValues: List<Expression>) : IAstModification {
|
||||||
|
override fun perform() {
|
||||||
|
targetStructLv.values = typecastValues
|
||||||
|
typecastValues.forEach { it.linkParents(targetStructLv) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return if(structLv.values.zip(newValues).any { (v1, v2) -> v1 !== v2})
|
||||||
|
listOf(StructLvValueReplacer(structLv, newValues))
|
||||||
|
else
|
||||||
|
emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
val decl = structLv.parent as? VarDecl
|
||||||
if(decl != null) {
|
if(decl != null) {
|
||||||
val struct = decl.struct
|
val struct = decl.struct
|
||||||
if(struct != null) {
|
if(struct != null)
|
||||||
addTypecastsIfNeeded(litval, struct)
|
return addTypecastsIfNeeded(struct)
|
||||||
}
|
|
||||||
} else {
|
} else {
|
||||||
val assign = litval.parent as? Assignment
|
val assign = structLv.parent as? Assignment
|
||||||
if (assign != null) {
|
if (assign != null) {
|
||||||
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
val decl2 = assign.target.identifier?.targetVarDecl(program.namespace)
|
||||||
if(decl2 != null) {
|
if(decl2 != null) {
|
||||||
val struct = decl2.struct
|
val struct = decl2.struct
|
||||||
if(struct != null) {
|
if(struct != null)
|
||||||
addTypecastsIfNeeded(litval, struct)
|
return addTypecastsIfNeeded(struct)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return emptyList()
|
||||||
}
|
}
|
||||||
|
|
||||||
return litval
|
override fun after(returnStmt: Return, parent: Node): Iterable<IAstModification> {
|
||||||
}
|
// add a typecast to the return type if it doesn't match the subroutine's signature
|
||||||
|
val returnValue = returnStmt.value
|
||||||
private fun addTypecastsIfNeeded(structLv: StructLiteralValue, struct: StructDecl) {
|
if(returnValue!=null) {
|
||||||
structLv.values = struct.statements.zip(structLv.values).map {
|
val subroutine = returnStmt.definingSubroutine()!!
|
||||||
val memberDt = (it.first as VarDecl).datatype
|
if(subroutine.returntypes.size==1) {
|
||||||
val valueDt = it.second.inferType(program)
|
val subReturnType = subroutine.returntypes.first()
|
||||||
if (valueDt.typeOrElse(memberDt) != memberDt)
|
if (returnValue.inferType(program).istype(subReturnType))
|
||||||
TypecastExpression(it.second, memberDt, true, it.second.position)
|
return emptyList()
|
||||||
else
|
if (returnValue is NumericLiteralValue) {
|
||||||
it.second
|
returnStmt.value = returnValue.cast(subroutine.returntypes.single())
|
||||||
|
} else {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
returnValue,
|
||||||
|
TypecastExpression(returnValue, subReturnType, true, returnValue.position),
|
||||||
|
returnStmt))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,156 +0,0 @@
|
|||||||
package prog8.ast.processing
|
|
||||||
|
|
||||||
import prog8.ast.INameScope
|
|
||||||
import prog8.ast.Module
|
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.statements.*
|
|
||||||
import prog8.compiler.CompilerException
|
|
||||||
import prog8.functions.BuiltinFunctions
|
|
||||||
import prog8.functions.FunctionSignature
|
|
||||||
|
|
||||||
|
|
||||||
internal class VarInitValueAndAddressOfCreator(private val program: Program): 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, MutableList<VarDecl>>()
|
|
||||||
|
|
||||||
override fun visit(module: Module) {
|
|
||||||
vardeclsToAdd.clear()
|
|
||||||
super.visit(module)
|
|
||||||
// add any new vardecls to the various scopes
|
|
||||||
for((where, decls) in vardeclsToAdd) {
|
|
||||||
where.statements.addAll(0, decls)
|
|
||||||
decls.forEach { it.linkParents(where as Node) }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
|
||||||
super.visit(decl)
|
|
||||||
|
|
||||||
if(decl.isArray && decl.value==null) {
|
|
||||||
// array datatype without initialization value, add list of zeros
|
|
||||||
val arraysize = decl.arraysize!!.size()!!
|
|
||||||
val array = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype),
|
|
||||||
Array(arraysize) { NumericLiteralValue.optimalInteger(0, decl.position) },
|
|
||||||
decl.position)
|
|
||||||
decl.value = array
|
|
||||||
}
|
|
||||||
|
|
||||||
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 NumericLiteralValue)
|
|
||||||
declvalue.cast(decl.datatype)
|
|
||||||
else
|
|
||||||
declvalue
|
|
||||||
val identifierName = listOf(decl.name) // 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): Expression {
|
|
||||||
var parentStatement: Node = functionCall
|
|
||||||
while(parentStatement !is Statement)
|
|
||||||
parentStatement = parentStatement.parent
|
|
||||||
val targetStatement = functionCall.target.targetSubroutine(program.namespace)
|
|
||||||
if(targetStatement!=null) {
|
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCall.args, parentStatement)
|
|
||||||
} else {
|
|
||||||
val builtinFunc = BuiltinFunctions[functionCall.target.nameInSource.joinToString (".")]
|
|
||||||
if(builtinFunc!=null)
|
|
||||||
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCall.args, parentStatement)
|
|
||||||
}
|
|
||||||
return functionCall
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
|
||||||
val targetStatement = functionCallStatement.target.targetSubroutine(program.namespace)
|
|
||||||
if(targetStatement!=null) {
|
|
||||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.args, functionCallStatement)
|
|
||||||
} else {
|
|
||||||
val builtinFunc = BuiltinFunctions[functionCallStatement.target.nameInSource.joinToString (".")]
|
|
||||||
if(builtinFunc!=null)
|
|
||||||
addAddressOfExprIfNeededForBuiltinFuncs(builtinFunc, functionCallStatement.args, functionCallStatement)
|
|
||||||
}
|
|
||||||
return functionCallStatement
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<Expression>, parent: Statement) {
|
|
||||||
// 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 == DataType.STR) {
|
|
||||||
if(argparam.second is AddressOf)
|
|
||||||
continue
|
|
||||||
val idref = argparam.second as? IdentifierReference
|
|
||||||
val strvalue = argparam.second as? StringLiteralValue
|
|
||||||
if(idref!=null) {
|
|
||||||
val variable = idref.targetVarDecl(program.namespace)
|
|
||||||
if(variable!=null && variable.datatype in IterableDatatypes) {
|
|
||||||
val pointerExpr = AddressOf(idref, idref.position)
|
|
||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
|
||||||
arglist[argparam.first.index] = pointerExpr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(strvalue!=null) {
|
|
||||||
// add a vardecl so that the autovar can be resolved in later lookups
|
|
||||||
val variable = VarDecl.createAuto(strvalue)
|
|
||||||
addVarDecl(strvalue.definingScope(), variable)
|
|
||||||
// replace the argument with &autovar
|
|
||||||
val autoHeapvarRef = IdentifierReference(listOf(variable.name), strvalue.position)
|
|
||||||
val pointerExpr = AddressOf(autoHeapvarRef, strvalue.position)
|
|
||||||
pointerExpr.linkParents(arglist[argparam.first.index].parent)
|
|
||||||
arglist[argparam.first.index] = pointerExpr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun addAddressOfExprIfNeededForBuiltinFuncs(signature: FunctionSignature, args: MutableList<Expression>, parent: Statement) {
|
|
||||||
// val paramTypesForAddressOf = PassByReferenceDatatypes + DataType.UWORD
|
|
||||||
for(arg in args.withIndex().zip(signature.parameters)) {
|
|
||||||
val argvalue = arg.first.value
|
|
||||||
val argDt = argvalue.inferType(program)
|
|
||||||
if(argDt.typeOrElse(DataType.UBYTE) in PassByReferenceDatatypes && DataType.UWORD in arg.second.possibleDatatypes) {
|
|
||||||
if(argvalue !is IdentifierReference)
|
|
||||||
throw CompilerException("pass-by-reference parameter isn't an identifier? $argvalue")
|
|
||||||
val addrOf = AddressOf(argvalue, argvalue.position)
|
|
||||||
args[arg.first.index] = addrOf
|
|
||||||
addrOf.linkParents(parent)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
|
|
||||||
if(scope !in vardeclsToAdd)
|
|
||||||
vardeclsToAdd[scope] = mutableListOf()
|
|
||||||
val declList = vardeclsToAdd.getValue(scope)
|
|
||||||
if(declList.all{it.name!=variable.name})
|
|
||||||
declList.add(variable)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
@ -3,6 +3,7 @@ package prog8.ast.statements
|
|||||||
import prog8.ast.*
|
import prog8.ast.*
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
|
|
||||||
@ -10,6 +11,8 @@ import prog8.ast.processing.IAstVisitor
|
|||||||
sealed class Statement : Node {
|
sealed class Statement : Node {
|
||||||
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
||||||
abstract fun accept(visitor: IAstVisitor)
|
abstract fun accept(visitor: IAstVisitor)
|
||||||
|
abstract fun accept(visitor: AstWalker, parent: Node)
|
||||||
|
|
||||||
fun makeScopedName(name: String): String {
|
fun makeScopedName(name: String): String {
|
||||||
// easy way out is to always return the full scoped name.
|
// 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.
|
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||||
@ -37,12 +40,15 @@ sealed class Statement : Node {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
||||||
override var parent: Node = ParentSentinel
|
override var parent: Node = ParentSentinel
|
||||||
override fun linkParents(parent: Node) {}
|
override fun linkParents(parent: Node) {}
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
override fun definingScope(): INameScope = BuiltinFunctionScopePlaceholder
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {}
|
||||||
override val expensiveToInline = false
|
override val expensiveToInline = false
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -62,8 +68,15 @@ class Block(override val name: String,
|
|||||||
statements.forEach {it.linkParents(this)}
|
statements.forEach {it.linkParents(this)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Statement)
|
||||||
|
val idx = statements.indexOf(node)
|
||||||
|
statements[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Block(name=$name, address=$address, ${statements.size} statements)"
|
return "Block(name=$name, address=$address, ${statements.size} statements)"
|
||||||
@ -81,8 +94,10 @@ data class Directive(val directive: String, val args: List<DirectiveArg>, overri
|
|||||||
args.forEach{it.linkParents(this)}
|
args.forEach{it.linkParents(this)}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node {
|
data class DirectiveArg(val str: String?, val name: String?, val int: Int?, override val position: Position) : Node {
|
||||||
@ -91,6 +106,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
|||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
data class Label(val name: String, override val position: Position) : Statement() {
|
data class Label(val name: String, override val position: Position) : Statement() {
|
||||||
@ -101,8 +117,10 @@ data class Label(val name: String, override val position: Position) : Statement(
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Label(name=$name, pos=$position)"
|
return "Label(name=$name, pos=$position)"
|
||||||
@ -118,8 +136,14 @@ open class Return(var value: Expression?, override val position: Position) : Sta
|
|||||||
value?.linkParents(this)
|
value?.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression)
|
||||||
|
value = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Return($value, pos=$position)"
|
return "Return($value, pos=$position)"
|
||||||
@ -133,6 +157,7 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
|||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ReturnFromIrq(pos=$position)"
|
return "ReturnFromIrq(pos=$position)"
|
||||||
}
|
}
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
}
|
}
|
||||||
|
|
||||||
class Continue(override val position: Position) : Statement() {
|
class Continue(override val position: Position) : Statement() {
|
||||||
@ -143,8 +168,10 @@ class Continue(override val position: Position) : Statement() {
|
|||||||
this.parent=parent
|
this.parent=parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class Break(override val position: Position) : Statement() {
|
class Break(override val position: Position) : Statement() {
|
||||||
@ -155,8 +182,10 @@ class Break(override val position: Position) : Statement() {
|
|||||||
this.parent=parent
|
this.parent=parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -167,7 +196,8 @@ enum class ZeropageWish {
|
|||||||
NOT_IN_ZEROPAGE
|
NOT_IN_ZEROPAGE
|
||||||
}
|
}
|
||||||
|
|
||||||
class VarDecl(val type: VarDeclType,
|
|
||||||
|
open class VarDecl(val type: VarDeclType,
|
||||||
private val declaredDatatype: DataType,
|
private val declaredDatatype: DataType,
|
||||||
val zeropage: ZeropageWish,
|
val zeropage: ZeropageWish,
|
||||||
var arraysize: ArrayIndex?,
|
var arraysize: ArrayIndex?,
|
||||||
@ -204,6 +234,15 @@ class VarDecl(val type: VarDeclType,
|
|||||||
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
return VarDecl(VarDeclType.VAR, declaredType, ZeropageWish.NOT_IN_ZEROPAGE, arraysize, autoVarName, null, array,
|
||||||
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
isArray = true, autogeneratedDontRemove = true, position = array.position)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fun defaultZero(dt: DataType, position: Position) = when(dt) {
|
||||||
|
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
|
||||||
|
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
|
||||||
|
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
|
||||||
|
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
|
||||||
|
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
||||||
|
else -> throw FatalAstException("can only determine default zero value for a numeric type")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
val datatypeErrors = mutableListOf<SyntaxError>() // don't crash at init time, report them in the AstChecker
|
||||||
@ -232,29 +271,20 @@ class VarDecl(val type: VarDeclType,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression && node===value)
|
||||||
|
value = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
val scopedname: String by lazy { makeScopedName(name) }
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
return "VarDecl(name=$name, vartype=$type, datatype=$datatype, struct=$structName, value=$value, pos=$position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun asDefaultValueDecl(parent: Node?): VarDecl {
|
fun zeroElementValue() = defaultZero(declaredDatatype, position)
|
||||||
val constValue = when(declaredDatatype) {
|
|
||||||
DataType.UBYTE -> NumericLiteralValue(DataType.UBYTE, 0, position)
|
|
||||||
DataType.BYTE -> NumericLiteralValue(DataType.BYTE, 0, position)
|
|
||||||
DataType.UWORD -> NumericLiteralValue(DataType.UWORD, 0, position)
|
|
||||||
DataType.WORD -> NumericLiteralValue(DataType.WORD, 0, position)
|
|
||||||
DataType.FLOAT -> NumericLiteralValue(DataType.FLOAT, 0.0, position)
|
|
||||||
else -> throw FatalAstException("can only set a default value for a numeric type")
|
|
||||||
}
|
|
||||||
val decl = VarDecl(type, declaredDatatype, zeropage, arraysize, name, structName, constValue, isArray, false, position)
|
|
||||||
if(parent!=null)
|
|
||||||
decl.linkParents(parent)
|
|
||||||
return decl
|
|
||||||
}
|
|
||||||
|
|
||||||
fun flattenStructMembers(): MutableList<Statement> {
|
fun flattenStructMembers(): MutableList<Statement> {
|
||||||
val result = struct!!.statements.withIndex().map {
|
val result = struct!!.statements.withIndex().map {
|
||||||
@ -278,6 +308,11 @@ class VarDecl(val type: VarDeclType,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// a vardecl used only for subroutine parameters
|
||||||
|
class ParameterVarDecl(name: String, declaredDatatype: DataType, position: Position)
|
||||||
|
: VarDecl(VarDeclType.VAR, declaredDatatype, ZeropageWish.NOT_IN_ZEROPAGE, null, name, null, null, false, true, position)
|
||||||
|
|
||||||
|
|
||||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||||
override lateinit var parent: Node
|
override lateinit var parent: Node
|
||||||
|
|
||||||
@ -286,6 +321,11 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
index.linkParents(this)
|
index.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression && node===index)
|
||||||
|
index = replacement
|
||||||
|
}
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun forArray(v: ArrayLiteralValue): ArrayIndex {
|
fun forArray(v: ArrayLiteralValue): ArrayIndex {
|
||||||
return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position)
|
return ArrayIndex(NumericLiteralValue.optimalNumeric(v.value.size, v.position), v.position)
|
||||||
@ -295,10 +335,8 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
|
|||||||
fun accept(visitor: IAstModifyingVisitor) {
|
fun accept(visitor: IAstModifyingVisitor) {
|
||||||
index = index.accept(visitor)
|
index = index.accept(visitor)
|
||||||
}
|
}
|
||||||
|
fun accept(visitor: IAstVisitor) = index.accept(visitor)
|
||||||
fun accept(visitor: IAstVisitor) {
|
fun accept(visitor: AstWalker, parent: Node) = index.accept(visitor, parent)
|
||||||
index.accept(visitor)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("ArrayIndex($index, pos=$position)")
|
return("ArrayIndex($index, pos=$position)")
|
||||||
@ -318,19 +356,23 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value:
|
|||||||
value.linkParents(this)
|
value.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===target -> target = replacement as AssignTarget
|
||||||
|
node===value -> value = replacement as Expression
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
|
return("Assignment(augop: $aug_op, target: $target, value: $value, pos=$position)")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 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: Expression, position: Position)
|
|
||||||
: Assignment(target, aug_op, value, position)
|
|
||||||
|
|
||||||
data class AssignTarget(val register: Register?,
|
data class AssignTarget(val register: Register?,
|
||||||
var identifier: IdentifierReference?,
|
var identifier: IdentifierReference?,
|
||||||
var arrayindexed: ArrayIndexedExpression?,
|
var arrayindexed: ArrayIndexedExpression?,
|
||||||
@ -345,8 +387,17 @@ data class AssignTarget(val register: Register?,
|
|||||||
memoryAddress?.linkParents(this)
|
memoryAddress?.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===identifier -> identifier = replacement as IdentifierReference
|
||||||
|
node===arrayindexed -> arrayindexed = replacement as ArrayIndexedExpression
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun fromExpr(expr: Expression): AssignTarget {
|
fun fromExpr(expr: Expression): AssignTarget {
|
||||||
@ -381,7 +432,13 @@ data class AssignTarget(val register: Register?,
|
|||||||
|
|
||||||
infix fun isSameAs(value: Expression): Boolean {
|
infix fun isSameAs(value: Expression): Boolean {
|
||||||
return when {
|
return when {
|
||||||
this.memoryAddress!=null -> false
|
this.memoryAddress!=null -> {
|
||||||
|
// if the target is a memory write, and the value is a memory read, they're the same if the address matches
|
||||||
|
if(value is DirectMemoryRead)
|
||||||
|
this.memoryAddress.addressExpression isSameAs value.addressExpression
|
||||||
|
else
|
||||||
|
false
|
||||||
|
}
|
||||||
this.register!=null -> value is RegisterExpr && value.register==register
|
this.register!=null -> value is RegisterExpr && value.register==register
|
||||||
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
|
||||||
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
|
||||||
@ -443,8 +500,14 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
|||||||
target.linkParents(this)
|
target.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is AssignTarget && node===target)
|
||||||
|
target = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "PostIncrDecr(op: $operator, target: $target, pos=$position)"
|
return "PostIncrDecr(op: $operator, target: $target, pos=$position)"
|
||||||
@ -463,8 +526,10 @@ class Jump(val address: Int?,
|
|||||||
identifier?.linkParents(this)
|
identifier?.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
|
return "Jump(addr: $address, identifier: $identifier, label: $generatedLabel; pos=$position)"
|
||||||
@ -485,8 +550,18 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
|||||||
args.forEach { it.linkParents(this) }
|
args.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
if(node===target)
|
||||||
|
target = replacement as IdentifierReference
|
||||||
|
else {
|
||||||
|
val idx = args.indexOf(node)
|
||||||
|
args[idx] = replacement as Expression
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "FunctionCallStatement(target=$target, pos=$position)"
|
return "FunctionCallStatement(target=$target, pos=$position)"
|
||||||
@ -501,8 +576,10 @@ class InlineAssembly(val assembly: String, override val position: Position) : St
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class AnonymousScope(override var statements: MutableList<Statement>,
|
class AnonymousScope(override var statements: MutableList<Statement>,
|
||||||
@ -526,8 +603,15 @@ class AnonymousScope(override var statements: MutableList<Statement>,
|
|||||||
statements.forEach { it.linkParents(this) }
|
statements.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Statement)
|
||||||
|
val idx = statements.indexOf(node)
|
||||||
|
statements[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class NopStatement(override val position: Position): Statement() {
|
class NopStatement(override val position: Position): Statement() {
|
||||||
@ -538,8 +622,10 @@ class NopStatement(override val position: Position): Statement() {
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
companion object {
|
companion object {
|
||||||
fun insteadOf(stmt: Statement): NopStatement {
|
fun insteadOf(stmt: Statement): NopStatement {
|
||||||
@ -580,8 +666,15 @@ class Subroutine(override val name: String,
|
|||||||
statements.forEach { it.linkParents(this) }
|
statements.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Statement)
|
||||||
|
val idx = statements.indexOf(node)
|
||||||
|
statements[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
return "Subroutine(name=$name, parameters=$parameters, returntypes=$returntypes, ${statements.size} statements, address=$asmAddress)"
|
||||||
@ -603,6 +696,10 @@ open class SubroutineParameter(val name: String,
|
|||||||
override fun linkParents(parent: Node) {
|
override fun linkParents(parent: Node) {
|
||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
throw FatalAstException("can't replace anything in a subroutineparameter node")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
class IfStatement(var condition: Expression,
|
class IfStatement(var condition: Expression,
|
||||||
@ -620,8 +717,19 @@ class IfStatement(var condition: Expression,
|
|||||||
elsepart.linkParents(this)
|
elsepart.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===condition -> condition = replacement as Expression
|
||||||
|
node===truepart -> truepart = replacement as AnonymousScope
|
||||||
|
node===elsepart -> elsepart = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class BranchStatement(var condition: BranchCondition,
|
class BranchStatement(var condition: BranchCondition,
|
||||||
@ -638,8 +746,18 @@ class BranchStatement(var condition: BranchCondition,
|
|||||||
elsepart.linkParents(this)
|
elsepart.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===truepart -> truepart = replacement as AnonymousScope
|
||||||
|
node===elsepart -> elsepart = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
class ForLoop(val loopRegister: Register?,
|
class ForLoop(val loopRegister: Register?,
|
||||||
@ -657,8 +775,18 @@ class ForLoop(val loopRegister: Register?,
|
|||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===loopVar -> loopVar = replacement as IdentifierReference
|
||||||
|
node===iterable -> iterable = replacement as Expression
|
||||||
|
node===body -> body = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
|
||||||
@ -683,8 +811,36 @@ class WhileLoop(var condition: Expression,
|
|||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===condition -> condition = replacement as Expression
|
||||||
|
node===body -> body = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
class ForeverLoop(var body: AnonymousScope, override val position: Position) : Statement() {
|
||||||
|
override lateinit var parent: Node
|
||||||
|
override val expensiveToInline = true
|
||||||
|
|
||||||
|
override fun linkParents(parent: Node) {
|
||||||
|
this.parent = parent
|
||||||
|
body.linkParents(this)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is AnonymousScope && node===body)
|
||||||
|
body = replacement
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class RepeatLoop(var body: AnonymousScope,
|
class RepeatLoop(var body: AnonymousScope,
|
||||||
@ -699,8 +855,17 @@ class RepeatLoop(var body: AnonymousScope,
|
|||||||
body.linkParents(this)
|
body.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
when {
|
||||||
|
node===untilCondition -> untilCondition = replacement as Expression
|
||||||
|
node===body -> body = replacement as AnonymousScope
|
||||||
|
else -> throw FatalAstException("invalid replace")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenStatement(var condition: Expression,
|
class WhenStatement(var condition: Expression,
|
||||||
@ -715,6 +880,15 @@ class WhenStatement(var condition: Expression,
|
|||||||
choices.forEach { it.linkParents(this) }
|
choices.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
if(node===condition)
|
||||||
|
condition = replacement as Expression
|
||||||
|
else {
|
||||||
|
val idx = choices.indexOf(node)
|
||||||
|
choices[idx] = replacement as WhenChoice
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
fun choiceValues(program: Program): List<Pair<List<Int>?, WhenChoice>> {
|
fun choiceValues(program: Program): List<Pair<List<Int>?, WhenChoice>> {
|
||||||
// only gives sensible results when the choices are all valid (constant integers)
|
// only gives sensible results when the choices are all valid (constant integers)
|
||||||
val result = mutableListOf<Pair<List<Int>?, WhenChoice>>()
|
val result = mutableListOf<Pair<List<Int>?, WhenChoice>>()
|
||||||
@ -734,6 +908,7 @@ class WhenStatement(var condition: Expression,
|
|||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
class WhenChoice(var values: List<Expression>?, // if null, this is the 'else' part
|
class WhenChoice(var values: List<Expression>?, // if null, this is the 'else' part
|
||||||
@ -747,12 +922,18 @@ class WhenChoice(var values: List<Expression>?, // if null, this is t
|
|||||||
this.parent = parent
|
this.parent = parent
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is AnonymousScope && node===statements)
|
||||||
|
statements = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "Choice($values at $position)"
|
return "Choice($values at $position)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -768,11 +949,18 @@ class StructDecl(override val name: String,
|
|||||||
this.statements.forEach { it.linkParents(this) }
|
this.statements.forEach { it.linkParents(this) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Statement)
|
||||||
|
val idx = statements.indexOf(node)
|
||||||
|
statements[idx] = replacement
|
||||||
|
}
|
||||||
|
|
||||||
val numberOfElements: Int
|
val numberOfElements: Int
|
||||||
get() = this.statements.size
|
get() = this.statements.size
|
||||||
|
|
||||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
|
|
||||||
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
fun nameOfFirstMember() = (statements.first() as VarDecl).name
|
||||||
}
|
}
|
||||||
@ -785,10 +973,16 @@ class DirectMemoryWrite(var addressExpression: Expression, override val position
|
|||||||
this.addressExpression.linkParents(this)
|
this.addressExpression.linkParents(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun replaceChildNode(node: Node, replacement: Node) {
|
||||||
|
require(replacement is Expression && node===addressExpression)
|
||||||
|
addressExpression = replacement
|
||||||
|
}
|
||||||
|
|
||||||
override fun toString(): String {
|
override fun toString(): String {
|
||||||
return "DirectMemoryWrite($addressExpression)"
|
return "DirectMemoryWrite($addressExpression)"
|
||||||
}
|
}
|
||||||
|
|
||||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||||
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||||
|
fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||||
}
|
}
|
||||||
|
@ -8,9 +8,8 @@ import prog8.compiler.target.CompilationTarget
|
|||||||
import prog8.optimizer.constantFold
|
import prog8.optimizer.constantFold
|
||||||
import prog8.optimizer.optimizeStatements
|
import prog8.optimizer.optimizeStatements
|
||||||
import prog8.optimizer.simplifyExpressions
|
import prog8.optimizer.simplifyExpressions
|
||||||
|
import prog8.parser.ModuleImporter
|
||||||
import prog8.parser.ParsingFailedError
|
import prog8.parser.ParsingFailedError
|
||||||
import prog8.parser.importLibraryModule
|
|
||||||
import prog8.parser.importModule
|
|
||||||
import prog8.parser.moduleName
|
import prog8.parser.moduleName
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.measureTimeMillis
|
import kotlin.system.measureTimeMillis
|
||||||
@ -26,89 +25,29 @@ fun compileProgram(filepath: Path,
|
|||||||
optimize: Boolean,
|
optimize: Boolean,
|
||||||
writeAssembly: Boolean,
|
writeAssembly: Boolean,
|
||||||
outputDir: Path): CompilationResult {
|
outputDir: Path): CompilationResult {
|
||||||
|
var programName = ""
|
||||||
lateinit var programAst: Program
|
lateinit var programAst: Program
|
||||||
var programName: String? = null
|
lateinit var importedFiles: List<Path>
|
||||||
|
val errors = ErrorReporter()
|
||||||
var importedFiles: List<Path> = emptyList()
|
|
||||||
var success=false
|
|
||||||
|
|
||||||
try {
|
try {
|
||||||
val totalTime = measureTimeMillis {
|
val totalTime = measureTimeMillis {
|
||||||
// import main module and everything it needs
|
// import main module and everything it needs
|
||||||
println("Parsing...")
|
val (ast, compilationOptions, imported) = parseImports(filepath, errors)
|
||||||
programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
programAst = ast
|
||||||
importModule(programAst, filepath)
|
importedFiles = imported
|
||||||
|
processAst(programAst, errors, compilationOptions)
|
||||||
importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map{ it.source }
|
if (optimize)
|
||||||
|
optimizeAst(programAst, errors)
|
||||||
val compilerOptions = determineCompilationOptions(programAst)
|
postprocessAst(programAst, errors, compilationOptions)
|
||||||
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.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.reorderStatements()
|
|
||||||
programAst.addTypecasts()
|
|
||||||
}
|
|
||||||
//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()
|
|
||||||
if (optsDone1 + optsDone2 == 0)
|
|
||||||
break
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
programAst.addTypecasts()
|
|
||||||
programAst.removeNopsFlattenAnonScopes()
|
|
||||||
programAst.checkValid(compilerOptions) // check if final tree is valid
|
|
||||||
programAst.checkRecursion() // check if there are recursive subroutine calls
|
|
||||||
|
|
||||||
// printAst(programAst)
|
// printAst(programAst)
|
||||||
|
|
||||||
if(writeAssembly) {
|
if(writeAssembly)
|
||||||
// asm generation directly from the Ast, no need for intermediate code
|
programName = writeAssembly(programAst, errors, outputDir, optimize, compilationOptions)
|
||||||
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
|
||||||
programAst.anonscopeVarsCleanup()
|
|
||||||
val assembly = CompilationTarget.asmGenerator(programAst, zeropage, compilerOptions, outputDir).compileToAssembly(optimize)
|
|
||||||
assembly.assemble(compilerOptions)
|
|
||||||
programName = assembly.name
|
|
||||||
}
|
|
||||||
success = true
|
|
||||||
}
|
}
|
||||||
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
println("\nTotal compilation+assemble time: ${totalTime / 1000.0} sec.")
|
||||||
|
return CompilationResult(true, programAst, programName, importedFiles)
|
||||||
|
|
||||||
} catch (px: ParsingFailedError) {
|
} catch (px: ParsingFailedError) {
|
||||||
System.err.print("\u001b[91m") // bright red
|
System.err.print("\u001b[91m") // bright red
|
||||||
@ -131,16 +70,35 @@ fun compileProgram(filepath: Path,
|
|||||||
System.out.flush()
|
System.out.flush()
|
||||||
throw x
|
throw x
|
||||||
}
|
}
|
||||||
return CompilationResult(success, programAst, programName ?: "", importedFiles)
|
|
||||||
|
return CompilationResult(false, Program("failed", mutableListOf()), programName, emptyList())
|
||||||
}
|
}
|
||||||
|
|
||||||
fun printAst(programAst: Program) {
|
private fun parseImports(filepath: Path, errors: ErrorReporter): Triple<Program, CompilationOptions, List<Path>> {
|
||||||
println()
|
println("Parsing...")
|
||||||
val printer = AstToSourceCode(::print, programAst)
|
val importer = ModuleImporter(errors)
|
||||||
printer.visit(programAst)
|
val programAst = Program(moduleName(filepath.fileName), mutableListOf())
|
||||||
println()
|
importer.importModule(programAst, filepath)
|
||||||
|
errors.handle()
|
||||||
|
|
||||||
|
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
|
||||||
|
|
||||||
|
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) {
|
||||||
|
importer.importLibraryModule(programAst, "c64lib")
|
||||||
|
importer.importLibraryModule(programAst, "c64utils")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// always import prog8lib and math
|
||||||
|
importer.importLibraryModule(programAst, "math")
|
||||||
|
importer.importLibraryModule(programAst, "prog8lib")
|
||||||
|
errors.handle()
|
||||||
|
return Triple(programAst, compilerOptions, importedFiles)
|
||||||
|
}
|
||||||
|
|
||||||
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
private fun determineCompilationOptions(program: Program): CompilationOptions {
|
||||||
val mainModule = program.modules.first()
|
val mainModule = program.modules.first()
|
||||||
@ -177,3 +135,70 @@ private fun determineCompilationOptions(program: Program): CompilationOptions {
|
|||||||
zpType, zpReserved, floatsEnabled
|
zpType, zpReserved, floatsEnabled
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun processAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
|
// perform initial syntax checks and processings
|
||||||
|
println("Processing...")
|
||||||
|
programAst.checkIdentifiers(errors)
|
||||||
|
errors.handle()
|
||||||
|
programAst.makeForeverLoops()
|
||||||
|
programAst.constantFold(errors)
|
||||||
|
errors.handle()
|
||||||
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
|
programAst.reorderStatements()
|
||||||
|
programAst.addTypecasts(errors)
|
||||||
|
errors.handle()
|
||||||
|
programAst.checkValid(compilerOptions, errors)
|
||||||
|
errors.handle()
|
||||||
|
programAst.checkIdentifiers(errors)
|
||||||
|
errors.handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeAst(programAst: Program, errors: ErrorReporter) {
|
||||||
|
// 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(errors)
|
||||||
|
errors.handle()
|
||||||
|
if (optsDone1 + optsDone2 == 0)
|
||||||
|
break
|
||||||
|
}
|
||||||
|
// because simplified statements and expressions could give rise to more constants that can be folded away:
|
||||||
|
programAst.constantFold(errors)
|
||||||
|
errors.handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun postprocessAst(programAst: Program, errors: ErrorReporter, compilerOptions: CompilationOptions) {
|
||||||
|
programAst.addTypecasts(errors)
|
||||||
|
errors.handle()
|
||||||
|
programAst.removeNopsFlattenAnonScopes()
|
||||||
|
programAst.checkValid(compilerOptions, errors) // check if final tree is still valid
|
||||||
|
errors.handle()
|
||||||
|
programAst.checkRecursion(errors) // check if there are recursive subroutine calls
|
||||||
|
errors.handle()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun writeAssembly(programAst: Program, errors: ErrorReporter, outputDir: Path,
|
||||||
|
optimize: Boolean, compilerOptions: CompilationOptions): String {
|
||||||
|
// asm generation directly from the Ast,
|
||||||
|
val zeropage = CompilationTarget.machine.getZeropage(compilerOptions)
|
||||||
|
programAst.prepareAsmVariablesAndReturns(errors)
|
||||||
|
errors.handle()
|
||||||
|
val assembly = CompilationTarget.asmGenerator(
|
||||||
|
programAst,
|
||||||
|
zeropage,
|
||||||
|
compilerOptions,
|
||||||
|
outputDir).compileToAssembly(optimize)
|
||||||
|
assembly.assemble(compilerOptions)
|
||||||
|
return assembly.name
|
||||||
|
}
|
||||||
|
|
||||||
|
fun printAst(programAst: Program) {
|
||||||
|
println()
|
||||||
|
val printer = AstToSourceCode(::print, programAst)
|
||||||
|
printer.visit(programAst)
|
||||||
|
println()
|
||||||
|
}
|
||||||
|
|
||||||
|
@ -15,7 +15,7 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
|
|
||||||
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
fun available() = if(options.zeropage==ZeropageType.DONTUSE) 0 else free.size
|
||||||
|
|
||||||
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
|
fun allocate(scopedname: String, datatype: DataType, position: Position?, errors: ErrorReporter): Int {
|
||||||
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
|
||||||
|
|
||||||
if(options.zeropage==ZeropageType.DONTUSE)
|
if(options.zeropage==ZeropageType.DONTUSE)
|
||||||
@ -28,9 +28,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
|
|||||||
DataType.FLOAT -> {
|
DataType.FLOAT -> {
|
||||||
if (options.floats) {
|
if (options.floats) {
|
||||||
if(position!=null)
|
if(position!=null)
|
||||||
printWarning("allocated a large value (float) in zeropage", position)
|
errors.warn("allocated a large value (float) in zeropage", position)
|
||||||
else
|
else
|
||||||
printWarning("$scopedname: allocated a large value (float) in zeropage")
|
errors.warn("$scopedname: allocated a large value (float) in zeropage", position ?: Position.DUMMY)
|
||||||
5
|
5
|
||||||
} else throw CompilerException("floating point option not enabled")
|
} else throw CompilerException("floating point option not enabled")
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,80 @@
|
|||||||
|
package prog8.compiler.target
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.ErrorReporter
|
||||||
|
import prog8.ast.base.NumericDatatypes
|
||||||
|
import prog8.ast.base.VarDeclType
|
||||||
|
import prog8.ast.expressions.IdentifierReference
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.*
|
||||||
|
|
||||||
|
|
||||||
|
class AsmVariableAndReturnsPreparer(val program: Program, val errors: ErrorReporter): AstWalker() {
|
||||||
|
|
||||||
|
override fun after(decl: VarDecl, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(decl.value==null && decl.type==VarDeclType.VAR && decl.datatype in NumericDatatypes) {
|
||||||
|
// a numeric vardecl without an initial value is initialized with zero.
|
||||||
|
decl.value = decl.zeroElementValue()
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(scope: AnonymousScope, parent: Node): Iterable<IAstModification> {
|
||||||
|
val decls = scope.statements.filterIsInstance<VarDecl>()
|
||||||
|
val sub = scope.definingSubroutine()
|
||||||
|
if(sub!=null) {
|
||||||
|
val existingVariables = sub.statements.filterIsInstance<VarDecl>().associateBy { it.name }
|
||||||
|
var conflicts = false
|
||||||
|
decls.forEach {
|
||||||
|
val existing = existingVariables[it.name]
|
||||||
|
if (existing!=null) {
|
||||||
|
errors.err("variable ${it.name} already defined in subroutine ${sub.name} at ${existing.position}", it.position)
|
||||||
|
conflicts = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if(!conflicts) {
|
||||||
|
val numericVarsWithValue = decls.filter { it.value!=null && it.datatype in NumericDatatypes }
|
||||||
|
return numericVarsWithValue.map {
|
||||||
|
val initValue = it.value!! // assume here that value has always been set by now
|
||||||
|
it.value = null // make sure no value init assignment for this vardecl will be created later (would be superfluous)
|
||||||
|
val target = AssignTarget(null, IdentifierReference(listOf(it.name), it.position), null, null, it.position)
|
||||||
|
val assign = Assignment(target, null, initValue, it.position)
|
||||||
|
IAstModification.InsertFirst(assign, scope)
|
||||||
|
} +
|
||||||
|
decls.map { IAstModification.ReplaceNode(it, NopStatement(it.position), scope) } +
|
||||||
|
decls.map { IAstModification.InsertFirst(it, sub) } // move it up to the subroutine
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(subroutine: Subroutine, parent: Node): Iterable<IAstModification> {
|
||||||
|
// 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, and some other situations.
|
||||||
|
val mods = mutableListOf<IAstModification>()
|
||||||
|
val returnStmt = Return(null, subroutine.position)
|
||||||
|
if(subroutine.asmAddress==null
|
||||||
|
&& subroutine.statements.isNotEmpty()
|
||||||
|
&& subroutine.amountOfRtsInAsm()==0
|
||||||
|
&& subroutine.statements.lastOrNull {it !is VarDecl } !is Return
|
||||||
|
&& subroutine.statements.last() !is Subroutine) {
|
||||||
|
mods += IAstModification.InsertAfter(subroutine.statements.last(), returnStmt, subroutine)
|
||||||
|
}
|
||||||
|
|
||||||
|
// precede a subroutine with a return to avoid falling through into the subroutine from code above it
|
||||||
|
val outerScope = subroutine.definingScope()
|
||||||
|
val outerStatements = outerScope.statements
|
||||||
|
val subroutineStmtIdx = outerStatements.indexOf(subroutine)
|
||||||
|
if(subroutineStmtIdx>0
|
||||||
|
&& outerStatements[subroutineStmtIdx-1] !is Jump
|
||||||
|
&& outerStatements[subroutineStmtIdx-1] !is Subroutine
|
||||||
|
&& outerStatements[subroutineStmtIdx-1] !is Return
|
||||||
|
&& outerScope !is Block) {
|
||||||
|
mods += IAstModification.InsertAfter(outerStatements[subroutineStmtIdx-1], returnStmt, outerScope as Node)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
}
|
@ -5,6 +5,7 @@ import prog8.compiler.CompilationOptions
|
|||||||
import prog8.compiler.Zeropage
|
import prog8.compiler.Zeropage
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
|
|
||||||
|
|
||||||
internal interface CompilationTarget {
|
internal interface CompilationTarget {
|
||||||
companion object {
|
companion object {
|
||||||
lateinit var name: String
|
lateinit var name: String
|
||||||
|
@ -6,6 +6,8 @@ internal interface IAssemblyGenerator {
|
|||||||
fun compileToAssembly(optimize: Boolean): IAssemblyProgram
|
fun compileToAssembly(optimize: Boolean): IAssemblyProgram
|
||||||
}
|
}
|
||||||
|
|
||||||
|
internal const val generatedLabelPrefix = "_prog8_label_"
|
||||||
|
|
||||||
internal interface IAssemblyProgram {
|
internal interface IAssemblyProgram {
|
||||||
val name: String
|
val name: String
|
||||||
fun assemble(options: CompilationOptions)
|
fun assemble(options: CompilationOptions)
|
||||||
|
@ -3,6 +3,7 @@ package prog8.compiler.target.c64
|
|||||||
import prog8.compiler.CompilationOptions
|
import prog8.compiler.CompilationOptions
|
||||||
import prog8.compiler.OutputType
|
import prog8.compiler.OutputType
|
||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
|
import prog8.compiler.target.generatedLabelPrefix
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import kotlin.system.exitProcess
|
import kotlin.system.exitProcess
|
||||||
|
|
||||||
@ -39,13 +40,25 @@ class AssemblyProgram(override val name: String, outputDir: Path): IAssemblyProg
|
|||||||
exitProcess(result)
|
exitProcess(result)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
removeGeneratedLabelsFromMonlist()
|
||||||
generateBreakpointList()
|
generateBreakpointList()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun removeGeneratedLabelsFromMonlist() {
|
||||||
|
val pattern = Regex("""al (\w+) \S+${generatedLabelPrefix}.+?""")
|
||||||
|
val lines = viceMonListFile.toFile().readLines()
|
||||||
|
viceMonListFile.toFile().outputStream().bufferedWriter().use {
|
||||||
|
for (line in lines) {
|
||||||
|
if(pattern.matchEntire(line)==null)
|
||||||
|
it.write(line+"\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun generateBreakpointList() {
|
private fun generateBreakpointList() {
|
||||||
// builds list of breakpoints, appends to monitor list file
|
// builds list of breakpoints, appends to monitor list file
|
||||||
val breakpoints = mutableListOf<String>()
|
val breakpoints = mutableListOf<String>()
|
||||||
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that"s generated for them
|
val pattern = Regex("""al (\w+) \S+_prog8_breakpoint_\d+.?""") // gather breakpoints by the source label that's generated for them
|
||||||
for (line in viceMonListFile.toFile().readLines()) {
|
for (line in viceMonListFile.toFile().readLines()) {
|
||||||
val match = pattern.matchEntire(line)
|
val match = pattern.matchEntire(line)
|
||||||
if (match != null)
|
if (match != null)
|
||||||
|
@ -11,16 +11,17 @@ import prog8.compiler.target.IAssemblyGenerator
|
|||||||
import prog8.compiler.target.IAssemblyProgram
|
import prog8.compiler.target.IAssemblyProgram
|
||||||
import prog8.compiler.target.c64.AssemblyProgram
|
import prog8.compiler.target.c64.AssemblyProgram
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
|
import prog8.compiler.target.generatedLabelPrefix
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import prog8.functions.FunctionSignature
|
import prog8.functions.FSignature
|
||||||
import java.math.RoundingMode
|
import java.math.RoundingMode
|
||||||
import java.nio.file.Path
|
import java.nio.file.Path
|
||||||
import java.time.LocalDate
|
import java.time.LocalDate
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
import java.util.ArrayDeque
|
import java.util.*
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
|
|
||||||
@ -41,6 +42,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
|
private val expressionsAsmGen = ExpressionsAsmGen(program, this)
|
||||||
internal val loopEndLabels = ArrayDeque<String>()
|
internal val loopEndLabels = ArrayDeque<String>()
|
||||||
internal val loopContinueLabels = ArrayDeque<String>()
|
internal val loopContinueLabels = ArrayDeque<String>()
|
||||||
|
internal val blockLevelVarInits = mutableMapOf<Block, MutableSet<VarDecl>>()
|
||||||
|
|
||||||
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
|
override fun compileToAssembly(optimize: Boolean): IAssemblyProgram {
|
||||||
assemblyLines.clear()
|
assemblyLines.clear()
|
||||||
@ -96,11 +98,15 @@ internal class AsmGen(private val program: Program,
|
|||||||
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
|
out(" .null $9e, format(' %d ', _prog8_entrypoint), $3a, $8f, ' prog8 by idj'")
|
||||||
out("+\t.word 0")
|
out("+\t.word 0")
|
||||||
out("_prog8_entrypoint\t; assembly code starts here\n")
|
out("_prog8_entrypoint\t; assembly code starts here\n")
|
||||||
|
out(" tsx")
|
||||||
|
out(" stx prog8_lib.orig_stackpointer")
|
||||||
out(" jsr prog8_lib.init_system")
|
out(" jsr prog8_lib.init_system")
|
||||||
}
|
}
|
||||||
options.output == OutputType.PRG -> {
|
options.output == OutputType.PRG -> {
|
||||||
out("; ---- program without basic sys call ----")
|
out("; ---- program without basic sys call ----")
|
||||||
out("* = ${program.actualLoadAddress.toHex()}\n")
|
out("* = ${program.actualLoadAddress.toHex()}\n")
|
||||||
|
out(" tsx")
|
||||||
|
out(" stx prog8_lib.orig_stackpointer")
|
||||||
out(" jsr prog8_lib.init_system")
|
out(" jsr prog8_lib.init_system")
|
||||||
}
|
}
|
||||||
options.output == OutputType.RAW -> {
|
options.output == OutputType.RAW -> {
|
||||||
@ -120,11 +126,10 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
out(" ldx #\$ff\t; init estack pointer")
|
out(" ldx #\$ff\t; init estack pointer")
|
||||||
|
|
||||||
out(" ; initialize the variables in each block")
|
out(" ; initialize the variables in each block that has globals")
|
||||||
for (block in program.allBlocks()) {
|
program.allBlocks().forEach {
|
||||||
val initVarsSub = block.statements.singleOrNull { it is Subroutine && it.name == initvarsSubName }
|
if(it.statements.filterIsInstance<VarDecl>().any { vd->vd.value!=null && vd.type==VarDeclType.VAR && vd.datatype in NumericDatatypes})
|
||||||
if(initVarsSub!=null)
|
out(" jsr ${it.name}.prog8_init_vars")
|
||||||
out(" jsr ${block.name}.$initvarsSubName")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
out(" clc")
|
out(" clc")
|
||||||
@ -137,7 +142,6 @@ internal class AsmGen(private val program: Program,
|
|||||||
out(" jmp (c64.RESET_VEC)\t; cold reset")
|
out(" jmp (c64.RESET_VEC)\t; cold reset")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
out("")
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun footer() {
|
private fun footer() {
|
||||||
@ -171,6 +175,21 @@ internal class AsmGen(private val program: Program,
|
|||||||
stmts.forEach { translate(it) }
|
stmts.forEach { translate(it) }
|
||||||
subroutine.forEach { translateSubroutine(it as Subroutine) }
|
subroutine.forEach { translateSubroutine(it as Subroutine) }
|
||||||
|
|
||||||
|
// if any global vars need to be initialized, generate a subroutine that does this
|
||||||
|
// it will be called from program init.
|
||||||
|
if(block in blockLevelVarInits) {
|
||||||
|
out("prog8_init_vars\t.proc\n")
|
||||||
|
blockLevelVarInits.getValue(block).forEach { decl ->
|
||||||
|
val scopedFullName = decl.makeScopedName(decl.name).split('.')
|
||||||
|
require(scopedFullName.first()==block.name)
|
||||||
|
val target = AssignTarget(null, IdentifierReference(scopedFullName.drop(1), decl.position), null, null, decl.position)
|
||||||
|
val assign = Assignment(target, null, decl.value!!, decl.position)
|
||||||
|
assign.linkParents(decl.parent)
|
||||||
|
assignmentAsmGen.translate(assign)
|
||||||
|
}
|
||||||
|
out(" rts\n .pend")
|
||||||
|
}
|
||||||
|
|
||||||
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
out(if("force_output" in block.options()) "\n\t.bend\n" else "\n\t.pend\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -178,7 +197,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
internal fun makeLabel(postfix: String): String {
|
internal fun makeLabel(postfix: String): String {
|
||||||
generatedLabelSequenceNumber++
|
generatedLabelSequenceNumber++
|
||||||
return "_prog8_label_${generatedLabelSequenceNumber}_$postfix"
|
return "${generatedLabelPrefix}${generatedLabelSequenceNumber}_$postfix"
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun outputSourceLine(node: Node) {
|
private fun outputSourceLine(node: Node) {
|
||||||
@ -216,7 +235,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
val variables = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
|
||||||
for(variable in variables) {
|
for(variable in variables) {
|
||||||
// should NOT allocate subroutine parameters on the zero page
|
// should NOT allocate subroutine parameters on the zero page
|
||||||
val fullName = variable.scopedname
|
val fullName = variable.makeScopedName(variable.name)
|
||||||
val zpVar = allocatedZeropageVariables[fullName]
|
val zpVar = allocatedZeropageVariables[fullName]
|
||||||
if(zpVar==null) {
|
if(zpVar==null) {
|
||||||
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
// This var is not on the ZP yet. Attempt to move it there (if it's not a float, those take up too much space)
|
||||||
@ -225,7 +244,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
&& variable.datatype != DataType.FLOAT
|
&& variable.datatype != DataType.FLOAT
|
||||||
&& options.zeropage != ZeropageType.DONTUSE) {
|
&& options.zeropage != ZeropageType.DONTUSE) {
|
||||||
try {
|
try {
|
||||||
val address = zeropage.allocate(fullName, variable.datatype, null)
|
val errors = ErrorReporter()
|
||||||
|
val address = zeropage.allocate(fullName, variable.datatype, null, errors)
|
||||||
|
errors.handle()
|
||||||
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
out("${variable.name} = $address\t; auto zp ${variable.datatype}")
|
||||||
// make sure we add the var to the set of zpvars for this block
|
// make sure we add the var to the set of zpvars for this block
|
||||||
allocatedZeropageVariables[fullName] = Pair(address, variable.datatype)
|
allocatedZeropageVariables[fullName] = Pair(address, variable.datatype)
|
||||||
@ -290,7 +311,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.ARRAY_F -> {
|
DataType.ARRAY_F -> {
|
||||||
val array = (decl.value as ArrayLiteralValue).value
|
val array =
|
||||||
|
if(decl.value!=null)
|
||||||
|
(decl.value as ArrayLiteralValue).value
|
||||||
|
else {
|
||||||
|
// no init value, use zeros
|
||||||
|
val zero = decl.zeroElementValue()
|
||||||
|
Array(decl.arraysize!!.size()!!) { zero }
|
||||||
|
}
|
||||||
val floatFills = array.map {
|
val floatFills = array.map {
|
||||||
val number = (it as NumericLiteralValue).number
|
val number = (it as NumericLiteralValue).number
|
||||||
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
|
makeFloatFill(C64MachineDefinition.Mflpt5.fromNumber(number))
|
||||||
@ -344,7 +372,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
// non-string variables
|
// non-string variables
|
||||||
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
normalVars.filter{ it.datatype != DataType.STR }.sortedBy { it.datatype }.forEach {
|
||||||
if(it.scopedname !in allocatedZeropageVariables)
|
if(it.makeScopedName(it.name) !in allocatedZeropageVariables)
|
||||||
vardecl2asm(it)
|
vardecl2asm(it)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -358,7 +386,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
private fun makeArrayFillDataUnsigned(decl: VarDecl): List<String> {
|
||||||
val array = (decl.value as ArrayLiteralValue).value
|
val array =
|
||||||
|
if(decl.value!=null)
|
||||||
|
(decl.value as ArrayLiteralValue).value
|
||||||
|
else {
|
||||||
|
// no array init value specified, use a list of zeros
|
||||||
|
val zero = decl.zeroElementValue()
|
||||||
|
Array(decl.arraysize!!.size()!!) { zero }
|
||||||
|
}
|
||||||
return when (decl.datatype) {
|
return when (decl.datatype) {
|
||||||
DataType.ARRAY_UB ->
|
DataType.ARRAY_UB ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
@ -378,17 +413,22 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
|
private fun makeArrayFillDataSigned(decl: VarDecl): List<String> {
|
||||||
val array = (decl.value as ArrayLiteralValue).value
|
val array =
|
||||||
|
if(decl.value!=null)
|
||||||
return when {
|
(decl.value as ArrayLiteralValue).value
|
||||||
decl.datatype == DataType.ARRAY_UB ->
|
else {
|
||||||
|
// no array init value specified, use a list of zeros
|
||||||
|
val zero = decl.zeroElementValue()
|
||||||
|
Array(decl.arraysize!!.size()!!) { zero }
|
||||||
|
}
|
||||||
|
return when (decl.datatype) {
|
||||||
|
DataType.ARRAY_UB ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
array.map {
|
array.map {
|
||||||
val number = (it as NumericLiteralValue).number.toInt()
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
val hexnum = number.toString(16).padStart(2, '0')
|
"$"+number.toString(16).padStart(2, '0')
|
||||||
"$$hexnum"
|
|
||||||
}
|
}
|
||||||
decl.datatype == DataType.ARRAY_B ->
|
DataType.ARRAY_B ->
|
||||||
// byte array can never contain pointer-to types, so treat values as all integers
|
// byte array can never contain pointer-to types, so treat values as all integers
|
||||||
array.map {
|
array.map {
|
||||||
val number = (it as NumericLiteralValue).number.toInt()
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
@ -398,12 +438,11 @@ internal class AsmGen(private val program: Program,
|
|||||||
else
|
else
|
||||||
"-$$hexnum"
|
"-$$hexnum"
|
||||||
}
|
}
|
||||||
decl.datatype== DataType.ARRAY_UW -> array.map {
|
DataType.ARRAY_UW -> array.map {
|
||||||
val number = (it as NumericLiteralValue).number.toInt()
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
val hexnum = number.toString(16).padStart(4, '0')
|
"$" + number.toString(16).padStart(4, '0')
|
||||||
"$$hexnum"
|
|
||||||
}
|
}
|
||||||
decl.datatype== DataType.ARRAY_W -> array.map {
|
DataType.ARRAY_W -> array.map {
|
||||||
val number = (it as NumericLiteralValue).number.toInt()
|
val number = (it as NumericLiteralValue).number.toInt()
|
||||||
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
val hexnum = number.absoluteValue.toString(16).padStart(4, '0')
|
||||||
if(number>=0)
|
if(number>=0)
|
||||||
@ -565,7 +604,9 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translate(stmt: Statement) {
|
internal fun translate(stmt: Statement) {
|
||||||
outputSourceLine(stmt)
|
outputSourceLine(stmt)
|
||||||
when(stmt) {
|
when(stmt) {
|
||||||
is VarDecl, is StructDecl, is NopStatement -> {}
|
is ParameterVarDecl -> { /* subroutine parameter vardecls don't get any special treatment here */ }
|
||||||
|
is VarDecl -> translate(stmt)
|
||||||
|
is StructDecl, is NopStatement -> {}
|
||||||
is Directive -> translate(stmt)
|
is Directive -> translate(stmt)
|
||||||
is Return -> translate(stmt)
|
is Return -> translate(stmt)
|
||||||
is Subroutine -> translateSubroutine(stmt)
|
is Subroutine -> translateSubroutine(stmt)
|
||||||
@ -598,11 +639,13 @@ internal class AsmGen(private val program: Program,
|
|||||||
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
|
is Continue -> out(" jmp ${loopContinueLabels.peek()}")
|
||||||
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
is Break -> out(" jmp ${loopEndLabels.peek()}")
|
||||||
is WhileLoop -> translate(stmt)
|
is WhileLoop -> translate(stmt)
|
||||||
|
is ForeverLoop -> translate(stmt)
|
||||||
is RepeatLoop -> translate(stmt)
|
is RepeatLoop -> translate(stmt)
|
||||||
is WhenStatement -> translate(stmt)
|
is WhenStatement -> translate(stmt)
|
||||||
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
|
is BuiltinFunctionStatementPlaceholder -> throw AssemblyError("builtin function should not have placeholder anymore?")
|
||||||
is AnonymousScope -> translate(stmt)
|
is AnonymousScope -> translate(stmt)
|
||||||
is Block -> throw AssemblyError("block should have been handled elsewhere")
|
is Block -> throw AssemblyError("block should have been handled elsewhere")
|
||||||
|
else -> throw AssemblyError("missing asm translation for $stmt")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -628,6 +671,19 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: ForeverLoop) {
|
||||||
|
val foreverLabel = makeLabel("forever")
|
||||||
|
val endLabel = makeLabel("foreverend")
|
||||||
|
loopEndLabels.push(endLabel)
|
||||||
|
loopContinueLabels.push(foreverLabel)
|
||||||
|
out(foreverLabel)
|
||||||
|
translate(stmt.body)
|
||||||
|
out(" jmp $foreverLabel")
|
||||||
|
out(endLabel)
|
||||||
|
loopEndLabels.pop()
|
||||||
|
loopContinueLabels.pop()
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: WhileLoop) {
|
private fun translate(stmt: WhileLoop) {
|
||||||
val whileLabel = makeLabel("while")
|
val whileLabel = makeLabel("while")
|
||||||
val endLabel = makeLabel("whileend")
|
val endLabel = makeLabel("whileend")
|
||||||
@ -729,7 +785,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(stmt: Label) {
|
private fun translate(stmt: Label) {
|
||||||
out(stmt.name)
|
out("_${stmt.name}") // underscore prefix to make sure it's a local label
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translate(scope: AnonymousScope) {
|
private fun translate(scope: AnonymousScope) {
|
||||||
@ -768,6 +824,27 @@ internal class AsmGen(private val program: Program,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun translate(stmt: VarDecl) {
|
||||||
|
if(stmt.value!=null && stmt.type==VarDeclType.VAR && stmt.datatype in NumericDatatypes) {
|
||||||
|
// generate an assignment statement to (re)initialize the variable's value.
|
||||||
|
// if the vardecl is not in a subroutine however, we have to initialize it globally.
|
||||||
|
if(stmt.definingSubroutine()==null) {
|
||||||
|
val block = stmt.definingBlock()
|
||||||
|
var inits = blockLevelVarInits[block]
|
||||||
|
if(inits==null) {
|
||||||
|
inits = mutableSetOf()
|
||||||
|
blockLevelVarInits[block] = inits
|
||||||
|
}
|
||||||
|
inits.add(stmt)
|
||||||
|
} else {
|
||||||
|
val target = AssignTarget(null, IdentifierReference(listOf(stmt.name), stmt.position), null, null, stmt.position)
|
||||||
|
val assign = Assignment(target, null, stmt.value!!, stmt.position)
|
||||||
|
assign.linkParents(stmt.parent)
|
||||||
|
translate(assign)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private fun translate(stmt: Directive) {
|
private fun translate(stmt: Directive) {
|
||||||
when(stmt.directive) {
|
when(stmt.directive) {
|
||||||
"%asminclude" -> {
|
"%asminclude" -> {
|
||||||
@ -798,7 +875,14 @@ internal class AsmGen(private val program: Program,
|
|||||||
|
|
||||||
private fun getJumpTarget(jmp: Jump): String {
|
private fun getJumpTarget(jmp: Jump): String {
|
||||||
return when {
|
return when {
|
||||||
jmp.identifier!=null -> asmIdentifierName(jmp.identifier)
|
jmp.identifier!=null -> {
|
||||||
|
val target = jmp.identifier.targetStatement(program.namespace)
|
||||||
|
val asmName = asmIdentifierName(jmp.identifier)
|
||||||
|
if(target is Label)
|
||||||
|
"_$asmName" // prefix with underscore to jump to local label
|
||||||
|
else
|
||||||
|
asmName
|
||||||
|
}
|
||||||
jmp.generatedLabel!=null -> jmp.generatedLabel
|
jmp.generatedLabel!=null -> jmp.generatedLabel
|
||||||
jmp.address!=null -> jmp.address.toHex()
|
jmp.address!=null -> jmp.address.toHex()
|
||||||
else -> "????"
|
else -> "????"
|
||||||
@ -839,7 +923,7 @@ internal class AsmGen(private val program: Program,
|
|||||||
internal fun translateExpression(expression: Expression) =
|
internal fun translateExpression(expression: Expression) =
|
||||||
expressionsAsmGen.translateExpression(expression)
|
expressionsAsmGen.translateExpression(expression)
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FunctionSignature) =
|
internal fun translateFunctioncallExpression(functionCall: FunctionCall, signature: FSignature) =
|
||||||
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
builtinFunctionsAsmGen.translateFunctioncallExpression(functionCall, signature)
|
||||||
|
|
||||||
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
internal fun translateFunctionCall(functionCall: FunctionCall) =
|
||||||
|
@ -13,43 +13,45 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
|||||||
|
|
||||||
var linesByFour = getLinesBy(lines, 4)
|
var linesByFour = getLinesBy(lines, 4)
|
||||||
|
|
||||||
var removeLines = optimizeUselessStackByteWrites(linesByFour)
|
var mods = optimizeUselessStackByteWrites(linesByFour)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
apply(mods, lines)
|
||||||
lines.removeAt(i)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
removeLines = optimizeIncDec(linesByFour)
|
mods = optimizeIncDec(linesByFour)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
apply(mods, lines)
|
||||||
lines.removeAt(i)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
removeLines = optimizeCmpSequence(linesByFour)
|
mods = optimizeCmpSequence(linesByFour)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
apply(mods, lines)
|
||||||
lines.removeAt(i)
|
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
removeLines = optimizeStoreLoadSame(linesByFour)
|
mods = optimizeStoreLoadSame(linesByFour)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
apply(mods, lines)
|
||||||
lines.removeAt(i)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
|
numberOfOptimizations++
|
||||||
|
}
|
||||||
|
|
||||||
|
mods= optimizeJsrRts(linesByFour)
|
||||||
|
if(mods.isNotEmpty()) {
|
||||||
|
apply(mods, lines)
|
||||||
linesByFour = getLinesBy(lines, 4)
|
linesByFour = getLinesBy(lines, 4)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
|
|
||||||
var linesByFourteen = getLinesBy(lines, 14)
|
var linesByFourteen = getLinesBy(lines, 14)
|
||||||
removeLines = optimizeSameAssignments(linesByFourteen)
|
mods = optimizeSameAssignments(linesByFourteen)
|
||||||
if(removeLines.isNotEmpty()) {
|
if(mods.isNotEmpty()) {
|
||||||
for (i in removeLines.reversed())
|
apply(mods, lines)
|
||||||
lines.removeAt(i)
|
|
||||||
linesByFourteen = getLinesBy(lines, 14)
|
linesByFourteen = getLinesBy(lines, 14)
|
||||||
numberOfOptimizations++
|
numberOfOptimizations++
|
||||||
}
|
}
|
||||||
@ -59,7 +61,22 @@ fun optimizeAssembly(lines: MutableList<String>): Int {
|
|||||||
return numberOfOptimizations
|
return numberOfOptimizations
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
private class Modification(val lineIndex: Int, val remove: Boolean, val replacement: String?)
|
||||||
|
|
||||||
|
private fun apply(modifications: List<Modification>, lines: MutableList<String>) {
|
||||||
|
for (modification in modifications.sortedBy { it.lineIndex }.reversed()) {
|
||||||
|
if(modification.remove)
|
||||||
|
lines.removeAt(modification.lineIndex)
|
||||||
|
else
|
||||||
|
lines[modification.lineIndex] = modification.replacement!!
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
||||||
|
// all lines (that aren't empty or comments) in sliding windows of certain size
|
||||||
|
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
||||||
|
|
||||||
|
private fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// the when statement (on bytes) generates a sequence of:
|
// the when statement (on bytes) generates a sequence of:
|
||||||
// lda $ce01,x
|
// lda $ce01,x
|
||||||
// cmp #$20
|
// cmp #$20
|
||||||
@ -68,42 +85,42 @@ fun optimizeCmpSequence(linesByFour: List<List<IndexedValue<String>>>): List<Int
|
|||||||
// cmp #$21
|
// cmp #$21
|
||||||
// beq check_prog8_s73choice_33
|
// beq check_prog8_s73choice_33
|
||||||
// the repeated lda can be removed
|
// the repeated lda can be removed
|
||||||
val removeLines = mutableListOf<Int>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
for(lines in linesByFour) {
|
||||||
if(lines[0].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x" &&
|
if(lines[0].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x" &&
|
||||||
lines[1].value.trim().startsWith("cmp ") &&
|
lines[1].value.trim().startsWith("cmp ") &&
|
||||||
lines[2].value.trim().startsWith("beq ") &&
|
lines[2].value.trim().startsWith("beq ") &&
|
||||||
lines[3].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x") {
|
lines[3].value.trim()=="lda $ESTACK_LO_PLUS1_HEX,x") {
|
||||||
removeLines.add(lines[3].index) // remove the second lda
|
mods.add(Modification(lines[3].index, true, null)) // remove the second lda
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removeLines
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
private fun optimizeUselessStackByteWrites(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
// sta on stack, dex, inx, lda from stack -> eliminate this useless stack byte write
|
||||||
// this is a lot harder for word values because the instruction sequence varies.
|
// this is a lot harder for word values because the instruction sequence varies.
|
||||||
val removeLines = mutableListOf<Int>()
|
val mods = mutableListOf<Modification>()
|
||||||
for(lines in linesByFour) {
|
for(lines in linesByFour) {
|
||||||
if(lines[0].value.trim()=="sta $ESTACK_LO_HEX,x" &&
|
if(lines[0].value.trim()=="sta $ESTACK_LO_HEX,x" &&
|
||||||
lines[1].value.trim()=="dex" &&
|
lines[1].value.trim()=="dex" &&
|
||||||
lines[2].value.trim()=="inx" &&
|
lines[2].value.trim()=="inx" &&
|
||||||
lines[3].value.trim()=="lda $ESTACK_LO_HEX,x") {
|
lines[3].value.trim()=="lda $ESTACK_LO_HEX,x") {
|
||||||
removeLines.add(lines[1].index)
|
mods.add(Modification(lines[1].index, true, null))
|
||||||
removeLines.add(lines[2].index)
|
mods.add(Modification(lines[2].index, true, null))
|
||||||
removeLines.add(lines[3].index)
|
mods.add(Modification(lines[3].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removeLines
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Int> {
|
private fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
|
||||||
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
|
// optimize sequential assignments of the isSameAs value to various targets (bytes, words, floats)
|
||||||
// the float one is the one that requires 2*7=14 lines of code to check...
|
// the float one is the one that requires 2*7=14 lines of code to check...
|
||||||
// @todo a better place to do this is in the Compiler instead and transform the Ast, or the AsmGen, and never even create the inefficient asm in the first place...
|
// @todo a better place to do this is in the Compiler instead and transform the Ast, or the AsmGen, and never even create the inefficient asm in the first place...
|
||||||
|
|
||||||
val removeLines = mutableListOf<Int>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByFourteen) {
|
for (pair in linesByFourteen) {
|
||||||
val first = pair[0].value.trimStart()
|
val first = pair[0].value.trimStart()
|
||||||
val second = pair[1].value.trimStart()
|
val second = pair[1].value.trimStart()
|
||||||
@ -122,8 +139,8 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
val fourthvalue = sixth.substring(4)
|
val fourthvalue = sixth.substring(4)
|
||||||
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
if(firstvalue==thirdvalue && secondvalue==fourthvalue) {
|
||||||
// lda/ldy sta/sty twice the isSameAs word --> remove second lda/ldy pair (fifth and sixth lines)
|
// lda/ldy sta/sty twice the isSameAs word --> remove second lda/ldy pair (fifth and sixth lines)
|
||||||
removeLines.add(pair[4].index)
|
mods.add(Modification(pair[4].index, true, null))
|
||||||
removeLines.add(pair[5].index)
|
mods.add(Modification(pair[5].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -132,7 +149,7 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
val secondvalue = third.substring(4)
|
val secondvalue = third.substring(4)
|
||||||
if(firstvalue==secondvalue) {
|
if(firstvalue==secondvalue) {
|
||||||
// lda value / sta ? / lda isSameAs-value / sta ? -> remove second lda (third line)
|
// lda value / sta ? / lda isSameAs-value / sta ? -> remove second lda (third line)
|
||||||
removeLines.add(pair[2].index)
|
mods.add(Modification(pair[2].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -151,24 +168,20 @@ fun optimizeSameAssignments(linesByFourteen: List<List<IndexedValue<String>>>):
|
|||||||
|
|
||||||
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
if(first.substring(4) == eighth.substring(4) && second.substring(4)==nineth.substring(4)) {
|
||||||
// identical float init
|
// identical float init
|
||||||
removeLines.add(pair[7].index)
|
mods.add(Modification(pair[7].index, true, null))
|
||||||
removeLines.add(pair[8].index)
|
mods.add(Modification(pair[8].index, true, null))
|
||||||
removeLines.add(pair[9].index)
|
mods.add(Modification(pair[9].index, true, null))
|
||||||
removeLines.add(pair[10].index)
|
mods.add(Modification(pair[10].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removeLines
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun getLinesBy(lines: MutableList<String>, windowSize: Int) =
|
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// all lines (that aren't empty or comments) in sliding windows of certain size
|
|
||||||
lines.withIndex().filter { it.value.isNotBlank() && !it.value.trimStart().startsWith(';') }.windowed(windowSize, partialWindows = false)
|
|
||||||
|
|
||||||
private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>): List<Int> {
|
|
||||||
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
// sta X + lda X, sty X + ldy X, stx X + ldx X -> the second instruction can be eliminated
|
||||||
val removeLines = mutableListOf<Int>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByFour) {
|
for (pair in linesByFour) {
|
||||||
val first = pair[0].value.trimStart()
|
val first = pair[0].value.trimStart()
|
||||||
val second = pair[1].value.trimStart()
|
val second = pair[1].value.trimStart()
|
||||||
@ -186,26 +199,40 @@ private fun optimizeStoreLoadSame(linesByFour: List<List<IndexedValue<String>>>)
|
|||||||
val firstLoc = first.substring(4)
|
val firstLoc = first.substring(4)
|
||||||
val secondLoc = second.substring(4)
|
val secondLoc = second.substring(4)
|
||||||
if (firstLoc == secondLoc) {
|
if (firstLoc == secondLoc) {
|
||||||
removeLines.add(pair[1].index)
|
mods.add(Modification(pair[1].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removeLines
|
return mods
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun optimizeIncDec(linesByTwo: List<List<IndexedValue<String>>>): List<Int> {
|
private fun optimizeIncDec(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
// sometimes, iny+dey / inx+dex / dey+iny / dex+inx sequences are generated, these can be eliminated.
|
||||||
val removeLines = mutableListOf<Int>()
|
val mods = mutableListOf<Modification>()
|
||||||
for (pair in linesByTwo) {
|
for (pair in linesByFour) {
|
||||||
val first = pair[0].value
|
val first = pair[0].value
|
||||||
val second = pair[1].value
|
val second = pair[1].value
|
||||||
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
if ((" iny" in first || "\tiny" in first) && (" dey" in second || "\tdey" in second)
|
||||||
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
|| (" inx" in first || "\tinx" in first) && (" dex" in second || "\tdex" in second)
|
||||||
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
|| (" dey" in first || "\tdey" in first) && (" iny" in second || "\tiny" in second)
|
||||||
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)) {
|
|| (" dex" in first || "\tdex" in first) && (" inx" in second || "\tinx" in second)) {
|
||||||
removeLines.add(pair[0].index)
|
mods.add(Modification(pair[0].index, true, null))
|
||||||
removeLines.add(pair[1].index)
|
mods.add(Modification(pair[1].index, true, null))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return removeLines
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeJsrRts(linesByFour: List<List<IndexedValue<String>>>): List<Modification> {
|
||||||
|
// jsr Sub + rts -> jmp Sub
|
||||||
|
val mods = mutableListOf<Modification>()
|
||||||
|
for (pair in linesByFour) {
|
||||||
|
val first = pair[0].value
|
||||||
|
val second = pair[1].value
|
||||||
|
if ((" jsr" in first || "\tjsr" in first ) && (" rts" in second || "\trts" in second)) {
|
||||||
|
mods += Modification(pair[0].index, false, pair[0].value.replace("jsr", "jmp"))
|
||||||
|
mods += Modification(pair[1].index, true, null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return mods
|
||||||
}
|
}
|
||||||
|
@ -7,12 +7,12 @@ import prog8.ast.statements.AssignTarget
|
|||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.DirectMemoryWrite
|
import prog8.ast.statements.DirectMemoryWrite
|
||||||
import prog8.ast.statements.VarDecl
|
import prog8.ast.statements.VarDecl
|
||||||
import prog8.compiler.toHex
|
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class AssignmentAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -58,8 +58,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
assignFromMemoryByte(assign.target, null, read.addressExpression as IdentifierReference)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
asmgen.translateExpression(read.addressExpression)
|
throw AssemblyError("missing asm gen for memread assignment into ${assign.target}")
|
||||||
TODO("read memory byte from result and put that in ${assign.target}")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -117,9 +116,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(assign.value as FunctionCall)
|
asmgen.translateExpression(assign.value as FunctionCall)
|
||||||
assignFromEvalResult(assign.target)
|
assignFromEvalResult(assign.target)
|
||||||
}
|
}
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment $assign")
|
||||||
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened ${assign.value.position}")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values ${assign.value.position}")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -199,14 +198,14 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
target.memoryAddress!=null -> {
|
target.memoryAddress!=null -> {
|
||||||
TODO("assign address $sourceName to memory word $target")
|
throw AssemblyError("no asm gen for assign address $sourceName to memory word $target")
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
TODO("assign address $sourceName to array $targetName [ $index ]")
|
throw AssemblyError("no asm gen for assign address $sourceName to array $targetName [ $index ]")
|
||||||
}
|
}
|
||||||
else -> TODO("assign address $sourceName to $target")
|
else -> throw AssemblyError("no asm gen for assign address $sourceName to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -225,7 +224,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
target.memoryAddress!=null -> {
|
target.memoryAddress!=null -> {
|
||||||
TODO("assign wordvar $sourceName to memory ${target.memoryAddress}")
|
throw AssemblyError("no asm gen for assign wordvar $sourceName to memory ${target.memoryAddress}")
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
@ -236,7 +235,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
val arrayDt = targetArrayIdx.identifier.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
popAndWriteArrayvalueWithIndexA(arrayDt, targetName)
|
||||||
}
|
}
|
||||||
else -> TODO("assign wordvar to $target")
|
else -> throw AssemblyError("no asm gen for assign wordvar to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -267,7 +266,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out(" lda #<$targetName | ldy #>$targetName | jsr c64flt.pop_float_to_indexed_var")
|
asmgen.out(" lda #<$targetName | ldy #>$targetName | jsr c64flt.pop_float_to_indexed_var")
|
||||||
}
|
}
|
||||||
else -> TODO("assign floatvar to $target")
|
else -> throw AssemblyError("no asm gen for assign floatvar to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -313,12 +312,12 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta (+) +1
|
sta (+) +1
|
||||||
sty (+) +2
|
sty (+) +2
|
||||||
lda $sourceName
|
lda $sourceName
|
||||||
+ sta ${65535.toHex()} ; modified
|
+ sta ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("assign bytevar to $target")
|
else -> throw AssemblyError("no asm gen for assign bytevar to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -413,7 +412,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("assign register $register to $target")
|
else -> throw AssemblyError("no asm gen for assign register $register to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -428,30 +427,22 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when(register) {
|
when(register) {
|
||||||
Register.A -> asmgen.out("""
|
Register.A -> asmgen.out("""
|
||||||
ldy $targetName
|
ldy $targetName
|
||||||
sty ${C64Zeropage.SCRATCH_W1}
|
sty (+) +1
|
||||||
ldy $targetName+1
|
ldy $targetName+1
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
sty (+) +2
|
||||||
ldy #0
|
+ sta ${'$'}ffff ; modified""")
|
||||||
sta (${C64Zeropage.SCRATCH_W1}),y
|
|
||||||
""")
|
|
||||||
Register.X -> asmgen.out("""
|
Register.X -> asmgen.out("""
|
||||||
txa
|
|
||||||
ldy $targetName
|
ldy $targetName
|
||||||
sty ${C64Zeropage.SCRATCH_W1}
|
sty (+) +1
|
||||||
ldy $targetName+1
|
ldy $targetName+1
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
sty (+) +2
|
||||||
ldy #0
|
+ stx ${'$'}ffff ; modified""")
|
||||||
sta (${C64Zeropage.SCRATCH_W1}),y
|
|
||||||
""")
|
|
||||||
Register.Y -> asmgen.out("""
|
Register.Y -> asmgen.out("""
|
||||||
tya
|
lda $targetName
|
||||||
ldy $targetName
|
sta (+) +1
|
||||||
sty ${C64Zeropage.SCRATCH_W1}
|
lda $targetName+1
|
||||||
ldy $targetName+1
|
sta (+) +2
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
+ sty ${'$'}ffff ; modified""")
|
||||||
ldy #0
|
|
||||||
sta (${C64Zeropage.SCRATCH_W1}),y
|
|
||||||
""")
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
@ -469,7 +460,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta (+) +1
|
sta (+) +1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) +2
|
sta (+) +2
|
||||||
+ sty ${65535.toHex()} ; modified
|
+ sty ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -498,7 +489,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
target.memoryAddress!=null -> {
|
target.memoryAddress!=null -> {
|
||||||
TODO("assign word $word to memory ${target.memoryAddress}")
|
throw AssemblyError("no asm gen for assign word $word to memory ${target.memoryAddress}")
|
||||||
}
|
}
|
||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
@ -516,7 +507,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta $targetName+1,y
|
sta $targetName+1,y
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> TODO("assign word $word to $target")
|
else -> throw AssemblyError("no asm gen for assign word $word to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -547,7 +538,7 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
sta $targetName,y
|
sta $targetName,y
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
else -> TODO("assign byte $byte to $target")
|
else -> throw AssemblyError("no asm gen for assign byte $byte to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -584,23 +575,15 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(index)
|
asmgen.translateExpression(index)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
inx
|
lda #<${targetName}
|
||||||
lda $ESTACK_LO_HEX,x
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
asl a
|
lda #>${targetName}
|
||||||
asl a
|
sta ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
clc
|
jsr c64flt.set_0_array_float
|
||||||
adc $ESTACK_LO_HEX,x
|
""")
|
||||||
tay
|
|
||||||
lda #0
|
|
||||||
sta $targetName,y
|
|
||||||
sta $targetName+1,y
|
|
||||||
sta $targetName+2,y
|
|
||||||
sta $targetName+3,y
|
|
||||||
sta $targetName+4,y
|
|
||||||
""") // TODO use a subroutine for this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("assign float 0.0 to $target")
|
else -> throw AssemblyError("no asm gen for assign float 0.0 to $target")
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// non-zero value
|
// non-zero value
|
||||||
@ -641,26 +624,19 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
} else {
|
} else {
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
sta ${C64Zeropage.SCRATCH_REG}
|
lda #<${constFloat}
|
||||||
asl a
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
asl a
|
lda #>${constFloat}
|
||||||
clc
|
sta ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
adc ${C64Zeropage.SCRATCH_REG}
|
lda #<${arrayVarName}
|
||||||
tay
|
sta ${C64Zeropage.SCRATCH_W2}
|
||||||
lda $constFloat
|
lda #>${arrayVarName}
|
||||||
sta $arrayVarName,y
|
sta ${C64Zeropage.SCRATCH_W2 + 1}
|
||||||
lda $constFloat+1
|
jsr c64flt.set_array_float
|
||||||
sta $arrayVarName+1,y
|
""")
|
||||||
lda $constFloat+2
|
|
||||||
sta $arrayVarName+2,y
|
|
||||||
lda $constFloat+3
|
|
||||||
sta $arrayVarName+3,y
|
|
||||||
lda $constFloat+4
|
|
||||||
sta $arrayVarName+4,y
|
|
||||||
""") // TODO use a subroutine for this
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else -> TODO("assign float $float to $target")
|
else -> throw AssemblyError("no asm gen for assign float $float to $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -687,9 +663,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
TODO("assign memory byte at $address to array $targetName [ $index ]")
|
throw AssemblyError("no asm gen for assign memory byte at $address to array $targetName [ $index ]")
|
||||||
}
|
}
|
||||||
else -> TODO("assign memory byte $target")
|
else -> throw AssemblyError("no asm gen for assign memory byte $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if(identifier!=null) {
|
else if(identifier!=null) {
|
||||||
@ -697,22 +673,25 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
when {
|
when {
|
||||||
target.register!=null -> {
|
target.register!=null -> {
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
lda $sourceName
|
||||||
lda ($sourceName),y
|
sta (+) + 1
|
||||||
""")
|
lda $sourceName+1
|
||||||
|
sta (+) + 2""")
|
||||||
when(target.register){
|
when(target.register){
|
||||||
Register.A -> {}
|
Register.A -> asmgen.out("+ lda ${'$'}ffff\t; modified")
|
||||||
Register.X -> asmgen.out(" tax")
|
Register.X -> asmgen.out("+ ldx ${'$'}ffff\t; modified")
|
||||||
Register.Y -> asmgen.out(" tay")
|
Register.Y -> asmgen.out("+ ldy ${'$'}ffff\t; modified")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
targetIdent!=null -> {
|
targetIdent!=null -> {
|
||||||
val targetName = asmgen.asmIdentifierName(targetIdent)
|
val targetName = asmgen.asmIdentifierName(targetIdent)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
ldy #0
|
lda $sourceName
|
||||||
lda ($sourceName),y
|
sta (+) + 1
|
||||||
sta $targetName
|
lda $sourceName+1
|
||||||
""")
|
sta (+) + 2
|
||||||
|
+ lda ${'$'}ffff\t; modified
|
||||||
|
sta $targetName""")
|
||||||
}
|
}
|
||||||
target.memoryAddress!=null -> {
|
target.memoryAddress!=null -> {
|
||||||
asmgen.out(" ldy $sourceName")
|
asmgen.out(" ldy $sourceName")
|
||||||
@ -721,9 +700,9 @@ internal class AssignmentAsmGen(private val program: Program, private val asmgen
|
|||||||
targetArrayIdx!=null -> {
|
targetArrayIdx!=null -> {
|
||||||
val index = targetArrayIdx.arrayspec.index
|
val index = targetArrayIdx.arrayspec.index
|
||||||
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
val targetName = asmgen.asmIdentifierName(targetArrayIdx.identifier)
|
||||||
TODO("assign memory byte $sourceName to array $targetName [ $index ]")
|
throw AssemblyError("no asm gen for assign memory byte $sourceName to array $targetName [ $index ]")
|
||||||
}
|
}
|
||||||
else -> TODO("assign memory byte $target")
|
else -> throw AssemblyError("no asm gen for assign memory byte $target")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -9,26 +9,26 @@ import prog8.ast.base.WordDatatypes
|
|||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.FunctionCallStatement
|
import prog8.ast.statements.FunctionCallStatement
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.functions.FSignature
|
||||||
import prog8.functions.FunctionSignature
|
|
||||||
|
|
||||||
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class BuiltinFunctionsAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
|
||||||
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
|
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FSignature) {
|
||||||
translateFunctioncall(fcall, func, false)
|
translateFunctioncall(fcall, func, false)
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FunctionSignature) {
|
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FSignature) {
|
||||||
translateFunctioncall(fcall, func, true)
|
translateFunctioncall(fcall, func, true)
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
|
private fun translateFunctioncall(fcall: IFunctionCall, func: FSignature, discardResult: Boolean) {
|
||||||
val functionName = fcall.target.nameInSource.last()
|
val functionName = fcall.target.nameInSource.last()
|
||||||
if (discardResult) {
|
if (discardResult) {
|
||||||
if (func.pure)
|
if (func.pure)
|
||||||
@ -38,109 +38,191 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
}
|
}
|
||||||
|
|
||||||
when (functionName) {
|
when (functionName) {
|
||||||
"msb" -> {
|
"msb" -> funcMsb(fcall)
|
||||||
val arg = fcall.args.single()
|
"mkword" -> funcMkword(fcall, func)
|
||||||
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
"abs" -> funcAbs(fcall, func)
|
||||||
throw AssemblyError("msb required word argument")
|
"swap" -> funcSwap(fcall)
|
||||||
if (arg is NumericLiteralValue)
|
"strlen" -> funcStrlen(fcall)
|
||||||
throw AssemblyError("should have been const-folded")
|
"min", "max", "sum" -> funcMinMaxSum(fcall, functionName)
|
||||||
if (arg is IdentifierReference) {
|
"any", "all" -> funcAnyAll(fcall, functionName)
|
||||||
val sourceName = asmgen.asmIdentifierName(arg)
|
"sgn" -> funcSgn(fcall, func)
|
||||||
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(arg)
|
|
||||||
asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"mkword" -> {
|
|
||||||
translateFunctionArguments(fcall.args, func)
|
|
||||||
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
|
||||||
}
|
|
||||||
"abs" -> {
|
|
||||||
translateFunctionArguments(fcall.args, func)
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
|
||||||
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"swap" -> {
|
|
||||||
val first = fcall.args[0]
|
|
||||||
val second = fcall.args[1]
|
|
||||||
asmgen.translateExpression(first)
|
|
||||||
asmgen.translateExpression(second)
|
|
||||||
// pop in reverse order
|
|
||||||
val firstTarget = AssignTarget.fromExpr(first)
|
|
||||||
val secondTarget = AssignTarget.fromExpr(second)
|
|
||||||
asmgen.assignFromEvalResult(firstTarget)
|
|
||||||
asmgen.assignFromEvalResult(secondTarget)
|
|
||||||
}
|
|
||||||
"strlen" -> {
|
|
||||||
outputPushAddressOfIdentifier(fcall.args[0])
|
|
||||||
asmgen.out(" jsr prog8_lib.func_strlen")
|
|
||||||
}
|
|
||||||
"min", "max", "sum" -> {
|
|
||||||
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
|
||||||
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
|
||||||
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
|
||||||
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"any", "all" -> {
|
|
||||||
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
|
||||||
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"sgn" -> {
|
|
||||||
translateFunctionArguments(fcall.args, func)
|
|
||||||
val dt = fcall.args.single().inferType(program)
|
|
||||||
when(dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
|
|
||||||
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
|
||||||
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
|
||||||
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
|
||||||
DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f")
|
|
||||||
else -> throw AssemblyError("weird type $dt")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"sin", "cos", "tan", "atan",
|
"sin", "cos", "tan", "atan",
|
||||||
"ln", "log2", "sqrt", "rad",
|
"ln", "log2", "sqrt", "rad",
|
||||||
"deg", "round", "floor", "ceil",
|
"deg", "round", "floor", "ceil",
|
||||||
"rdnf" -> {
|
"rdnf" -> funcVariousFloatFuncs(fcall, func, functionName)
|
||||||
translateFunctionArguments(fcall.args, func)
|
"lsl" -> funcLsl(fcall)
|
||||||
asmgen.out(" jsr c64flt.func_$functionName")
|
"lsr" -> funcLsr(fcall)
|
||||||
|
"rol" -> funcRol(fcall)
|
||||||
|
"rol2" -> funcRol2(fcall)
|
||||||
|
"ror" -> funcRor(fcall)
|
||||||
|
"ror2" -> funcRor2(fcall)
|
||||||
|
"sort" -> funcSort(fcall)
|
||||||
|
"reverse" -> funcReverse(fcall)
|
||||||
|
"rsave" -> {
|
||||||
|
// save cpu status flag and all registers A, X, Y.
|
||||||
|
// see http://6502.org/tutorials/register_preservation.html
|
||||||
|
asmgen.out(" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}")
|
||||||
}
|
}
|
||||||
"lsl" -> {
|
"rrestore" -> {
|
||||||
// in-place
|
// restore all registers and cpu status flag
|
||||||
|
asmgen.out(" pla | tay | pla | tax | pla | plp")
|
||||||
|
}
|
||||||
|
"clear_carry" -> asmgen.out(" clc")
|
||||||
|
"set_carry" -> asmgen.out(" sec")
|
||||||
|
"clear_irqd" -> asmgen.out(" cli")
|
||||||
|
"set_irqd" -> asmgen.out(" sei")
|
||||||
|
else -> {
|
||||||
|
translateFunctionArguments(fcall.args, func)
|
||||||
|
asmgen.out(" jsr prog8_lib.func_$functionName")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcReverse(fcall: IFunctionCall) {
|
||||||
|
val variable = fcall.args.single()
|
||||||
|
if (variable is IdentifierReference) {
|
||||||
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
|
val varName = asmgen.asmIdentifierName(variable)
|
||||||
|
val numElements = decl.arraysize!!.size()
|
||||||
|
when (decl.datatype) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
jsr prog8_lib.reverse_b
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
jsr prog8_lib.reverse_w
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
jsr prog8_lib.reverse_f
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSort(fcall: IFunctionCall) {
|
||||||
|
val variable = fcall.args.single()
|
||||||
|
if (variable is IdentifierReference) {
|
||||||
|
val decl = variable.targetVarDecl(program.namespace)!!
|
||||||
|
val varName = asmgen.asmIdentifierName(variable)
|
||||||
|
val numElements = decl.arraysize!!.size()
|
||||||
|
when (decl.datatype) {
|
||||||
|
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
sta ${C64Zeropage.SCRATCH_B1}
|
||||||
|
""")
|
||||||
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
||||||
|
asmgen.out("""
|
||||||
|
lda #<$varName
|
||||||
|
ldy #>$varName
|
||||||
|
sta ${C64Zeropage.SCRATCH_W1}
|
||||||
|
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
||||||
|
lda #$numElements
|
||||||
|
sta ${C64Zeropage.SCRATCH_B1}
|
||||||
|
""")
|
||||||
|
asmgen.out(if (decl.datatype == DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
||||||
|
}
|
||||||
|
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
} else
|
||||||
|
throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRor2(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
in ByteDatatypes -> {
|
DataType.UBYTE -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is RegisterExpr -> {
|
is ArrayIndexedExpression -> {
|
||||||
when (what.register) {
|
asmgen.translateExpression(what.identifier)
|
||||||
Register.A -> asmgen.out(" asl a")
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
Register.X -> asmgen.out(" txa | asl a | tax")
|
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
||||||
Register.Y -> asmgen.out(" tya | asl a | tay")
|
|
||||||
}
|
}
|
||||||
}
|
|
||||||
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteralValue).number
|
||||||
asmgen.out(" asl ${number.toHex()}")
|
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(what.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when (what.register) {
|
||||||
|
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
|
||||||
|
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
|
||||||
|
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRor(fcall: IFunctionCall) {
|
||||||
|
val what = fcall.args.single()
|
||||||
|
val dt = what.inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
|
val number = (what.addressExpression as NumericLiteralValue).number
|
||||||
|
asmgen.out(" ror ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -149,28 +231,34 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ asl 0 ; modified
|
+ ror ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is ArrayIndexedExpression -> {
|
is RegisterExpr -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
when (what.register) {
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
Register.A -> asmgen.out(" ror a")
|
||||||
asmgen.out(" jsr prog8_lib.lsl_array_b")
|
Register.X -> asmgen.out(" txa | ror a | tax")
|
||||||
|
Register.Y -> asmgen.out(" tya | ror a | tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
in WordDatatypes -> {
|
DataType.UWORD -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.identifier)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
asmgen.out(" jsr prog8_lib.lsl_array_w")
|
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" asl $variable | rol $variable+1")
|
asmgen.out(" ror $variable+1 | ror $variable")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -178,8 +266,119 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"lsr" -> {
|
|
||||||
// in-place
|
private fun funcRol2(fcall: IFunctionCall) {
|
||||||
|
val what = fcall.args.single()
|
||||||
|
val dt = what.inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
|
val number = (what.addressExpression as NumericLiteralValue).number
|
||||||
|
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(what.addressExpression)
|
||||||
|
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when (what.register) {
|
||||||
|
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
|
||||||
|
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
|
||||||
|
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcRol(fcall: IFunctionCall) {
|
||||||
|
val what = fcall.args.single()
|
||||||
|
val dt = what.inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
||||||
|
}
|
||||||
|
is DirectMemoryRead -> {
|
||||||
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
|
val number = (what.addressExpression as NumericLiteralValue).number
|
||||||
|
asmgen.out(" rol ${number.toHex()}")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(what.addressExpression)
|
||||||
|
asmgen.out("""
|
||||||
|
inx
|
||||||
|
lda $ESTACK_LO_HEX,x
|
||||||
|
sta (+) + 1
|
||||||
|
lda $ESTACK_HI_HEX,x
|
||||||
|
sta (+) + 2
|
||||||
|
+ rol ${'$'}ffff ; modified
|
||||||
|
""")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is RegisterExpr -> {
|
||||||
|
when (what.register) {
|
||||||
|
Register.A -> asmgen.out(" rol a")
|
||||||
|
Register.X -> asmgen.out(" txa | rol a | tax")
|
||||||
|
Register.Y -> asmgen.out(" tya | rol a | tay")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" rol $variable")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
when (what) {
|
||||||
|
is ArrayIndexedExpression -> {
|
||||||
|
asmgen.translateExpression(what.identifier)
|
||||||
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
|
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
||||||
|
}
|
||||||
|
is IdentifierReference -> {
|
||||||
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
|
asmgen.out(" rol $variable | rol $variable+1")
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcLsr(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
@ -205,7 +404,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ lsr 0 ; modified
|
+ lsr ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -262,22 +461,25 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"rol" -> {
|
|
||||||
// in-place
|
private fun funcLsl(fcall: IFunctionCall) {
|
||||||
val what = fcall.args.single()
|
val what = fcall.args.single()
|
||||||
val dt = what.inferType(program)
|
val dt = what.inferType(program)
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
DataType.UBYTE -> {
|
in ByteDatatypes -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is RegisterExpr -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
when (what.register) {
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
Register.A -> asmgen.out(" asl a")
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_ub")
|
Register.X -> asmgen.out(" txa | asl a | tax")
|
||||||
|
Register.Y -> asmgen.out(" tya | asl a | tay")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
is IdentifierReference -> asmgen.out(" asl ${asmgen.asmIdentifierName(what)}")
|
||||||
is DirectMemoryRead -> {
|
is DirectMemoryRead -> {
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
if (what.addressExpression is NumericLiteralValue) {
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
val number = (what.addressExpression as NumericLiteralValue).number
|
||||||
asmgen.out(" rol ${number.toHex()}")
|
asmgen.out(" asl ${number.toHex()}")
|
||||||
} else {
|
} else {
|
||||||
asmgen.translateExpression(what.addressExpression)
|
asmgen.translateExpression(what.addressExpression)
|
||||||
asmgen.out("""
|
asmgen.out("""
|
||||||
@ -286,34 +488,28 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
sta (+) + 1
|
sta (+) + 1
|
||||||
lda $ESTACK_HI_HEX,x
|
lda $ESTACK_HI_HEX,x
|
||||||
sta (+) + 2
|
sta (+) + 2
|
||||||
+ rol 0 ; modified
|
+ asl ${'$'}ffff ; modified
|
||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
is ArrayIndexedExpression -> {
|
||||||
when(what.register) {
|
asmgen.translateExpression(what.identifier)
|
||||||
Register.A -> asmgen.out(" rol a")
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
Register.X -> asmgen.out(" txa | rol a | tax")
|
asmgen.out(" jsr prog8_lib.lsl_array_b")
|
||||||
Register.Y -> asmgen.out(" tya | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" rol $variable")
|
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
DataType.UWORD -> {
|
in WordDatatypes -> {
|
||||||
when (what) {
|
when (what) {
|
||||||
is ArrayIndexedExpression -> {
|
is ArrayIndexedExpression -> {
|
||||||
asmgen.translateExpression(what.identifier)
|
asmgen.translateExpression(what.identifier)
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
asmgen.translateExpression(what.arrayspec.index)
|
||||||
asmgen.out(" jsr prog8_lib.rol_array_uw")
|
asmgen.out(" jsr prog8_lib.lsl_array_w")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
val variable = asmgen.asmIdentifierName(what)
|
||||||
asmgen.out(" rol $variable | rol $variable+1")
|
asmgen.out(" asl $variable | rol $variable+1")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
@ -321,258 +517,94 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"rol2" -> {
|
|
||||||
// in-place
|
private fun funcVariousFloatFuncs(fcall: IFunctionCall, func: FSignature, functionName: String) {
|
||||||
val what = fcall.args.single()
|
|
||||||
val dt = what.inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_ub")
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
|
||||||
asmgen.out(" lda ${number.toHex()} | cmp #\$80 | rol a | sta ${number.toHex()}")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(what.addressExpression)
|
|
||||||
asmgen.out(" jsr prog8_lib.rol2_mem_ub")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when(what.register) {
|
|
||||||
Register.A -> asmgen.out(" cmp #\$80 | rol a ")
|
|
||||||
Register.X -> asmgen.out(" txa | cmp #\$80 | rol a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | cmp #\$80 | rol a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lda $variable | cmp #\$80 | rol a | sta $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.rol2_array_uw")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" asl $variable | rol $variable+1 | bcc + | inc $variable |+ ")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"ror" -> {
|
|
||||||
// in-place
|
|
||||||
val what = fcall.args.single()
|
|
||||||
val dt = what.inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_ub")
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
|
||||||
asmgen.out(" ror ${number.toHex()}")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(what.addressExpression)
|
|
||||||
asmgen.out("""
|
|
||||||
inx
|
|
||||||
lda $ESTACK_LO_HEX,x
|
|
||||||
sta (+) + 1
|
|
||||||
lda $ESTACK_HI_HEX,x
|
|
||||||
sta (+) + 2
|
|
||||||
+ ror 0 ; modified
|
|
||||||
""") }
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when(what.register) {
|
|
||||||
Register.A -> asmgen.out(" ror a")
|
|
||||||
Register.X -> asmgen.out(" txa | ror a | tax")
|
|
||||||
Register.Y -> asmgen.out(" tya | ror a | tay")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" ror $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.ror_array_uw")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" ror $variable+1 | ror $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"ror2" -> {
|
|
||||||
// in-place
|
|
||||||
val what = fcall.args.single()
|
|
||||||
val dt = what.inferType(program)
|
|
||||||
when (dt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_ub")
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
if (what.addressExpression is NumericLiteralValue) {
|
|
||||||
val number = (what.addressExpression as NumericLiteralValue).number
|
|
||||||
asmgen.out(" lda ${number.toHex()} | lsr a | bcc + | ora #\$80 |+ | sta ${number.toHex()}")
|
|
||||||
} else {
|
|
||||||
asmgen.translateExpression(what.addressExpression)
|
|
||||||
asmgen.out(" jsr prog8_lib.ror2_mem_ub")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RegisterExpr -> {
|
|
||||||
when(what.register) {
|
|
||||||
Register.A -> asmgen.out(" lsr a | bcc + | ora #\$80 |+ ")
|
|
||||||
Register.X -> asmgen.out(" txa | lsr a | bcc + | ora #\$80 |+ tax ")
|
|
||||||
Register.Y -> asmgen.out(" tya | lsr a | bcc + | ora #\$80 |+ tay ")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lda $variable | lsr a | bcc + | ora #\$80 |+ | sta $variable")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when(what) {
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
asmgen.translateExpression(what.identifier)
|
|
||||||
asmgen.translateExpression(what.arrayspec.index)
|
|
||||||
asmgen.out(" jsr prog8_lib.ror2_array_uw")
|
|
||||||
}
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val variable = asmgen.asmIdentifierName(what)
|
|
||||||
asmgen.out(" lsr $variable+1 | ror $variable | bcc + | lda $variable+1 | ora #\$80 | sta $variable+1 |+ ")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"sort" -> {
|
|
||||||
val variable = fcall.args.single()
|
|
||||||
if(variable is IdentifierReference) {
|
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
|
||||||
val numElements = decl.arraysize!!.size()
|
|
||||||
when(decl.datatype) {
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$varName
|
|
||||||
ldy #>$varName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
|
||||||
lda #$numElements
|
|
||||||
sta ${C64Zeropage.SCRATCH_B1}
|
|
||||||
""")
|
|
||||||
asmgen.out(if(decl.datatype==DataType.ARRAY_UB) " jsr prog8_lib.sort_ub" else " jsr prog8_lib.sort_b")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$varName
|
|
||||||
ldy #>$varName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1+1}
|
|
||||||
lda #$numElements
|
|
||||||
sta ${C64Zeropage.SCRATCH_B1}
|
|
||||||
""")
|
|
||||||
asmgen.out(if(decl.datatype==DataType.ARRAY_UW) " jsr prog8_lib.sort_uw" else " jsr prog8_lib.sort_w")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> throw AssemblyError("sorting of floating point array is not supported")
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else
|
|
||||||
throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
"reverse" -> {
|
|
||||||
val variable = fcall.args.single()
|
|
||||||
if (variable is IdentifierReference) {
|
|
||||||
val decl = variable.targetVarDecl(program.namespace)!!
|
|
||||||
val varName = asmgen.asmIdentifierName(variable)
|
|
||||||
val numElements = decl.arraysize!!.size()
|
|
||||||
when (decl.datatype) {
|
|
||||||
DataType.ARRAY_UB, DataType.ARRAY_B -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$varName
|
|
||||||
ldy #>$varName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
|
||||||
lda #$numElements
|
|
||||||
jsr prog8_lib.reverse_b
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_UW, DataType.ARRAY_W -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$varName
|
|
||||||
ldy #>$varName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
|
||||||
lda #$numElements
|
|
||||||
jsr prog8_lib.reverse_w
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
DataType.ARRAY_F -> {
|
|
||||||
asmgen.out("""
|
|
||||||
lda #<$varName
|
|
||||||
ldy #>$varName
|
|
||||||
sta ${C64Zeropage.SCRATCH_W1}
|
|
||||||
sty ${C64Zeropage.SCRATCH_W1 + 1}
|
|
||||||
lda #$numElements
|
|
||||||
jsr prog8_lib.reverse_f
|
|
||||||
""")
|
|
||||||
}
|
|
||||||
else -> throw AssemblyError("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"rsave" -> {
|
|
||||||
// save cpu status flag and all registers A, X, Y.
|
|
||||||
// see http://6502.org/tutorials/register_preservation.html
|
|
||||||
asmgen.out(" php | sta ${C64Zeropage.SCRATCH_REG} | pha | txa | pha | tya | pha | lda ${C64Zeropage.SCRATCH_REG}")
|
|
||||||
}
|
|
||||||
"rrestore" -> {
|
|
||||||
// restore all registers and cpu status flag
|
|
||||||
asmgen.out(" pla | tay | pla | tax | pla | plp")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
translateFunctionArguments(fcall.args, func)
|
translateFunctionArguments(fcall.args, func)
|
||||||
asmgen.out(" jsr prog8_lib.func_$functionName")
|
asmgen.out(" jsr c64flt.func_$functionName")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun funcSgn(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
translateFunctionArguments(fcall.args, func)
|
||||||
|
val dt = fcall.args.single().inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.UBYTE -> asmgen.out(" jsr math.sign_ub")
|
||||||
|
DataType.BYTE -> asmgen.out(" jsr math.sign_b")
|
||||||
|
DataType.UWORD -> asmgen.out(" jsr math.sign_uw")
|
||||||
|
DataType.WORD -> asmgen.out(" jsr math.sign_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.sign_f")
|
||||||
|
else -> throw AssemblyError("weird type $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAnyAll(fcall: IFunctionCall, functionName: String) {
|
||||||
|
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
||||||
|
val dt = fcall.args.single().inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.ARRAY_B, DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
|
DataType.ARRAY_UW, DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
|
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
||||||
|
else -> throw AssemblyError("weird type $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMinMaxSum(fcall: IFunctionCall, functionName: String) {
|
||||||
|
outputPushAddressAndLenghtOfArray(fcall.args[0])
|
||||||
|
val dt = fcall.args.single().inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
DataType.ARRAY_UB, DataType.STR -> asmgen.out(" jsr prog8_lib.func_${functionName}_ub")
|
||||||
|
DataType.ARRAY_B -> asmgen.out(" jsr prog8_lib.func_${functionName}_b")
|
||||||
|
DataType.ARRAY_UW -> asmgen.out(" jsr prog8_lib.func_${functionName}_uw")
|
||||||
|
DataType.ARRAY_W -> asmgen.out(" jsr prog8_lib.func_${functionName}_w")
|
||||||
|
DataType.ARRAY_F -> asmgen.out(" jsr c64flt.func_${functionName}_f")
|
||||||
|
else -> throw AssemblyError("weird type $dt")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcStrlen(fcall: IFunctionCall) {
|
||||||
|
outputPushAddressOfIdentifier(fcall.args[0])
|
||||||
|
asmgen.out(" jsr prog8_lib.func_strlen")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcSwap(fcall: IFunctionCall) {
|
||||||
|
val first = fcall.args[0]
|
||||||
|
val second = fcall.args[1]
|
||||||
|
asmgen.translateExpression(first)
|
||||||
|
asmgen.translateExpression(second)
|
||||||
|
// pop in reverse order
|
||||||
|
val firstTarget = AssignTarget.fromExpr(first)
|
||||||
|
val secondTarget = AssignTarget.fromExpr(second)
|
||||||
|
asmgen.assignFromEvalResult(firstTarget)
|
||||||
|
asmgen.assignFromEvalResult(secondTarget)
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcAbs(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
translateFunctionArguments(fcall.args, func)
|
||||||
|
val dt = fcall.args.single().inferType(program)
|
||||||
|
when (dt.typeOrElse(DataType.STRUCT)) {
|
||||||
|
in ByteDatatypes -> asmgen.out(" jsr prog8_lib.abs_b")
|
||||||
|
in WordDatatypes -> asmgen.out(" jsr prog8_lib.abs_w")
|
||||||
|
DataType.FLOAT -> asmgen.out(" jsr c64flt.abs_f")
|
||||||
|
else -> throw AssemblyError("weird type")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMkword(fcall: IFunctionCall, func: FSignature) {
|
||||||
|
translateFunctionArguments(fcall.args, func)
|
||||||
|
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun funcMsb(fcall: IFunctionCall) {
|
||||||
|
val arg = fcall.args.single()
|
||||||
|
if (arg.inferType(program).typeOrElse(DataType.STRUCT) !in WordDatatypes)
|
||||||
|
throw AssemblyError("msb required word argument")
|
||||||
|
if (arg is NumericLiteralValue)
|
||||||
|
throw AssemblyError("should have been const-folded")
|
||||||
|
if (arg is IdentifierReference) {
|
||||||
|
val sourceName = asmgen.asmIdentifierName(arg)
|
||||||
|
asmgen.out(" lda $sourceName+1 | sta $ESTACK_LO_HEX,x | dex")
|
||||||
|
} else {
|
||||||
|
asmgen.translateExpression(arg)
|
||||||
|
asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | sta $ESTACK_LO_PLUS1_HEX,x")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -603,7 +635,7 @@ internal class BuiltinFunctionsAsmGen(private val program: Program, private val
|
|||||||
""")
|
""")
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun translateFunctionArguments(args: MutableList<Expression>, signature: FunctionSignature) {
|
private fun translateFunctionArguments(args: MutableList<Expression>, signature: FSignature) {
|
||||||
args.forEach {
|
args.forEach {
|
||||||
asmgen.translateExpression(it)
|
asmgen.translateExpression(it)
|
||||||
}
|
}
|
||||||
|
@ -3,13 +3,14 @@ package prog8.compiler.target.c64.codegen
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.compiler.toHex
|
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS2_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS2_HEX
|
||||||
|
import prog8.compiler.toHex
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
@ -27,7 +28,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
is RegisterExpr -> translateExpression(expression)
|
is RegisterExpr -> translateExpression(expression)
|
||||||
is IdentifierReference -> translateExpression(expression)
|
is IdentifierReference -> translateExpression(expression)
|
||||||
is FunctionCall -> translateExpression(expression)
|
is FunctionCall -> translateExpression(expression)
|
||||||
is ArrayLiteralValue, is StringLiteralValue -> TODO("string/array/struct assignment?")
|
is ArrayLiteralValue, is StringLiteralValue -> throw AssemblyError("no asm gen for string/array assignment")
|
||||||
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
is StructLiteralValue -> throw AssemblyError("struct literal value assignment should have been flattened")
|
||||||
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
is RangeExpr -> throw AssemblyError("range expression should have been changed into array values")
|
||||||
}
|
}
|
||||||
@ -126,8 +127,16 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
asmgen.out(" lda ${address.toHex()} | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out(" lda ${address.toHex()} | sta $ESTACK_LO_HEX,x | dex")
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
|
// the identifier is a pointer variable, so read the value from the address in it
|
||||||
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
val sourceName = asmgen.asmIdentifierName(expr.addressExpression as IdentifierReference)
|
||||||
asmgen.out(" lda $sourceName | sta $ESTACK_LO_HEX,x | dex")
|
asmgen.out("""
|
||||||
|
lda $sourceName
|
||||||
|
sta (+) +1
|
||||||
|
lda $sourceName+1
|
||||||
|
sta (+) +2
|
||||||
|
+ lda ${'$'}ffff ; modified
|
||||||
|
sta $ESTACK_LO_HEX,x
|
||||||
|
dex""")
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
translateExpression(expr.addressExpression)
|
translateExpression(expr.addressExpression)
|
||||||
@ -158,7 +167,7 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
private fun translateExpression(expr: RegisterExpr) {
|
private fun translateExpression(expr: RegisterExpr) {
|
||||||
when(expr.register) {
|
when(expr.register) {
|
||||||
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
Register.A -> asmgen.out(" sta $ESTACK_LO_HEX,x | dex")
|
||||||
Register.X -> throw AssemblyError("cannot push X - use a variable instead of the X register")
|
Register.X -> asmgen.out(" txa | sta $ESTACK_LO_HEX,x | dex")
|
||||||
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
Register.Y -> asmgen.out(" tya | sta $ESTACK_LO_HEX,x | dex")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -201,10 +210,36 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
when (leftDt) {
|
when (leftDt) {
|
||||||
DataType.UBYTE -> repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") }
|
DataType.UBYTE -> {
|
||||||
DataType.BYTE -> repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") }
|
if(amount<=2)
|
||||||
DataType.UWORD -> repeat(amount) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" lsr $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
DataType.WORD -> repeat(amount) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
else {
|
||||||
|
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
repeat(amount) { asmgen.out(" lsr a") }
|
||||||
|
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if(amount<=2)
|
||||||
|
repeat(amount) { asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | asl a | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
|
else {
|
||||||
|
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x | sta ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}")
|
||||||
|
repeat(amount) { asmgen.out(" asl a | ror ${C64MachineDefinition.C64Zeropage.SCRATCH_B1} | lda ${C64MachineDefinition.C64Zeropage.SCRATCH_B1}") }
|
||||||
|
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if(amount<=2)
|
||||||
|
repeat(amount) { asmgen.out(" lsr $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_right_uw_$amount") // 3-7 (8+ is done via other optimizations)
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if(amount<=2)
|
||||||
|
repeat(amount) { asmgen.out(" lda $ESTACK_HI_PLUS1_HEX,x | asl a | ror $ESTACK_HI_PLUS1_HEX,x | ror $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
|
else
|
||||||
|
asmgen.out(" jsr math.shift_right_w_$amount") // 3-7 (8+ is done via other optimizations)
|
||||||
|
}
|
||||||
else -> throw AssemblyError("weird type")
|
else -> throw AssemblyError("weird type")
|
||||||
}
|
}
|
||||||
return
|
return
|
||||||
@ -213,10 +248,22 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
// bit-shifts are always by a constant number (for now)
|
// bit-shifts are always by a constant number (for now)
|
||||||
translateExpression(expr.left)
|
translateExpression(expr.left)
|
||||||
val amount = expr.right.constValue(program)!!.number.toInt()
|
val amount = expr.right.constValue(program)!!.number.toInt()
|
||||||
if (leftDt in ByteDatatypes)
|
if (leftDt in ByteDatatypes) {
|
||||||
|
if(amount<=2)
|
||||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x") }
|
||||||
else
|
else {
|
||||||
|
asmgen.out(" lda $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
repeat(amount) { asmgen.out(" asl a") }
|
||||||
|
asmgen.out(" sta $ESTACK_LO_PLUS1_HEX,x")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
if(amount<=2) {
|
||||||
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
repeat(amount) { asmgen.out(" asl $ESTACK_LO_PLUS1_HEX,x | rol $ESTACK_HI_PLUS1_HEX,x") }
|
||||||
|
} else {
|
||||||
|
asmgen.out(" jsr math.shift_left_w_$amount") // 3-7 (8+ is done via other optimizations)
|
||||||
|
}
|
||||||
|
}
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
"*" -> {
|
"*" -> {
|
||||||
@ -224,8 +271,6 @@ internal class ExpressionsAsmGen(private val program: Program, private val asmge
|
|||||||
if(value!=null) {
|
if(value!=null) {
|
||||||
if(rightDt in IntegerDatatypes) {
|
if(rightDt in IntegerDatatypes) {
|
||||||
val amount = value.number.toInt()
|
val amount = value.number.toInt()
|
||||||
if(amount in powersOfTwo)
|
|
||||||
printWarning("${expr.right.position} multiplication by power of 2 should have been optimized into a left shift instruction: $amount")
|
|
||||||
when(rightDt) {
|
when(rightDt) {
|
||||||
DataType.UBYTE -> {
|
DataType.UBYTE -> {
|
||||||
if(amount in optimizedByteMultiplications) {
|
if(amount in optimizedByteMultiplications) {
|
||||||
|
@ -8,11 +8,11 @@ import prog8.ast.expressions.RangeExpr
|
|||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Assignment
|
import prog8.ast.statements.Assignment
|
||||||
import prog8.ast.statements.ForLoop
|
import prog8.ast.statements.ForLoop
|
||||||
|
import prog8.compiler.AssemblyError
|
||||||
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_PLUS1_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_PLUS1_HEX
|
|
||||||
import prog8.compiler.toHex
|
import prog8.compiler.toHex
|
||||||
import prog8.compiler.AssemblyError
|
|
||||||
import kotlin.math.absoluteValue
|
import kotlin.math.absoluteValue
|
||||||
|
|
||||||
// todo choose more efficient comparisons to avoid needless lda's
|
// todo choose more efficient comparisons to avoid needless lda's
|
||||||
|
@ -7,10 +7,10 @@ import prog8.ast.expressions.*
|
|||||||
import prog8.ast.statements.AssignTarget
|
import prog8.ast.statements.AssignTarget
|
||||||
import prog8.ast.statements.Subroutine
|
import prog8.ast.statements.Subroutine
|
||||||
import prog8.ast.statements.SubroutineParameter
|
import prog8.ast.statements.SubroutineParameter
|
||||||
import prog8.compiler.toHex
|
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_HI_HEX
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
import prog8.compiler.target.c64.C64MachineDefinition.ESTACK_LO_HEX
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class FunctionCallAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
|
@ -6,9 +6,9 @@ import prog8.ast.expressions.IdentifierReference
|
|||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.RegisterExpr
|
import prog8.ast.expressions.RegisterExpr
|
||||||
import prog8.ast.statements.PostIncrDecr
|
import prog8.ast.statements.PostIncrDecr
|
||||||
import prog8.compiler.toHex
|
|
||||||
import prog8.compiler.AssemblyError
|
import prog8.compiler.AssemblyError
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
import prog8.compiler.target.c64.C64MachineDefinition.C64Zeropage
|
||||||
|
import prog8.compiler.toHex
|
||||||
|
|
||||||
|
|
||||||
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
internal class PostIncrDecrAsmGen(private val program: Program, private val asmgen: AsmGen) {
|
||||||
@ -66,7 +66,11 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
val what = asmgen.asmIdentifierName(addressExpr)
|
val what = asmgen.asmIdentifierName(addressExpr)
|
||||||
asmgen.out(if(incr) " inc $what" else " dec $what")
|
asmgen.out(" lda $what | sta (+) +1 | lda $what+1 | sta (+) +2")
|
||||||
|
if(incr)
|
||||||
|
asmgen.out("+\tinc ${'$'}ffff\t; modified")
|
||||||
|
else
|
||||||
|
asmgen.out("+\tdec ${'$'}ffff\t; modified")
|
||||||
}
|
}
|
||||||
else -> throw AssemblyError("weird target type $targetMemory")
|
else -> throw AssemblyError("weird target type $targetMemory")
|
||||||
}
|
}
|
||||||
@ -100,17 +104,14 @@ internal class PostIncrDecrAsmGen(private val program: Program, private val asmg
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
is RegisterExpr -> {
|
is RegisterExpr -> {
|
||||||
// TODO optimize common cases
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
}
|
}
|
||||||
is IdentifierReference -> {
|
is IdentifierReference -> {
|
||||||
// TODO optimize common cases
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
}
|
}
|
||||||
else -> {
|
else -> {
|
||||||
// TODO optimize common cases
|
|
||||||
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
asmgen.translateArrayIndexIntoA(targetArrayIdx)
|
||||||
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
incrDecrArrayvalueWithIndexA(incr, arrayDt, what)
|
||||||
}
|
}
|
||||||
|
@ -7,88 +7,87 @@ import prog8.compiler.CompilerException
|
|||||||
import kotlin.math.*
|
import kotlin.math.*
|
||||||
|
|
||||||
|
|
||||||
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
|
class FParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||||
|
|
||||||
|
|
||||||
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
||||||
|
|
||||||
|
|
||||||
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
class FSignature(val pure: Boolean, // does it have side effects?
|
||||||
val parameters: List<BuiltinFunctionParam>,
|
val parameters: List<FParam>,
|
||||||
val returntype: DataType?,
|
val returntype: DataType?,
|
||||||
val constExpressionFunc: ConstExpressionCaller? = null)
|
val constExpressionFunc: ConstExpressionCaller? = null)
|
||||||
|
|
||||||
|
|
||||||
val BuiltinFunctions = mapOf(
|
val BuiltinFunctions = mapOf(
|
||||||
// this set of function have no return value and operate in-place:
|
// this set of function have no return value and operate in-place:
|
||||||
"rol" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"rol" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"ror" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"rol2" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"rol2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"ror2" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
"ror2" to FSignature(false, listOf(FParam("item", setOf(DataType.UBYTE, DataType.UWORD))), null),
|
||||||
"lsl" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
"lsl" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
||||||
"lsr" to FunctionSignature(false, listOf(BuiltinFunctionParam("item", IntegerDatatypes)), null),
|
"lsr" to FSignature(false, listOf(FParam("item", IntegerDatatypes)), null),
|
||||||
"sort" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null),
|
"sort" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
"reverse" to FunctionSignature(false, listOf(BuiltinFunctionParam("array", ArrayDatatypes)), null),
|
"reverse" to FSignature(false, listOf(FParam("array", ArrayDatatypes)), null),
|
||||||
// these few have a return value depending on the argument(s):
|
// these few have a return value depending on the argument(s):
|
||||||
"max" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
"max" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMax) }, // type depends on args
|
||||||
"min" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
"min" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinMin) }, // type depends on args
|
||||||
"sum" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
"sum" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), null) { a, p, prg -> collectionArg(a, p, prg, ::builtinSum) }, // type depends on args
|
||||||
"abs" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
"abs" to FSignature(true, listOf(FParam("value", NumericDatatypes)), null, ::builtinAbs), // type depends on argument
|
||||||
"len" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
"len" to FSignature(true, listOf(FParam("values", IterableDatatypes)), null, ::builtinLen), // type is UBYTE or UWORD depending on actual length
|
||||||
// normal functions follow:
|
// normal functions follow:
|
||||||
"sgn" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
"sgn" to FSignature(true, listOf(FParam("value", NumericDatatypes)), DataType.BYTE, ::builtinSgn ),
|
||||||
"sin" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
"sin" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sin) },
|
||||||
"sin8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
"sin8" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinSin8 ),
|
||||||
"sin8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
"sin8u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinSin8u ),
|
||||||
"sin16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
"sin16" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinSin16 ),
|
||||||
"sin16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
"sin16u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinSin16u ),
|
||||||
"cos" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
"cos" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::cos) },
|
||||||
"cos8" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
"cos8" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.BYTE, ::builtinCos8 ),
|
||||||
"cos8u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
"cos8u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UBYTE, ::builtinCos8u ),
|
||||||
"cos16" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
"cos16" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.WORD, ::builtinCos16 ),
|
||||||
"cos16u" to FunctionSignature(true, listOf(BuiltinFunctionParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
"cos16u" to FSignature(true, listOf(FParam("angle8", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinCos16u ),
|
||||||
"tan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
"tan" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::tan) },
|
||||||
"atan" to FunctionSignature(true, listOf(BuiltinFunctionParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
"atan" to FSignature(true, listOf(FParam("rads", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::atan) },
|
||||||
"ln" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
"ln" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::log) },
|
||||||
"log2" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
"log2" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, ::log2) },
|
||||||
"sqrt16" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
"sqrt16" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { sqrt(it.toDouble()).toInt() } },
|
||||||
"sqrt" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
"sqrt" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::sqrt) },
|
||||||
"rad" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
"rad" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toRadians) },
|
||||||
"deg" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
"deg" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArg(a, p, prg, Math::toDegrees) },
|
||||||
"round" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
"round" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::round) },
|
||||||
"floor" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
"floor" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::floor) },
|
||||||
"ceil" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
"ceil" to FSignature(true, listOf(FParam("value", setOf(DataType.FLOAT))), DataType.FLOAT) { a, p, prg -> oneDoubleArgOutputWord(a, p, prg, Math::ceil) },
|
||||||
"any" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
"any" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAny) },
|
||||||
"all" to FunctionSignature(true, listOf(BuiltinFunctionParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
"all" to FSignature(true, listOf(FParam("values", ArrayDatatypes)), DataType.UBYTE) { a, p, prg -> collectionArg(a, p, prg, ::builtinAll) },
|
||||||
"lsb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
"lsb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x and 255 }},
|
||||||
"msb" to FunctionSignature(true, listOf(BuiltinFunctionParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
"msb" to FSignature(true, listOf(FParam("value", setOf(DataType.UWORD, DataType.WORD))), DataType.UBYTE) { a, p, prg -> oneIntArgOutputInt(a, p, prg) { x: Int -> x ushr 8 and 255}},
|
||||||
"mkword" to FunctionSignature(true, listOf(
|
"mkword" to FSignature(true, listOf(FParam("lsb", setOf(DataType.UBYTE)), FParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
||||||
BuiltinFunctionParam("lsb", setOf(DataType.UBYTE)),
|
"rnd" to FSignature(true, emptyList(), DataType.UBYTE),
|
||||||
BuiltinFunctionParam("msb", setOf(DataType.UBYTE))), DataType.UWORD, ::builtinMkword),
|
"rndw" to FSignature(true, emptyList(), DataType.UWORD),
|
||||||
"rnd" to FunctionSignature(true, emptyList(), DataType.UBYTE),
|
"rndf" to FSignature(true, emptyList(), DataType.FLOAT),
|
||||||
"rndw" to FunctionSignature(true, emptyList(), DataType.UWORD),
|
"exit" to FSignature(false, listOf(FParam("returnvalue", setOf(DataType.UBYTE))), null),
|
||||||
"rndf" to FunctionSignature(true, emptyList(), DataType.FLOAT),
|
"rsave" to FSignature(false, emptyList(), null),
|
||||||
"rsave" to FunctionSignature(false, emptyList(), null),
|
"rrestore" to FSignature(false, emptyList(), null),
|
||||||
"rrestore" to FunctionSignature(false, emptyList(), null),
|
"set_carry" to FSignature(false, emptyList(), null),
|
||||||
"set_carry" to FunctionSignature(false, emptyList(), null),
|
"clear_carry" to FSignature(false, emptyList(), null),
|
||||||
"clear_carry" to FunctionSignature(false, emptyList(), null),
|
"set_irqd" to FSignature(false, emptyList(), null),
|
||||||
"set_irqd" to FunctionSignature(false, emptyList(), null),
|
"clear_irqd" to FSignature(false, emptyList(), null),
|
||||||
"clear_irqd" to FunctionSignature(false, emptyList(), null),
|
"read_flags" to FSignature(false, emptyList(), DataType.UBYTE),
|
||||||
"read_flags" to FunctionSignature(false, emptyList(), DataType.UBYTE),
|
"swap" to FSignature(false, listOf(FParam("first", NumericDatatypes), FParam("second", NumericDatatypes)), null),
|
||||||
"swap" to FunctionSignature(false, listOf(BuiltinFunctionParam("first", NumericDatatypes), BuiltinFunctionParam("second", NumericDatatypes)), null),
|
"memcopy" to FSignature(false, listOf(
|
||||||
"memcopy" to FunctionSignature(false, listOf(
|
FParam("from", IterableDatatypes + DataType.UWORD),
|
||||||
BuiltinFunctionParam("from", IterableDatatypes + DataType.UWORD),
|
FParam("to", IterableDatatypes + DataType.UWORD),
|
||||||
BuiltinFunctionParam("to", IterableDatatypes + DataType.UWORD),
|
FParam("numbytes", setOf(DataType.UBYTE))), null),
|
||||||
BuiltinFunctionParam("numbytes", setOf(DataType.UBYTE))), null),
|
"memset" to FSignature(false, listOf(
|
||||||
"memset" to FunctionSignature(false, listOf(
|
FParam("address", IterableDatatypes + DataType.UWORD),
|
||||||
BuiltinFunctionParam("address", IterableDatatypes + DataType.UWORD),
|
FParam("numbytes", setOf(DataType.UWORD)),
|
||||||
BuiltinFunctionParam("numbytes", setOf(DataType.UWORD)),
|
FParam("bytevalue", ByteDatatypes)), null),
|
||||||
BuiltinFunctionParam("bytevalue", ByteDatatypes)), null),
|
"memsetw" to FSignature(false, listOf(
|
||||||
"memsetw" to FunctionSignature(false, listOf(
|
FParam("address", IterableDatatypes + DataType.UWORD),
|
||||||
BuiltinFunctionParam("address", IterableDatatypes + DataType.UWORD),
|
FParam("numwords", setOf(DataType.UWORD)),
|
||||||
BuiltinFunctionParam("numwords", setOf(DataType.UWORD)),
|
FParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
||||||
BuiltinFunctionParam("wordvalue", setOf(DataType.UWORD, DataType.WORD))), null),
|
"strlen" to FSignature(true, listOf(FParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen)
|
||||||
"strlen" to FunctionSignature(true, listOf(BuiltinFunctionParam("string", setOf(DataType.STR))), DataType.UBYTE, ::builtinStrlen)
|
|
||||||
)
|
)
|
||||||
|
|
||||||
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
fun builtinMax(array: List<Number>): Number = array.maxBy { it.toDouble() }!!
|
||||||
|
@ -6,8 +6,6 @@ import prog8.ast.Node
|
|||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
import prog8.ast.base.ParentSentinel
|
import prog8.ast.base.ParentSentinel
|
||||||
import prog8.ast.base.VarDeclType
|
|
||||||
import prog8.ast.base.initvarsSubName
|
|
||||||
import prog8.ast.expressions.FunctionCall
|
import prog8.ast.expressions.FunctionCall
|
||||||
import prog8.ast.expressions.IdentifierReference
|
import prog8.ast.expressions.IdentifierReference
|
||||||
import prog8.ast.processing.IAstVisitor
|
import prog8.ast.processing.IAstVisitor
|
||||||
@ -120,7 +118,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
|
|
||||||
override fun visit(subroutine: Subroutine) {
|
override fun visit(subroutine: Subroutine) {
|
||||||
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
if (Pair(subroutine.definingScope().name, subroutine.name) in alwaysKeepSubroutines
|
||||||
|| subroutine.name == initvarsSubName || subroutine.definingModule().isLibraryModule) {
|
|| subroutine.definingModule().isLibraryModule) {
|
||||||
// make sure the entrypoint is mentioned in the used symbols
|
// make sure the entrypoint is mentioned in the used symbols
|
||||||
addNodeAndParentScopes(subroutine)
|
addNodeAndParentScopes(subroutine)
|
||||||
}
|
}
|
||||||
@ -128,7 +126,7 @@ class CallGraph(private val program: Program) : IAstVisitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(decl: VarDecl) {
|
override fun visit(decl: VarDecl) {
|
||||||
if (decl.autogeneratedDontRemove || (decl.definingModule().isLibraryModule && decl.type != VarDeclType.VAR)) {
|
if (decl.autogeneratedDontRemove || decl.definingModule().isLibraryModule) {
|
||||||
// make sure autogenerated vardecls are in the used symbols
|
// make sure autogenerated vardecls are in the used symbols
|
||||||
addNodeAndParentScopes(decl)
|
addNodeAndParentScopes(decl)
|
||||||
}
|
}
|
||||||
|
@ -8,27 +8,16 @@ import prog8.ast.processing.IAstModifyingVisitor
|
|||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
import kotlin.math.floor
|
|
||||||
|
|
||||||
|
|
||||||
class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
internal class ConstantFoldingOptimizer(private val program: Program, private val errors: ErrorReporter) : IAstModifyingVisitor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
var errors : MutableList<AstException> = mutableListOf()
|
|
||||||
private val reportedErrorMessages = mutableSetOf<String>()
|
|
||||||
|
|
||||||
fun addError(x: AstException) {
|
|
||||||
// check that we don't add the isSameAs error more than once
|
|
||||||
if(x.toString() !in reportedErrorMessages) {
|
|
||||||
reportedErrorMessages.add(x.toString())
|
|
||||||
errors.add(x)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
override fun visit(decl: VarDecl): Statement {
|
||||||
// the initializer value can't refer to the variable itself (recursive definition)
|
// the initializer value can't refer to the variable itself (recursive definition)
|
||||||
// TODO: use call tree for this?
|
// TODO: use call tree for this?
|
||||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||||
errors.add(ExpressionError("recursive var declaration", decl.position))
|
errors.err("recursive var declaration", decl.position)
|
||||||
return decl
|
return decl
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -69,7 +58,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
// convert the initializer range expression to an actual array
|
// convert the initializer range expression to an actual array
|
||||||
val declArraySize = decl.arraysize?.size()
|
val declArraySize = decl.arraysize?.size()
|
||||||
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
if(declArraySize!=null && declArraySize!=rangeExpr.size())
|
||||||
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
|
errors.err("range expression size doesn't match declared array size", decl.value?.position!!)
|
||||||
val constRange = rangeExpr.toConstantIntegerRange()
|
val constRange = rangeExpr.toConstantIntegerRange()
|
||||||
if(constRange!=null) {
|
if(constRange!=null) {
|
||||||
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
val eltType = rangeExpr.inferType(program).typeOrElse(DataType.UBYTE)
|
||||||
@ -88,7 +77,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
if(numericLv!=null && numericLv.type== DataType.FLOAT)
|
||||||
errors.add(ExpressionError("arraysize requires only integers here", numericLv.position))
|
errors.err("arraysize requires only integers here", numericLv.position)
|
||||||
val size = decl.arraysize?.size() ?: return decl
|
val size = decl.arraysize?.size() ?: return decl
|
||||||
if (rangeExpr==null && numericLv!=null) {
|
if (rangeExpr==null && numericLv!=null) {
|
||||||
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
// arraysize initializer is empty or a single int, and we know the size; create the arraysize.
|
||||||
@ -96,24 +85,24 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
when(decl.datatype){
|
when(decl.datatype){
|
||||||
DataType.ARRAY_UB -> {
|
DataType.ARRAY_UB -> {
|
||||||
if(fillvalue !in 0..255)
|
if(fillvalue !in 0..255)
|
||||||
errors.add(ExpressionError("ubyte value overflow", numericLv.position))
|
errors.err("ubyte value overflow", numericLv.position)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_B -> {
|
DataType.ARRAY_B -> {
|
||||||
if(fillvalue !in -128..127)
|
if(fillvalue !in -128..127)
|
||||||
errors.add(ExpressionError("byte value overflow", numericLv.position))
|
errors.err("byte value overflow", numericLv.position)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_UW -> {
|
DataType.ARRAY_UW -> {
|
||||||
if(fillvalue !in 0..65535)
|
if(fillvalue !in 0..65535)
|
||||||
errors.add(ExpressionError("uword value overflow", numericLv.position))
|
errors.err("uword value overflow", numericLv.position)
|
||||||
}
|
}
|
||||||
DataType.ARRAY_W -> {
|
DataType.ARRAY_W -> {
|
||||||
if(fillvalue !in -32768..32767)
|
if(fillvalue !in -32768..32767)
|
||||||
errors.add(ExpressionError("word value overflow", numericLv.position))
|
errors.err("word value overflow", numericLv.position)
|
||||||
}
|
}
|
||||||
else -> {}
|
else -> {}
|
||||||
}
|
}
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(ArrayElementTypes.getValue(decl.datatype), it, numericLv.position) as Expression}.toTypedArray()
|
||||||
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
val refValue = ArrayLiteralValue(InferredTypes.InferredType.known(decl.datatype), array, position = numericLv.position)
|
||||||
decl.value = refValue
|
decl.value = refValue
|
||||||
refValue.parent=decl
|
refValue.parent=decl
|
||||||
@ -131,7 +120,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
// arraysize initializer is a single int, and we know the size.
|
// arraysize initializer is a single int, and we know the size.
|
||||||
val fillvalue = litval.number.toDouble()
|
val fillvalue = litval.number.toDouble()
|
||||||
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
if (fillvalue < CompilationTarget.machine.FLOAT_MAX_NEGATIVE || fillvalue > CompilationTarget.machine.FLOAT_MAX_POSITIVE)
|
||||||
errors.add(ExpressionError("float value overflow", litval.position))
|
errors.err("float value overflow", litval.position)
|
||||||
else {
|
else {
|
||||||
// create the array itself, filled with the fillvalue.
|
// create the array itself, filled with the fillvalue.
|
||||||
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
|
||||||
@ -150,6 +139,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
val declValue = decl.value
|
||||||
|
if(declValue!=null && decl.type==VarDeclType.VAR
|
||||||
|
&& declValue is NumericLiteralValue && !declValue.inferType(program).istype(decl.datatype)) {
|
||||||
|
// cast the numeric literal to the appropriate datatype of the variable
|
||||||
|
decl.value = declValue.cast(decl.datatype)
|
||||||
|
}
|
||||||
|
|
||||||
return super.visit(decl)
|
return super.visit(decl)
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -166,7 +162,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
if(forloop!=null && identifier===forloop.loopVar)
|
if(forloop!=null && identifier===forloop.loopVar)
|
||||||
return identifier
|
return identifier
|
||||||
|
|
||||||
return try {
|
|
||||||
val cval = identifier.constValue(program) ?: return identifier
|
val cval = identifier.constValue(program) ?: return identifier
|
||||||
return when (cval.type) {
|
return when (cval.type) {
|
||||||
in NumericDatatypes -> {
|
in NumericDatatypes -> {
|
||||||
@ -177,21 +172,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
in PassByReferenceDatatypes -> throw FatalAstException("pass-by-reference type should not be considered a constant")
|
||||||
else -> identifier
|
else -> identifier
|
||||||
}
|
}
|
||||||
} catch (ax: AstException) {
|
|
||||||
addError(ax)
|
|
||||||
identifier
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCall: FunctionCall): Expression {
|
override fun visit(functionCall: FunctionCall): Expression {
|
||||||
super.visit(functionCall)
|
super.visit(functionCall)
|
||||||
typeCastConstArguments(functionCall)
|
typeCastConstArguments(functionCall)
|
||||||
return try {
|
return functionCall.constValue(program) ?: functionCall
|
||||||
functionCall.constValue(program) ?: functionCall
|
|
||||||
} catch (ax: AstException) {
|
|
||||||
addError(ax)
|
|
||||||
functionCall
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||||
@ -247,7 +233,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: PrefixExpression): Expression {
|
override fun visit(expr: PrefixExpression): Expression {
|
||||||
return try {
|
|
||||||
val prefixExpr=super.visit(expr)
|
val prefixExpr=super.visit(expr)
|
||||||
if(prefixExpr !is PrefixExpression)
|
if(prefixExpr !is PrefixExpression)
|
||||||
return prefixExpr
|
return prefixExpr
|
||||||
@ -283,10 +268,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
return prefixExpr
|
return prefixExpr
|
||||||
} catch (ax: AstException) {
|
|
||||||
addError(ax)
|
|
||||||
expr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -307,7 +288,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
* (X + c1) - c2 -> X + (c1-c2)
|
* (X + c1) - c2 -> X + (c1-c2)
|
||||||
*/
|
*/
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
override fun visit(expr: BinaryExpression): Expression {
|
||||||
return try {
|
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
|
|
||||||
if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|
if(expr.left is StringLiteralValue || expr.left is ArrayLiteralValue
|
||||||
@ -343,10 +323,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
|
|
||||||
else -> expr
|
else -> expr
|
||||||
}
|
}
|
||||||
} catch (ax: AstException) {
|
|
||||||
addError(ax)
|
|
||||||
expr
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun groupTwoConstsTogether(expr: BinaryExpression,
|
private fun groupTwoConstsTogether(expr: BinaryExpression,
|
||||||
@ -643,72 +619,4 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
|||||||
}
|
}
|
||||||
return array
|
return array
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
super.visit(assignment)
|
|
||||||
val lv = assignment.value as? NumericLiteralValue
|
|
||||||
if(lv!=null) {
|
|
||||||
// see if we can promote/convert a literal value to the required datatype
|
|
||||||
val idt = assignment.target.inferType(program, assignment)
|
|
||||||
if(!idt.isKnown)
|
|
||||||
return assignment
|
|
||||||
when(idt.typeOrElse(DataType.STRUCT)) {
|
|
||||||
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 = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.BYTE && lv.number.toInt()>=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.WORD && lv.number.toInt()>=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=0 && d<=65535)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UWORD, floor(d).toInt(), 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.number.toInt() <= 255)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.BYTE && lv.number.toInt() >=0)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d >=0 && d<=255)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.UBYTE, floor(d).toShort(), 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.number.toInt() <= 127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.UBYTE && lv.number.toInt() <= 127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, lv.number.toShort(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=0 && d<=127)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), 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 = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.UWORD && lv.number.toInt() <= 32767)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.WORD, lv.number.toInt(), lv.position)
|
|
||||||
else if(lv.type== DataType.FLOAT) {
|
|
||||||
val d = lv.number.toDouble()
|
|
||||||
if(floor(d)==d && d>=-32768 && d<=32767)
|
|
||||||
assignment.value = NumericLiteralValue(DataType.BYTE, floor(d).toShort(), lv.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
assignment.value = NumericLiteralValue(DataType.FLOAT, lv.number.toDouble(), lv.position)
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return assignment
|
|
||||||
}
|
|
||||||
}
|
}
|
617
compiler/src/prog8/optimizer/ExpressionSimplifier.kt
Normal file
617
compiler/src/prog8/optimizer/ExpressionSimplifier.kt
Normal file
@ -0,0 +1,617 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.base.*
|
||||||
|
import prog8.ast.expressions.*
|
||||||
|
import prog8.ast.processing.AstWalker
|
||||||
|
import prog8.ast.processing.IAstModification
|
||||||
|
import prog8.ast.statements.Assignment
|
||||||
|
import kotlin.math.abs
|
||||||
|
import kotlin.math.log2
|
||||||
|
import kotlin.math.pow
|
||||||
|
|
||||||
|
/*
|
||||||
|
todo add more expression optimizations
|
||||||
|
|
||||||
|
x + x -> x << 1 (for words... for bytes too?)
|
||||||
|
x + x + x + x -> x << 2 (for words... for bytes too?)
|
||||||
|
x + x + x -> ???? x*3 ??? words/bytes?
|
||||||
|
x - x -> 0
|
||||||
|
|
||||||
|
|
||||||
|
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
internal class ExpressionSimplifier(private val program: Program) : AstWalker() {
|
||||||
|
private val powersOfTwo = (1..16).map { (2.0).pow(it) }.toSet()
|
||||||
|
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
||||||
|
|
||||||
|
override fun after(assignment: Assignment, parent: Node): Iterable<IAstModification> {
|
||||||
|
if (assignment.aug_op != null)
|
||||||
|
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(typecast: TypecastExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val mods = mutableListOf<IAstModification>()
|
||||||
|
|
||||||
|
// try to statically convert a literal value into one of the desired type
|
||||||
|
val literal = typecast.expression as? NumericLiteralValue
|
||||||
|
if (literal != null) {
|
||||||
|
val newLiteral = literal.cast(typecast.type)
|
||||||
|
if (newLiteral !== literal)
|
||||||
|
mods += IAstModification.ReplaceNode(typecast.expression, newLiteral, typecast)
|
||||||
|
}
|
||||||
|
|
||||||
|
// remove redundant nested typecasts:
|
||||||
|
// if the typecast casts a value to the same type, remove the cast.
|
||||||
|
// if the typecast contains another typecast, remove the inner typecast.
|
||||||
|
val subTypecast = typecast.expression as? TypecastExpression
|
||||||
|
if (subTypecast != null) {
|
||||||
|
mods += IAstModification.ReplaceNode(typecast.expression, subTypecast.expression, typecast)
|
||||||
|
} else {
|
||||||
|
if (typecast.expression.inferType(program).istype(typecast.type))
|
||||||
|
mods += IAstModification.ReplaceNode(typecast, typecast.expression, parent)
|
||||||
|
}
|
||||||
|
|
||||||
|
return mods
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun before(expr: PrefixExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if (expr.operator == "+") {
|
||||||
|
// +X --> X
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, expr.expression, parent))
|
||||||
|
} else if (expr.operator == "not") {
|
||||||
|
when(expr.expression) {
|
||||||
|
is PrefixExpression -> {
|
||||||
|
// NOT(NOT(...)) -> ...
|
||||||
|
val pe = expr.expression as PrefixExpression
|
||||||
|
if(pe.operator == "not")
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, pe.expression, parent))
|
||||||
|
}
|
||||||
|
is BinaryExpression -> {
|
||||||
|
// NOT (xxxx) -> invert the xxxx
|
||||||
|
val be = expr.expression as BinaryExpression
|
||||||
|
val newExpr = when (be.operator) {
|
||||||
|
"<" -> BinaryExpression(be.left, ">=", be.right, be.position)
|
||||||
|
">" -> BinaryExpression(be.left, "<=", be.right, be.position)
|
||||||
|
"<=" -> BinaryExpression(be.left, ">", be.right, be.position)
|
||||||
|
">=" -> BinaryExpression(be.left, "<", be.right, be.position)
|
||||||
|
"==" -> BinaryExpression(be.left, "!=", be.right, be.position)
|
||||||
|
"!=" -> BinaryExpression(be.left, "==", be.right, be.position)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (newExpr != null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
}
|
||||||
|
else -> return emptyList()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
val leftVal = expr.left.constValue(program)
|
||||||
|
val rightVal = expr.right.constValue(program)
|
||||||
|
|
||||||
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
val rightIDt = expr.right.inferType(program)
|
||||||
|
if (!leftIDt.isKnown || !rightIDt.isKnown)
|
||||||
|
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
||||||
|
|
||||||
|
|
||||||
|
// ConstValue <associativeoperator> X --> X <associativeoperator> ConstValue
|
||||||
|
if (leftVal != null && expr.operator in associativeOperators && rightVal == null)
|
||||||
|
return listOf(IAstModification.SwapOperands(expr))
|
||||||
|
|
||||||
|
// X + (-A) --> X - A
|
||||||
|
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
BinaryExpression(expr.left, "-", (expr.right as PrefixExpression).expression, expr.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// (-A) + X --> X - A
|
||||||
|
if (expr.operator == "+" && (expr.left as? PrefixExpression)?.operator == "-") {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
BinaryExpression(expr.right, "-", (expr.left as PrefixExpression).expression, expr.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
// X - (-A) --> X + A
|
||||||
|
if (expr.operator == "-" && (expr.right as? PrefixExpression)?.operator == "-") {
|
||||||
|
return listOf(IAstModification.ReplaceNode(
|
||||||
|
expr,
|
||||||
|
BinaryExpression(expr.left, "+", (expr.right as PrefixExpression).expression, expr.position),
|
||||||
|
parent
|
||||||
|
))
|
||||||
|
}
|
||||||
|
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
|
||||||
|
if (expr.operator == "+" || expr.operator == "-"
|
||||||
|
&& leftVal == null && rightVal == null
|
||||||
|
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
||||||
|
val leftBinExpr = expr.left as? BinaryExpression
|
||||||
|
val rightBinExpr = expr.right as? BinaryExpression
|
||||||
|
if (leftBinExpr?.operator == "*") {
|
||||||
|
if (expr.operator == "+") {
|
||||||
|
// Y*X + X -> X*(Y + 1)
|
||||||
|
// X*Y + X -> X*(Y + 1)
|
||||||
|
val x = expr.right
|
||||||
|
val y = determineY(x, leftBinExpr)
|
||||||
|
if (y != null) {
|
||||||
|
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||||
|
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// Y*X - X -> X*(Y - 1)
|
||||||
|
// X*Y - X -> X*(Y - 1)
|
||||||
|
val x = expr.right
|
||||||
|
val y = determineY(x, leftBinExpr)
|
||||||
|
if (y != null) {
|
||||||
|
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
||||||
|
val newExpr = BinaryExpression(x, "*", yMinus1, x.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else if (rightBinExpr?.operator == "*") {
|
||||||
|
if (expr.operator == "+") {
|
||||||
|
// X + Y*X -> X*(Y + 1)
|
||||||
|
// X + X*Y -> X*(Y + 1)
|
||||||
|
val x = expr.left
|
||||||
|
val y = determineY(x, rightBinExpr)
|
||||||
|
if (y != null) {
|
||||||
|
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
|
||||||
|
val newExpr = BinaryExpression(x, "*", yPlus1, x.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// simplify when a term is constant and directly determines the outcome
|
||||||
|
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
||||||
|
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
||||||
|
val newExpr: Expression? = when (expr.operator) {
|
||||||
|
"or" -> {
|
||||||
|
if ((leftVal != null && leftVal.asBooleanValue) || (rightVal != null && rightVal.asBooleanValue))
|
||||||
|
constTrue
|
||||||
|
else if (leftVal != null && !leftVal.asBooleanValue)
|
||||||
|
expr.right
|
||||||
|
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||||
|
expr.left
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"and" -> {
|
||||||
|
if ((leftVal != null && !leftVal.asBooleanValue) || (rightVal != null && !rightVal.asBooleanValue))
|
||||||
|
constFalse
|
||||||
|
else if (leftVal != null && leftVal.asBooleanValue)
|
||||||
|
expr.right
|
||||||
|
else if (rightVal != null && rightVal.asBooleanValue)
|
||||||
|
expr.left
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"xor" -> {
|
||||||
|
if (leftVal != null && !leftVal.asBooleanValue)
|
||||||
|
expr.right
|
||||||
|
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||||
|
expr.left
|
||||||
|
else if (leftVal != null && leftVal.asBooleanValue)
|
||||||
|
PrefixExpression("not", expr.right, expr.right.position)
|
||||||
|
else if (rightVal != null && rightVal.asBooleanValue)
|
||||||
|
PrefixExpression("not", expr.left, expr.left.position)
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"|", "^" -> {
|
||||||
|
if (leftVal != null && !leftVal.asBooleanValue)
|
||||||
|
expr.right
|
||||||
|
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||||
|
expr.left
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"&" -> {
|
||||||
|
if (leftVal != null && !leftVal.asBooleanValue)
|
||||||
|
constFalse
|
||||||
|
else if (rightVal != null && !rightVal.asBooleanValue)
|
||||||
|
constFalse
|
||||||
|
else
|
||||||
|
null
|
||||||
|
}
|
||||||
|
"*" -> optimizeMultiplication(expr, leftVal, rightVal)
|
||||||
|
"/" -> optimizeDivision(expr, leftVal, rightVal)
|
||||||
|
"+" -> optimizeAdd(expr, leftVal, rightVal)
|
||||||
|
"-" -> optimizeSub(expr, leftVal, rightVal)
|
||||||
|
"**" -> optimizePower(expr, leftVal, rightVal)
|
||||||
|
"%" -> optimizeRemainder(expr, leftVal, rightVal)
|
||||||
|
">>" -> optimizeShiftRight(expr, rightVal)
|
||||||
|
"<<" -> optimizeShiftLeft(expr, rightVal)
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
|
||||||
|
if(newExpr != null)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, newExpr, parent))
|
||||||
|
|
||||||
|
return emptyList()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||||
|
return when {
|
||||||
|
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||||
|
subBinExpr.right isSameAs x -> subBinExpr.left
|
||||||
|
else -> null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeAdd(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if(expr.left.isSameAs(expr.right)) {
|
||||||
|
// optimize X+X into X *2
|
||||||
|
expr.operator = "*"
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(2, expr.right.position)
|
||||||
|
expr.right.linkParents(expr)
|
||||||
|
return expr
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||||
|
if (rightVal2 != null) {
|
||||||
|
// right value is a constant, see if we can optimize
|
||||||
|
val rightConst: NumericLiteralValue = rightVal2
|
||||||
|
when (rightConst.number.toDouble()) {
|
||||||
|
0.0 -> {
|
||||||
|
// left
|
||||||
|
return expr2.left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no need to check for left val constant (because of associativity)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if(expr.left.isSameAs(expr.right)) {
|
||||||
|
// optimize X-X into 0
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (rightVal != null) {
|
||||||
|
// right value is a constant, see if we can optimize
|
||||||
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
|
when (rightConst.number.toDouble()) {
|
||||||
|
0.0 -> {
|
||||||
|
// left
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftVal != null) {
|
||||||
|
// left value is a constant, see if we can optimize
|
||||||
|
when (leftVal.number.toDouble()) {
|
||||||
|
0.0 -> {
|
||||||
|
// -right
|
||||||
|
return PrefixExpression("-", expr.right, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
if (rightVal != null) {
|
||||||
|
// right value is a constant, see if we can optimize
|
||||||
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
|
when (rightConst.number.toDouble()) {
|
||||||
|
-3.0 -> {
|
||||||
|
// -1/(left*left*left)
|
||||||
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
|
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
|
||||||
|
expr.position)
|
||||||
|
}
|
||||||
|
-2.0 -> {
|
||||||
|
// -1/(left*left)
|
||||||
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
|
BinaryExpression(expr.left, "*", expr.left, expr.position),
|
||||||
|
expr.position)
|
||||||
|
}
|
||||||
|
-1.0 -> {
|
||||||
|
// -1/left
|
||||||
|
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
||||||
|
expr.left, expr.position)
|
||||||
|
}
|
||||||
|
0.0 -> {
|
||||||
|
// 1
|
||||||
|
return NumericLiteralValue(rightConst.type, 1, expr.position)
|
||||||
|
}
|
||||||
|
0.5 -> {
|
||||||
|
// sqrt(left)
|
||||||
|
return FunctionCall(IdentifierReference(listOf("sqrt"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
}
|
||||||
|
1.0 -> {
|
||||||
|
// left
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
2.0 -> {
|
||||||
|
// left*left
|
||||||
|
return BinaryExpression(expr.left, "*", expr.left, expr.position)
|
||||||
|
}
|
||||||
|
3.0 -> {
|
||||||
|
// left*left*left
|
||||||
|
return BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (leftVal != null) {
|
||||||
|
// left value is a constant, see if we can optimize
|
||||||
|
when (leftVal.number.toDouble()) {
|
||||||
|
-1.0 -> {
|
||||||
|
// -1
|
||||||
|
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
|
||||||
|
}
|
||||||
|
0.0 -> {
|
||||||
|
// 0
|
||||||
|
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||||
|
}
|
||||||
|
1.0 -> {
|
||||||
|
//1
|
||||||
|
return NumericLiteralValue(leftVal.type, 1, expr.position)
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
// simplify assignments A = B <operator> C
|
||||||
|
|
||||||
|
val cv = rightVal?.number?.toInt()?.toDouble()
|
||||||
|
when (expr.operator) {
|
||||||
|
"%" -> {
|
||||||
|
if (cv == 1.0) {
|
||||||
|
return NumericLiteralValue(expr.inferType(program).typeOrElse(DataType.STRUCT), 0, expr.position)
|
||||||
|
} else if (cv == 2.0) {
|
||||||
|
expr.operator = "&"
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
// cannot shuffle assiciativity with division!
|
||||||
|
if (rightVal != null) {
|
||||||
|
// right value is a constant, see if we can optimize
|
||||||
|
val rightConst: NumericLiteralValue = rightVal
|
||||||
|
val cv = rightConst.number.toDouble()
|
||||||
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
if (!leftIDt.isKnown)
|
||||||
|
return null
|
||||||
|
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
||||||
|
when (cv) {
|
||||||
|
-1.0 -> {
|
||||||
|
// '/' -> -left
|
||||||
|
if (expr.operator == "/") {
|
||||||
|
return PrefixExpression("-", expr.left, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
1.0 -> {
|
||||||
|
// '/' -> left
|
||||||
|
if (expr.operator == "/") {
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in powersOfTwo -> {
|
||||||
|
if (leftDt in IntegerDatatypes) {
|
||||||
|
// divided by a power of two => shift right
|
||||||
|
val numshifts = log2(cv).toInt()
|
||||||
|
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in negativePowersOfTwo -> {
|
||||||
|
if (leftDt in IntegerDatatypes) {
|
||||||
|
// divided by a negative power of two => negate, then shift right
|
||||||
|
val numshifts = log2(-cv).toInt()
|
||||||
|
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftDt == DataType.UBYTE) {
|
||||||
|
if (abs(rightConst.number.toDouble()) >= 256.0) {
|
||||||
|
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||||
|
}
|
||||||
|
} else if (leftDt == DataType.UWORD) {
|
||||||
|
if (abs(rightConst.number.toDouble()) >= 65536.0) {
|
||||||
|
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (leftVal != null) {
|
||||||
|
// left value is a constant, see if we can optimize
|
||||||
|
when (leftVal.number.toDouble()) {
|
||||||
|
0.0 -> {
|
||||||
|
// 0
|
||||||
|
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeMultiplication(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression? {
|
||||||
|
if (leftVal == null && rightVal == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
val (expr2, _, rightVal2) = reorderAssociative(expr, leftVal)
|
||||||
|
if (rightVal2 != null) {
|
||||||
|
// right value is a constant, see if we can optimize
|
||||||
|
val leftValue: Expression = expr2.left
|
||||||
|
val rightConst: NumericLiteralValue = rightVal2
|
||||||
|
when (val cv = rightConst.number.toDouble()) {
|
||||||
|
-1.0 -> {
|
||||||
|
// -left
|
||||||
|
return PrefixExpression("-", leftValue, expr.position)
|
||||||
|
}
|
||||||
|
0.0 -> {
|
||||||
|
// 0
|
||||||
|
return NumericLiteralValue(rightConst.type, 0, expr.position)
|
||||||
|
}
|
||||||
|
1.0 -> {
|
||||||
|
// left
|
||||||
|
return expr2.left
|
||||||
|
}
|
||||||
|
in powersOfTwo -> {
|
||||||
|
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||||
|
// times a power of two => shift left
|
||||||
|
val numshifts = log2(cv).toInt()
|
||||||
|
return BinaryExpression(expr2.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
in negativePowersOfTwo -> {
|
||||||
|
if (leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
||||||
|
// times a negative power of two => negate, then shift left
|
||||||
|
val numshifts = log2(-cv).toInt()
|
||||||
|
return BinaryExpression(PrefixExpression("-", expr2.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// no need to check for left val constant (because of associativity)
|
||||||
|
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
|
||||||
|
if (amountLv == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
val amount = amountLv.number.toInt()
|
||||||
|
if (amount == 0) {
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when (targetDt) {
|
||||||
|
DataType.UBYTE, DataType.BYTE -> {
|
||||||
|
if (amount >= 8) {
|
||||||
|
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD, DataType.WORD -> {
|
||||||
|
if (amount >= 16) {
|
||||||
|
return NumericLiteralValue(targetDt, 0, expr.position)
|
||||||
|
} else if (amount >= 8) {
|
||||||
|
val lsb = TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
||||||
|
if (amount == 8) {
|
||||||
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position)
|
||||||
|
}
|
||||||
|
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
|
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression? {
|
||||||
|
if (amountLv == null)
|
||||||
|
return null
|
||||||
|
|
||||||
|
val amount = amountLv.number.toInt()
|
||||||
|
if (amount == 0) {
|
||||||
|
return expr.left
|
||||||
|
}
|
||||||
|
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
||||||
|
when (targetDt) {
|
||||||
|
DataType.UBYTE -> {
|
||||||
|
if (amount >= 8) {
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.BYTE -> {
|
||||||
|
if (amount > 8) {
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position)
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.UWORD -> {
|
||||||
|
if (amount >= 16) {
|
||||||
|
return NumericLiteralValue.optimalInteger(0, expr.position)
|
||||||
|
} else if (amount >= 8) {
|
||||||
|
val msb = FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
||||||
|
if (amount == 8)
|
||||||
|
return msb
|
||||||
|
return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
DataType.WORD -> {
|
||||||
|
if (amount > 16) {
|
||||||
|
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
||||||
|
return null
|
||||||
|
} else if (amount >= 8) {
|
||||||
|
val msbAsByte = TypecastExpression(
|
||||||
|
FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position),
|
||||||
|
DataType.BYTE,
|
||||||
|
true, expr.position)
|
||||||
|
if (amount == 8)
|
||||||
|
return msbAsByte
|
||||||
|
return BinaryExpression(msbAsByte, ">>", NumericLiteralValue.optimalInteger(amount - 8, expr.position), expr.position)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return null
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
|
||||||
|
if (expr.operator in associativeOperators && leftVal != null) {
|
||||||
|
// swap left and right so that right is always the constant
|
||||||
|
val tmp = expr.left
|
||||||
|
expr.left = expr.right
|
||||||
|
expr.right = tmp
|
||||||
|
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
|
||||||
|
}
|
||||||
|
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
||||||
|
}
|
||||||
|
|
||||||
|
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
||||||
|
|
||||||
|
}
|
@ -1,34 +1,25 @@
|
|||||||
package prog8.optimizer
|
package prog8.optimizer
|
||||||
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.AstException
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.parser.ParsingFailedError
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.constantFold() {
|
internal fun Program.constantFold(errors: ErrorReporter) {
|
||||||
val optimizer = ConstantFolding(this)
|
val optimizer = ConstantFoldingOptimizer(this, errors)
|
||||||
try {
|
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
} catch (ax: AstException) {
|
|
||||||
optimizer.addError(ax)
|
|
||||||
}
|
|
||||||
|
|
||||||
while(optimizer.errors.isEmpty() && optimizer.optimizationsDone>0) {
|
while(errors.isEmpty() && optimizer.optimizationsDone>0) {
|
||||||
optimizer.optimizationsDone = 0
|
optimizer.optimizationsDone = 0
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
}
|
}
|
||||||
|
|
||||||
if(optimizer.errors.isNotEmpty()) {
|
if(errors.isEmpty())
|
||||||
optimizer.errors.forEach { System.err.println(it) }
|
|
||||||
throw ParsingFailedError("There are ${optimizer.errors.size} errors.")
|
|
||||||
} else {
|
|
||||||
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(namespace) } // re-link in final configuration
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
internal fun Program.optimizeStatements(): Int {
|
internal fun Program.optimizeStatements(errors: ErrorReporter): Int {
|
||||||
val optimizer = StatementOptimizer(this)
|
val optimizer = StatementOptimizer(this, errors)
|
||||||
optimizer.visit(this)
|
optimizer.visit(this)
|
||||||
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
modules.forEach { it.linkParents(this.namespace) } // re-link in final configuration
|
||||||
|
|
||||||
@ -36,7 +27,7 @@ internal fun Program.optimizeStatements(): Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
internal fun Program.simplifyExpressions() : Int {
|
internal fun Program.simplifyExpressions() : Int {
|
||||||
val optimizer = SimplifyExpressions(this)
|
val opti = ExpressionSimplifier(this)
|
||||||
optimizer.visit(this)
|
opti.visit(this)
|
||||||
return optimizer.optimizationsDone
|
return opti.applyModifications()
|
||||||
}
|
}
|
||||||
|
@ -0,0 +1,46 @@
|
|||||||
|
package prog8.optimizer
|
||||||
|
|
||||||
|
import prog8.ast.INameScope
|
||||||
|
import prog8.ast.Node
|
||||||
|
import prog8.ast.Program
|
||||||
|
import prog8.ast.processing.IAstVisitor
|
||||||
|
import prog8.ast.statements.AnonymousScope
|
||||||
|
import prog8.ast.statements.NopStatement
|
||||||
|
import prog8.ast.statements.Statement
|
||||||
|
|
||||||
|
internal class FlattenAnonymousScopesAndNopRemover: IAstVisitor {
|
||||||
|
private var scopesToFlatten = mutableListOf<INameScope>()
|
||||||
|
private val nopStatements = mutableListOf<NopStatement>()
|
||||||
|
|
||||||
|
override fun visit(program: Program) {
|
||||||
|
super.visit(program)
|
||||||
|
for(scope in scopesToFlatten.reversed()) {
|
||||||
|
val namescope = scope.parent as INameScope
|
||||||
|
val idx = namescope.statements.indexOf(scope as Statement)
|
||||||
|
if(idx>=0) {
|
||||||
|
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
||||||
|
nop.parent = namescope as Node
|
||||||
|
namescope.statements[idx] = nop
|
||||||
|
namescope.statements.addAll(idx, scope.statements)
|
||||||
|
scope.statements.forEach { it.parent = namescope }
|
||||||
|
visit(nop)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
this.nopStatements.forEach {
|
||||||
|
it.definingScope().remove(it)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(scope: AnonymousScope) {
|
||||||
|
if(scope.parent is INameScope) {
|
||||||
|
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
||||||
|
}
|
||||||
|
|
||||||
|
return super.visit(scope)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun visit(nopStatement: NopStatement) {
|
||||||
|
nopStatements.add(nopStatement)
|
||||||
|
}
|
||||||
|
}
|
@ -1,828 +0,0 @@
|
|||||||
package prog8.optimizer
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.*
|
|
||||||
import prog8.ast.expressions.*
|
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.statements.Assignment
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.log2
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
/*
|
|
||||||
todo add more expression optimizations
|
|
||||||
|
|
||||||
Investigate what optimizations binaryen has, also see https://egorbo.com/peephole-optimizations.html
|
|
||||||
|
|
||||||
*/
|
|
||||||
|
|
||||||
internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor {
|
|
||||||
var optimizationsDone: Int = 0
|
|
||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
|
||||||
if (assignment.aug_op != null)
|
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
|
||||||
return super.visit(assignment)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(memread: DirectMemoryRead): Expression {
|
|
||||||
// @( &thing ) --> thing
|
|
||||||
val addrOf = memread.addressExpression as? AddressOf
|
|
||||||
if(addrOf!=null)
|
|
||||||
return super.visit(addrOf.identifier)
|
|
||||||
return super.visit(memread)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(typecast: TypecastExpression): Expression {
|
|
||||||
var tc = typecast
|
|
||||||
|
|
||||||
// try to statically convert a literal value into one of the desired type
|
|
||||||
val literal = tc.expression as? NumericLiteralValue
|
|
||||||
if(literal!=null) {
|
|
||||||
val newLiteral = literal.cast(tc.type)
|
|
||||||
if(newLiteral!==literal) {
|
|
||||||
optimizationsDone++
|
|
||||||
return newLiteral
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// remove redundant typecasts
|
|
||||||
while(true) {
|
|
||||||
val expr = tc.expression
|
|
||||||
if(expr !is TypecastExpression || expr.type!=tc.type) {
|
|
||||||
val assignment = typecast.parent as? Assignment
|
|
||||||
if(assignment!=null) {
|
|
||||||
val targetDt = assignment.target.inferType(program, assignment)
|
|
||||||
if(tc.expression.inferType(program)==targetDt) {
|
|
||||||
optimizationsDone++
|
|
||||||
return tc.expression
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
val subTc = tc.expression as? TypecastExpression
|
|
||||||
if(subTc!=null) {
|
|
||||||
// if the previous typecast was casting to a 'bigger' type, just ignore that one
|
|
||||||
// if the previous typecast was casting to a similar type, ignore that one
|
|
||||||
if(subTc.type largerThan tc.type || subTc.type equalsSize tc.type) {
|
|
||||||
subTc.type = tc.type
|
|
||||||
subTc.parent = tc.parent
|
|
||||||
optimizationsDone++
|
|
||||||
return subTc
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(tc)
|
|
||||||
}
|
|
||||||
|
|
||||||
optimizationsDone++
|
|
||||||
tc = expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(expr: PrefixExpression): Expression {
|
|
||||||
if (expr.operator == "+") {
|
|
||||||
// +X --> X
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.expression.accept(this)
|
|
||||||
} else if (expr.operator == "not") {
|
|
||||||
(expr.expression as? BinaryExpression)?.let {
|
|
||||||
// NOT (...) -> invert ...
|
|
||||||
when (it.operator) {
|
|
||||||
"<" -> {
|
|
||||||
it.operator = ">="
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
">" -> {
|
|
||||||
it.operator = "<="
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
"<=" -> {
|
|
||||||
it.operator = ">"
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
">=" -> {
|
|
||||||
it.operator = "<"
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
"==" -> {
|
|
||||||
it.operator = "!="
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
"!=" -> {
|
|
||||||
it.operator = "=="
|
|
||||||
optimizationsDone++
|
|
||||||
return it
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(expr)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression): Expression {
|
|
||||||
super.visit(expr)
|
|
||||||
val leftVal = expr.left.constValue(program)
|
|
||||||
val rightVal = expr.right.constValue(program)
|
|
||||||
val constTrue = NumericLiteralValue.fromBoolean(true, expr.position)
|
|
||||||
val constFalse = NumericLiteralValue.fromBoolean(false, expr.position)
|
|
||||||
|
|
||||||
val leftIDt = expr.left.inferType(program)
|
|
||||||
val rightIDt = expr.right.inferType(program)
|
|
||||||
if(!leftIDt.isKnown || !rightIDt.isKnown)
|
|
||||||
throw FatalAstException("can't determine datatype of both expression operands $expr")
|
|
||||||
|
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
val rightDt = rightIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
if (leftDt != rightDt) {
|
|
||||||
// try to convert a datatype into the other (where ddd
|
|
||||||
if (adjustDatatypes(expr, leftVal, leftDt, rightVal, rightDt)) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Value <associativeoperator> X --> X <associativeoperator> Value
|
|
||||||
if (leftVal != null && expr.operator in associativeOperators && rightVal == null) {
|
|
||||||
val tmp = expr.left
|
|
||||||
expr.left = expr.right
|
|
||||||
expr.right = tmp
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// X + (-A) --> X - A
|
|
||||||
if (expr.operator == "+" && (expr.right as? PrefixExpression)?.operator == "-") {
|
|
||||||
expr.operator = "-"
|
|
||||||
expr.right = (expr.right as PrefixExpression).expression
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// (-A) + X --> X - A
|
|
||||||
if (expr.operator == "+" && (expr.left as? PrefixExpression)?.operator == "-") {
|
|
||||||
expr.operator = "-"
|
|
||||||
val newRight = (expr.left as PrefixExpression).expression
|
|
||||||
expr.left = expr.right
|
|
||||||
expr.right = newRight
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// X + (-value) --> X - value
|
|
||||||
if (expr.operator == "+" && rightVal != null) {
|
|
||||||
val rv = rightVal.number.toDouble()
|
|
||||||
if (rv < 0.0) {
|
|
||||||
expr.operator = "-"
|
|
||||||
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// (-value) + X --> X - value
|
|
||||||
if (expr.operator == "+" && leftVal != null) {
|
|
||||||
val lv = leftVal.number.toDouble()
|
|
||||||
if (lv < 0.0) {
|
|
||||||
expr.operator = "-"
|
|
||||||
expr.right = NumericLiteralValue(leftVal.type, -lv, leftVal.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// X - (-A) --> X + A
|
|
||||||
if (expr.operator == "-" && (expr.right as? PrefixExpression)?.operator == "-") {
|
|
||||||
expr.operator = "+"
|
|
||||||
expr.right = (expr.right as PrefixExpression).expression
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
// X - (-value) --> X + value
|
|
||||||
if (expr.operator == "-" && rightVal != null) {
|
|
||||||
val rv = rightVal.number.toDouble()
|
|
||||||
if (rv < 0.0) {
|
|
||||||
expr.operator = "+"
|
|
||||||
expr.right = NumericLiteralValue(rightVal.type, -rv, rightVal.position)
|
|
||||||
optimizationsDone++
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (expr.operator == "+" || expr.operator == "-"
|
|
||||||
&& leftVal == null && rightVal == null
|
|
||||||
&& leftDt in NumericDatatypes && rightDt in NumericDatatypes) {
|
|
||||||
val leftBinExpr = expr.left as? BinaryExpression
|
|
||||||
val rightBinExpr = expr.right as? BinaryExpression
|
|
||||||
if (leftBinExpr?.operator == "*") {
|
|
||||||
if (expr.operator == "+") {
|
|
||||||
// Y*X + X -> X*(Y - 1)
|
|
||||||
// X*Y + X -> X*(Y - 1)
|
|
||||||
val x = expr.right
|
|
||||||
val y = determineY(x, leftBinExpr)
|
|
||||||
if(y!=null) {
|
|
||||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
|
||||||
return BinaryExpression(x, "*", yPlus1, x.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// Y*X - X -> X*(Y - 1)
|
|
||||||
// X*Y - X -> X*(Y - 1)
|
|
||||||
val x = expr.right
|
|
||||||
val y = determineY(x, leftBinExpr)
|
|
||||||
if(y!=null) {
|
|
||||||
val yMinus1 = BinaryExpression(y, "-", NumericLiteralValue(leftDt, 1, y.position), y.position)
|
|
||||||
return BinaryExpression(x, "*", yMinus1, x.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if(rightBinExpr?.operator=="*") {
|
|
||||||
if(expr.operator=="+") {
|
|
||||||
// X + Y*X -> X*(Y + 1)
|
|
||||||
// X + X*Y -> X*(Y + 1)
|
|
||||||
val x = expr.left
|
|
||||||
val y = determineY(x, rightBinExpr)
|
|
||||||
if(y!=null) {
|
|
||||||
val yPlus1 = BinaryExpression(y, "+", NumericLiteralValue.optimalInteger(1, y.position), y.position)
|
|
||||||
return BinaryExpression(x, "*", yPlus1, x.position)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// X - Y*X -> X*(1 - Y)
|
|
||||||
// X - X*Y -> X*(1 - Y)
|
|
||||||
val x = expr.left
|
|
||||||
val y = determineY(x, rightBinExpr)
|
|
||||||
if(y!=null) {
|
|
||||||
val oneMinusY = BinaryExpression(NumericLiteralValue.optimalInteger(1, y.position), "-", y, y.position)
|
|
||||||
return BinaryExpression(x, "*", oneMinusY, x.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// simplify when a term is constant and determines the outcome
|
|
||||||
when (expr.operator) {
|
|
||||||
"or" -> {
|
|
||||||
if ((leftVal != null && leftVal.asBooleanValue) || (rightVal != null && rightVal.asBooleanValue)) {
|
|
||||||
optimizationsDone++
|
|
||||||
return constTrue
|
|
||||||
}
|
|
||||||
if (leftVal != null && !leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.right
|
|
||||||
}
|
|
||||||
if (rightVal != null && !rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"and" -> {
|
|
||||||
if ((leftVal != null && !leftVal.asBooleanValue) || (rightVal != null && !rightVal.asBooleanValue)) {
|
|
||||||
optimizationsDone++
|
|
||||||
return constFalse
|
|
||||||
}
|
|
||||||
if (leftVal != null && leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.right
|
|
||||||
}
|
|
||||||
if (rightVal != null && rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"xor" -> {
|
|
||||||
if (leftVal != null && !leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.right
|
|
||||||
}
|
|
||||||
if (rightVal != null && !rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
if (leftVal != null && leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return PrefixExpression("not", expr.right, expr.right.position)
|
|
||||||
}
|
|
||||||
if (rightVal != null && rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return PrefixExpression("not", expr.left, expr.left.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"|", "^" -> {
|
|
||||||
if (leftVal != null && !leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.right
|
|
||||||
}
|
|
||||||
if (rightVal != null && !rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"&" -> {
|
|
||||||
if (leftVal != null && !leftVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return constFalse
|
|
||||||
}
|
|
||||||
if (rightVal != null && !rightVal.asBooleanValue) {
|
|
||||||
optimizationsDone++
|
|
||||||
return constFalse
|
|
||||||
}
|
|
||||||
}
|
|
||||||
"*" -> return optimizeMultiplication(expr, leftVal, rightVal)
|
|
||||||
"/" -> return optimizeDivision(expr, leftVal, rightVal)
|
|
||||||
"+" -> return optimizeAdd(expr, leftVal, rightVal)
|
|
||||||
"-" -> return optimizeSub(expr, leftVal, rightVal)
|
|
||||||
"**" -> return optimizePower(expr, leftVal, rightVal)
|
|
||||||
"%" -> return optimizeRemainder(expr, leftVal, rightVal)
|
|
||||||
">>" -> return optimizeShiftRight(expr, rightVal)
|
|
||||||
"<<" -> return optimizeShiftLeft(expr, rightVal)
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
|
||||||
return when {
|
|
||||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
|
||||||
subBinExpr.right isSameAs x -> subBinExpr.left
|
|
||||||
else -> null
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun adjustDatatypes(expr: BinaryExpression,
|
|
||||||
leftConstVal: NumericLiteralValue?, leftDt: DataType,
|
|
||||||
rightConstVal: NumericLiteralValue?, rightDt: DataType): Boolean {
|
|
||||||
|
|
||||||
fun adjust(value: NumericLiteralValue, targetDt: DataType): Pair<Boolean, NumericLiteralValue>{
|
|
||||||
if(value.type==targetDt)
|
|
||||||
return Pair(false, value)
|
|
||||||
when(value.type) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
if (targetDt == DataType.BYTE) {
|
|
||||||
if(value.number.toInt() < 127)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.UWORD || targetDt == DataType.WORD)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
if (targetDt == DataType.UBYTE) {
|
|
||||||
if(value.number.toInt() >= 0)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.UWORD) {
|
|
||||||
if(value.number.toInt() >= 0)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.WORD) return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
if (targetDt == DataType.UBYTE) {
|
|
||||||
if(value.number.toInt() <= 255)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.BYTE) {
|
|
||||||
if(value.number.toInt() <= 127)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.WORD) {
|
|
||||||
if(value.number.toInt() <= 32767)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toInt(), value.position))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
if (targetDt == DataType.UBYTE) {
|
|
||||||
if(value.number.toInt() in 0..255)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.BYTE) {
|
|
||||||
if(value.number.toInt() in -128..127)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
else if (targetDt == DataType.UWORD) {
|
|
||||||
if(value.number.toInt() >= 0)
|
|
||||||
return Pair(true, NumericLiteralValue(targetDt, value.number.toShort(), value.position))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
return Pair(false, value)
|
|
||||||
}
|
|
||||||
|
|
||||||
if(leftConstVal==null && rightConstVal!=null) {
|
|
||||||
if(leftDt largerThan rightDt) {
|
|
||||||
val (adjusted, newValue) = adjust(rightConstVal, leftDt)
|
|
||||||
if (adjusted) {
|
|
||||||
expr.right = newValue
|
|
||||||
optimizationsDone++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
} else if(leftConstVal!=null && rightConstVal==null) {
|
|
||||||
if(rightDt largerThan leftDt) {
|
|
||||||
val (adjusted, newValue) = adjust(leftConstVal, rightDt)
|
|
||||||
if (adjusted) {
|
|
||||||
expr.left = newValue
|
|
||||||
optimizationsDone++
|
|
||||||
return true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return false
|
|
||||||
} else {
|
|
||||||
return false // two const values, don't adjust (should have been const-folded away)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
private data class ReorderedAssociativeBinaryExpr(val expr: BinaryExpression, val leftVal: NumericLiteralValue?, val rightVal: NumericLiteralValue?)
|
|
||||||
|
|
||||||
private fun reorderAssociative(expr: BinaryExpression, leftVal: NumericLiteralValue?): ReorderedAssociativeBinaryExpr {
|
|
||||||
if(expr.operator in associativeOperators && leftVal!=null) {
|
|
||||||
// swap left and right so that right is always the constant
|
|
||||||
val tmp = expr.left
|
|
||||||
expr.left = expr.right
|
|
||||||
expr.right = tmp
|
|
||||||
optimizationsDone++
|
|
||||||
return ReorderedAssociativeBinaryExpr(expr, expr.right.constValue(program), leftVal)
|
|
||||||
}
|
|
||||||
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(pleftVal==null && prightVal==null)
|
|
||||||
return pexpr
|
|
||||||
|
|
||||||
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
|
||||||
if(rightVal!=null) {
|
|
||||||
// right value is a constant, see if we can optimize
|
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
|
||||||
when(rightConst.number.toDouble()) {
|
|
||||||
0.0 -> {
|
|
||||||
// left
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no need to check for left val constant (because of associativity)
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(leftVal==null && rightVal==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
if(rightVal!=null) {
|
|
||||||
// right value is a constant, see if we can optimize
|
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
|
||||||
when(rightConst.number.toDouble()) {
|
|
||||||
0.0 -> {
|
|
||||||
// left
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(leftVal!=null) {
|
|
||||||
// left value is a constant, see if we can optimize
|
|
||||||
when(leftVal.number.toDouble()) {
|
|
||||||
0.0 -> {
|
|
||||||
// -right
|
|
||||||
optimizationsDone++
|
|
||||||
return PrefixExpression("-", expr.right, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(leftVal==null && rightVal==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
if(rightVal!=null) {
|
|
||||||
// right value is a constant, see if we can optimize
|
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
|
||||||
when(rightConst.number.toDouble()) {
|
|
||||||
-3.0 -> {
|
|
||||||
// -1/(left*left*left)
|
|
||||||
optimizationsDone++
|
|
||||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
|
||||||
BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position),
|
|
||||||
expr.position)
|
|
||||||
}
|
|
||||||
-2.0 -> {
|
|
||||||
// -1/(left*left)
|
|
||||||
optimizationsDone++
|
|
||||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
|
||||||
BinaryExpression(expr.left, "*", expr.left, expr.position),
|
|
||||||
expr.position)
|
|
||||||
}
|
|
||||||
-1.0 -> {
|
|
||||||
// -1/left
|
|
||||||
optimizationsDone++
|
|
||||||
return BinaryExpression(NumericLiteralValue(DataType.FLOAT, -1.0, expr.position), "/",
|
|
||||||
expr.left, expr.position)
|
|
||||||
}
|
|
||||||
0.0 -> {
|
|
||||||
// 1
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(rightConst.type, 1, expr.position)
|
|
||||||
}
|
|
||||||
0.5 -> {
|
|
||||||
// sqrt(left)
|
|
||||||
optimizationsDone++
|
|
||||||
return FunctionCall(IdentifierReference(listOf("sqrt"), expr.position), mutableListOf(expr.left), expr.position)
|
|
||||||
}
|
|
||||||
1.0 -> {
|
|
||||||
// left
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
2.0 -> {
|
|
||||||
// left*left
|
|
||||||
optimizationsDone++
|
|
||||||
return BinaryExpression(expr.left, "*", expr.left, expr.position)
|
|
||||||
}
|
|
||||||
3.0 -> {
|
|
||||||
// left*left*left
|
|
||||||
optimizationsDone++
|
|
||||||
return BinaryExpression(expr.left, "*", BinaryExpression(expr.left, "*", expr.left, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if(leftVal!=null) {
|
|
||||||
// left value is a constant, see if we can optimize
|
|
||||||
when(leftVal.number.toDouble()) {
|
|
||||||
-1.0 -> {
|
|
||||||
// -1
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(DataType.FLOAT, -1.0, expr.position)
|
|
||||||
}
|
|
||||||
0.0 -> {
|
|
||||||
// 0
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
|
||||||
}
|
|
||||||
1.0 -> {
|
|
||||||
//1
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(leftVal.type, 1, expr.position)
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(leftVal==null && rightVal==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
// simplify assignments A = B <operator> C
|
|
||||||
|
|
||||||
val cv = rightVal?.number?.toInt()?.toDouble()
|
|
||||||
when(expr.operator) {
|
|
||||||
"%" -> {
|
|
||||||
if (cv == 1.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(expr.inferType(program).typeOrElse(DataType.STRUCT), 0, expr.position)
|
|
||||||
} else if (cv == 2.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
expr.operator = "&"
|
|
||||||
expr.right = NumericLiteralValue.optimalInteger(1, expr.position)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
private val powersOfTwo = (1 .. 16).map { (2.0).pow(it) }.toSet()
|
|
||||||
private val negativePowersOfTwo = powersOfTwo.map { -it }.toSet()
|
|
||||||
|
|
||||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(leftVal==null && rightVal==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
// cannot shuffle assiciativity with division!
|
|
||||||
if(rightVal!=null) {
|
|
||||||
// right value is a constant, see if we can optimize
|
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
|
||||||
val cv = rightConst.number.toDouble()
|
|
||||||
val leftIDt = expr.left.inferType(program)
|
|
||||||
if(!leftIDt.isKnown)
|
|
||||||
return expr
|
|
||||||
val leftDt = leftIDt.typeOrElse(DataType.STRUCT)
|
|
||||||
when(cv) {
|
|
||||||
-1.0 -> {
|
|
||||||
// '/' -> -left
|
|
||||||
if (expr.operator == "/") {
|
|
||||||
optimizationsDone++
|
|
||||||
return PrefixExpression("-", expr.left, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
1.0 -> {
|
|
||||||
// '/' -> left
|
|
||||||
if (expr.operator == "/") {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in powersOfTwo -> {
|
|
||||||
if(leftDt in IntegerDatatypes) {
|
|
||||||
// divided by a power of two => shift right
|
|
||||||
optimizationsDone++
|
|
||||||
val numshifts = log2(cv).toInt()
|
|
||||||
return BinaryExpression(expr.left, ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in negativePowersOfTwo -> {
|
|
||||||
if(leftDt in IntegerDatatypes) {
|
|
||||||
// divided by a negative power of two => negate, then shift right
|
|
||||||
optimizationsDone++
|
|
||||||
val numshifts = log2(-cv).toInt()
|
|
||||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), ">>", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (leftDt == DataType.UBYTE) {
|
|
||||||
if(abs(rightConst.number.toDouble()) >= 256.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else if (leftDt == DataType.UWORD) {
|
|
||||||
if(abs(rightConst.number.toDouble()) >= 65536.0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(DataType.UBYTE, 0, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(leftVal!=null) {
|
|
||||||
// left value is a constant, see if we can optimize
|
|
||||||
when(leftVal.number.toDouble()) {
|
|
||||||
0.0 -> {
|
|
||||||
// 0
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(leftVal.type, 0, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
|
||||||
if(pleftVal==null && prightVal==null)
|
|
||||||
return pexpr
|
|
||||||
|
|
||||||
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
|
||||||
if(rightVal!=null) {
|
|
||||||
// right value is a constant, see if we can optimize
|
|
||||||
val leftValue: Expression = expr.left
|
|
||||||
val rightConst: NumericLiteralValue = rightVal
|
|
||||||
when(val cv = rightConst.number.toDouble()) {
|
|
||||||
-1.0 -> {
|
|
||||||
// -left
|
|
||||||
optimizationsDone++
|
|
||||||
return PrefixExpression("-", leftValue, expr.position)
|
|
||||||
}
|
|
||||||
0.0 -> {
|
|
||||||
// 0
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue(rightConst.type, 0, expr.position)
|
|
||||||
}
|
|
||||||
1.0 -> {
|
|
||||||
// left
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
in powersOfTwo -> {
|
|
||||||
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
|
||||||
// times a power of two => shift left
|
|
||||||
optimizationsDone++
|
|
||||||
val numshifts = log2(cv).toInt()
|
|
||||||
return BinaryExpression(expr.left, "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
in negativePowersOfTwo -> {
|
|
||||||
if(leftValue.inferType(program).typeOrElse(DataType.STRUCT) in IntegerDatatypes) {
|
|
||||||
// times a negative power of two => negate, then shift left
|
|
||||||
optimizationsDone++
|
|
||||||
val numshifts = log2(-cv).toInt()
|
|
||||||
return BinaryExpression(PrefixExpression("-", expr.left, expr.position), "<<", NumericLiteralValue.optimalInteger(numshifts, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// no need to check for left val constant (because of associativity)
|
|
||||||
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeShiftLeft(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression {
|
|
||||||
if(amountLv==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
val amount=amountLv.number.toInt()
|
|
||||||
if(amount==0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
when(targetDt) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
if(amount>=8) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
if(amount>=16) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
|
||||||
}
|
|
||||||
else if(amount>=8) {
|
|
||||||
optimizationsDone++
|
|
||||||
val lsb=TypecastExpression(expr.left, DataType.UBYTE, true, expr.position)
|
|
||||||
if(amount==8) {
|
|
||||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), lsb), expr.position)
|
|
||||||
}
|
|
||||||
val shifted = BinaryExpression(lsb, "<<", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
|
||||||
return FunctionCall(IdentifierReference(listOf("mkword"), expr.position), mutableListOf(NumericLiteralValue.optimalInteger(0, expr.position), shifted), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
|
|
||||||
private fun optimizeShiftRight(expr: BinaryExpression, amountLv: NumericLiteralValue?): Expression {
|
|
||||||
if(amountLv==null)
|
|
||||||
return expr
|
|
||||||
|
|
||||||
val amount=amountLv.number.toInt()
|
|
||||||
if(amount==0) {
|
|
||||||
optimizationsDone++
|
|
||||||
return expr.left
|
|
||||||
}
|
|
||||||
val targetDt = expr.left.inferType(program).typeOrElse(DataType.STRUCT)
|
|
||||||
when(targetDt) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
if(amount>=8) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
if(amount>8) {
|
|
||||||
expr.right = NumericLiteralValue.optimalInteger(8, expr.right.position)
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
if(amount>=16) {
|
|
||||||
optimizationsDone++
|
|
||||||
return NumericLiteralValue.optimalInteger(0, expr.position)
|
|
||||||
}
|
|
||||||
else if(amount>=8) {
|
|
||||||
optimizationsDone++
|
|
||||||
val msb=FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position)
|
|
||||||
if(amount==8)
|
|
||||||
return msb
|
|
||||||
return BinaryExpression(msb, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
if(amount>16) {
|
|
||||||
expr.right = NumericLiteralValue.optimalInteger(16, expr.right.position)
|
|
||||||
return expr
|
|
||||||
} else if(amount>=8) {
|
|
||||||
optimizationsDone++
|
|
||||||
val msbAsByte = TypecastExpression(
|
|
||||||
FunctionCall(IdentifierReference(listOf("msb"), expr.position), mutableListOf(expr.left), expr.position),
|
|
||||||
DataType.BYTE,
|
|
||||||
true, expr.position)
|
|
||||||
if(amount==8)
|
|
||||||
return msbAsByte
|
|
||||||
return BinaryExpression(msbAsByte, ">>", NumericLiteralValue.optimalInteger(amount-8, expr.position), expr.position)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> {}
|
|
||||||
}
|
|
||||||
return expr
|
|
||||||
}
|
|
||||||
}
|
|
@ -2,12 +2,10 @@ package prog8.optimizer
|
|||||||
|
|
||||||
import prog8.ast.INameScope
|
import prog8.ast.INameScope
|
||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Node
|
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.base.*
|
import prog8.ast.base.*
|
||||||
import prog8.ast.expressions.*
|
import prog8.ast.expressions.*
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
import prog8.ast.processing.IAstModifyingVisitor
|
||||||
import prog8.ast.processing.IAstVisitor
|
|
||||||
import prog8.ast.statements.*
|
import prog8.ast.statements.*
|
||||||
import prog8.compiler.target.CompilationTarget
|
import prog8.compiler.target.CompilationTarget
|
||||||
import prog8.functions.BuiltinFunctions
|
import prog8.functions.BuiltinFunctions
|
||||||
@ -15,12 +13,13 @@ import kotlin.math.floor
|
|||||||
|
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO: remove unreachable code?
|
TODO: remove unreachable code after return and exit()
|
||||||
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
TODO: proper inlining of tiny subroutines (at first, restrict to subs without parameters and variables in them, and build it up from there: correctly renaming/relocating all variables in them and refs to those as well)
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
|
||||||
internal class StatementOptimizer(private val program: Program) : IAstModifyingVisitor {
|
internal class StatementOptimizer(private val program: Program,
|
||||||
|
private val errors: ErrorReporter) : IAstModifyingVisitor {
|
||||||
var optimizationsDone: Int = 0
|
var optimizationsDone: Int = 0
|
||||||
private set
|
private set
|
||||||
|
|
||||||
@ -80,13 +79,13 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if("force_output" !in block.options()) {
|
if("force_output" !in block.options()) {
|
||||||
if (block.containsNoCodeNorVars()) {
|
if (block.containsNoCodeNorVars()) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
printWarning("removing empty block '${block.name}'", block.position)
|
errors.warn("removing empty block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block)
|
return NopStatement.insteadOf(block)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (block !in callgraph.usedSymbols) {
|
if (block !in callgraph.usedSymbols) {
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
printWarning("removing unused block '${block.name}'", block.position)
|
errors.warn("removing unused block '${block.name}'", block.position)
|
||||||
return NopStatement.insteadOf(block) // remove unused block
|
return NopStatement.insteadOf(block) // remove unused block
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -99,7 +98,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||||
if(subroutine.asmAddress==null && !forceOutput) {
|
if(subroutine.asmAddress==null && !forceOutput) {
|
||||||
if(subroutine.containsNoCodeNorVars()) {
|
if(subroutine.containsNoCodeNorVars()) {
|
||||||
printWarning("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing empty subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement.insteadOf(subroutine)
|
return NopStatement.insteadOf(subroutine)
|
||||||
}
|
}
|
||||||
@ -111,7 +110,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
}
|
}
|
||||||
|
|
||||||
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
if(subroutine !in callgraph.usedSymbols && !forceOutput) {
|
||||||
printWarning("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
errors.warn("removing unused subroutine '${subroutine.name}'", subroutine.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement.insteadOf(subroutine)
|
return NopStatement.insteadOf(subroutine)
|
||||||
}
|
}
|
||||||
@ -123,7 +122,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||||
if(decl.type == VarDeclType.VAR)
|
if(decl.type == VarDeclType.VAR)
|
||||||
printWarning("removing unused variable ${decl.type} '${decl.name}'", decl.position)
|
errors.warn("removing unused variable ${decl.type} '${decl.name}'", decl.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement.insteadOf(decl)
|
return NopStatement.insteadOf(decl)
|
||||||
}
|
}
|
||||||
@ -160,7 +159,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||||
val functionName = functionCallStatement.target.nameInSource[0]
|
val functionName = functionCallStatement.target.nameInSource[0]
|
||||||
if (functionName in pureBuiltinFunctions) {
|
if (functionName in pureBuiltinFunctions) {
|
||||||
printWarning("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return NopStatement.insteadOf(functionCallStatement)
|
return NopStatement.insteadOf(functionCallStatement)
|
||||||
}
|
}
|
||||||
@ -263,12 +262,12 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only if-part
|
// always true -> keep only if-part
|
||||||
printWarning("condition is always true", ifStatement.position)
|
errors.warn("condition is always true", ifStatement.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
ifStatement.truepart
|
ifStatement.truepart
|
||||||
} else {
|
} else {
|
||||||
// always false -> keep only else-part
|
// always false -> keep only else-part
|
||||||
printWarning("condition is always false", ifStatement.position)
|
errors.warn("condition is always false", ifStatement.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
ifStatement.elsepart
|
ifStatement.elsepart
|
||||||
}
|
}
|
||||||
@ -311,21 +310,13 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
val constvalue = whileLoop.condition.constValue(program)
|
val constvalue = whileLoop.condition.constValue(program)
|
||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
// always true -> print a warning, and optimize into a forever-loop
|
||||||
printWarning("condition is always true", whileLoop.condition.position)
|
errors.warn("condition is always true", whileLoop.condition.position)
|
||||||
if(hasContinueOrBreak(whileLoop.body))
|
|
||||||
return whileLoop
|
|
||||||
val backLabelName = "_prog8_back${whileLoop.position.line}"
|
|
||||||
val label = Label(backLabelName, whileLoop.condition.position)
|
|
||||||
whileLoop.body.statements.add(0, label)
|
|
||||||
whileLoop.body.statements.add(Jump(null,
|
|
||||||
IdentifierReference(listOf(backLabelName), whileLoop.condition.position),
|
|
||||||
null, whileLoop.condition.position))
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return whileLoop.body
|
ForeverLoop(whileLoop.body, whileLoop.position)
|
||||||
} else {
|
} else {
|
||||||
// always false -> ditch whole statement
|
// always false -> remove the while statement altogether
|
||||||
printWarning("condition is always false", whileLoop.condition.position)
|
errors.warn("condition is always false", whileLoop.condition.position)
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
NopStatement.insteadOf(whileLoop)
|
NopStatement.insteadOf(whileLoop)
|
||||||
}
|
}
|
||||||
@ -339,7 +330,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
if(constvalue!=null) {
|
if(constvalue!=null) {
|
||||||
return if(constvalue.asBooleanValue){
|
return if(constvalue.asBooleanValue){
|
||||||
// always true -> keep only the statement block (if there are no continue and break statements)
|
// always true -> keep only the statement block (if there are no continue and break statements)
|
||||||
printWarning("condition is always true", repeatLoop.untilCondition.position)
|
errors.warn("condition is always true", repeatLoop.untilCondition.position)
|
||||||
if(hasContinueOrBreak(repeatLoop.body))
|
if(hasContinueOrBreak(repeatLoop.body))
|
||||||
repeatLoop
|
repeatLoop
|
||||||
else {
|
else {
|
||||||
@ -347,18 +338,10 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
repeatLoop.body
|
repeatLoop.body
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
// always false -> print a warning, and optimize into body + jump (if there are no continue and break statements)
|
// always false -> print a warning, and optimize into a forever loop
|
||||||
printWarning("condition is always false", repeatLoop.untilCondition.position)
|
errors.warn("condition is always false", repeatLoop.untilCondition.position)
|
||||||
if(hasContinueOrBreak(repeatLoop.body))
|
|
||||||
return repeatLoop
|
|
||||||
val backLabelName = "_prog8_back${repeatLoop.position.line}"
|
|
||||||
val label = Label(backLabelName, repeatLoop.untilCondition.position)
|
|
||||||
repeatLoop.body.statements.add(0, label)
|
|
||||||
repeatLoop.body.statements.add(Jump(null,
|
|
||||||
IdentifierReference(listOf(backLabelName), repeatLoop.untilCondition.position),
|
|
||||||
null, repeatLoop.untilCondition.position))
|
|
||||||
optimizationsDone++
|
optimizationsDone++
|
||||||
return repeatLoop.body
|
ForeverLoop(repeatLoop.body, repeatLoop.position)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return repeatLoop
|
return repeatLoop
|
||||||
@ -424,7 +407,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
|
|
||||||
override fun visit(assignment: Assignment): Statement {
|
override fun visit(assignment: Assignment): Statement {
|
||||||
if(assignment.aug_op!=null)
|
if(assignment.aug_op!=null)
|
||||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
throw FatalAstException("augmented assignments should have been converted to normal assignments before this optimizer: $assignment")
|
||||||
|
|
||||||
if(assignment.target isSameAs assignment.value) {
|
if(assignment.target isSameAs assignment.value) {
|
||||||
if(assignment.target.isNotMemory(program.namespace)) {
|
if(assignment.target.isNotMemory(program.namespace)) {
|
||||||
@ -571,7 +554,7 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
// remove duplicate labels
|
// remove duplicate labels
|
||||||
val stmts = label.definingScope().statements
|
val stmts = label.definingScope().statements
|
||||||
val startIdx = stmts.indexOf(label)
|
val startIdx = stmts.indexOf(label)
|
||||||
if(startIdx<(stmts.size-1) && stmts[startIdx+1] == label)
|
if(startIdx< stmts.lastIndex && stmts[startIdx+1] == label)
|
||||||
return NopStatement.insteadOf(label)
|
return NopStatement.insteadOf(label)
|
||||||
|
|
||||||
return super.visit(label)
|
return super.visit(label)
|
||||||
@ -580,39 +563,3 @@ internal class StatementOptimizer(private val program: Program) : IAstModifyingV
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
|
|
||||||
private var scopesToFlatten = mutableListOf<INameScope>()
|
|
||||||
private val nopStatements = mutableListOf<NopStatement>()
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
super.visit(program)
|
|
||||||
for(scope in scopesToFlatten.reversed()) {
|
|
||||||
val namescope = scope.parent as INameScope
|
|
||||||
val idx = namescope.statements.indexOf(scope as Statement)
|
|
||||||
if(idx>=0) {
|
|
||||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
|
||||||
nop.parent = namescope as Node
|
|
||||||
namescope.statements[idx] = nop
|
|
||||||
namescope.statements.addAll(idx, scope.statements)
|
|
||||||
scope.statements.forEach { it.parent = namescope }
|
|
||||||
visit(nop)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
this.nopStatements.forEach {
|
|
||||||
it.definingScope().remove(it)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(scope: AnonymousScope) {
|
|
||||||
if(scope.parent is INameScope) {
|
|
||||||
scopesToFlatten.add(scope) // get rid of the anonymous scope
|
|
||||||
}
|
|
||||||
|
|
||||||
return super.visit(scope)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(nopStatement: NopStatement) {
|
|
||||||
nopStatements.add(nopStatement)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
@ -4,6 +4,7 @@ import org.antlr.v4.runtime.*
|
|||||||
import prog8.ast.Module
|
import prog8.ast.Module
|
||||||
import prog8.ast.Program
|
import prog8.ast.Program
|
||||||
import prog8.ast.antlr.toAst
|
import prog8.ast.antlr.toAst
|
||||||
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.base.SyntaxError
|
import prog8.ast.base.SyntaxError
|
||||||
import prog8.ast.base.checkImportedValid
|
import prog8.ast.base.checkImportedValid
|
||||||
@ -33,6 +34,8 @@ internal class CustomLexer(val modulePath: Path, input: CharStream?) : prog8Lexe
|
|||||||
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
internal fun moduleName(fileName: Path) = fileName.toString().substringBeforeLast('.')
|
||||||
|
|
||||||
|
|
||||||
|
internal class ModuleImporter(private val errors: ErrorReporter) {
|
||||||
|
|
||||||
internal fun importModule(program: Program, filePath: Path): Module {
|
internal fun importModule(program: Program, filePath: Path): Module {
|
||||||
print("importing '${moduleName(filePath.fileName)}'")
|
print("importing '${moduleName(filePath.fileName)}'")
|
||||||
if(filePath.parent!=null) {
|
if(filePath.parent!=null) {
|
||||||
@ -58,7 +61,7 @@ internal fun importLibraryModule(program: Program, name: String): Module? {
|
|||||||
return executeImportDirective(program, import, Paths.get(""))
|
return executeImportDirective(program, import, Paths.get(""))
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
private fun importModule(program: Program, stream: CharStream, modulePath: Path, isLibrary: Boolean): Module {
|
||||||
val moduleName = moduleName(modulePath.fileName)
|
val moduleName = moduleName(modulePath.fileName)
|
||||||
val lexer = CustomLexer(modulePath, stream)
|
val lexer = CustomLexer(modulePath, stream)
|
||||||
val lexerErrors = LexerErrorListener()
|
val lexerErrors = LexerErrorListener()
|
||||||
@ -141,6 +144,7 @@ private fun executeImportDirective(program: Program, import: Directive, source:
|
|||||||
return importedModule
|
return importedModule
|
||||||
}
|
}
|
||||||
|
|
||||||
internal fun tryGetEmbeddedResource(name: String): InputStream? {
|
private fun tryGetEmbeddedResource(name: String): InputStream? {
|
||||||
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
return object{}.javaClass.getResourceAsStream("/prog8lib/$name")
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
22
compiler/src/prog8/server/dbus/IrmenDbusTest.kt
Normal file
22
compiler/src/prog8/server/dbus/IrmenDbusTest.kt
Normal file
@ -0,0 +1,22 @@
|
|||||||
|
package prog8.server.dbus
|
||||||
|
|
||||||
|
//import org.freedesktop.dbus.interfaces.DBusInterface
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//interface IrmenDbusTest: DBusInterface
|
||||||
|
//{
|
||||||
|
// fun Status(address: String): Map<Int, String>
|
||||||
|
//}
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//internal class TestService: IrmenDbusTest {
|
||||||
|
// override fun Status(address: String): Map<Int, String> {
|
||||||
|
// return mapOf(
|
||||||
|
// 5 to "hello",
|
||||||
|
// 42 to address
|
||||||
|
// )
|
||||||
|
// }
|
||||||
|
//
|
||||||
|
// override fun isRemote() = true
|
||||||
|
// override fun getObjectPath() = "/razorvine/TestService"
|
||||||
|
//}
|
17
compiler/src/prog8/server/dbus/clientmain.kt
Normal file
17
compiler/src/prog8/server/dbus/clientmain.kt
Normal file
@ -0,0 +1,17 @@
|
|||||||
|
package prog8.server.dbus
|
||||||
|
|
||||||
|
|
||||||
|
//import org.freedesktop.dbus.connections.impl.DBusConnection
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//fun main() {
|
||||||
|
// DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION).use {
|
||||||
|
// println(it.names.toList())
|
||||||
|
// println(it.uniqueName)
|
||||||
|
// println(it.address)
|
||||||
|
// println(it.machineId)
|
||||||
|
// val obj = it.getRemoteObject("local.net.razorvine.dbus.test", "/razorvine/TestService", IrmenDbusTest::class.java)
|
||||||
|
// println(obj.Status("irmen"))
|
||||||
|
// }
|
||||||
|
//}
|
||||||
|
//
|
18
compiler/src/prog8/server/dbus/testdbus.kt
Normal file
18
compiler/src/prog8/server/dbus/testdbus.kt
Normal file
@ -0,0 +1,18 @@
|
|||||||
|
package prog8.server.dbus
|
||||||
|
|
||||||
|
//import org.freedesktop.dbus.connections.impl.DBusConnection
|
||||||
|
//
|
||||||
|
//
|
||||||
|
//fun main() {
|
||||||
|
// DBusConnection.getConnection(DBusConnection.DBusBusType.SESSION).use {
|
||||||
|
// it.requestBusName("local.net.razorvine.dbus.test")
|
||||||
|
// println(it.names.toList())
|
||||||
|
// println(it.uniqueName)
|
||||||
|
// println(it.address)
|
||||||
|
// println(it.machineId)
|
||||||
|
// val service = TestService()
|
||||||
|
// it.exportObject(service.objectPath, service)
|
||||||
|
//
|
||||||
|
// Thread.sleep(100000)
|
||||||
|
// }
|
||||||
|
//}
|
@ -1,658 +0,0 @@
|
|||||||
package prog8.vm
|
|
||||||
|
|
||||||
import prog8.ast.base.ByteDatatypes
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.WordDatatypes
|
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
|
||||||
import prog8.vm.astvm.VmExecutionException
|
|
||||||
import java.util.Objects
|
|
||||||
import kotlin.math.abs
|
|
||||||
import kotlin.math.pow
|
|
||||||
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Rather than a literal value (NumericLiteralValue) that occurs in the parsed source code,
|
|
||||||
* this runtime value can be used to *execute* the parsed Ast (or another intermediary form)
|
|
||||||
* It contains a value of a variable during run time of the program and provides arithmetic operations on the value.
|
|
||||||
*/
|
|
||||||
|
|
||||||
abstract class RuntimeValueBase(val type: DataType) {
|
|
||||||
abstract fun numericValue(): Number
|
|
||||||
abstract fun integerValue(): Int
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RuntimeValueNumeric(type: DataType, num: Number): RuntimeValueBase(type) {
|
|
||||||
|
|
||||||
val byteval: Short?
|
|
||||||
val wordval: Int?
|
|
||||||
val floatval: Double?
|
|
||||||
val asBoolean: Boolean
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromLv(literalValue: NumericLiteralValue): RuntimeValueNumeric {
|
|
||||||
return RuntimeValueNumeric(literalValue.type, num = literalValue.number)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init {
|
|
||||||
when (type) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
val inum = num.toInt()
|
|
||||||
require(inum in 0..255) { "invalid value for ubyte: $inum" }
|
|
||||||
byteval = inum.toShort()
|
|
||||||
wordval = null
|
|
||||||
floatval = null
|
|
||||||
asBoolean = byteval != 0.toShort()
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val inum = num.toInt()
|
|
||||||
require(inum in -128..127) { "invalid value for byte: $inum" }
|
|
||||||
byteval = inum.toShort()
|
|
||||||
wordval = null
|
|
||||||
floatval = null
|
|
||||||
asBoolean = byteval != 0.toShort()
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
val inum = num.toInt()
|
|
||||||
require(inum in 0..65535) { "invalid value for uword: $inum" }
|
|
||||||
wordval = inum
|
|
||||||
byteval = null
|
|
||||||
floatval = null
|
|
||||||
asBoolean = wordval != 0
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val inum = num.toInt()
|
|
||||||
require(inum in -32768..32767) { "invalid value for word: $inum" }
|
|
||||||
wordval = inum
|
|
||||||
byteval = null
|
|
||||||
floatval = null
|
|
||||||
asBoolean = wordval != 0
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
floatval = num.toDouble()
|
|
||||||
byteval = null
|
|
||||||
wordval = null
|
|
||||||
asBoolean = floatval != 0.0
|
|
||||||
}
|
|
||||||
else -> throw VmExecutionException("not a numeric value")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> "ub:%02x".format(byteval)
|
|
||||||
DataType.BYTE -> {
|
|
||||||
if (byteval!! < 0)
|
|
||||||
"b:-%02x".format(abs(byteval.toInt()))
|
|
||||||
else
|
|
||||||
"b:%02x".format(byteval)
|
|
||||||
}
|
|
||||||
DataType.UWORD -> "uw:%04x".format(wordval)
|
|
||||||
DataType.WORD -> {
|
|
||||||
if (wordval!! < 0)
|
|
||||||
"w:-%04x".format(abs(wordval))
|
|
||||||
else
|
|
||||||
"w:%04x".format(wordval)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> "f:$floatval"
|
|
||||||
else -> "???"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun numericValue(): Number {
|
|
||||||
return when (type) {
|
|
||||||
in ByteDatatypes -> byteval!!
|
|
||||||
in WordDatatypes -> wordval!!
|
|
||||||
DataType.FLOAT -> floatval!!
|
|
||||||
else -> throw ArithmeticException("invalid datatype for numeric value: $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun integerValue(): Int {
|
|
||||||
return when (type) {
|
|
||||||
in ByteDatatypes -> byteval!!.toInt()
|
|
||||||
in WordDatatypes -> wordval!!
|
|
||||||
DataType.FLOAT -> throw ArithmeticException("float to integer loss of precision")
|
|
||||||
else -> throw ArithmeticException("invalid datatype for integer value: $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(byteval, wordval, floatval, type)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other == null || other !is RuntimeValueNumeric)
|
|
||||||
return false
|
|
||||||
return compareTo(other) == 0 // note: datatype doesn't matter
|
|
||||||
}
|
|
||||||
|
|
||||||
operator fun compareTo(other: RuntimeValueNumeric): Int = numericValue().toDouble().compareTo(other.numericValue().toDouble())
|
|
||||||
|
|
||||||
private fun arithResult(leftDt: DataType, result: Number, rightDt: DataType, op: String): RuntimeValueNumeric {
|
|
||||||
if (leftDt != rightDt)
|
|
||||||
throw ArithmeticException("left and right datatypes are not the same")
|
|
||||||
if (result.toDouble() < 0) {
|
|
||||||
return when (leftDt) {
|
|
||||||
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)
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, (number xor 255) + 1)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, (number xor 65535) + 1)
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val v = result.toInt() and 255
|
|
||||||
if (v < 128)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v - 256)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val v = result.toInt() and 65535
|
|
||||||
if (v < 32768)
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v - 65536)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
|
|
||||||
else -> throw ArithmeticException("$op on non-numeric type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return when (leftDt) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result.toInt() and 255)
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val v = result.toInt() and 255
|
|
||||||
if (v < 128)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v - 256)
|
|
||||||
}
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result.toInt() and 65535)
|
|
||||||
DataType.WORD -> {
|
|
||||||
val v = result.toInt() and 65535
|
|
||||||
if (v < 32768)
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v - 65536)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
|
|
||||||
else -> throw ArithmeticException("$op on non-numeric type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun add(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
|
|
||||||
throw ArithmeticException("floating point loss of precision on type $type")
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
val result = v1.toDouble() + v2.toDouble()
|
|
||||||
return arithResult(type, result, other.type, "add")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun sub(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
|
|
||||||
throw ArithmeticException("floating point loss of precision on type $type")
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
val result = v1.toDouble() - v2.toDouble()
|
|
||||||
return arithResult(type, result, other.type, "sub")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun mul(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
|
|
||||||
throw ArithmeticException("floating point loss of precision on type $type")
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
val result = v1.toDouble() * v2.toDouble()
|
|
||||||
return arithResult(type, result, other.type, "mul")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun div(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
if (other.type == DataType.FLOAT && (type != DataType.FLOAT))
|
|
||||||
throw ArithmeticException("floating point loss of precision on type $type")
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
if (v2.toDouble() == 0.0) {
|
|
||||||
when (type) {
|
|
||||||
DataType.UBYTE -> return RuntimeValueNumeric(DataType.UBYTE, 255)
|
|
||||||
DataType.BYTE -> return RuntimeValueNumeric(DataType.BYTE, 127)
|
|
||||||
DataType.UWORD -> return RuntimeValueNumeric(DataType.UWORD, 65535)
|
|
||||||
DataType.WORD -> return RuntimeValueNumeric(DataType.WORD, 32767)
|
|
||||||
else -> {
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
val result = v1.toDouble() / v2.toDouble()
|
|
||||||
// NOTE: integer division returns integer result!
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, result)
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, result)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, result)
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, result)
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, result)
|
|
||||||
else -> throw ArithmeticException("div on non-numeric type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun remainder(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
val result = v1.toDouble() % v2.toDouble()
|
|
||||||
return arithResult(type, result, other.type, "remainder")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun pow(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
val v1 = numericValue()
|
|
||||||
val v2 = other.numericValue()
|
|
||||||
val result = v1.toDouble().pow(v2.toDouble())
|
|
||||||
return arithResult(type, result, other.type, "pow")
|
|
||||||
}
|
|
||||||
|
|
||||||
fun shl(): RuntimeValueNumeric {
|
|
||||||
val v = integerValue()
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(type, (v shl 1) and 255)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(type, (v shl 1) and 65535)
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val value = v shl 1
|
|
||||||
if (value < 128)
|
|
||||||
RuntimeValueNumeric(type, value)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, value - 256)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val value = v shl 1
|
|
||||||
if (value < 32768)
|
|
||||||
RuntimeValueNumeric(type, value)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, value - 65536)
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("invalid type for shl: $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun shr(): RuntimeValueNumeric {
|
|
||||||
val v = integerValue()
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(type, v ushr 1)
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(type, v shr 1)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(type, v ushr 1)
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(type, v shr 1)
|
|
||||||
else -> throw ArithmeticException("invalid type for shr: $type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rol(carry: Boolean): Pair<RuntimeValueNumeric, Boolean> {
|
|
||||||
// 9 or 17 bit rotate left (with carry))
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
val v = byteval!!.toInt()
|
|
||||||
val newCarry = (v and 0x80) != 0
|
|
||||||
val newval = (v and 0x7f shl 1) or (if (carry) 1 else 0)
|
|
||||||
Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry)
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
val v = wordval!!
|
|
||||||
val newCarry = (v and 0x8000) != 0
|
|
||||||
val newval = (v and 0x7fff shl 1) or (if (carry) 1 else 0)
|
|
||||||
Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry)
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("rol can only work on byte/word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ror(carry: Boolean): Pair<RuntimeValueNumeric, Boolean> {
|
|
||||||
// 9 or 17 bit rotate right (with carry)
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
val v = byteval!!.toInt()
|
|
||||||
val newCarry = v and 1 != 0
|
|
||||||
val newval = (v ushr 1) or (if (carry) 0x80 else 0)
|
|
||||||
Pair(RuntimeValueNumeric(DataType.UBYTE, newval), newCarry)
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
val v = wordval!!
|
|
||||||
val newCarry = v and 1 != 0
|
|
||||||
val newval = (v ushr 1) or (if (carry) 0x8000 else 0)
|
|
||||||
Pair(RuntimeValueNumeric(DataType.UWORD, newval), newCarry)
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("ror2 can only work on byte/word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun rol2(): RuntimeValueNumeric {
|
|
||||||
// 8 or 16 bit rotate left
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
val v = byteval!!.toInt()
|
|
||||||
val carry = (v and 0x80) ushr 7
|
|
||||||
val newval = (v and 0x7f shl 1) or carry
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, newval)
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
val v = wordval!!
|
|
||||||
val carry = (v and 0x8000) ushr 15
|
|
||||||
val newval = (v and 0x7fff shl 1) or carry
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, newval)
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("rol2 can only work on byte/word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun ror2(): RuntimeValueNumeric {
|
|
||||||
// 8 or 16 bit rotate right
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE, DataType.BYTE -> {
|
|
||||||
val v = byteval!!.toInt()
|
|
||||||
val carry = v and 1 shl 7
|
|
||||||
val newval = (v ushr 1) or carry
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, newval)
|
|
||||||
}
|
|
||||||
DataType.UWORD, DataType.WORD -> {
|
|
||||||
val v = wordval!!
|
|
||||||
val carry = v and 1 shl 15
|
|
||||||
val newval = (v ushr 1) or carry
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, newval)
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("ror2 can only work on byte/word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun neg(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, -(byteval!!))
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, -(wordval!!))
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, -(floatval)!!)
|
|
||||||
else -> throw ArithmeticException("neg can only work on byte/word/float")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun abs(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, abs(byteval!!.toInt()))
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, abs(wordval!!))
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, abs(floatval!!))
|
|
||||||
else -> throw ArithmeticException("abs can only work on byte/word/float")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bitand(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
val v1 = integerValue()
|
|
||||||
val v2 = other.integerValue()
|
|
||||||
val result = v1 and v2
|
|
||||||
return RuntimeValueNumeric(type, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bitor(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
val v1 = integerValue()
|
|
||||||
val v2 = other.integerValue()
|
|
||||||
val result = v1 or v2
|
|
||||||
return RuntimeValueNumeric(type, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun bitxor(other: RuntimeValueNumeric): RuntimeValueNumeric {
|
|
||||||
val v1 = integerValue()
|
|
||||||
val v2 = other.integerValue()
|
|
||||||
val result = v1 xor v2
|
|
||||||
return RuntimeValueNumeric(type, result)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun and(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean && other.asBoolean) 1 else 0)
|
|
||||||
fun or(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean || other.asBoolean) 1 else 0)
|
|
||||||
fun xor(other: RuntimeValueNumeric) = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean xor other.asBoolean) 1 else 0)
|
|
||||||
fun not() = RuntimeValueNumeric(DataType.UBYTE, if (this.asBoolean) 0 else 1)
|
|
||||||
|
|
||||||
fun inv(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv() and 255)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(type, wordval!!.inv() and 65535)
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(type, byteval!!.toInt().inv())
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(type, wordval!!.inv())
|
|
||||||
else -> throw ArithmeticException("inv can only work on byte/word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun inc(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! + 1) and 255)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! + 1) and 65535)
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val newval = byteval!! + 1
|
|
||||||
if (newval == 128)
|
|
||||||
RuntimeValueNumeric(type, -128)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, newval)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val newval = wordval!! + 1
|
|
||||||
if (newval == 32768)
|
|
||||||
RuntimeValueNumeric(type, -32768)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, newval)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! + 1)
|
|
||||||
else -> throw ArithmeticException("inc can only work on numeric types")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun dec(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(type, (byteval!! - 1) and 255)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(type, (wordval!! - 1) and 65535)
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val newval = byteval!! - 1
|
|
||||||
if (newval == -129)
|
|
||||||
RuntimeValueNumeric(type, 127)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, newval)
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
val newval = wordval!! - 1
|
|
||||||
if (newval == -32769)
|
|
||||||
RuntimeValueNumeric(type, 32767)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(type, newval)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, floatval!! - 1)
|
|
||||||
else -> throw ArithmeticException("dec can only work on numeric types")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun msb(): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
in ByteDatatypes -> RuntimeValueNumeric(DataType.UBYTE, 0)
|
|
||||||
in WordDatatypes -> RuntimeValueNumeric(DataType.UBYTE, wordval!! ushr 8 and 255)
|
|
||||||
else -> throw ArithmeticException("msb can only work on (u)byte/(u)word")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun cast(targetType: DataType): RuntimeValueNumeric {
|
|
||||||
return when (type) {
|
|
||||||
DataType.UBYTE -> {
|
|
||||||
when (targetType) {
|
|
||||||
DataType.UBYTE -> this
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val nval = byteval!!.toInt()
|
|
||||||
if (nval < 128)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, nval)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, nval - 256)
|
|
||||||
}
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue())
|
|
||||||
DataType.WORD -> {
|
|
||||||
val nval = numericValue().toInt()
|
|
||||||
if (nval < 32768)
|
|
||||||
RuntimeValueNumeric(DataType.WORD, nval)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.WORD, nval - 65536)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.BYTE -> {
|
|
||||||
when (targetType) {
|
|
||||||
DataType.BYTE -> this
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue() and 65535)
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, integerValue())
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.UWORD -> {
|
|
||||||
when (targetType) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val v = integerValue()
|
|
||||||
if (v < 128)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v - 256)
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 255)
|
|
||||||
DataType.UWORD -> this
|
|
||||||
DataType.WORD -> {
|
|
||||||
val v = integerValue()
|
|
||||||
if (v < 32768)
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.WORD, v - 65536)
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.WORD -> {
|
|
||||||
when (targetType) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val v = integerValue() and 255
|
|
||||||
if (v < 128)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v)
|
|
||||||
else
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, v - 256)
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, integerValue() and 65535)
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, integerValue())
|
|
||||||
DataType.WORD -> this
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, numericValue())
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> {
|
|
||||||
when (targetType) {
|
|
||||||
DataType.BYTE -> {
|
|
||||||
val integer = numericValue().toInt()
|
|
||||||
if (integer in -128..127)
|
|
||||||
RuntimeValueNumeric(DataType.BYTE, integer)
|
|
||||||
else
|
|
||||||
throw ArithmeticException("overflow when casting float to byte: $this")
|
|
||||||
}
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, numericValue().toInt())
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, numericValue().toInt())
|
|
||||||
DataType.WORD -> {
|
|
||||||
val integer = numericValue().toInt()
|
|
||||||
if (integer in -32768..32767)
|
|
||||||
RuntimeValueNumeric(DataType.WORD, integer)
|
|
||||||
else
|
|
||||||
throw ArithmeticException("overflow when casting float to word: $this")
|
|
||||||
}
|
|
||||||
DataType.FLOAT -> this
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
else -> throw ArithmeticException("invalid type cast from $type to $targetType")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RuntimeValueString(val str: String, val altEncoding: Boolean, val heapId: Int?): RuntimeValueBase(DataType.STR) {
|
|
||||||
companion object {
|
|
||||||
fun fromLv(string: StringLiteralValue): RuntimeValueString {
|
|
||||||
return RuntimeValueString(string.value, string.altEncoding, string.heapId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String = if(type==DataType.STR) "str:$str" else "???"
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, str)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other == null || other !is RuntimeValueString)
|
|
||||||
return false
|
|
||||||
return type == other.type && str == other.str
|
|
||||||
}
|
|
||||||
|
|
||||||
fun iterator(): Iterator<Number> = str.map { it.toShort() }.iterator()
|
|
||||||
|
|
||||||
override fun numericValue(): Number {
|
|
||||||
throw VmExecutionException("string is not a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun integerValue(): Int {
|
|
||||||
throw VmExecutionException("string is not a number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
open class RuntimeValueArray(type: DataType, val array: Array<Number>, val heapId: Int?): RuntimeValueBase(type) {
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
fun fromLv(array: ArrayLiteralValue): RuntimeValueArray {
|
|
||||||
return if (array.type.istype(DataType.ARRAY_F)) {
|
|
||||||
val doubleArray = array.value.map { (it as NumericLiteralValue).number }.toTypedArray()
|
|
||||||
RuntimeValueArray(DataType.ARRAY_F, doubleArray, array.heapId)
|
|
||||||
} else {
|
|
||||||
val resultArray = mutableListOf<Number>()
|
|
||||||
for (elt in array.value.withIndex()) {
|
|
||||||
if (elt.value is NumericLiteralValue)
|
|
||||||
resultArray.add((elt.value as NumericLiteralValue).number.toInt())
|
|
||||||
else {
|
|
||||||
resultArray.add((elt.hashCode())) // ...poor man's implementation of ADDRESSOF(array), it probably won't work very well
|
|
||||||
}
|
|
||||||
}
|
|
||||||
RuntimeValueArray(array.type.typeOrElse(DataType.STRUCT), resultArray.toTypedArray(), array.heapId)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun toString(): String {
|
|
||||||
return when (type) {
|
|
||||||
DataType.ARRAY_UB -> "array_ub:..."
|
|
||||||
DataType.ARRAY_B -> "array_b:..."
|
|
||||||
DataType.ARRAY_UW -> "array_uw:..."
|
|
||||||
DataType.ARRAY_W -> "array_w:..."
|
|
||||||
DataType.ARRAY_F -> "array_f:..."
|
|
||||||
else -> "???"
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun hashCode(): Int = Objects.hash(type, array)
|
|
||||||
|
|
||||||
override fun equals(other: Any?): Boolean {
|
|
||||||
if (other == null || other !is RuntimeValueArray)
|
|
||||||
return false
|
|
||||||
return type == other.type && array.contentEquals(other.array)
|
|
||||||
}
|
|
||||||
|
|
||||||
open fun iterator(): Iterator<Number> = array.iterator()
|
|
||||||
|
|
||||||
override fun numericValue(): Number {
|
|
||||||
throw VmExecutionException("array is not a number")
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun integerValue(): Int {
|
|
||||||
throw VmExecutionException("array is not a number")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValueArray(type, range.toList().toTypedArray(), null) {
|
|
||||||
override fun iterator(): Iterator<Number> {
|
|
||||||
return range.iterator()
|
|
||||||
}
|
|
||||||
}
|
|
File diff suppressed because it is too large
Load Diff
@ -1,177 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.ArrayElementTypes
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.FatalAstException
|
|
||||||
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.*
|
|
||||||
|
|
||||||
|
|
||||||
typealias BuiltinfunctionCaller = (name: String, args: List<RuntimeValueNumeric>, flags: StatusFlags) -> RuntimeValueNumeric?
|
|
||||||
typealias SubroutineCaller = (sub: Subroutine, args: List<RuntimeValueNumeric>, startAtLabel: Label?) -> RuntimeValueNumeric?
|
|
||||||
|
|
||||||
|
|
||||||
class EvalContext(val program: Program, val mem: Memory, val statusflags: StatusFlags,
|
|
||||||
val runtimeVars: RuntimeVariables,
|
|
||||||
val performBuiltinFunction: BuiltinfunctionCaller,
|
|
||||||
val executeSubroutine: SubroutineCaller)
|
|
||||||
|
|
||||||
fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValueBase {
|
|
||||||
val constval = expr.constValue(ctx.program)
|
|
||||||
if(constval!=null)
|
|
||||||
return RuntimeValueNumeric.fromLv(constval)
|
|
||||||
|
|
||||||
when(expr) {
|
|
||||||
is NumericLiteralValue -> return RuntimeValueNumeric.fromLv(expr)
|
|
||||||
is StringLiteralValue -> return RuntimeValueString.fromLv(expr)
|
|
||||||
is ArrayLiteralValue -> return RuntimeValueArray.fromLv(expr)
|
|
||||||
is PrefixExpression -> {
|
|
||||||
return when(expr.operator) {
|
|
||||||
"-" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).neg()
|
|
||||||
"~" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).inv()
|
|
||||||
"not" -> (evaluate(expr.expression, ctx) as RuntimeValueNumeric).not()
|
|
||||||
// unary '+' should have been optimized away
|
|
||||||
else -> throw VmExecutionException("unsupported prefix operator "+expr.operator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is BinaryExpression -> {
|
|
||||||
val left = evaluate(expr.left, ctx) as RuntimeValueNumeric
|
|
||||||
val right = evaluate(expr.right, ctx) as RuntimeValueNumeric
|
|
||||||
return when(expr.operator) {
|
|
||||||
"<" -> RuntimeValueNumeric(DataType.UBYTE, if (left < right) 1 else 0)
|
|
||||||
"<=" -> RuntimeValueNumeric(DataType.UBYTE, if (left <= right) 1 else 0)
|
|
||||||
">" -> RuntimeValueNumeric(DataType.UBYTE, if (left > right) 1 else 0)
|
|
||||||
">=" -> RuntimeValueNumeric(DataType.UBYTE, if (left >= right) 1 else 0)
|
|
||||||
"==" -> RuntimeValueNumeric(DataType.UBYTE, if (left == right) 1 else 0)
|
|
||||||
"!=" -> RuntimeValueNumeric(DataType.UBYTE, if (left != right) 1 else 0)
|
|
||||||
"+" -> left.add(right)
|
|
||||||
"-" -> left.sub(right)
|
|
||||||
"*" -> left.mul(right)
|
|
||||||
"/" -> left.div(right)
|
|
||||||
"**" -> left.pow(right)
|
|
||||||
"<<" -> {
|
|
||||||
var result = left
|
|
||||||
repeat(right.integerValue()) {result = result.shl()}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
">>" -> {
|
|
||||||
var result = left
|
|
||||||
repeat(right.integerValue()) {result = result.shr()}
|
|
||||||
result
|
|
||||||
}
|
|
||||||
"%" -> left.remainder(right)
|
|
||||||
"|" -> left.bitor(right)
|
|
||||||
"&" -> left.bitand(right)
|
|
||||||
"^" -> left.bitxor(right)
|
|
||||||
"and" -> left.and(right)
|
|
||||||
"or" -> left.or(right)
|
|
||||||
"xor" -> left.xor(right)
|
|
||||||
else -> throw VmExecutionException("unsupported operator "+expr.operator)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is ArrayIndexedExpression -> {
|
|
||||||
val array = evaluate(expr.identifier, ctx)
|
|
||||||
val index = evaluate(expr.arrayspec.index, ctx) as RuntimeValueNumeric
|
|
||||||
return when (array) {
|
|
||||||
is RuntimeValueString -> {
|
|
||||||
val value = array.str[index.integerValue()]
|
|
||||||
RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value.toShort())
|
|
||||||
}
|
|
||||||
is RuntimeValueArray -> {
|
|
||||||
val value = array.array[index.integerValue()]
|
|
||||||
RuntimeValueNumeric(ArrayElementTypes.getValue(array.type), value)
|
|
||||||
}
|
|
||||||
else -> throw VmExecutionException("weird type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is TypecastExpression -> {
|
|
||||||
return (evaluate(expr.expression, ctx) as RuntimeValueNumeric).cast(expr.type)
|
|
||||||
}
|
|
||||||
is AddressOf -> {
|
|
||||||
// we support: address of heap var -> the heap id
|
|
||||||
return try {
|
|
||||||
val heapId = expr.identifier.heapId(ctx.program.namespace)
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, heapId)
|
|
||||||
} catch( f: FatalAstException) {
|
|
||||||
// fallback: use the hash of the name, so we have at least *a* value...
|
|
||||||
val address = expr.identifier.hashCode() and 65535
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, address)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is DirectMemoryRead -> {
|
|
||||||
val address = (evaluate(expr.addressExpression, ctx) as RuntimeValueNumeric).wordval!!
|
|
||||||
return RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address))
|
|
||||||
}
|
|
||||||
is RegisterExpr -> return ctx.runtimeVars.get(ctx.program.namespace, expr.register.name)
|
|
||||||
is IdentifierReference -> {
|
|
||||||
val scope = expr.definingScope()
|
|
||||||
val variable = scope.lookup(expr.nameInSource, expr)
|
|
||||||
if(variable is VarDecl) {
|
|
||||||
when {
|
|
||||||
variable.type==VarDeclType.VAR -> return ctx.runtimeVars.get(variable.definingScope(), variable.name)
|
|
||||||
variable.datatype==DataType.STRUCT -> throw VmExecutionException("cannot process structs by-value. at ${expr.position}")
|
|
||||||
else -> {
|
|
||||||
val address = ctx.runtimeVars.getMemoryAddress(variable.definingScope(), variable.name)
|
|
||||||
return when(variable.datatype) {
|
|
||||||
DataType.UBYTE -> RuntimeValueNumeric(DataType.UBYTE, ctx.mem.getUByte(address))
|
|
||||||
DataType.BYTE -> RuntimeValueNumeric(DataType.BYTE, ctx.mem.getSByte(address))
|
|
||||||
DataType.UWORD -> RuntimeValueNumeric(DataType.UWORD, ctx.mem.getUWord(address))
|
|
||||||
DataType.WORD -> RuntimeValueNumeric(DataType.WORD, ctx.mem.getSWord(address))
|
|
||||||
DataType.FLOAT -> RuntimeValueNumeric(DataType.FLOAT, ctx.mem.getFloat(address))
|
|
||||||
DataType.STR -> RuntimeValueString(ctx.mem.getString(address, false), false, null)
|
|
||||||
else -> throw VmExecutionException("unexpected datatype $variable")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else
|
|
||||||
throw VmExecutionException("weird identifier reference $variable")
|
|
||||||
}
|
|
||||||
is FunctionCall -> {
|
|
||||||
val sub = expr.target.targetStatement(ctx.program.namespace)
|
|
||||||
val args = expr.args.map { evaluate(it, ctx) as RuntimeValueNumeric }
|
|
||||||
return when(sub) {
|
|
||||||
is Subroutine -> {
|
|
||||||
val result = ctx.executeSubroutine(sub, args, null)
|
|
||||||
?: throw VmExecutionException("expected a result from functioncall $expr")
|
|
||||||
result
|
|
||||||
}
|
|
||||||
is BuiltinFunctionStatementPlaceholder -> {
|
|
||||||
val result = ctx.performBuiltinFunction(sub.name, args, ctx.statusflags)
|
|
||||||
?: throw VmExecutionException("expected 1 result from functioncall $expr")
|
|
||||||
result
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw VmExecutionException("unimplemented function call target $sub")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
is RangeExpr -> {
|
|
||||||
val cRange = expr.toConstantIntegerRange()
|
|
||||||
if(cRange!=null) {
|
|
||||||
val dt = expr.inferType(ctx.program)
|
|
||||||
if(dt.isKnown)
|
|
||||||
return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), cRange)
|
|
||||||
else
|
|
||||||
throw VmExecutionException("couldn't determine datatype")
|
|
||||||
}
|
|
||||||
val fromVal = (evaluate(expr.from, ctx) as RuntimeValueNumeric).integerValue()
|
|
||||||
val toVal = (evaluate(expr.to, ctx) as RuntimeValueNumeric).integerValue()
|
|
||||||
val stepVal = (evaluate(expr.step, ctx) as RuntimeValueNumeric).integerValue()
|
|
||||||
val range = makeRange(fromVal, toVal, stepVal)
|
|
||||||
val dt = expr.inferType(ctx.program)
|
|
||||||
if(dt.isKnown)
|
|
||||||
return RuntimeValueRange(dt.typeOrElse(DataType.UBYTE), range)
|
|
||||||
else
|
|
||||||
throw VmExecutionException("couldn't determine datatype")
|
|
||||||
}
|
|
||||||
else -> {
|
|
||||||
throw VmExecutionException("unimplemented expression node $expr")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,122 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.compiler.target.CompilationTarget
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
import kotlin.math.abs
|
|
||||||
|
|
||||||
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 getUByteDirectly(address: Int): Short {
|
|
||||||
return mem[address]
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getSByte(address: Int): Short {
|
|
||||||
val ubyte = getUByte(address)
|
|
||||||
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 $value")
|
|
||||||
mem[address] =
|
|
||||||
if(observed[address]) writeObserver(address, value)
|
|
||||||
else value
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setUByteDirectly(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 $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 getUByte(address) + 256*getUByte(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 $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 $value")
|
|
||||||
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 = C64MachineDefinition.Mflpt5.fromNumber(value)
|
|
||||||
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 C64MachineDefinition.Mflpt5(getUByte(address), getUByte(address + 1), getUByte(address + 2),
|
|
||||||
getUByte(address + 3), getUByte(address + 4)).toDouble()
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setString(address: Int, str: String, altEncoding: Boolean) {
|
|
||||||
val encoded = CompilationTarget.encodeString(str, altEncoding)
|
|
||||||
var addr = address
|
|
||||||
for (c in encoded) setUByte(addr++, c)
|
|
||||||
setUByte(addr, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun getString(strAddress: Int, altEncoding: Boolean): String {
|
|
||||||
val encoded = mutableListOf<Short>()
|
|
||||||
var addr = strAddress
|
|
||||||
while(true) {
|
|
||||||
val byte = getUByte(addr++)
|
|
||||||
if(byte==0.toShort()) break
|
|
||||||
encoded.add(byte)
|
|
||||||
}
|
|
||||||
return CompilationTarget.decodeString(encoded, altEncoding)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun clear() {
|
|
||||||
for(i in 0..65535) setUByte(i, 0)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun copy(from: Int, to: Int, numbytes: Int) {
|
|
||||||
for(i in 0 until numbytes)
|
|
||||||
setUByte(to+i, getUByte(from+i))
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,184 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition
|
|
||||||
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 java.util.ArrayDeque
|
|
||||||
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
|
|
||||||
val keyboardBuffer = ArrayDeque<Char>()
|
|
||||||
|
|
||||||
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) {
|
|
||||||
keyboardBuffer.add(p0.keyChar)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun keyPressed(p0: KeyEvent) {
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun keyReleased(p0: KeyEvent?) {
|
|
||||||
}
|
|
||||||
|
|
||||||
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 = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size]
|
|
||||||
g2d.clearRect(0, 0, SCREENWIDTH, SCREENHEIGHT)
|
|
||||||
cursorX = 0
|
|
||||||
cursorY = 0
|
|
||||||
}
|
|
||||||
fun setPixel(x: Int, y: Int, color: Short) {
|
|
||||||
image.setRGB(x, y, C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size].rgb)
|
|
||||||
}
|
|
||||||
fun drawLine(x1: Int, y1: Int, x2: Int, y2: Int, color: Short) {
|
|
||||||
g2d.color = C64MachineDefinition.colorPalette[color % C64MachineDefinition.colorPalette.size]
|
|
||||||
g2d.drawLine(x1, y1, x2, y2)
|
|
||||||
}
|
|
||||||
|
|
||||||
fun printAsciiText(text: String) {
|
|
||||||
val t2 = text.substringBefore(0.toChar())
|
|
||||||
val petscii = Petscii.encodePetscii(t2, true)
|
|
||||||
petscii.forEach { printPetsciiChar(it) }
|
|
||||||
}
|
|
||||||
|
|
||||||
fun printPetsciiChar(petscii: Short) {
|
|
||||||
if(petscii in listOf(0x0d.toShort(), 0x8d.toShort())) {
|
|
||||||
// Return and shift-Return
|
|
||||||
cursorX=0
|
|
||||||
cursorY++
|
|
||||||
} else {
|
|
||||||
val scr = Petscii.petscii2scr(petscii, false)
|
|
||||||
setScreenChar(cursorX, cursorY, scr, 1)
|
|
||||||
cursorX++
|
|
||||||
if (cursorX >= (SCREENWIDTH / 8)) {
|
|
||||||
cursorY++
|
|
||||||
cursorX = 0
|
|
||||||
}
|
|
||||||
}
|
|
||||||
while(cursorY>=(SCREENHEIGHT/8)) {
|
|
||||||
// scroll the screen up because the cursor went past the last line
|
|
||||||
Thread.sleep(10)
|
|
||||||
val screen = image.copy()
|
|
||||||
val graphics = image.graphics as Graphics2D
|
|
||||||
graphics.drawImage(screen, 0, -8, null)
|
|
||||||
val color = graphics.color
|
|
||||||
graphics.color = C64MachineDefinition.colorPalette[6]
|
|
||||||
graphics.fillRect(0, 24*8, SCREENWIDTH, 25*8)
|
|
||||||
graphics.color=color
|
|
||||||
cursorY--
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
fun setScreenChar(x: Int, y: Int, screencode: Short, color: Short) {
|
|
||||||
g2d.clearRect(8*x, 8*y, 8, 8)
|
|
||||||
val colorIdx = (color % C64MachineDefinition.colorPalette.size).toShort()
|
|
||||||
val coloredImage = C64MachineDefinition.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)
|
|
||||||
}
|
|
||||||
|
|
||||||
companion object {
|
|
||||||
const val SCREENWIDTH = 320
|
|
||||||
const val SCREENHEIGHT = 200
|
|
||||||
const val SCALING = 3
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
class ScreenDialog(title: String) : JFrame(title) {
|
|
||||||
val canvas = BitmapScreenPanel()
|
|
||||||
val keyboardBuffer = canvas.keyboardBuffer
|
|
||||||
|
|
||||||
init {
|
|
||||||
val borderWidth = 16
|
|
||||||
layout = GridBagLayout()
|
|
||||||
defaultCloseOperation = 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 = C64MachineDefinition.colorPalette[14]
|
|
||||||
}
|
|
||||||
val borderBottom = JPanel().apply {
|
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * (BitmapScreenPanel.SCREENWIDTH +2*borderWidth), BitmapScreenPanel.SCALING * borderWidth)
|
|
||||||
background = C64MachineDefinition.colorPalette[14]
|
|
||||||
}
|
|
||||||
val borderLeft = JPanel().apply {
|
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
|
||||||
background = C64MachineDefinition.colorPalette[14]
|
|
||||||
}
|
|
||||||
val borderRight = JPanel().apply {
|
|
||||||
preferredSize =Dimension(BitmapScreenPanel.SCALING * borderWidth, BitmapScreenPanel.SCALING * BitmapScreenPanel.SCREENHEIGHT)
|
|
||||||
background = C64MachineDefinition.colorPalette[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()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
private fun BufferedImage.copy(): BufferedImage {
|
|
||||||
val bcopy = BufferedImage(this.width, this.height, this.type)
|
|
||||||
val g = bcopy.graphics
|
|
||||||
g.drawImage(this, 0, 0, null)
|
|
||||||
g.dispose()
|
|
||||||
return bcopy
|
|
||||||
}
|
|
@ -1,80 +0,0 @@
|
|||||||
package prog8.vm.astvm
|
|
||||||
|
|
||||||
import prog8.ast.Program
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.ast.base.Position
|
|
||||||
import prog8.ast.base.Register
|
|
||||||
import prog8.ast.base.VarDeclType
|
|
||||||
import prog8.ast.expressions.ArrayLiteralValue
|
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
|
||||||
import prog8.ast.processing.IAstModifyingVisitor
|
|
||||||
import prog8.ast.statements.Statement
|
|
||||||
import prog8.ast.statements.StructDecl
|
|
||||||
import prog8.ast.statements.VarDecl
|
|
||||||
import prog8.ast.statements.ZeropageWish
|
|
||||||
import prog8.vm.RuntimeValueArray
|
|
||||||
import prog8.vm.RuntimeValueNumeric
|
|
||||||
import prog8.vm.RuntimeValueString
|
|
||||||
|
|
||||||
class VariablesCreator(private val runtimeVariables: RuntimeVariables) : IAstModifyingVisitor {
|
|
||||||
|
|
||||||
override fun visit(program: Program) {
|
|
||||||
// define the three registers as global variables
|
|
||||||
runtimeVariables.define(program.namespace, Register.A.name, RuntimeValueNumeric(DataType.UBYTE, 0))
|
|
||||||
runtimeVariables.define(program.namespace, Register.X.name, RuntimeValueNumeric(DataType.UBYTE, 255))
|
|
||||||
runtimeVariables.define(program.namespace, Register.Y.name, RuntimeValueNumeric(DataType.UBYTE, 0))
|
|
||||||
|
|
||||||
val globalpos = Position("<<global>>", 0, 0, 0)
|
|
||||||
val vdA = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.A.name, null,
|
|
||||||
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
|
||||||
val vdX = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.X.name, null,
|
|
||||||
NumericLiteralValue.optimalInteger(255, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
|
||||||
val vdY = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.DONTCARE, null, Register.Y.name, null,
|
|
||||||
NumericLiteralValue.optimalInteger(0, globalpos), isArray = false, autogeneratedDontRemove = true, position = globalpos)
|
|
||||||
vdA.linkParents(program.namespace)
|
|
||||||
vdX.linkParents(program.namespace)
|
|
||||||
vdY.linkParents(program.namespace)
|
|
||||||
program.namespace.statements.add(vdA)
|
|
||||||
program.namespace.statements.add(vdX)
|
|
||||||
program.namespace.statements.add(vdY)
|
|
||||||
|
|
||||||
super.visit(program)
|
|
||||||
}
|
|
||||||
|
|
||||||
override fun visit(decl: VarDecl): Statement {
|
|
||||||
// if the decl is part of a struct, just skip it
|
|
||||||
if(decl.parent !is StructDecl) {
|
|
||||||
when (decl.type) {
|
|
||||||
VarDeclType.VAR -> {
|
|
||||||
if(decl.datatype!=DataType.STRUCT) {
|
|
||||||
val numericLv = decl.value as? NumericLiteralValue
|
|
||||||
val value = if(numericLv!=null) {
|
|
||||||
RuntimeValueNumeric.fromLv(numericLv)
|
|
||||||
} else {
|
|
||||||
val strLv = decl.value as? StringLiteralValue
|
|
||||||
val arrayLv = decl.value as? ArrayLiteralValue
|
|
||||||
when {
|
|
||||||
strLv!=null -> {
|
|
||||||
RuntimeValueString.fromLv(strLv)
|
|
||||||
}
|
|
||||||
arrayLv!=null -> {
|
|
||||||
RuntimeValueArray.fromLv(arrayLv)
|
|
||||||
}
|
|
||||||
else -> throw VmExecutionException("weird var type")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
runtimeVariables.define(decl.definingScope(), decl.name, value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
VarDeclType.MEMORY -> {
|
|
||||||
runtimeVariables.defineMemory(decl.definingScope(), decl.name, (decl.value as NumericLiteralValue).number.toInt())
|
|
||||||
}
|
|
||||||
VarDeclType.CONST -> {
|
|
||||||
// consts should have been const-folded away
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return super.visit(decl)
|
|
||||||
}
|
|
||||||
}
|
|
@ -1,352 +0,0 @@
|
|||||||
package prog8tests
|
|
||||||
|
|
||||||
import org.junit.jupiter.api.Test
|
|
||||||
import org.junit.jupiter.api.TestInstance
|
|
||||||
import prog8.ast.base.DataType
|
|
||||||
import prog8.vm.RuntimeValueNumeric
|
|
||||||
import kotlin.test.*
|
|
||||||
|
|
||||||
|
|
||||||
private fun sameValueAndType(v1: RuntimeValueNumeric, v2: RuntimeValueNumeric): Boolean {
|
|
||||||
return v1.type==v2.type && v1==v2
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
|
||||||
class TestRuntimeValueNumeric {
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testValueRanges() {
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).integerValue())
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 255).integerValue())
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UBYTE, -1)}
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UBYTE, 256)}
|
|
||||||
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).integerValue())
|
|
||||||
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -128).integerValue())
|
|
||||||
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 127).integerValue())
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.BYTE, -129)}
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.BYTE, 128)}
|
|
||||||
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).integerValue())
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65535).integerValue())
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UWORD, -1)}
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.UWORD, 65536)}
|
|
||||||
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).integerValue())
|
|
||||||
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32768).integerValue())
|
|
||||||
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32767).integerValue())
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.WORD, -32769)}
|
|
||||||
assertFailsWith<IllegalArgumentException> { RuntimeValueNumeric(DataType.WORD, 32768)}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testTruthiness()
|
|
||||||
{
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.BYTE, 0).asBoolean)
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 0).asBoolean)
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.WORD, 0).asBoolean)
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UWORD, 0).asBoolean)
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 0.0).asBoolean)
|
|
||||||
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.BYTE, 42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.WORD, 42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UWORD, 42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 42.0).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.BYTE, -42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.WORD, -42).asBoolean)
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, -42.0).asBoolean)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testIdentity() {
|
|
||||||
val v = RuntimeValueNumeric(DataType.UWORD, 12345)
|
|
||||||
assertEquals(v, v)
|
|
||||||
assertFalse(v != v)
|
|
||||||
assertTrue(v<=v)
|
|
||||||
assertTrue(v>=v)
|
|
||||||
assertFalse(v<v)
|
|
||||||
assertFalse(v>v)
|
|
||||||
|
|
||||||
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testEqualsAndNotEquals() {
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239))
|
|
||||||
assertEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99))
|
|
||||||
|
|
||||||
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 100)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 100)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 100)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 254), RuntimeValueNumeric(DataType.UBYTE, 254)))
|
|
||||||
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12345)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12345)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 100.0), RuntimeValueNumeric(DataType.UBYTE, 100)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 22239.0), RuntimeValueNumeric(DataType.UWORD, 22239)))
|
|
||||||
assertTrue(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.99)))
|
|
||||||
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9))
|
|
||||||
assertNotEquals(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0))
|
|
||||||
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UBYTE, 101)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.UWORD, 101)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UBYTE, 100), RuntimeValueNumeric(DataType.FLOAT, 101)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 245), RuntimeValueNumeric(DataType.UBYTE, 246)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.UWORD, 12346)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.UWORD, 12345), RuntimeValueNumeric(DataType.FLOAT, 12346)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UBYTE, 9)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.UWORD, 9)))
|
|
||||||
assertFalse(sameValueAndType(RuntimeValueNumeric(DataType.FLOAT, 9.99), RuntimeValueNumeric(DataType.FLOAT, 9.0)))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testGreaterThan(){
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 99))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 253))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 99.9))
|
|
||||||
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 254))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.0))
|
|
||||||
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) > RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) > RuntimeValueNumeric(DataType.UWORD, 254))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) > RuntimeValueNumeric(DataType.FLOAT, 100.0))
|
|
||||||
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) >= RuntimeValueNumeric(DataType.UBYTE, 101))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) >= RuntimeValueNumeric(DataType.UWORD, 255))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) >= RuntimeValueNumeric(DataType.FLOAT, 100.1))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testLessThan() {
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 101))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 255))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.1))
|
|
||||||
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 254))
|
|
||||||
assertTrue(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 100.0))
|
|
||||||
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) < RuntimeValueNumeric(DataType.UBYTE, 100))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) < RuntimeValueNumeric(DataType.UWORD, 254))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) < RuntimeValueNumeric(DataType.FLOAT, 100.0))
|
|
||||||
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UBYTE, 100) <= RuntimeValueNumeric(DataType.UBYTE, 99))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.UWORD, 254) <= RuntimeValueNumeric(DataType.UWORD, 253))
|
|
||||||
assertFalse(RuntimeValueNumeric(DataType.FLOAT, 100.0) <= RuntimeValueNumeric(DataType.FLOAT, 99.9))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNoDtConversion() {
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, 100).add(RuntimeValueNumeric(DataType.UBYTE, 120))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, 100).add(RuntimeValueNumeric(DataType.UWORD, 120))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UWORD, 120))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, 1002).add(RuntimeValueNumeric(DataType.FLOAT, 120.22))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.FLOAT, 100.22).add(RuntimeValueNumeric(DataType.UBYTE, 120))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, 12).add(RuntimeValueNumeric(DataType.FLOAT, 120.22))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testNoAutoFloatConversion() {
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, 233).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, 233).mul(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UBYTE, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
assertFailsWith<ArithmeticException> {
|
|
||||||
RuntimeValueNumeric(DataType.UWORD, 233).div(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
val result = RuntimeValueNumeric(DataType.FLOAT, 233.333).add(RuntimeValueNumeric(DataType.FLOAT, 1.234))
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun arithmetictestUbyte() {
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 55)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 56)).integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 200).add(RuntimeValueNumeric(DataType.UBYTE, 57)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 1)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue())
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 2).sub(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 254).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).dec().integerValue())
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).dec().integerValue())
|
|
||||||
|
|
||||||
assertEquals(255, RuntimeValueNumeric(DataType.UBYTE, 0).inv().integerValue())
|
|
||||||
assertEquals(0b00110011, RuntimeValueNumeric(DataType.UBYTE, 0b11001100).inv().integerValue())
|
|
||||||
// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue())
|
|
||||||
// assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 0).neg().integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UBYTE, 0).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 1).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 111).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UBYTE, 255).not().integerValue())
|
|
||||||
|
|
||||||
assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 10)).integerValue())
|
|
||||||
assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 20).mul(RuntimeValueNumeric(DataType.UBYTE, 20)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(25, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 2)).integerValue())
|
|
||||||
assertEquals(125, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 3)).integerValue())
|
|
||||||
assertEquals(113, RuntimeValueNumeric(DataType.UBYTE, 5).pow(RuntimeValueNumeric(DataType.UBYTE, 4)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(100, RuntimeValueNumeric(DataType.UBYTE, 50).shl().integerValue())
|
|
||||||
assertEquals(200, RuntimeValueNumeric(DataType.UBYTE, 100).shl().integerValue())
|
|
||||||
assertEquals(144, RuntimeValueNumeric(DataType.UBYTE, 200).shl().integerValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun arithmetictestUWord() {
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5535)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5536)).integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 60000).add(RuntimeValueNumeric(DataType.UWORD, 5537)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 1)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 2)).integerValue())
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 2).sub(RuntimeValueNumeric(DataType.UWORD, 3)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 65534).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).dec().integerValue())
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).dec().integerValue())
|
|
||||||
|
|
||||||
assertEquals(65535, RuntimeValueNumeric(DataType.UWORD, 0).inv().integerValue())
|
|
||||||
assertEquals(0b0011001101010101, RuntimeValueNumeric(DataType.UWORD, 0b1100110010101010).inv().integerValue())
|
|
||||||
// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue())
|
|
||||||
// assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 0).neg().integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.UWORD, 0).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 1).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 11111).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.UWORD, 65535).not().integerValue())
|
|
||||||
|
|
||||||
assertEquals(2000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 10)).integerValue())
|
|
||||||
assertEquals(40000, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 200)).integerValue())
|
|
||||||
assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 200).mul(RuntimeValueNumeric(DataType.UWORD, 400)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(15625, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 6)).integerValue())
|
|
||||||
assertEquals(12589, RuntimeValueNumeric(DataType.UWORD, 5).pow(RuntimeValueNumeric(DataType.UWORD, 7)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(10000, RuntimeValueNumeric(DataType.UWORD, 5000).shl().integerValue())
|
|
||||||
assertEquals(60000, RuntimeValueNumeric(DataType.UWORD, 30000).shl().integerValue())
|
|
||||||
assertEquals(14464, RuntimeValueNumeric(DataType.UWORD, 40000).shl().integerValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun arithmetictestByte() {
|
|
||||||
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 27)).integerValue())
|
|
||||||
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue())
|
|
||||||
assertEquals(-127, RuntimeValueNumeric(DataType.BYTE, 100).add(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 1)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue())
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 2).sub(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue())
|
|
||||||
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 28)).integerValue())
|
|
||||||
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -100).sub(RuntimeValueNumeric(DataType.BYTE, 29)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, 126).inc().integerValue())
|
|
||||||
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, 127).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).dec().integerValue())
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).dec().integerValue())
|
|
||||||
assertEquals(-128, RuntimeValueNumeric(DataType.BYTE, -127).dec().integerValue())
|
|
||||||
assertEquals(127, RuntimeValueNumeric(DataType.BYTE, -128).dec().integerValue())
|
|
||||||
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.BYTE, 0).inv().integerValue())
|
|
||||||
assertEquals(-103, RuntimeValueNumeric(DataType.BYTE, 0b01100110).inv().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 0).neg().integerValue())
|
|
||||||
assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, 2).neg().integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.BYTE, 0).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 1).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, 111).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.BYTE, -33).not().integerValue())
|
|
||||||
|
|
||||||
assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 10).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue())
|
|
||||||
assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 20).mul(RuntimeValueNumeric(DataType.BYTE, 10)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(25, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 2)).integerValue())
|
|
||||||
assertEquals(125, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 3)).integerValue())
|
|
||||||
assertEquals(113, RuntimeValueNumeric(DataType.BYTE, 5).pow(RuntimeValueNumeric(DataType.BYTE, 4)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(100, RuntimeValueNumeric(DataType.BYTE, 50).shl().integerValue())
|
|
||||||
assertEquals(-56, RuntimeValueNumeric(DataType.BYTE, 100).shl().integerValue())
|
|
||||||
assertEquals(-2, RuntimeValueNumeric(DataType.BYTE, -1).shl().integerValue())
|
|
||||||
}
|
|
||||||
|
|
||||||
@Test
|
|
||||||
fun arithmetictestWorrd() {
|
|
||||||
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 67)).integerValue())
|
|
||||||
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 68)).integerValue())
|
|
||||||
assertEquals(-32767, RuntimeValueNumeric(DataType.WORD, 32700).add(RuntimeValueNumeric(DataType.WORD, 69)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 1)).integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 2)).integerValue())
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 2).sub(RuntimeValueNumeric(DataType.WORD, 3)).integerValue())
|
|
||||||
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 68)).integerValue())
|
|
||||||
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32700).sub(RuntimeValueNumeric(DataType.WORD, 69)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, 32766).inc().integerValue())
|
|
||||||
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, 32767).inc().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).dec().integerValue())
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).dec().integerValue())
|
|
||||||
assertEquals(-32768, RuntimeValueNumeric(DataType.WORD, -32767).dec().integerValue())
|
|
||||||
assertEquals(32767, RuntimeValueNumeric(DataType.WORD, -32768).dec().integerValue())
|
|
||||||
|
|
||||||
assertEquals(-1, RuntimeValueNumeric(DataType.WORD, 0).inv().integerValue())
|
|
||||||
assertEquals(-103, RuntimeValueNumeric(DataType.WORD, 0b01100110).inv().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 0).neg().integerValue())
|
|
||||||
assertEquals(-2, RuntimeValueNumeric(DataType.WORD, 2).neg().integerValue())
|
|
||||||
assertEquals(1, RuntimeValueNumeric(DataType.WORD, 0).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 1).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, 111).not().integerValue())
|
|
||||||
assertEquals(0, RuntimeValueNumeric(DataType.WORD, -33).not().integerValue())
|
|
||||||
|
|
||||||
assertEquals(10000, RuntimeValueNumeric(DataType.WORD, 100).mul(RuntimeValueNumeric(DataType.WORD, 100)).integerValue())
|
|
||||||
assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 200).mul(RuntimeValueNumeric(DataType.WORD, 200)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(15625, RuntimeValueNumeric(DataType.WORD, 5).pow(RuntimeValueNumeric(DataType.WORD, 6)).integerValue())
|
|
||||||
assertEquals(-6487, RuntimeValueNumeric(DataType.WORD, 9).pow(RuntimeValueNumeric(DataType.WORD, 5)).integerValue())
|
|
||||||
|
|
||||||
assertEquals(18000, RuntimeValueNumeric(DataType.WORD, 9000).shl().integerValue())
|
|
||||||
assertEquals(-25536, RuntimeValueNumeric(DataType.WORD, 20000).shl().integerValue())
|
|
||||||
assertEquals(-2, RuntimeValueNumeric(DataType.WORD, -1).shl().integerValue())
|
|
||||||
}
|
|
||||||
}
|
|
@ -6,6 +6,7 @@ import org.hamcrest.Matchers.equalTo
|
|||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
import org.junit.jupiter.api.TestInstance
|
import org.junit.jupiter.api.TestInstance
|
||||||
import prog8.ast.base.DataType
|
import prog8.ast.base.DataType
|
||||||
|
import prog8.ast.base.ErrorReporter
|
||||||
import prog8.ast.base.Position
|
import prog8.ast.base.Position
|
||||||
import prog8.ast.expressions.NumericLiteralValue
|
import prog8.ast.expressions.NumericLiteralValue
|
||||||
import prog8.ast.expressions.StringLiteralValue
|
import prog8.ast.expressions.StringLiteralValue
|
||||||
@ -15,7 +16,6 @@ import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_NEGATIVE
|
|||||||
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
import prog8.compiler.target.c64.C64MachineDefinition.FLOAT_MAX_POSITIVE
|
||||||
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
import prog8.compiler.target.c64.C64MachineDefinition.Mflpt5
|
||||||
import prog8.compiler.target.c64.Petscii
|
import prog8.compiler.target.c64.Petscii
|
||||||
import prog8.vm.RuntimeValueNumeric
|
|
||||||
import java.io.CharConversionException
|
import java.io.CharConversionException
|
||||||
import kotlin.test.*
|
import kotlin.test.*
|
||||||
|
|
||||||
@ -124,31 +124,34 @@ class TestCompiler {
|
|||||||
|
|
||||||
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
@TestInstance(TestInstance.Lifecycle.PER_CLASS)
|
||||||
class TestZeropage {
|
class TestZeropage {
|
||||||
|
|
||||||
|
private val errors = ErrorReporter()
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testNames() {
|
fun testNames() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false))
|
||||||
|
|
||||||
zp.allocate("", DataType.UBYTE, null)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
zp.allocate("", DataType.UBYTE, null)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
zp.allocate("varname", DataType.UBYTE, null)
|
zp.allocate("varname", DataType.UBYTE, null, errors)
|
||||||
assertFailsWith<AssertionError> {
|
assertFailsWith<AssertionError> {
|
||||||
zp.allocate("varname", DataType.UBYTE, null)
|
zp.allocate("varname", DataType.UBYTE, null, errors)
|
||||||
}
|
}
|
||||||
zp.allocate("varname2", DataType.UBYTE, null)
|
zp.allocate("varname2", DataType.UBYTE, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testZpFloatEnable() {
|
fun testZpFloatEnable() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate("", DataType.FLOAT, null)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp2.allocate("", DataType.FLOAT, null)
|
zp2.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
|
||||||
zp3.allocate("", DataType.FLOAT, null)
|
zp3.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@ -173,7 +176,7 @@ class TestZeropage {
|
|||||||
println(zp.free)
|
println(zp.free)
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<CompilerException> {
|
assertFailsWith<CompilerException> {
|
||||||
zp.allocate("", DataType.BYTE, null)
|
zp.allocate("", DataType.BYTE, null, errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -218,19 +221,19 @@ class TestZeropage {
|
|||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
// in regular zp there aren't 5 sequential bytes free
|
// in regular zp there aren't 5 sequential bytes free
|
||||||
zp.allocate("", DataType.FLOAT, null)
|
zp.allocate("", DataType.FLOAT, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i in 0 until zp.available()) {
|
for (i in 0 until zp.available()) {
|
||||||
val loc = zp.allocate("", DataType.UBYTE, null)
|
val loc = zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
assertTrue(loc > 0)
|
assertTrue(loc > 0)
|
||||||
}
|
}
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
zp.allocate("", DataType.UBYTE, null)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
}
|
}
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
zp.allocate("", DataType.UWORD, null)
|
zp.allocate("", DataType.UWORD, null, errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -238,29 +241,29 @@ class TestZeropage {
|
|||||||
fun testFullAllocation() {
|
fun testFullAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
|
||||||
assertEquals(238, zp.available())
|
assertEquals(238, zp.available())
|
||||||
val loc = zp.allocate("", DataType.UWORD, null)
|
val loc = zp.allocate("", DataType.UWORD, null, errors)
|
||||||
assertTrue(loc > 3)
|
assertTrue(loc > 3)
|
||||||
assertFalse(loc in zp.free)
|
assertFalse(loc in zp.free)
|
||||||
val num = zp.available() / 2
|
val num = zp.available() / 2
|
||||||
|
|
||||||
for(i in 0..num-4) {
|
for(i in 0..num-4) {
|
||||||
zp.allocate("", DataType.UWORD, null)
|
zp.allocate("", DataType.UWORD, null, errors)
|
||||||
}
|
}
|
||||||
assertEquals(6,zp.available())
|
assertEquals(6,zp.available())
|
||||||
|
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
// can't allocate because no more sequential bytes, only fragmented
|
// can't allocate because no more sequential bytes, only fragmented
|
||||||
zp.allocate("", DataType.UWORD, null)
|
zp.allocate("", DataType.UWORD, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
for(i in 0..5) {
|
for(i in 0..5) {
|
||||||
zp.allocate("", DataType.UBYTE, null)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
assertFailsWith<ZeropageDepletedError> {
|
assertFailsWith<ZeropageDepletedError> {
|
||||||
// no more space
|
// no more space
|
||||||
zp.allocate("", DataType.UBYTE, null)
|
zp.allocate("", DataType.UBYTE, null, errors)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -268,16 +271,16 @@ class TestZeropage {
|
|||||||
fun testEfficientAllocation() {
|
fun testEfficientAllocation() {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
|
||||||
assertEquals(16, zp.available())
|
assertEquals(16, zp.available())
|
||||||
assertEquals(0x04, zp.allocate("", DataType.WORD, null))
|
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
|
||||||
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null))
|
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null))
|
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0x94, zp.allocate("", DataType.UWORD, null))
|
assertEquals(0x94, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null))
|
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null))
|
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null))
|
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null))
|
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null, errors))
|
||||||
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null))
|
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null))
|
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null, errors))
|
||||||
assertEquals(0, zp.available())
|
assertEquals(0, zp.available())
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -352,8 +355,8 @@ class TestPetscii {
|
|||||||
|
|
||||||
@Test
|
@Test
|
||||||
fun testLiteralValueComparisons() {
|
fun testLiteralValueComparisons() {
|
||||||
val ten = NumericLiteralValue(DataType.UWORD, 10, Position("", 0, 0, 0))
|
val ten = NumericLiteralValue(DataType.UWORD, 10, Position.DUMMY)
|
||||||
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position("", 0, 0, 0))
|
val nine = NumericLiteralValue(DataType.UBYTE, 9, Position.DUMMY)
|
||||||
assertEquals(ten, ten)
|
assertEquals(ten, ten)
|
||||||
assertNotEquals(ten, nine)
|
assertNotEquals(ten, nine)
|
||||||
assertFalse(ten != ten)
|
assertFalse(ten != ten)
|
||||||
@ -369,30 +372,10 @@ class TestPetscii {
|
|||||||
assertTrue(ten <= ten)
|
assertTrue(ten <= ten)
|
||||||
assertFalse(ten < ten)
|
assertFalse(ten < ten)
|
||||||
|
|
||||||
val abc = StringLiteralValue("abc", false, Position("", 0, 0, 0))
|
val abc = StringLiteralValue("abc", false, Position.DUMMY)
|
||||||
val abd = StringLiteralValue("abd", false, Position("", 0, 0, 0))
|
val abd = StringLiteralValue("abd", false, Position.DUMMY)
|
||||||
assertEquals(abc, abc)
|
assertEquals(abc, abc)
|
||||||
assertTrue(abc!=abd)
|
assertTrue(abc!=abd)
|
||||||
assertFalse(abc!=abc)
|
assertFalse(abc!=abc)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
|
||||||
fun testStackvmValueComparisons() {
|
|
||||||
val ten = RuntimeValueNumeric(DataType.FLOAT, 10)
|
|
||||||
val nine = RuntimeValueNumeric(DataType.UWORD, 9)
|
|
||||||
assertEquals(ten, ten)
|
|
||||||
assertNotEquals(ten, nine)
|
|
||||||
assertFalse(ten != ten)
|
|
||||||
assertTrue(ten != nine)
|
|
||||||
|
|
||||||
assertTrue(ten > nine)
|
|
||||||
assertTrue(ten >= nine)
|
|
||||||
assertTrue(ten >= ten)
|
|
||||||
assertFalse(ten > ten)
|
|
||||||
|
|
||||||
assertFalse(ten < nine)
|
|
||||||
assertFalse(ten <= nine)
|
|
||||||
assertTrue(ten <= ten)
|
|
||||||
assertFalse(ten < ten)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
@ -168,18 +168,3 @@ or::
|
|||||||
|
|
||||||
$ ./p8compile.sh -emu examples/rasterbars.p8
|
$ ./p8compile.sh -emu examples/rasterbars.p8
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Virtual Machine / Simulator
|
|
||||||
---------------------------
|
|
||||||
|
|
||||||
You may have noticed the ``-sim`` command line option for the compiler:
|
|
||||||
|
|
||||||
-sim
|
|
||||||
Launches the "AST virtual machine Simulator" that directly executes the parsed program.
|
|
||||||
No compilation steps will be performed.
|
|
||||||
Allows for very fast testing and debugging before actually compiling programs
|
|
||||||
to machine code.
|
|
||||||
It simulates a bare minimum of features from the target platform, so most stuff
|
|
||||||
that calls ROM routines or writes into hardware registers won't work. But basic
|
|
||||||
system routines are emulated.
|
|
||||||
|
@ -135,30 +135,29 @@ Design principles and features
|
|||||||
|
|
||||||
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
- It is a cross-compiler running on modern machines (Linux, MacOS, Windows, ...)
|
||||||
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
|
The generated output is a machine code program runnable on actual 8-bit 6502 hardware.
|
||||||
- Usable on most operating systems.
|
- Based on simple and familiar imperative structured programming (it looks like a mix of C and Python)
|
||||||
- Based on simple and familiar imperative structured programming paradigm.
|
- 'One statement per line' code, resulting in clear readable programs.
|
||||||
- 'One statement per line' code style, resulting in clear readable programs.
|
|
||||||
- Modular programming and scoping via modules, code blocks, and subroutines.
|
- Modular programming and scoping via modules, code blocks, and subroutines.
|
||||||
- Provide high level programming constructs but stay close to the metal;
|
- Provide high level programming constructs but at the same time stay close to the metal;
|
||||||
still able to directly use memory addresses, CPU registers and ROM subroutines,
|
still able to directly use memory addresses, CPU registers and ROM subroutines,
|
||||||
and inline assembly to have full control when every cycle or byte matters
|
and inline assembly to have full control when every cycle or byte matters
|
||||||
- Arbitrary number of subroutine parameters (constrained only by available memory)
|
- Arbitrary number of subroutine parameters
|
||||||
- Complex nested expressions are possible
|
- Complex nested expressions are possible
|
||||||
- Values are typed. Types supported include signed and unsigned bytes and words, arrays, strings and floats.
|
- Nested subroutines can access variables from outer scopes to avoids the overhead to pass everything via parameters
|
||||||
|
- Values are typed. Available data types include signed and unsigned bytes and words, arrays, strings and floats.
|
||||||
- No dynamic memory allocation or sizing! All variables stay fixed size as determined at compile time.
|
- No dynamic memory allocation or sizing! All variables stay fixed size as determined at compile time.
|
||||||
- Provide various quality of life language features and library subroutines specifically for the target platform.
|
- Provide various quality of life language features and library subroutines specifically for the target platform.
|
||||||
- Provide a very convenient edit/compile/run cycle by being able to directly launch
|
- Provide a very convenient edit/compile/run cycle by being able to directly launch
|
||||||
the compiled program in an emulator and provide debugging information to the emulator.
|
the compiled program in an emulator and provide debugging information to this emulator.
|
||||||
- The compiler outputs a regular 6502 assembly source code file, but doesn't assemble this itself.
|
|
||||||
The (separate) '64tass' cross-assembler tool is used for that.
|
|
||||||
- Arbitrary control flow jumps and branches are possible,
|
- Arbitrary control flow jumps and branches are possible,
|
||||||
and will usually translate directly into the appropriate single 6502 jump/branch instruction.
|
and will usually translate directly into the appropriate single 6502 jump/branch instruction.
|
||||||
- There are no complicated built-in error handling or overflow checks, you'll have to take care
|
- There are no complicated built-in error handling or overflow checks, you'll have to take care
|
||||||
of this yourself if required. This keeps the language and code simple and efficient.
|
of this yourself if required. This keeps the language and code simple and efficient.
|
||||||
- The compiler tries to optimize the program and generated code, but hand-tuning of the
|
- The compiler tries to optimize the program and generated code a bit, but hand-tuning of the
|
||||||
performance or space-critical parts will likely still be required. This is supported by
|
performance or space-critical parts will likely still be required. This is supported by
|
||||||
the ability to easily write embedded assembly code directly in the program source code.
|
the ability to easily write embedded assembly code directly in the program source code.
|
||||||
- There are many built-in functions such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
- There are many built-in functions, such as ``sin``, ``cos``, ``rnd``, ``abs``, ``min``, ``max``, ``sqrt``, ``msb``, ``rol``, ``ror``, ``swap``, ``memset``, ``memcopy``, ``sort`` and ``reverse``
|
||||||
|
- Assembling the generated code into a program wil be done by an external cross-assembler tool.
|
||||||
|
|
||||||
|
|
||||||
.. _requirements:
|
.. _requirements:
|
||||||
@ -167,26 +166,25 @@ Required tools
|
|||||||
--------------
|
--------------
|
||||||
|
|
||||||
`64tass <https://sourceforge.net/projects/tass64/>`_ - cross assembler. Install this on your shell path.
|
`64tass <https://sourceforge.net/projects/tass64/>`_ - cross assembler. Install this on your shell path.
|
||||||
A recent .exe version of this tool for Windows can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project.
|
It's very easy to compile yourself.
|
||||||
For other platforms it is very easy to compile it yourself (make ; make install).
|
A recent precompiled .exe for Windows can be obtained from my `clone <https://github.com/irmen/64tass/releases>`_ of this project.
|
||||||
|
|
||||||
A **Java runtime (jre or jdk), version 8 or newer** is required to run the packaged compiler.
|
A **Java runtime (jre or jdk), version 8 or newer** is required to run the prog8 compiler itself.
|
||||||
If you're scared of Oracle's licensing terms, most Linux distributions ship OpenJDK instead
|
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. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ for
|
Fnd for Windows it's possible to get that as well. Check out `AdoptOpenJDK <https://adoptopenjdk.net/>`_ .
|
||||||
downloads.
|
|
||||||
|
|
||||||
Finally: a **C-64 emulator** (or a real C-64 ofcourse) to run the programs on. The compiler assumes the presence
|
Finally: a **C-64 emulator** (or a real C-64 ofcourse) can be nice to test and run your programs on.
|
||||||
of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
The compiler assumes the presence of the `Vice emulator <http://vice-emu.sourceforge.net/>`_.
|
||||||
|
|
||||||
.. important::
|
.. important::
|
||||||
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
**Building the compiler itself:** (*Only needed if you have not downloaded a pre-built 'fat-jar'*)
|
||||||
|
|
||||||
(re)building the compiler itself requires a recent Kotlin SDK.
|
(Re)building the compiler itself requires a recent Kotlin SDK.
|
||||||
The compiler is developed using the `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_
|
The compiler is developed using `IntelliJ IDEA <https://www.jetbrains.com/idea/>`_ ,
|
||||||
IDE from Jetbrains, with the Kotlin plugin (free community edition of this IDE is available).
|
but only a Kotlin SDK installation should work as well, because the gradle tool is
|
||||||
But a bare Kotlin SDK installation should work just as well.
|
used to compile everything from the commandline.
|
||||||
|
|
||||||
Instructions on how to obtain a working compiler are in :ref:`building_compiler`.
|
Instructions on how to obtain a prebuilt compiler are in :ref:`building_compiler`.
|
||||||
|
|
||||||
|
|
||||||
.. toctree::
|
.. toctree::
|
||||||
|
@ -15,18 +15,13 @@ Program
|
|||||||
Consists of one or more *modules*.
|
Consists of one or more *modules*.
|
||||||
|
|
||||||
Module
|
Module
|
||||||
A file on disk with the ``.p8`` suffix. It contains *directives* and *code blocks*.
|
A file on disk with the ``.p8`` suffix. It can contain *directives* and *code blocks*.
|
||||||
Whitespace and indentation in the source code are arbitrary and can be tabs or spaces or both.
|
Whitespace and indentation in the source code are arbitrary and can be mixed tabs or spaces.
|
||||||
You can also add *comments* to the source code.
|
A module file can *import* other modules, including *library modules*.
|
||||||
One moudule file can *import* others, and also import *library modules*.
|
|
||||||
|
|
||||||
Comments
|
Comments
|
||||||
Everything after a semicolon ``;`` is a comment and is ignored by the compiler.
|
Everything after a semicolon ``;`` is a comment and is ignored by the compiler.
|
||||||
If the whole line is just a comment, it will be copied into the resulting assembly source code.
|
If the whole line is just a comment, this line will be copied into the resulting assembly source code for reference.
|
||||||
This makes it easier to understand and relate the generated code. Examples::
|
|
||||||
|
|
||||||
A = 42 ; set the initial value to 42
|
|
||||||
; next is the code that...
|
|
||||||
|
|
||||||
Directive
|
Directive
|
||||||
These are special instructions for the compiler, to change how it processes the code
|
These are special instructions for the compiler, to change how it processes the code
|
||||||
@ -34,8 +29,9 @@ Directive
|
|||||||
starts with ``%``, optionally followed by some arguments.
|
starts with ``%``, optionally followed by some arguments.
|
||||||
|
|
||||||
Code block
|
Code block
|
||||||
A block of actual program code. It defines a *scope* (also known as 'namespace') and
|
A block of actual program code. It has a starting address in memory,
|
||||||
can contain Prog8 *code*, *variable declarations* and *subroutines*.
|
and defines a *scope* (also known as 'namespace').
|
||||||
|
It contains variables and subroutines.
|
||||||
More details about this below: :ref:`blocks`.
|
More details about this below: :ref:`blocks`.
|
||||||
|
|
||||||
Variable declarations
|
Variable declarations
|
||||||
@ -49,8 +45,9 @@ Variable declarations
|
|||||||
that don't allocate storage but instead point to a fixed location in the address space.
|
that don't allocate storage but instead point to a fixed location in the address space.
|
||||||
|
|
||||||
Code
|
Code
|
||||||
These are the instructions that make up the program's logic. There are different kinds of instructions
|
These are the instructions that make up the program's logic.
|
||||||
('statements' is a better name):
|
Code can only occur inside a subroutine.
|
||||||
|
There are different kinds of instructions ('statements' is a better name) such as:
|
||||||
|
|
||||||
- value assignment
|
- value assignment
|
||||||
- looping (for, while, repeat, unconditional jumps)
|
- looping (for, while, repeat, unconditional jumps)
|
||||||
@ -63,6 +60,8 @@ Subroutine
|
|||||||
It accepts parameters and can return a value (optional).
|
It accepts parameters and can return a value (optional).
|
||||||
It can define its own variables, and it is even possible to define subroutines nested inside other subroutines.
|
It can define its own variables, and it is even possible to define subroutines nested inside other subroutines.
|
||||||
Their contents is scoped accordingly.
|
Their contents is scoped accordingly.
|
||||||
|
Nested subroutines can access the variables from outer scopes.
|
||||||
|
This removes the need and overhead to pass everything via parameters.
|
||||||
|
|
||||||
Label
|
Label
|
||||||
This is a named position in your code where you can jump to from another place.
|
This is a named position in your code where you can jump to from another place.
|
||||||
@ -90,16 +89,20 @@ Scope
|
|||||||
Blocks, Scopes, and accessing Symbols
|
Blocks, Scopes, and accessing Symbols
|
||||||
-------------------------------------
|
-------------------------------------
|
||||||
|
|
||||||
**Blocks** are the top level separate pieces of code and data of your program. They are combined
|
**Blocks** are the top level separate pieces of code and data of your program. They have a
|
||||||
into a single output program. No code or data can occur outside a block. Here's an example::
|
starting address in memory and will be combined together into a single output program.
|
||||||
|
They can only contain *directives*, *variable declarations*, *subroutines* and *inline assembly code*.
|
||||||
|
Your actual program code can only exist inside these subroutines.
|
||||||
|
(except the occasional inline assembly)
|
||||||
|
|
||||||
|
Here's an example::
|
||||||
|
|
||||||
main $c000 {
|
main $c000 {
|
||||||
; this is code inside the block...
|
; this is code inside the block...
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
The name of a block must be unique in your entire program.
|
The name of a block must be unique in your entire program.
|
||||||
Also be careful when importing other modules; blocks in your own code cannot have
|
Be careful when importing other modules; blocks in your own code cannot have
|
||||||
the same name as a block defined in an imported module or library.
|
the same name as a block defined in an imported module or library.
|
||||||
|
|
||||||
If you omit both the name and address, the entire block is *ignored* by the compiler (and a warning is displayed).
|
If you omit both the name and address, the entire block is *ignored* by the compiler (and a warning is displayed).
|
||||||
@ -109,7 +112,7 @@ want to work on later, because the contents of the ignored block are not fully p
|
|||||||
The address can be used to place a block at a specific location in memory.
|
The address can be used to place a block at a specific location in memory.
|
||||||
Usually it is omitted, and the compiler will automatically choose the location (usually immediately after
|
Usually it is omitted, and the compiler will automatically choose the location (usually immediately after
|
||||||
the previous block in memory).
|
the previous block in memory).
|
||||||
The address must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100``--``$200`` is the cpu stack).
|
It must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100``--``$1ff`` is the cpu stack).
|
||||||
|
|
||||||
|
|
||||||
.. _scopes:
|
.. _scopes:
|
||||||
@ -132,15 +135,18 @@ Scopes are created using either of these two statements:
|
|||||||
- blocks (top-level named scope)
|
- blocks (top-level named scope)
|
||||||
- subroutines (nested named scope)
|
- subroutines (nested named scope)
|
||||||
|
|
||||||
.. note::
|
.. important::
|
||||||
In contrast to many other programming languages, a new scope is *not* created inside
|
Unlike most other programming languages, a new scope is *not* created inside
|
||||||
for, while and repeat statements, nor for the if statement and branching conditionals.
|
for, while and repeat statements, the if statement, and the branching conditionals.
|
||||||
This is a bit restrictive because you have to think harder about what variables you
|
These all share the same scope from the subroutine they're defined in.
|
||||||
want to use inside a subroutine. But it is done precisely for this reason; memory in the
|
You can define variables in these blocks, but these will be treated as if they
|
||||||
target system is very limited and it would be a waste to allocate a lot of variables.
|
were defined in the subroutine instead.
|
||||||
|
This can seem a bit restrictive because you have to think harder about what variables you
|
||||||
Right now the prog8 compiler is not advanced enough to be able to 'share' or 'overlap'
|
want to use inside the subroutine, to avoid clashes.
|
||||||
variables intelligently by itself. So for now, it's something the programmer has to think about.
|
But this decision was made for a good reason: memory in prog8's
|
||||||
|
target systems is usually very limited and it would be a waste to allocate a lot of variables.
|
||||||
|
The prog8 compiler is not yet advanced enough to be able to share or overlap
|
||||||
|
variables intelligently. So for now that is something you have to think about yourself.
|
||||||
|
|
||||||
|
|
||||||
Program Start and Entry Point
|
Program Start and Entry Point
|
||||||
@ -150,13 +156,6 @@ Your program must have a single entry point where code execution begins.
|
|||||||
The compiler expects a ``start`` subroutine in the ``main`` block for this,
|
The compiler expects a ``start`` subroutine in the ``main`` block for this,
|
||||||
taking no parameters and having no return value.
|
taking no parameters and having no return value.
|
||||||
|
|
||||||
.. sidebar::
|
|
||||||
60hz IRQ entry point
|
|
||||||
|
|
||||||
When running the generated code on the StackVm virtual machine,
|
|
||||||
it will use the ``irq`` subroutine in the ``irq`` block for the
|
|
||||||
60hz irq routine. This is optional.
|
|
||||||
|
|
||||||
As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call)::
|
As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call)::
|
||||||
|
|
||||||
main {
|
main {
|
||||||
@ -175,12 +174,11 @@ calls with the SYS statement.
|
|||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Variables and values
|
Variables and values
|
||||||
--------------------
|
--------------------
|
||||||
|
|
||||||
Variables are named values that can change during the execution of the program.
|
Variables are named values that can change during the execution of the program.
|
||||||
They can be defined inside any scope (blocks, subroutines, for loops, etc.) See :ref:`Scopes <scopes>`.
|
They can be defined inside any scope (blocks, subroutines etc.) See :ref:`Scopes <scopes>`.
|
||||||
When declaring a numeric variable it is possible to specify the initial value, if you don't want it to be zero.
|
When declaring a numeric variable it is possible to specify the initial value, if you don't want it to be zero.
|
||||||
For other data types it is required to specify that initial value it should get.
|
For other data types it is required to specify that initial value it should get.
|
||||||
Values will usually be part of an expression or assignment statement::
|
Values will usually be part of an expression or assignment statement::
|
||||||
@ -374,10 +372,8 @@ Initial values across multiple runs of the program
|
|||||||
|
|
||||||
When declaring values with an initial value, this value will be set into the variable each time
|
When declaring values with an initial value, this value will be set into the variable each time
|
||||||
the program reaches the declaration again. This can be in loops, multiple subroutine calls,
|
the program reaches the declaration again. This can be in loops, multiple subroutine calls,
|
||||||
or even multiple invocations of the entire program. If you omit an initial value, it will
|
or even multiple invocations of the entire program.
|
||||||
be set to zero *but only for the first run of the program*. A second run will utilize the last value
|
If you omit the initial value, zero will be used instead.
|
||||||
where it left off (but your code will be a bit smaller because no initialization instructions
|
|
||||||
are generated)
|
|
||||||
|
|
||||||
This only works for simple types, *and not for string variables and arrays*.
|
This only works for simple types, *and not for string variables and arrays*.
|
||||||
It is assumed these are left unchanged by the program; they are not re-initialized on
|
It is assumed these are left unchanged by the program; they are not re-initialized on
|
||||||
@ -386,15 +382,6 @@ If you do modify them in-place, you should take care yourself that they work as
|
|||||||
expected when the program is restarted.
|
expected when the program is restarted.
|
||||||
(This is an optimization choice to avoid having to store two copies of every string and array)
|
(This is an optimization choice to avoid having to store two copies of every string and array)
|
||||||
|
|
||||||
.. caution::
|
|
||||||
variables that get allocated in zero-page will *not* have a zero starting value when you omit
|
|
||||||
the variable's initialization. They'll be whatever the last value in that zero page
|
|
||||||
location was. So it's best to don't depend on the uninitialized starting value!
|
|
||||||
|
|
||||||
.. warning::
|
|
||||||
this behavior may change in a future version so that subsequent runs always
|
|
||||||
use the same initial values
|
|
||||||
|
|
||||||
|
|
||||||
Loops
|
Loops
|
||||||
-----
|
-----
|
||||||
@ -406,6 +393,10 @@ Iterating with a floating point variable is not supported. If you want to loop o
|
|||||||
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
The *while*-loop is used to repeat a piece of code while a certain condition is still true.
|
||||||
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
|
The *repeat--until* loop is used to repeat a piece of code until a certain condition is true.
|
||||||
|
|
||||||
|
The *forever*-loop is used to simply run a piece of code in a loop, forever. You can still
|
||||||
|
break out of this loop if desired. A "while true" or "until false" loop is equivalent to
|
||||||
|
a forever-loop.
|
||||||
|
|
||||||
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
You can also create loops by using the ``goto`` statement, but this should usually be avoided.
|
||||||
|
|
||||||
.. attention::
|
.. attention::
|
||||||
@ -836,6 +827,10 @@ rrestore()
|
|||||||
read_flags()
|
read_flags()
|
||||||
Returns the current value of the CPU status register.
|
Returns the current value of the CPU status register.
|
||||||
|
|
||||||
|
exit(returncode)
|
||||||
|
Immediately stops the program and exits it, with the returncode in the A register.
|
||||||
|
Note: custom interrupt handlers remain active unless manually cleared first!
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Library routines
|
Library routines
|
||||||
|
@ -172,13 +172,13 @@ Code blocks
|
|||||||
-----------
|
-----------
|
||||||
|
|
||||||
A named block of actual program code. Itefines a *scope* (also known as 'namespace') and
|
A named block of actual program code. Itefines a *scope* (also known as 'namespace') and
|
||||||
can contain Prog8 *code*, *directives*, *variable declarations* and *subroutines*::
|
can only contain *directives*, *variable declarations*, *subroutines* or *inline assembly*::
|
||||||
|
|
||||||
<blockname> [<address>] {
|
<blockname> [<address>] {
|
||||||
<directives>
|
<directives>
|
||||||
<variables>
|
<variables>
|
||||||
<statements>
|
|
||||||
<subroutines>
|
<subroutines>
|
||||||
|
<inline asm>
|
||||||
}
|
}
|
||||||
|
|
||||||
The <blockname> must be a valid identifier.
|
The <blockname> must be a valid identifier.
|
||||||
@ -191,7 +191,6 @@ Also read :ref:`blocks`. Here is an example of a code block, to be loaded at ``
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
Labels
|
Labels
|
||||||
------
|
------
|
||||||
|
|
||||||
@ -217,7 +216,8 @@ Variable declarations
|
|||||||
|
|
||||||
Variables should be declared with their exact type and size so the compiler can allocate storage
|
Variables should be declared with their exact type and size so the compiler can allocate storage
|
||||||
for them. You can give them an initial value as well. That value can be a simple literal value,
|
for them. You can give them an initial value as well. That value can be a simple literal value,
|
||||||
or an expression. You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
or an expression. If you don't provide an intial value yourself, zero will be used.
|
||||||
|
You can add a ``@zp`` zeropage-tag, to tell the compiler to prioritize it
|
||||||
when selecting variables to be put into zeropage.
|
when selecting variables to be put into zeropage.
|
||||||
The syntax is::
|
The syntax is::
|
||||||
|
|
||||||
@ -322,7 +322,7 @@ Constants
|
|||||||
^^^^^^^^^
|
^^^^^^^^^
|
||||||
|
|
||||||
All variables can be assigned new values unless you use the ``const`` keyword.
|
All variables can be assigned new values unless you use the ``const`` keyword.
|
||||||
The initial value will now be evaluated at compile time (it must be a compile time constant expression).
|
The initial value must be known at compile time (it must be a compile time constant expression).
|
||||||
This is only valid for the simple numeric types (byte, word, float)::
|
This is only valid for the simple numeric types (byte, word, float)::
|
||||||
|
|
||||||
const byte max_age = 99
|
const byte max_age = 99
|
||||||
@ -598,8 +598,8 @@ You can use a single statement, or a statement block like in the example below::
|
|||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
repeat--until loop
|
repeat-until loop
|
||||||
^^^^^^^^^^^^^^^^^^
|
^^^^^^^^^^^^^^^^^
|
||||||
|
|
||||||
Until the given condition is true (1), repeat the given statement(s).
|
Until the given condition is true (1), repeat the given statement(s).
|
||||||
You can use a single statement, or a statement block like in the example below::
|
You can use a single statement, or a statement block like in the example below::
|
||||||
@ -611,6 +611,19 @@ You can use a single statement, or a statement block like in the example below::
|
|||||||
} until <condition>
|
} until <condition>
|
||||||
|
|
||||||
|
|
||||||
|
forever loop
|
||||||
|
^^^^^^^^^^^^
|
||||||
|
|
||||||
|
Simply run the code in a loop, forever. It's the same as a while true or until false loop,
|
||||||
|
or just a jump back to a previous label. You can still break out of this loop as well, if you want::
|
||||||
|
|
||||||
|
forever {
|
||||||
|
; .. do stuff
|
||||||
|
if something
|
||||||
|
break ; you can exit the loop if you want
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
Conditional Execution and Jumps
|
Conditional Execution and Jumps
|
||||||
-------------------------------
|
-------------------------------
|
||||||
|
|
||||||
|
@ -2,7 +2,10 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
- option to load library files from a directory instead of the embedded ones
|
- aliases for imported symbols for example perhaps '%alias print = c64scr.print'
|
||||||
|
- option to load library files from a directory instead of the embedded ones (easier library development/debugging)
|
||||||
|
|
||||||
|
- investigate support for 8bitguy's Commander X16 platform https://murray2.com/forums/commander-x16.9/
|
||||||
|
|
||||||
|
|
||||||
Memory Block Operations integrated in language?
|
Memory Block Operations integrated in language?
|
||||||
@ -26,12 +29,15 @@ More optimizations
|
|||||||
|
|
||||||
Add more compiler optimizations to the existing ones.
|
Add more compiler optimizations to the existing ones.
|
||||||
|
|
||||||
- on the language AST level
|
- remove unreachable code after an exit(), return or goto
|
||||||
- on the final assembly source level
|
- working subroutine inlining (start with trivial routines, grow to taking care of vars and identifier refs to them)
|
||||||
|
- add a compiler option to not include variable initialization code (useful if the program is expected to run only once, such as a game)
|
||||||
|
the program will then rely solely on the values as they are in memory at the time of program startup.
|
||||||
|
- Also some library routines and code patterns could perhaps be optimized further
|
||||||
- can the parameter passing to subroutines be optimized to avoid copying?
|
- can the parameter passing to subroutines be optimized to avoid copying?
|
||||||
- working subroutine inlining (taking care of vars and identifier refs to them)
|
- subroutine calling convention? like: 1 byte arg -> pass in A, 2 bytes -> pass in A+Y, return value likewise.
|
||||||
|
- more optimizations on the language AST level
|
||||||
Also some library routines and code patterns could perhaps be optimized further
|
- more optimizations on the final assembly source level
|
||||||
|
|
||||||
|
|
||||||
Eval stack redesign? (lot of work)
|
Eval stack redesign? (lot of work)
|
||||||
@ -43,13 +49,14 @@ It could then even be moved into the zeropage to greatly reduce code size and sl
|
|||||||
|
|
||||||
Or just move the LSB portion into a slab of the zeropage.
|
Or just move the LSB portion into a slab of the zeropage.
|
||||||
|
|
||||||
Allocate a fixed word in ZP that is the TOS so we can always operate on TOS directly
|
Allocate a fixed word in ZP that is the Top Of Stack value so we can always operate on TOS directly
|
||||||
without having to to index into the stack?
|
without having to index with X into the eval stack all the time?
|
||||||
|
This could GREATLY improvde code size and speed for operatios that work on just a single value.
|
||||||
|
|
||||||
|
|
||||||
Bugs
|
Bug Fixing
|
||||||
^^^^
|
^^^^^^^^^^
|
||||||
Ofcourse there are still bugs to fix ;)
|
Ofcourse there are always bugs to fix ;)
|
||||||
|
|
||||||
|
|
||||||
Misc
|
Misc
|
||||||
|
@ -104,7 +104,17 @@ main {
|
|||||||
ub = all(farr)
|
ub = all(farr)
|
||||||
if ub==0 c64scr.print("error all10\n")
|
if ub==0 c64scr.print("error all10\n")
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
|
|
||||||
c64scr.print("\nyou should see no errors above.")
|
c64scr.print("\nyou should see no errors above.")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
File diff suppressed because it is too large
Load Diff
@ -25,6 +25,7 @@ main {
|
|||||||
div_float(0,1,0)
|
div_float(0,1,0)
|
||||||
div_float(999.9,111.0,9.008108108108107)
|
div_float(999.9,111.0,9.008108108108107)
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -102,4 +103,12 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -33,6 +33,7 @@ main {
|
|||||||
minus_float(2.5,1.5,1.0)
|
minus_float(2.5,1.5,1.0)
|
||||||
minus_float(-1.5,3.5,-5.0)
|
minus_float(-1.5,3.5,-5.0)
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -110,4 +111,13 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -27,6 +27,7 @@ main {
|
|||||||
mul_float(2.5,10,25)
|
mul_float(2.5,10,25)
|
||||||
mul_float(-1.5,10,-15)
|
mul_float(-1.5,10,-15)
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -104,4 +105,12 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -31,6 +31,7 @@ main {
|
|||||||
plus_float(-1.5,3.5,2.0)
|
plus_float(-1.5,3.5,2.0)
|
||||||
plus_float(-1.1,3.3,2.2)
|
plus_float(-1.1,3.3,2.2)
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -108,4 +109,13 @@ main {
|
|||||||
c64flt.print_f(r)
|
c64flt.print_f(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@
|
|||||||
|
|
||||||
main {
|
main {
|
||||||
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
|
|
||||||
c64scr.plot(0,24)
|
c64scr.plot(0,24)
|
||||||
@ -64,6 +63,7 @@ main {
|
|||||||
warr[1]--
|
warr[1]--
|
||||||
flarr[1] --
|
flarr[1] --
|
||||||
check_ub(ub, 200)
|
check_ub(ub, 200)
|
||||||
|
|
||||||
Y=100
|
Y=100
|
||||||
Y--
|
Y--
|
||||||
check_ub(Y, 99)
|
check_ub(Y, 99)
|
||||||
@ -77,7 +77,7 @@ main {
|
|||||||
check_uw(uwarr[1], 2000)
|
check_uw(uwarr[1], 2000)
|
||||||
check_w(warr[1], -1000)
|
check_w(warr[1], -1000)
|
||||||
|
|
||||||
@($0400+400-1) = X
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub check_ub(ubyte value, ubyte expected) {
|
sub check_ub(ubyte value, ubyte expected) {
|
||||||
@ -139,4 +139,13 @@ main {
|
|||||||
c64flt.print_f(expected)
|
c64flt.print_f(expected)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -15,6 +15,8 @@ main {
|
|||||||
remainder_uword(40000,511,142)
|
remainder_uword(40000,511,142)
|
||||||
remainder_uword(40000,500,0)
|
remainder_uword(40000,500,0)
|
||||||
remainder_uword(43211,12,11)
|
remainder_uword(43211,12,11)
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
}
|
}
|
||||||
|
|
||||||
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
|
||||||
@ -46,4 +48,12 @@ main {
|
|||||||
c64scr.print_uw(r)
|
c64scr.print_uw(r)
|
||||||
c64.CHROUT('\n')
|
c64.CHROUT('\n')
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
130
examples/arithmetic/sgn.p8
Normal file
130
examples/arithmetic/sgn.p8
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
%import c64flt
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
byte b1
|
||||||
|
byte b2
|
||||||
|
ubyte ub1
|
||||||
|
ubyte ub2
|
||||||
|
word w1
|
||||||
|
word w2
|
||||||
|
uword uw1
|
||||||
|
uword uw2
|
||||||
|
float f1
|
||||||
|
float f2
|
||||||
|
|
||||||
|
b1 = 10
|
||||||
|
b2 = 10
|
||||||
|
if sgn(b2-b1) != 0
|
||||||
|
c64scr.print("sgn1 error1\n")
|
||||||
|
|
||||||
|
b1 = -100
|
||||||
|
b2 = -100
|
||||||
|
if sgn(b2-b1) != 0
|
||||||
|
c64scr.print("sgn1 error2\n")
|
||||||
|
|
||||||
|
ub1 = 200
|
||||||
|
ub2 = 200
|
||||||
|
if sgn(ub2-ub1) != 0
|
||||||
|
c64scr.print("sgn1 error3\n")
|
||||||
|
|
||||||
|
w1 = 100
|
||||||
|
w2 = 100
|
||||||
|
if sgn(w2-w1) != 0
|
||||||
|
c64scr.print("sgn1 error4\n")
|
||||||
|
|
||||||
|
w1 = -2000
|
||||||
|
w2 = -2000
|
||||||
|
if sgn(w2-w1) != 0
|
||||||
|
c64scr.print("sgn1 error5\n")
|
||||||
|
|
||||||
|
uw1 = 999
|
||||||
|
uw2 = 999
|
||||||
|
if sgn(uw2-uw1) != 0
|
||||||
|
c64scr.print("sgn1 error6\n")
|
||||||
|
|
||||||
|
f1 = 3.45
|
||||||
|
f2 = 3.45
|
||||||
|
if sgn(f2-f1) != 0
|
||||||
|
c64scr.print("sgn1 error7\n")
|
||||||
|
|
||||||
|
|
||||||
|
; -1
|
||||||
|
b1 = 11
|
||||||
|
b2 = 10
|
||||||
|
if sgn(b2-b1) != -1
|
||||||
|
c64scr.print("sgn2 error1\n")
|
||||||
|
|
||||||
|
b1 = -10
|
||||||
|
b2 = -100
|
||||||
|
if sgn(b2-b1) != -1
|
||||||
|
c64scr.print("sgn2 error2\n")
|
||||||
|
|
||||||
|
ub1 = 202
|
||||||
|
ub2 = 200
|
||||||
|
if sgn(ub2 as byte - ub1 as byte) != -1
|
||||||
|
c64scr.print("sgn2 error3\n")
|
||||||
|
|
||||||
|
w1 = 101
|
||||||
|
w2 = 100
|
||||||
|
if sgn(w2-w1) != -1
|
||||||
|
c64scr.print("sgn2 error4\n")
|
||||||
|
|
||||||
|
w1 = -200
|
||||||
|
w2 = -2000
|
||||||
|
if sgn(w2-w1) != -1
|
||||||
|
c64scr.print("sgn2 error5\n")
|
||||||
|
|
||||||
|
uw1 = 2222
|
||||||
|
uw2 = 999
|
||||||
|
if sgn((uw2 as word) - (uw1 as word)) != -1
|
||||||
|
c64scr.print("sgn2 error6a\n")
|
||||||
|
if sgn(uw2 - uw1) != 1 ; always 0 or 1 if unsigned
|
||||||
|
c64scr.print("sgn2 error6b\n")
|
||||||
|
|
||||||
|
f1 = 3.45
|
||||||
|
f2 = 1.11
|
||||||
|
if sgn(f2-f1) != -1
|
||||||
|
c64scr.print("sgn2 error7\n")
|
||||||
|
|
||||||
|
; +1
|
||||||
|
b1 = 11
|
||||||
|
b2 = 20
|
||||||
|
if sgn(b2-b1) != 1
|
||||||
|
c64scr.print("sgn3 error1\n")
|
||||||
|
|
||||||
|
b1 = -10
|
||||||
|
b2 = -1
|
||||||
|
if sgn(b2-b1) != 1
|
||||||
|
c64scr.print("sgn3 error2\n")
|
||||||
|
|
||||||
|
ub1 = 202
|
||||||
|
ub2 = 205
|
||||||
|
if sgn(ub2-ub1) != 1
|
||||||
|
c64scr.print("sgn3 error3\n")
|
||||||
|
|
||||||
|
w1 = 101
|
||||||
|
w2 = 200
|
||||||
|
if sgn(w2-w1) != 1
|
||||||
|
c64scr.print("sgn3 error4\n")
|
||||||
|
|
||||||
|
w1 = -200
|
||||||
|
w2 = -20
|
||||||
|
if sgn(w2-w1) != 1
|
||||||
|
c64scr.print("sgn3 error5\n")
|
||||||
|
|
||||||
|
uw1 = 2222
|
||||||
|
uw2 = 9999
|
||||||
|
if sgn(uw2-uw1) != 1
|
||||||
|
c64scr.print("sgn3 error6\n")
|
||||||
|
|
||||||
|
f1 = 3.45
|
||||||
|
f2 = 5.11
|
||||||
|
if sgn(f2-f1) != 1
|
||||||
|
c64scr.print("sgn3 error7\n")
|
||||||
|
|
||||||
|
c64scr.print("should see no sgn errors\n")
|
||||||
|
}
|
||||||
|
}
|
126
examples/balloonflight.p8
Normal file
126
examples/balloonflight.p8
Normal file
@ -0,0 +1,126 @@
|
|||||||
|
%import c64lib
|
||||||
|
%import c64utils
|
||||||
|
%zeropage basicsafe
|
||||||
|
|
||||||
|
main {
|
||||||
|
|
||||||
|
ubyte perform_scroll = false
|
||||||
|
|
||||||
|
sub start() {
|
||||||
|
c64.SPRPTR[0] = $0f00 / 64
|
||||||
|
c64.SPENA = 1
|
||||||
|
c64.SP0COL = 14
|
||||||
|
c64.SPXY[0] = 80
|
||||||
|
c64.SPXY[1] = 100
|
||||||
|
|
||||||
|
c64.SCROLX = c64.SCROLX & %11110111 ; 38 column mode
|
||||||
|
|
||||||
|
c64utils.set_rasterirq(1) ; enable animation
|
||||||
|
|
||||||
|
ubyte target_height = 10
|
||||||
|
ubyte active_height = 24
|
||||||
|
ubyte upwards = true
|
||||||
|
|
||||||
|
forever {
|
||||||
|
ubyte mountain = 223 ; slope upwards
|
||||||
|
if active_height < target_height {
|
||||||
|
active_height++
|
||||||
|
upwards = true
|
||||||
|
} else if active_height > target_height {
|
||||||
|
mountain = 233 ; slope downwards
|
||||||
|
active_height--
|
||||||
|
upwards = false
|
||||||
|
} else {
|
||||||
|
target_height = 8 + rnd() % 16
|
||||||
|
if upwards
|
||||||
|
mountain = 233
|
||||||
|
else
|
||||||
|
mountain = 223
|
||||||
|
}
|
||||||
|
|
||||||
|
while not perform_scroll {
|
||||||
|
; let the raster irq do its timing job
|
||||||
|
}
|
||||||
|
|
||||||
|
perform_scroll = false
|
||||||
|
c64scr.scroll_left_full(true)
|
||||||
|
if c64.RASTER & 1
|
||||||
|
c64.SPXY[1] ++
|
||||||
|
else
|
||||||
|
c64.SPXY[1] --
|
||||||
|
|
||||||
|
ubyte yy
|
||||||
|
for yy in 0 to active_height-1 {
|
||||||
|
c64scr.setcc(39, yy, 32, 2) ; clear top of screen
|
||||||
|
}
|
||||||
|
c64scr.setcc(39, active_height, mountain, 8) ; mountain edge
|
||||||
|
for yy in active_height+1 to 24 {
|
||||||
|
c64scr.setcc(39, yy, 160, 8) ; draw mountain
|
||||||
|
}
|
||||||
|
|
||||||
|
yy = rnd()
|
||||||
|
if yy > 100 {
|
||||||
|
; draw a star
|
||||||
|
c64scr.setcc(39, yy % (active_height-1), '.', rnd())
|
||||||
|
}
|
||||||
|
|
||||||
|
if yy > 200 {
|
||||||
|
; draw a tree
|
||||||
|
ubyte tree = 30
|
||||||
|
ubyte treecolor = 5
|
||||||
|
if yy & %01000000 != 0
|
||||||
|
tree = 88
|
||||||
|
else if yy & %00100000 != 0
|
||||||
|
tree = 65
|
||||||
|
if rnd() > 130
|
||||||
|
treecolor = 13
|
||||||
|
c64scr.setcc(39, active_height, tree, treecolor)
|
||||||
|
}
|
||||||
|
|
||||||
|
if yy > 235 {
|
||||||
|
; draw a camel
|
||||||
|
c64scr.setcc(39, active_height, 94, 9)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
spritedata $0f00 {
|
||||||
|
; this memory block contains the sprite data
|
||||||
|
; it must start on an address aligned to 64 bytes.
|
||||||
|
%option force_output ; make sure the data in this block appears in the resulting program
|
||||||
|
|
||||||
|
ubyte[] balloonsprite = [ %00000000,%01111111,%00000000,
|
||||||
|
%00000001,%11111111,%11000000,
|
||||||
|
%00000011,%11111111,%11100000,
|
||||||
|
%00000011,%11100011,%11100000,
|
||||||
|
%00000111,%11011100,%11110000,
|
||||||
|
%00000111,%11011101,%11110000,
|
||||||
|
%00000111,%11011100,%11110000,
|
||||||
|
%00000011,%11100011,%11100000,
|
||||||
|
%00000011,%11111111,%11100000,
|
||||||
|
%00000011,%11111111,%11100000,
|
||||||
|
%00000010,%11111111,%10100000,
|
||||||
|
%00000001,%01111111,%01000000,
|
||||||
|
%00000001,%00111110,%01000000,
|
||||||
|
%00000000,%10011100,%10000000,
|
||||||
|
%00000000,%10011100,%10000000,
|
||||||
|
%00000000,%01001001,%00000000,
|
||||||
|
%00000000,%01001001,%00000000,
|
||||||
|
%00000000,%00111110,%00000000,
|
||||||
|
%00000000,%00111110,%00000000,
|
||||||
|
%00000000,%00111110,%00000000,
|
||||||
|
%00000000,%00011100,%00000000 ]
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
irq {
|
||||||
|
ubyte smoothx=7
|
||||||
|
sub irq() {
|
||||||
|
smoothx = (smoothx-1) & 7
|
||||||
|
main.perform_scroll = smoothx==0
|
||||||
|
c64.SCROLX = (c64.SCROLX & %11111000) | smoothx
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -16,7 +16,7 @@ sub start() {
|
|||||||
void c64.CHRIN()
|
void c64.CHRIN()
|
||||||
c64.CLEARSCR()
|
c64.CLEARSCR()
|
||||||
|
|
||||||
while(true) {
|
forever {
|
||||||
uword note
|
uword note
|
||||||
for note in notes {
|
for note in notes {
|
||||||
ubyte note1 = lsb(note)
|
ubyte note1 = lsb(note)
|
||||||
@ -34,11 +34,12 @@ sub start() {
|
|||||||
delay()
|
delay()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
sub delay() {
|
sub delay() {
|
||||||
ubyte d
|
ubyte d
|
||||||
for d in 0 to 12 {
|
for d in 0 to 12 {
|
||||||
while(c64.RASTER!=0) {
|
while c64.RASTER!=0 {
|
||||||
; tempo delay synced to screen refresh
|
; tempo delay synced to screen refresh
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -53,7 +54,7 @@ sub start() {
|
|||||||
c64.COLOR=4
|
c64.COLOR=4
|
||||||
c64.CHROUT('Q')
|
c64.CHROUT('Q')
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
; details about the boulderdash music can be found here:
|
; details about the boulderdash music can be found here:
|
||||||
; https://www.elmerproductions.com/sp/peterb/sounds.html#Theme%20tune
|
; https://www.elmerproductions.com/sp/peterb/sounds.html#Theme%20tune
|
||||||
|
250
examples/c64graphics.p8
Normal file
250
examples/c64graphics.p8
Normal file
@ -0,0 +1,250 @@
|
|||||||
|
%import c64lib
|
||||||
|
|
||||||
|
; bitmap pixel graphics module for the C64
|
||||||
|
; only black/white monchrome for now
|
||||||
|
|
||||||
|
; you could put this code at $4000 which is after the bitmap screen in memory ($2000-$3fff),
|
||||||
|
; this leaves more space for user program code.
|
||||||
|
|
||||||
|
graphics {
|
||||||
|
const uword bitmap_address = $2000
|
||||||
|
|
||||||
|
sub enable_bitmap_mode() {
|
||||||
|
; enable bitmap screen, erase it and set colors to black/white.
|
||||||
|
c64.SCROLY |= %00100000
|
||||||
|
c64.VMCSB = (c64.VMCSB & %11110000) | %00001000 ; $2000-$3fff
|
||||||
|
memset(bitmap_address, 320*200/8, 0)
|
||||||
|
c64scr.clear_screen($10, 0) ; pixel color $1 (white) backround $0 (black)
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub line(uword x1, ubyte y1, uword x2, ubyte y2) {
|
||||||
|
; Bresenham algorithm
|
||||||
|
word dx
|
||||||
|
word dy
|
||||||
|
byte ix = 1
|
||||||
|
byte iy = 1
|
||||||
|
if x2>x1 {
|
||||||
|
dx = x2-x1
|
||||||
|
} else {
|
||||||
|
ix = -1
|
||||||
|
dx = x1-x2
|
||||||
|
}
|
||||||
|
if y2>y1 {
|
||||||
|
dy = y2-y1
|
||||||
|
} else {
|
||||||
|
iy = -1
|
||||||
|
dy = y1-y2
|
||||||
|
}
|
||||||
|
word dx2 = 2 * dx
|
||||||
|
word dy2 = 2 * dy
|
||||||
|
word d = 0
|
||||||
|
plotx = x1
|
||||||
|
|
||||||
|
if dx >= dy {
|
||||||
|
if ix<0 {
|
||||||
|
forever {
|
||||||
|
graphics.plot(y1)
|
||||||
|
if plotx==x2
|
||||||
|
return
|
||||||
|
plotx--
|
||||||
|
d += dy2
|
||||||
|
if d > dx {
|
||||||
|
y1 += iy
|
||||||
|
d -= dx2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
forever {
|
||||||
|
graphics.plot(y1)
|
||||||
|
if plotx==x2
|
||||||
|
return
|
||||||
|
plotx++
|
||||||
|
d += dy2
|
||||||
|
if d > dx {
|
||||||
|
y1 += iy
|
||||||
|
d -= dx2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
if iy<0 {
|
||||||
|
forever {
|
||||||
|
plot(y1)
|
||||||
|
if y1 == y2
|
||||||
|
return
|
||||||
|
y1--
|
||||||
|
d += dx2
|
||||||
|
if d > dy {
|
||||||
|
plotx += ix as word
|
||||||
|
d -= dy2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
forever {
|
||||||
|
plot(y1)
|
||||||
|
if y1 == y2
|
||||||
|
return
|
||||||
|
y1++
|
||||||
|
d += dx2
|
||||||
|
if d > dy {
|
||||||
|
plotx += ix as word
|
||||||
|
d -= dy2
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub circle(uword xcenter, ubyte ycenter, ubyte radius) {
|
||||||
|
; Midpoint algorithm
|
||||||
|
ubyte ploty
|
||||||
|
ubyte xx = radius
|
||||||
|
ubyte yy = 0
|
||||||
|
byte decisionOver2 = 1-xx
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
plotx = xcenter + xx
|
||||||
|
ploty = ycenter + yy
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter - xx
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter + xx
|
||||||
|
ploty = ycenter - yy
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter - xx
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter + yy
|
||||||
|
ploty = ycenter + xx
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter - yy
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter + yy
|
||||||
|
ploty = ycenter - xx
|
||||||
|
plot(ploty)
|
||||||
|
plotx = xcenter - yy
|
||||||
|
plot(ploty)
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sub disc(uword cx, ubyte cy, ubyte radius) {
|
||||||
|
; Midpoint algorithm, filled
|
||||||
|
ubyte xx = radius
|
||||||
|
ubyte yy = 0
|
||||||
|
byte decisionOver2 = 1-xx
|
||||||
|
|
||||||
|
while xx>=yy {
|
||||||
|
for plotx in cx to cx+xx {
|
||||||
|
plot(cy + yy)
|
||||||
|
plot(cy - yy)
|
||||||
|
}
|
||||||
|
for plotx in cx-xx to cx-1 {
|
||||||
|
plot(cy + yy)
|
||||||
|
plot(cy - yy)
|
||||||
|
}
|
||||||
|
for plotx in cx to cx+yy {
|
||||||
|
plot(cy + xx)
|
||||||
|
plot(cy - xx)
|
||||||
|
}
|
||||||
|
for plotx in cx-yy to cx {
|
||||||
|
plot(cy + xx)
|
||||||
|
plot(cy - xx)
|
||||||
|
}
|
||||||
|
yy++
|
||||||
|
if decisionOver2<=0
|
||||||
|
decisionOver2 += 2*yy+1
|
||||||
|
else {
|
||||||
|
xx--
|
||||||
|
decisionOver2 += 2*(yy-xx)+1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
; here is the non-asm code for the plot routine below:
|
||||||
|
; sub plot_nonasm(uword px, ubyte py) {
|
||||||
|
; ubyte[] ormask = [128, 64, 32, 16, 8, 4, 2, 1]
|
||||||
|
; uword addr = bitmap_address + 320*(py>>3) + (py & 7) + (px & %0000000111111000)
|
||||||
|
; @(addr) |= ormask[lsb(px) & 7]
|
||||||
|
; }
|
||||||
|
|
||||||
|
uword plotx ; 0..319
|
||||||
|
|
||||||
|
asmsub plot(ubyte ploty @A) { ; plotx is 16 bits 0 to 319... doesn't fit in a register
|
||||||
|
%asm {{
|
||||||
|
tay
|
||||||
|
stx c64.SCRATCH_ZPREGX
|
||||||
|
lda plotx+1
|
||||||
|
sta c64.SCRATCH_ZPWORD2+1
|
||||||
|
lsr a ; 0
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
lda plotx
|
||||||
|
pha
|
||||||
|
and #7
|
||||||
|
tax
|
||||||
|
|
||||||
|
lda _y_lookup_lo,y
|
||||||
|
clc
|
||||||
|
adc c64.SCRATCH_ZPWORD2
|
||||||
|
sta c64.SCRATCH_ZPWORD2
|
||||||
|
lda _y_lookup_hi,y
|
||||||
|
adc c64.SCRATCH_ZPWORD2+1
|
||||||
|
sta c64.SCRATCH_ZPWORD2+1
|
||||||
|
|
||||||
|
pla ; plotx
|
||||||
|
and #%11111000
|
||||||
|
tay
|
||||||
|
lda (c64.SCRATCH_ZPWORD2),y
|
||||||
|
ora _ormask,x
|
||||||
|
sta (c64.SCRATCH_ZPWORD2),y
|
||||||
|
|
||||||
|
ldx c64.SCRATCH_ZPREGX
|
||||||
|
rts
|
||||||
|
|
||||||
|
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
|
||||||
|
|
||||||
|
; note: this can be even faster if we also have a 256 byte x-lookup table, but hey.
|
||||||
|
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
|
||||||
|
; the y lookup tables encode this formula: bitmap_address + 320*(py>>3) + (py & 7) (y from 0..199)
|
||||||
|
_y_lookup_hi
|
||||||
|
.byte $20, $20, $20, $20, $20, $20, $20, $20, $21, $21, $21, $21, $21, $21, $21, $21
|
||||||
|
.byte $22, $22, $22, $22, $22, $22, $22, $22, $23, $23, $23, $23, $23, $23, $23, $23
|
||||||
|
.byte $25, $25, $25, $25, $25, $25, $25, $25, $26, $26, $26, $26, $26, $26, $26, $26
|
||||||
|
.byte $27, $27, $27, $27, $27, $27, $27, $27, $28, $28, $28, $28, $28, $28, $28, $28
|
||||||
|
.byte $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2a, $2b, $2b, $2b, $2b, $2b, $2b, $2b, $2b
|
||||||
|
.byte $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2c, $2d, $2d, $2d, $2d, $2d, $2d, $2d, $2d
|
||||||
|
.byte $2f, $2f, $2f, $2f, $2f, $2f, $2f, $2f, $30, $30, $30, $30, $30, $30, $30, $30
|
||||||
|
.byte $31, $31, $31, $31, $31, $31, $31, $31, $32, $32, $32, $32, $32, $32, $32, $32
|
||||||
|
.byte $34, $34, $34, $34, $34, $34, $34, $34, $35, $35, $35, $35, $35, $35, $35, $35
|
||||||
|
.byte $36, $36, $36, $36, $36, $36, $36, $36, $37, $37, $37, $37, $37, $37, $37, $37
|
||||||
|
.byte $39, $39, $39, $39, $39, $39, $39, $39, $3a, $3a, $3a, $3a, $3a, $3a, $3a, $3a
|
||||||
|
.byte $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3b, $3c, $3c, $3c, $3c, $3c, $3c, $3c, $3c
|
||||||
|
.byte $3e, $3e, $3e, $3e, $3e, $3e, $3e, $3e
|
||||||
|
|
||||||
|
_y_lookup_lo
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07, $40, $41, $42, $43, $44, $45, $46, $47
|
||||||
|
.byte $80, $81, $82, $83, $84, $85, $86, $87, $c0, $c1, $c2, $c3, $c4, $c5, $c6, $c7
|
||||||
|
.byte $00, $01, $02, $03, $04, $05, $06, $07
|
||||||
|
}}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
@ -106,12 +106,15 @@ main {
|
|||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
c64scr.print("error in 22>=22!\n")
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("stack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("error: stack x != 255 !\n")
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -106,11 +106,15 @@ main {
|
|||||||
else
|
else
|
||||||
c64scr.print("error in -22.2>=-22.2!\n")
|
c64scr.print("error in -22.2>=-22.2!\n")
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
}
|
||||||
c64scr.print("stack x ok!\n")
|
|
||||||
else
|
sub check_eval_stack() {
|
||||||
c64scr.print("error: stack x != 255 !\n")
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -106,12 +106,15 @@ main {
|
|||||||
else
|
else
|
||||||
c64scr.print("error in 22>=22!\n")
|
c64scr.print("error in 22>=22!\n")
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
}
|
||||||
c64scr.print("stack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("error: stack x != 255 !\n")
|
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -106,13 +106,15 @@ main {
|
|||||||
else
|
else
|
||||||
c64scr.print("error in 322>=322!\n")
|
c64scr.print("error in 322>=322!\n")
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
|
}
|
||||||
|
|
||||||
ubyte endX = X
|
sub check_eval_stack() {
|
||||||
if endX == 255
|
if X!=255 {
|
||||||
c64scr.print("stack x ok!\n")
|
c64scr.print("x=")
|
||||||
else
|
c64scr.print_ub(X)
|
||||||
c64scr.print("error: stack x != 255 !\n")
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -138,13 +138,15 @@ main {
|
|||||||
else
|
else
|
||||||
c64scr.print("error in 1000>=1000!\n")
|
c64scr.print("error in 1000>=1000!\n")
|
||||||
|
|
||||||
|
check_eval_stack()
|
||||||
|
}
|
||||||
|
|
||||||
ubyte endX = X
|
sub check_eval_stack() {
|
||||||
if endX == 255
|
if X!=255 {
|
||||||
c64scr.print("stack x ok!\n")
|
c64scr.print("x=")
|
||||||
else
|
c64scr.print_ub(X)
|
||||||
c64scr.print("error: stack x != 255 !\n")
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -52,15 +52,9 @@ main {
|
|||||||
c64scr.print("v1=20, v2=-111\n")
|
c64scr.print("v1=20, v2=-111\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("\nstack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("\nerror: stack x != 255 !\n")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
c64scr.print(" == != < > <= >=\n")
|
c64scr.print(" == != < > <= >=\n")
|
||||||
|
|
||||||
@ -98,4 +92,12 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -68,13 +68,7 @@ main {
|
|||||||
c64scr.print("v1 = v2 = 0\n")
|
c64scr.print("v1 = v2 = 0\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("\nstack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("\nerror: stack x != 255 !\n")
|
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -112,7 +106,13 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -52,12 +52,7 @@ main {
|
|||||||
c64scr.print("v1=220, v2=10\n")
|
c64scr.print("v1=220, v2=10\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("\nstack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("\nerror: stack x != 255 !\n")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -97,4 +92,12 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -82,13 +82,7 @@ main {
|
|||||||
c64scr.print("v1 = v2 = aa\n")
|
c64scr.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("\nstack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("\nerror: stack x != 255 !\n")
|
|
||||||
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -128,4 +122,12 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -118,12 +118,7 @@ main {
|
|||||||
c64scr.print("v1 = v2 = aa\n")
|
c64scr.print("v1 = v2 = aa\n")
|
||||||
compare()
|
compare()
|
||||||
|
|
||||||
ubyte endX = X
|
check_eval_stack()
|
||||||
if endX == 255
|
|
||||||
c64scr.print("\nstack x ok!\n")
|
|
||||||
else
|
|
||||||
c64scr.print("\nerror: stack x != 255 !\n")
|
|
||||||
|
|
||||||
return
|
return
|
||||||
|
|
||||||
sub compare() {
|
sub compare() {
|
||||||
@ -163,4 +158,12 @@ main {
|
|||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub check_eval_stack() {
|
||||||
|
if X!=255 {
|
||||||
|
c64scr.print("x=")
|
||||||
|
c64scr.print_ub(X)
|
||||||
|
c64scr.print(" error!\n")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
BIN
examples/compiled/balloonflight.prg
Normal file
BIN
examples/compiled/balloonflight.prg
Normal file
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user