Merge branch 'asmgen2-ast-only'

This commit is contained in:
Irmen de Jong 2019-08-05 21:07:32 +02:00
commit 094c8ab94c
96 changed files with 13322 additions and 3886 deletions

2
.idea/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
# Default ignored files
/shelf/

View File

@ -2,6 +2,7 @@
<project version="4">
<component name="ProjectModuleManager">
<modules>
<module fileurl="file://$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" filepath="$PROJECT_DIR$/DeprecatedStackVm/DeprecatedStackVm.iml" />
<module fileurl="file://$PROJECT_DIR$/compiler/compiler.iml" filepath="$PROJECT_DIR$/compiler/compiler.iml" />
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />

View File

@ -0,0 +1,10 @@
<?xml version="1.0" encoding="UTF-8"?>
<module type="JAVA_MODULE" version="4">
<component name="NewModuleRootManager" inherit-compiler-output="true">
<exclude-output />
<content url="file://$MODULE_DIR$" />
<orderEntry type="inheritedJdk" />
<orderEntry type="sourceFolder" forTests="false" />
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
</component>
</module>

File diff suppressed because it is too large Load Diff

View File

@ -1,4 +1,4 @@
package prog8.compiler.intermediate
package compiler.intermediate
import prog8.vm.RuntimeValue
import prog8.vm.stackvm.Syscall
@ -15,8 +15,8 @@ open class Instruction(val opcode: Opcode,
val argStr = arg?.toString() ?: ""
val result =
when {
opcode==Opcode.LINE -> "_line $callLabel"
opcode==Opcode.INLINE_ASSEMBLY -> {
opcode== Opcode.LINE -> "_line $callLabel"
opcode== Opcode.INLINE_ASSEMBLY -> {
// inline assembly is not written out (it can't be processed as intermediate language)
// instead, it is converted into a system call that can be intercepted by the vm
if(callLabel!=null)
@ -24,10 +24,10 @@ open class Instruction(val opcode: Opcode,
else
"inline_assembly"
}
opcode==Opcode.INCLUDE_FILE -> {
opcode== Opcode.INCLUDE_FILE -> {
"include_file \"$callLabel\" $arg $arg2"
}
opcode==Opcode.SYSCALL -> {
opcode== Opcode.SYSCALL -> {
val syscall = Syscall.values().find { it.callNr==arg!!.numericValue() }
"syscall $syscall"
}

View File

@ -1,4 +1,4 @@
package prog8.compiler.intermediate
package compiler.intermediate
import prog8.ast.antlr.escape
import prog8.ast.base.*
@ -85,7 +85,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
val branchOpcodes = setOf(Opcode.JZ, Opcode.JNZ, Opcode.JZW, Opcode.JNZW)
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach {
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
if (it[1].value.opcode in branchOpcodes) {
if (it[0].value.opcode in pushvalue) {
val value = it[0].value.arg!!.asBoolean
@ -138,8 +138,8 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
for(blk in blocks) {
val instructionsToReplace = mutableMapOf<Int, Instruction>()
blk.instructions.asSequence().withIndex().filter {it.value.opcode!=Opcode.LINE}.windowed(2).toList().forEach {
if(it[0].value.opcode==Opcode.CALL && it[1].value.opcode==Opcode.RETURN) {
blk.instructions.asSequence().withIndex().filter {it.value.opcode!= Opcode.LINE }.windowed(2).toList().forEach {
if(it[0].value.opcode== Opcode.CALL && it[1].value.opcode== Opcode.RETURN) {
instructionsToReplace[it[1].index] = Instruction(Opcode.JUMP, callLabel = it[0].value.callLabel)
instructionsToReplace[it[0].index] = Instruction(Opcode.NOP)
}
@ -315,12 +315,12 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_B_TO_F, Opcode.CAST_UB_TO_F-> {
Opcode.CAST_B_TO_F, Opcode.CAST_UB_TO_F -> {
val ins = Instruction(Opcode.PUSH_FLOAT, RuntimeValue(DataType.FLOAT, ins0.arg!!.integerValue().toDouble()))
instructionsToReplace[index0] = ins
instructionsToReplace[index1] = Instruction(Opcode.NOP)
}
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F-> throw CompilerException("invalid conversion following a byte")
Opcode.CAST_W_TO_F, Opcode.CAST_UW_TO_F -> throw CompilerException("invalid conversion following a byte")
Opcode.DISCARD_BYTE -> {
instructionsToReplace[index0] = Instruction(Opcode.NOP)
instructionsToReplace[index1] = Instruction(Opcode.NOP)
@ -462,7 +462,7 @@ class IntermediateProgram(val name: String, var loadAddress: Int, val heap: Heap
}
fun newBlock(name: String, address: Int?, options: Set<String>) {
currentBlock = ProgramBlock(name, address, force_output="force_output" in options)
currentBlock = ProgramBlock(name, address, force_output = "force_output" in options)
blocks.add(currentBlock)
}

View File

@ -1,4 +1,4 @@
package prog8.compiler.intermediate
package compiler.intermediate
enum class Opcode {

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package compiler.target.c64.codegen
// note: to put stuff on the stack, we use Absolute,X addressing mode which is 3 bytes / 4 cycles
// possible space optimization is to use zeropage (indirect),Y which is 2 bytes, but 5 cycles

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package compiler.target.c64.codegen
import prog8.compiler.CompilerException
import prog8.compiler.intermediate.Instruction
@ -459,8 +459,8 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
Opcode.CAST_B_TO_F -> " jsr c64flt.stack_b2float"
Opcode.CAST_UW_TO_F -> " jsr c64flt.stack_uw2float"
Opcode.CAST_W_TO_F -> " jsr c64flt.stack_w2float"
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2uw"
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2w"
Opcode.CAST_F_TO_UB -> " jsr c64flt.stack_float2ub"
Opcode.CAST_F_TO_B -> " jsr c64flt.stack_float2b"
Opcode.CAST_F_TO_UW -> " jsr c64flt.stack_float2uw"
Opcode.CAST_F_TO_W -> " jsr c64flt.stack_float2w"
Opcode.CAST_UB_TO_UW, Opcode.CAST_UB_TO_W -> " lda #0 | sta $ESTACK_HI_PLUS1_HEX,x" // clear the msb
@ -513,27 +513,27 @@ internal fun simpleInstr2Asm(ins: Instruction, block: IntermediateProgram.Progra
Opcode.BITOR_WORD -> " jsr prog8_lib.bitor_w"
Opcode.BITXOR_WORD -> " jsr prog8_lib.bitxor_w"
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"
Opcode.REMAINDER_UB -> " jsr prog8_lib.remainder_ub"
Opcode.REMAINDER_UW -> " jsr prog8_lib.remainder_uw"
Opcode.GREATER_B -> " jsr prog8_lib.greater_b"
Opcode.GREATER_UB -> " jsr prog8_lib.greater_ub"
Opcode.GREATER_W -> " jsr prog8_lib.greater_w"
Opcode.GREATER_UW -> " jsr prog8_lib.greater_uw"
Opcode.GREATER_F -> " jsr c64flt.greater_f"
Opcode.GREATER_B -> " jsr prog8_lib.greater_b"
Opcode.GREATER_UB -> " jsr prog8_lib.greater_ub"
Opcode.GREATER_W -> " jsr prog8_lib.greater_w"
Opcode.GREATER_UW -> " jsr prog8_lib.greater_uw"
Opcode.GREATER_F -> " jsr c64flt.greater_f"
Opcode.GREATEREQ_B -> " jsr prog8_lib.greatereq_b"
Opcode.GREATEREQ_UB -> " jsr prog8_lib.greatereq_ub"
Opcode.GREATEREQ_W -> " jsr prog8_lib.greatereq_w"
Opcode.GREATEREQ_UW -> " jsr prog8_lib.greatereq_uw"
Opcode.GREATEREQ_F -> " jsr c64flt.greatereq_f"
Opcode.GREATEREQ_B -> " jsr prog8_lib.greatereq_b"
Opcode.GREATEREQ_UB -> " jsr prog8_lib.greatereq_ub"
Opcode.GREATEREQ_W -> " jsr prog8_lib.greatereq_w"
Opcode.GREATEREQ_UW -> " jsr prog8_lib.greatereq_uw"
Opcode.GREATEREQ_F -> " jsr c64flt.greatereq_f"
Opcode.EQUAL_BYTE -> " jsr prog8_lib.equal_b"
Opcode.EQUAL_WORD -> " jsr prog8_lib.equal_w"
Opcode.EQUAL_F -> " jsr c64flt.equal_f"
Opcode.EQUAL_BYTE -> " jsr prog8_lib.equal_b"
Opcode.EQUAL_WORD -> " jsr prog8_lib.equal_w"
Opcode.EQUAL_F -> " jsr c64flt.equal_f"
Opcode.NOTEQUAL_BYTE -> " jsr prog8_lib.notequal_b"
Opcode.NOTEQUAL_WORD -> " jsr prog8_lib.notequal_w"
Opcode.NOTEQUAL_F -> " jsr c64flt.notequal_f"
Opcode.NOTEQUAL_F -> " jsr c64flt.notequal_f"
Opcode.LESS_UB -> " jsr prog8_lib.less_ub"
Opcode.LESS_B -> " jsr prog8_lib.less_b"

View File

@ -7,7 +7,7 @@
%option enable_floats
~ c64flt {
c64flt {
; ---- this block contains C-64 floating point related functions ----
const float PI = 3.141592653589793
@ -52,7 +52,7 @@ asmsub MOVMF (uword mflpt @ XY) clobbers(A,Y) = $bbd4 ; store fac1 to memory
; fac1-> signed word in Y/A (might throw ILLEGAL QUANTITY)
; (tip: use c64flt.FTOSWRDAY to get A/Y output; lo/hi switched to normal little endian order)
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa
asmsub FTOSWORDYA () clobbers(X) -> ubyte @ Y, ubyte @ A = $b1aa ; note: calls AYINT.
; fac1 -> unsigned word in Y/A (might throw ILLEGAL QUANTITY) (result also in $14/15)
; (tip: use c64flt.GETADRAY to get A/Y output; lo/hi switched to normal little endian order)
@ -196,8 +196,8 @@ sub print_f (float value) {
; ---- prints the floating point value (without a newline) using basic rom routines.
%asm {{
stx c64.SCRATCH_ZPREGX
lda #<print_f_value
ldy #>print_f_value
lda #<value
ldy #>value
jsr MOVFM ; load float into fac1
jsr FOUT ; fac1 to string in A/Y
jsr c64.STROUT ; print string in A/Y
@ -310,7 +310,7 @@ stack_uw2float .proc
jmp push_fac1_as_result
.pend
stack_float2w .proc
stack_float2w .proc ; also used for float2b
jsr pop_float_fac1
stx c64.SCRATCH_ZPREGX
jsr AYINT
@ -323,7 +323,7 @@ stack_float2w .proc
rts
.pend
stack_float2uw .proc
stack_float2uw .proc ; also used for float2ub
jsr pop_float_fac1
stx c64.SCRATCH_ZPREGX
jsr GETADR

View File

@ -6,7 +6,7 @@
; indent format: TABS, size=8
~ c64 {
c64 {
const uword ESTACK_LO = $ce00 ; evaluation stack (lsb)
const uword ESTACK_HI = $cf00 ; evaluation stack (msb)
&ubyte SCRATCH_ZPB1 = $02 ; scratch byte 1 in ZP
@ -21,7 +21,7 @@
&ubyte TIME_LO = $a2 ; .. lo byte. Updated by IRQ every 1/60 sec
&ubyte STKEY = $91 ; various keyboard statuses (updated by IRQ)
&ubyte SFDX = $cb ; current key pressed (matrix value) (updated by IRQ)
&ubyte COLOR = $0286 ; cursor color
&ubyte HIBASE = $0288 ; screen base address / 256 (hi-byte of screen memory address)
&uword CINV = $0314 ; IRQ vector
@ -202,12 +202,12 @@ asmsub CINT () clobbers(A,X,Y) = $FF81 ; (alias: SCINIT) initialize scree
asmsub IOINIT () clobbers(A, X) = $FF84 ; initialize I/O devices (CIA, SID, IRQ)
asmsub RAMTAS () clobbers(A,X,Y) = $FF87 ; initialize RAM, tape buffer, screen
asmsub RESTOR () clobbers(A,X,Y) = $FF8A ; restore default I/O vectors
asmsub VECTOR (ubyte dir @ Pc, uword userptr @ XY) clobbers(A,Y) = $FF8D ; read/set I/O vector table
asmsub VECTOR (uword userptr @ XY, ubyte dir @ Pc) clobbers(A,Y) = $FF8D ; read/set I/O vector table
asmsub SETMSG (ubyte value @ A) = $FF90 ; set Kernal message control flag
asmsub SECOND (ubyte address @ A) clobbers(A) = $FF93 ; (alias: LSTNSA) send secondary address after LISTEN
asmsub TKSA (ubyte address @ A) clobbers(A) = $FF96 ; (alias: TALKSA) send secondary address after TALK
asmsub MEMTOP (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (ubyte dir @ Pc, uword address @ XY) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
asmsub MEMTOP (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF99 ; read/set top of memory pointer
asmsub MEMBOT (uword address @ XY, ubyte dir @ Pc) -> uword @ XY = $FF9C ; read/set bottom of memory pointer
asmsub SCNKEY () clobbers(A,X,Y) = $FF9F ; scan the keyboard
asmsub SETTMO (ubyte timeout @ A) = $FFA2 ; set time-out flag for IEEE bus
asmsub ACPTR () -> ubyte @ A = $FFA5 ; (alias: IECIN) input byte from serial bus
@ -235,7 +235,7 @@ asmsub GETIN () clobbers(X,Y) -> ubyte @ A = $FFE4 ; (via 810 ($32A)) get a
asmsub CLALL () clobbers(A,X) = $FFE7 ; (via 812 ($32C)) close all files
asmsub UDTIM () clobbers(A,X) = $FFEA ; update the software clock
asmsub SCREEN () -> ubyte @ X, ubyte @ Y = $FFED ; read number of screen rows and columns
asmsub PLOT (ubyte dir @ Pc, ubyte col @ Y, ubyte row @ X) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
asmsub PLOT (ubyte col @ Y, ubyte row @ X, ubyte dir @ Pc) -> ubyte @ X, ubyte @ Y = $FFF0 ; read/set position of cursor on screen. Use c64scr.plot for a 'safe' wrapper that preserves X.
asmsub IOBASE () -> uword @ XY = $FFF3 ; read base address of I/O devices
; ---- end of C64 kernal routines ----

View File

@ -9,7 +9,7 @@
%import c64lib
~ c64utils {
c64utils {
const uword ESTACK_LO = $ce00
const uword ESTACK_HI = $cf00
@ -461,7 +461,7 @@ _raster_irq_handler
~ c64scr {
c64scr {
; ---- this block contains (character) Screen and text I/O related functions ----
@ -821,7 +821,7 @@ asmsub print_b (byte value @ A) clobbers(A,Y) {
}
asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
asmsub print_ubhex (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in hex form (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
stx c64.SCRATCH_ZPREGX
@ -840,7 +840,7 @@ asmsub print_ubhex (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
}
asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
asmsub print_ubbin (ubyte value @ A, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the ubyte in A in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
stx c64.SCRATCH_ZPREGX
@ -862,7 +862,7 @@ asmsub print_ubbin (ubyte prefix @ Pc, ubyte value @ A) clobbers(A,Y) {
}
asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
asmsub print_uwbin (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in binary form (if Carry is set, a radix prefix '%' is printed as well)
%asm {{
pha
@ -875,7 +875,7 @@ asmsub print_uwbin (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
}
asmsub print_uwhex (ubyte prefix @ Pc, uword value @ AY) clobbers(A,Y) {
asmsub print_uwhex (uword value @ AY, ubyte prefix @ Pc) clobbers(A,Y) {
; ---- print the uword in A/Y in hexadecimal form (4 digits)
; (if Carry is set, a radix prefix '$' is printed as well)
%asm {{
@ -1063,7 +1063,7 @@ _mod lda $ffff ; modified
sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) {
; ---- set char+color at the given position on the screen
%asm {{
lda setcc_row
lda row
asl a
tay
lda setchr._screenrows+1,y
@ -1072,15 +1072,15 @@ sub setcc (ubyte column, ubyte row, ubyte char, ubyte color) {
sta _colormod+2
lda setchr._screenrows,y
clc
adc setcc_column
adc column
sta _charmod+1
sta _colormod+1
bcc +
inc _charmod+2
inc _colormod+2
+ lda setcc_char
+ lda char
_charmod sta $ffff ; modified
lda setcc_color
lda color
_colormod sta $ffff ; modified
rts
}}

View File

@ -6,6 +6,6 @@
%import c64lib
~ math {
math {
%asminclude "library:math.asm", ""
}

View File

@ -35,6 +35,17 @@ init_system .proc
rts
.pend
read_byte_from_address .proc
; -- read the byte from the memory address on the top of the stack, return in A (stack remains unchanged)
lda c64.ESTACK_LO+1,x
ldy c64.ESTACK_HI+1,x
sta (+) +1
sty (+) +2
+ lda $ffff ; modified
rts
.pend
add_a_to_zpword .proc
; -- add ubyte in A to the uword in c64.SCRATCH_ZPWORD1

View File

@ -6,6 +6,6 @@
%import c64lib
~ prog8_lib {
prog8_lib {
%asminclude "library:prog8lib.asm", ""
}

View File

@ -1 +1 @@
1.21-dev
1.50-dev

View File

@ -2,20 +2,12 @@ package prog8
import prog8.compiler.compileProgram
import prog8.vm.astvm.AstVm
import prog8.vm.stackvm.stackVmMain
import java.nio.file.Paths
import kotlin.system.exitProcess
fun main(args: Array<String>) {
// check if the user wants to launch the VM instead
if("-vm" in args) {
val newArgs = args.toMutableList()
newArgs.remove("-vm")
return stackVmMain(newArgs.toTypedArray())
}
printSoftwareHeader("compiler")
if (args.isEmpty())
@ -33,19 +25,15 @@ internal fun printSoftwareHeader(what: String) {
private fun compileMain(args: Array<String>) {
var emulatorToStart = ""
var moduleFile = ""
var writeVmCode = false
var writeAssembly = true
var optimize = true
var optimizeInlining = true
var launchAstVm = false
var asm2 = false
for (arg in args) {
if(arg=="-emu")
emulatorToStart = "x64"
else if(arg=="-emu2")
emulatorToStart = "x64sc"
else if(arg=="-writevm")
writeVmCode = true
else if(arg=="-noasm")
writeAssembly = false
else if(arg=="-noopt")
@ -54,10 +42,6 @@ private fun compileMain(args: Array<String>) {
optimizeInlining = false
else if(arg=="-avm")
launchAstVm = true
else if(arg=="-asm2") {
writeVmCode = false
asm2 = true
}
else if(!arg.startsWith("-"))
moduleFile = arg
else
@ -68,8 +52,7 @@ private fun compileMain(args: Array<String>) {
val filepath = Paths.get(moduleFile).normalize()
val (programAst, programName) = compileProgram(filepath, optimize, optimizeInlining,
!launchAstVm && !asm2, writeVmCode, writeAssembly, asm2)
val (programAst, programName) = compileProgram(filepath, optimize, optimizeInlining, writeAssembly)
if(launchAstVm) {
println("\nLaunching AST-based vm...")
@ -95,10 +78,7 @@ private fun usage() {
System.err.println("Missing argument(s):")
System.err.println(" [-emu] auto-start the 'x64' C-64 emulator after successful compilation")
System.err.println(" [-emu2] auto-start the 'x64sc' C-64 emulator after successful compilation")
System.err.println(" [-writevm] write intermediate vm code to a file as well")
System.err.println(" [-noasm] don't create assembly code")
System.err.println(" [-asm2] use new Ast-Asmgen2 (WIP)")
System.err.println(" [-vm] launch the prog8 virtual machine instead of the compiler")
System.err.println(" [-avm] launch the prog8 ast-based virtual machine after compilation")
System.err.println(" [-noopt] don't perform any optimizations")
System.err.println(" [-nooptinline] don't perform subroutine inlining optimizations")

View File

@ -343,7 +343,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
}
override fun visit(repeatLoop: RepeatLoop) {
outputln("repeat ")
output("repeat ")
repeatLoop.body.accept(this)
output(" until ")
repeatLoop.untilCondition.accept(this)
@ -427,13 +427,14 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
}
override fun visit(whenChoice: WhenChoice) {
if(whenChoice.values==null)
val choiceValues = whenChoice.values
if(choiceValues==null)
outputi("else -> ")
else {
outputi("")
for(value in whenChoice.values) {
for(value in choiceValues) {
value.accept(this)
if(value !== whenChoice.values.last())
if(value !== choiceValues.last())
output(",")
}
output(" -> ")

View File

@ -440,7 +440,7 @@ private fun prog8Parser.ExpressionContext.toAst() : Expression {
litval.arrayliteral()!=null -> {
val array = litval.arrayliteral()?.toAst()
// the actual type of the arraysize can not yet be determined here (missing namespace & heap)
// the ConstantFolder takes care of that and converts the type if needed.
// the ConstantFold takes care of that and converts the type if needed.
ReferenceLiteralValue(DataType.ARRAY_UB, array = array, position = litval.toPosition())
}
litval.structliteral()!=null -> {

View File

@ -1,6 +1,7 @@
package prog8.ast.base
import prog8.ast.Node
import prog8.compiler.target.c64.MachineDefinition
/**************************** AST Data classes ****************************/
@ -25,10 +26,10 @@ enum class DataType {
infix fun isAssignableTo(targetType: DataType) =
// what types are assignable to others without loss of precision?
when(this) {
UBYTE -> targetType in setOf(UBYTE, UWORD, WORD, FLOAT)
BYTE -> targetType in setOf(BYTE, UBYTE, UWORD, WORD, FLOAT)
UBYTE -> targetType in setOf(UBYTE, WORD, UWORD, FLOAT)
BYTE -> targetType in setOf(BYTE, WORD, FLOAT)
UWORD -> targetType in setOf(UWORD, FLOAT)
WORD -> targetType in setOf(WORD, UWORD, FLOAT)
WORD -> targetType in setOf(WORD, FLOAT)
FLOAT -> targetType == FLOAT
STR -> targetType == STR || targetType==STR_S
STR_S -> targetType == STR || targetType==STR_S
@ -52,6 +53,16 @@ enum class DataType {
in WordDatatypes -> other in WordDatatypes
else -> false
}
fun memorySize(): Int {
return when(this) {
in ByteDatatypes -> 1
in WordDatatypes -> 2
FLOAT -> MachineDefinition.Mflpt5.MemorySize
in PassByReferenceDatatypes -> 2
else -> -9999999
}
}
}
enum class Register {
@ -111,6 +122,8 @@ val IterableDatatypes = setOf(
val PassByValueDatatypes = NumericDatatypes
val PassByReferenceDatatypes = IterableDatatypes.plus(DataType.STRUCT)
val ArrayElementTypes = mapOf(
DataType.STR to DataType.UBYTE,
DataType.STR_S to DataType.UBYTE,
DataType.ARRAY_B to DataType.BYTE,
DataType.ARRAY_UB to DataType.UBYTE,
DataType.ARRAY_W to DataType.WORD,

View File

@ -4,6 +4,7 @@ import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.processing.*
import prog8.compiler.CompilationOptions
import prog8.compiler.target.c64.codegen2.AnonymousScopeVarsCleanup
import prog8.optimizer.FlattenAnonymousScopesAndRemoveNops
@ -28,6 +29,13 @@ internal fun Program.checkValid(compilerOptions: CompilationOptions) {
}
internal fun Program.anonscopeVarsCleanup() {
val mover = AnonymousScopeVarsCleanup(this)
mover.visit(this)
printErrors(mover.result(), name)
}
internal fun Program.reorderStatements() {
val initvalueCreator = VarInitValueAndAddressOfCreator(namespace, heap)
initvalueCreator.visit(this)

View File

@ -97,14 +97,13 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
val leftDt = left.inferType(program)
val rightDt = right.inferType(program)
return when (operator) {
"+", "-", "*", "**", "%" -> if (leftDt == null || rightDt == null) null else {
"+", "-", "*", "**", "%", "/" -> if (leftDt == null || rightDt == null) null else {
try {
arithmeticOpDt(leftDt, rightDt)
commonDatatype(leftDt, rightDt, null, null).first
} catch (x: FatalAstException) {
null
}
}
"/" -> if (leftDt == null || rightDt == null) null else divisionOpDt(leftDt, rightDt)
"&" -> leftDt
"|" -> leftDt
"^" -> leftDt
@ -118,137 +117,66 @@ class BinaryExpression(var left: Expression, var operator: String, var right: Ex
}
companion object {
fun divisionOpDt(leftDt: DataType, rightDt: DataType): DataType {
fun commonDatatype(leftDt: DataType, rightDt: DataType,
left: Expression?, right: Expression?): Pair<DataType, Expression?> {
// byte + byte -> byte
// byte + word -> word
// word + byte -> word
// word + word -> word
// a combination with a float will be float (but give a warning about this!)
return when (leftDt) {
DataType.UBYTE -> when (rightDt) {
DataType.UBYTE, DataType.UWORD -> DataType.UBYTE
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.BYTE
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
DataType.UBYTE -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.UBYTE, null)
DataType.BYTE -> Pair(DataType.BYTE, left)
DataType.UWORD -> Pair(DataType.UWORD, left)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.BYTE -> when (rightDt) {
in NumericDatatypes -> DataType.BYTE
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
DataType.BYTE -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.BYTE, right)
DataType.BYTE -> Pair(DataType.BYTE, null)
DataType.UWORD -> Pair(DataType.WORD, left)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.UWORD -> when (rightDt) {
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
DataType.UWORD -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.UWORD, right)
DataType.BYTE -> Pair(DataType.WORD, right)
DataType.UWORD -> Pair(DataType.UWORD, null)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.WORD -> when (rightDt) {
in NumericDatatypes -> DataType.WORD
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
DataType.WORD -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.WORD, right)
DataType.BYTE -> Pair(DataType.WORD, right)
DataType.UWORD -> Pair(DataType.WORD, right)
DataType.WORD -> Pair(DataType.WORD, null)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.FLOAT -> when (rightDt) {
in NumericDatatypes -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
DataType.FLOAT -> {
Pair(DataType.FLOAT, right)
}
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
else -> Pair(leftDt, null) // non-numeric datatype
}
}
fun arithmeticOpDt(leftDt: DataType, rightDt: DataType): DataType {
return when (leftDt) {
DataType.UBYTE -> when (rightDt) {
DataType.UBYTE -> DataType.UBYTE
DataType.BYTE -> DataType.BYTE
DataType.UWORD -> DataType.UWORD
DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
DataType.BYTE -> when (rightDt) {
in ByteDatatypes -> DataType.BYTE
in WordDatatypes -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
DataType.UWORD -> when (rightDt) {
DataType.UBYTE, DataType.UWORD -> DataType.UWORD
DataType.BYTE, DataType.WORD -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
DataType.WORD -> when (rightDt) {
in IntegerDatatypes -> DataType.WORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
DataType.FLOAT -> when (rightDt) {
in NumericDatatypes -> DataType.FLOAT
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
else -> throw FatalAstException("arithmetic operation on incompatible datatypes: $leftDt and $rightDt")
}
}
}
fun commonDatatype(leftDt: DataType, rightDt: DataType,
left: Expression, right: Expression): Pair<DataType, Expression?> {
// byte + byte -> byte
// byte + word -> word
// word + byte -> word
// word + word -> word
// a combination with a float will be float (but give a warning about this!)
if(this.operator=="/") {
// division is a bit weird, don't cast the operands
val commondt = divisionOpDt(leftDt, rightDt)
return Pair(commondt, null)
}
return when (leftDt) {
DataType.UBYTE -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.UBYTE, null)
DataType.BYTE -> Pair(DataType.BYTE, left)
DataType.UWORD -> Pair(DataType.UWORD, left)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.BYTE -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.BYTE, right)
DataType.BYTE -> Pair(DataType.BYTE, null)
DataType.UWORD -> Pair(DataType.WORD, left)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.UWORD -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.UWORD, right)
DataType.BYTE -> Pair(DataType.UWORD, right)
DataType.UWORD -> Pair(DataType.UWORD, null)
DataType.WORD -> Pair(DataType.WORD, left)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.WORD -> {
when (rightDt) {
DataType.UBYTE -> Pair(DataType.WORD, right)
DataType.BYTE -> Pair(DataType.WORD, right)
DataType.UWORD -> Pair(DataType.WORD, right)
DataType.WORD -> Pair(DataType.WORD, null)
DataType.FLOAT -> Pair(DataType.FLOAT, left)
else -> Pair(leftDt, null) // non-numeric datatype
}
}
DataType.FLOAT -> {
Pair(DataType.FLOAT, right)
}
else -> Pair(leftDt, null) // non-numeric datatype
}
}
}
class ArrayIndexedExpression(val identifier: IdentifierReference,
var arrayspec: ArrayIndex,
class ArrayIndexedExpression(var identifier: IdentifierReference,
val arrayspec: ArrayIndex,
override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@ -304,7 +232,7 @@ class TypecastExpression(var expression: Expression, var type: DataType, val imp
}
}
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : Expression() {
data class AddressOf(var identifier: IdentifierReference, override val position: Position) : Expression() {
override lateinit var parent: Node
override fun linkParents(parent: Node) {
@ -367,6 +295,7 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
in 0..255 -> NumericLiteralValue(DataType.UBYTE, value, position)
in -128..127 -> NumericLiteralValue(DataType.BYTE, value, position)
in 0..65535 -> NumericLiteralValue(DataType.UWORD, value, position)
in -32768..32767 -> NumericLiteralValue(DataType.WORD, value, position)
else -> throw FatalAstException("integer overflow: $value")
}
}
@ -495,12 +424,12 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
init {
when(type){
in StringDatatypes ->
if(str==null && heapId==null) throw FatalAstException("literal value missing strvalue/heapId")
if(str==null) throw FatalAstException("literal value missing strvalue/heapId")
in ArrayDatatypes ->
if(array==null && heapId==null) throw FatalAstException("literal value missing arrayvalue/heapId")
if(array==null) throw FatalAstException("literal value missing arrayvalue/heapId")
else -> throw FatalAstException("invalid type $type")
}
if(array==null && str==null && heapId==null)
if(array==null && str==null)
throw FatalAstException("literal ref value without actual value")
}
@ -524,7 +453,7 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
in ArrayDatatypes -> "$array"
else -> throw FatalAstException("weird ref type")
}
return "ReferenceValueLiteral($type, $valueStr)"
return "RefValueLit($type, $valueStr)"
}
override fun inferType(program: Program) = type
@ -559,7 +488,24 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
when(type) {
in StringDatatypes -> {
if(targettype in StringDatatypes)
return ReferenceLiteralValue(targettype, str, initHeapId = heapId, position = position)
return ReferenceLiteralValue(targettype, str, position = position)
}
in ArrayDatatypes -> {
if(targettype in ArrayDatatypes) {
val elementType = ArrayElementTypes.getValue(targettype)
val castArray = array!!.map{
val num = it as? NumericLiteralValue
if(num==null) {
// an array of UWORDs could possibly also contain AddressOfs
if (elementType != DataType.UWORD || it !is AddressOf)
throw FatalAstException("weird array element $it")
it
} else {
num.cast(elementType)!!
}
}.toTypedArray()
return ReferenceLiteralValue(targettype, null, array=castArray, position = position)
}
}
else -> {}
}
@ -582,13 +528,15 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
}
heapId = heap.addIntegerArray(type, intArrayWithAddressOfs.toTypedArray())
} else {
val valuesInArray = array.map { (it as NumericLiteralValue).number }
heapId = if(type== DataType.ARRAY_F) {
val doubleArray = valuesInArray.map { it.toDouble() }.toDoubleArray()
heap.addDoublesArray(doubleArray)
} else {
val integerArray = valuesInArray.map { it.toInt() }
heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
val valuesInArray = array.map { (it as? NumericLiteralValue)?.number }
if(null !in valuesInArray) {
heapId = if (type == DataType.ARRAY_F) {
val doubleArray = valuesInArray.map { it!!.toDouble() }.toDoubleArray()
heap.addDoublesArray(doubleArray)
} else {
val integerArray = valuesInArray.map { it!!.toInt() }
heap.addIntegerArray(type, integerArray.map { IntegerOrAddressOf(it, null) }.toTypedArray())
}
}
}
}
@ -617,13 +565,13 @@ class RangeExpr(var from: Expression,
val toDt=to.inferType(program)
return when {
fromDt==null || toDt==null -> null
fromDt== DataType.UBYTE && toDt== DataType.UBYTE -> DataType.UBYTE
fromDt== DataType.UWORD && toDt== DataType.UWORD -> DataType.UWORD
fromDt== DataType.UBYTE && toDt== DataType.UBYTE -> DataType.ARRAY_UB
fromDt== DataType.UWORD && toDt== DataType.UWORD -> DataType.ARRAY_UW
fromDt== DataType.STR && toDt== DataType.STR -> DataType.STR
fromDt== DataType.STR_S && toDt== DataType.STR_S -> DataType.STR_S
fromDt== DataType.WORD || toDt== DataType.WORD -> DataType.WORD
fromDt== DataType.BYTE || toDt== DataType.BYTE -> DataType.BYTE
else -> DataType.UBYTE
fromDt== DataType.WORD || toDt== DataType.WORD -> DataType.ARRAY_W
fromDt== DataType.BYTE || toDt== DataType.BYTE -> DataType.ARRAY_B
else -> DataType.ARRAY_UB
}
}
override fun toString(): String {
@ -746,6 +694,13 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
else -> throw FatalAstException("requires a reference value")
}
}
fun withPrefixedName(nameprefix: String): IdentifierReference {
val prefixed = nameInSource.dropLast(1) + listOf(nameprefix+nameInSource.last())
val new = IdentifierReference(prefixed, position)
new.parent = parent
return new
}
}
class FunctionCall(override var target: IdentifierReference,

View File

@ -104,6 +104,12 @@ internal class AstChecker(private val program: Program,
super.visit(returnStmt)
}
override fun visit(ifStatement: IfStatement) {
if(ifStatement.condition.inferType(program) !in IntegerDatatypes)
checkResult.add(ExpressionError("condition value should be an integer type", ifStatement.condition.position))
super.visit(ifStatement)
}
override fun visit(forLoop: ForLoop) {
if(forLoop.body.containsNoCodeNorVars())
printWarning("for loop body is empty", forLoop.position)
@ -113,10 +119,11 @@ internal class AstChecker(private val program: Program,
checkResult.add(ExpressionError("can only loop over an iterable type", forLoop.position))
} else {
if (forLoop.loopRegister != null) {
printWarning("using a register as loop variable is risky (it could get clobbered)", forLoop.position)
// loop register
if (iterableDt != DataType.UBYTE && iterableDt!= DataType.ARRAY_UB && iterableDt !in StringDatatypes)
if (iterableDt != DataType.ARRAY_UB && iterableDt != DataType.ARRAY_B && iterableDt !in StringDatatypes)
checkResult.add(ExpressionError("register can only loop over bytes", forLoop.position))
if(forLoop.loopRegister!=Register.A)
checkResult.add(ExpressionError("it's only possible to use A as a loop register", forLoop.position))
} else {
// loop variable
val loopvar = forLoop.loopVar!!.targetVarDecl(program.namespace)
@ -143,14 +150,14 @@ internal class AstChecker(private val program: Program,
checkResult.add(ExpressionError("word loop variable can only loop over bytes or words", forLoop.position))
}
DataType.FLOAT -> {
if(iterableDt!= DataType.FLOAT && iterableDt != DataType.ARRAY_F)
checkResult.add(ExpressionError("float loop variable can only loop over floats", forLoop.position))
checkResult.add(ExpressionError("for loop only supports integers", forLoop.position))
}
else -> checkResult.add(ExpressionError("loop variable must be numeric type", forLoop.position))
}
}
}
}
super.visit(forLoop)
}
@ -297,6 +304,18 @@ internal class AstChecker(private val program: Program,
if(subroutine.asmClobbers.intersect(regCounts.keys).isNotEmpty())
err("a return register is also in the clobber list")
if(subroutine.statements.any{it !is InlineAssembly})
err("asmsub can only contain inline assembly (%asm)")
val statusFlagsNoCarry = subroutine.asmParameterRegisters.mapNotNull { it.statusflag }.toSet() - Statusflag.Pc
if(statusFlagsNoCarry.isNotEmpty())
err("can only use Carry as status flag parameter")
val carryParameter = subroutine.asmParameterRegisters.singleOrNull { it.statusflag==Statusflag.Pc }
if(carryParameter!=null && carryParameter !== subroutine.asmParameterRegisters.last())
err("carry parameter has to come last")
} else {
// TODO: non-asm subroutines can only take numeric arguments for now. (not strings and arrays) Maybe this can be improved now that we have '&' ?
// the way string params are treated is almost okay (their address is passed) but the receiving subroutine treats it as an integer rather than referring back to the original string.
@ -313,12 +332,16 @@ internal class AstChecker(private val program: Program,
override fun visit(repeatLoop: RepeatLoop) {
if(repeatLoop.untilCondition.referencesIdentifiers("A", "X", "Y"))
printWarning("using a register in the loop condition is risky (it could get clobbered)", repeatLoop.untilCondition.position)
if(repeatLoop.untilCondition.inferType(program) !in IntegerDatatypes)
checkResult.add(ExpressionError("condition value should be an integer type", repeatLoop.untilCondition.position))
super.visit(repeatLoop)
}
override fun visit(whileLoop: WhileLoop) {
if(whileLoop.condition.referencesIdentifiers("A", "X", "Y"))
printWarning("using a register in the loop condition is risky (it could get clobbered)", whileLoop.condition.position)
if(whileLoop.condition.inferType(program) !in IntegerDatatypes)
checkResult.add(ExpressionError("condition value should be an integer type", whileLoop.condition.position))
super.visit(whileLoop)
}
@ -353,6 +376,8 @@ internal class AstChecker(private val program: Program,
}
override fun visit(assignTarget: AssignTarget) {
super.visit(assignTarget)
val memAddr = assignTarget.memoryAddress?.addressExpression?.constValue(program)?.number?.toInt()
if (memAddr != null) {
if (memAddr < 0 || memAddr >= 65536)
@ -360,8 +385,9 @@ internal class AstChecker(private val program: Program,
}
val assignment = assignTarget.parent as Statement
if (assignTarget.identifier != null) {
val targetName = assignTarget.identifier.nameInSource
val targetIdentifier = assignTarget.identifier
if (targetIdentifier != null) {
val targetName = targetIdentifier.nameInSource
val targetSymbol = program.namespace.lookup(targetName, assignment)
when (targetSymbol) {
null -> {
@ -596,8 +622,9 @@ internal class AstChecker(private val program: Program,
directive.args[0].name != "basicsafe" &&
directive.args[0].name != "floatsafe" &&
directive.args[0].name != "kernalsafe" &&
directive.args[0].name != "dontuse" &&
directive.args[0].name != "full")
err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, or full")
err("invalid zp type, expected basicsafe, floatsafe, kernalsafe, dontuse, or full")
}
"%zpreserved" -> {
if(directive.parent !is Module) err("this directive may only occur at module level")
@ -726,12 +753,20 @@ internal class AstChecker(private val program: Program,
if(leftDt !in IntegerDatatypes || rightDt !in IntegerDatatypes)
checkResult.add(ExpressionError("bitwise operator can only be used on integer operands", expr.right.position))
}
"<<", ">>" -> {
// for now, bit-shifts can only shift by a constant number
val constRight = expr.right.constValue(program)
if(constRight==null)
checkResult.add(ExpressionError("bit-shift can only be done by a constant number (for now)", expr.right.position))
}
}
if(leftDt !in NumericDatatypes)
checkResult.add(ExpressionError("left operand is not numeric", expr.left.position))
if(rightDt!in NumericDatatypes)
checkResult.add(ExpressionError("right operand is not numeric", expr.right.position))
if(leftDt!=rightDt)
checkResult.add(ExpressionError("left and right operands aren't the same type", expr.left.position))
super.visit(expr)
}
@ -748,8 +783,11 @@ internal class AstChecker(private val program: Program,
super.visit(range)
val from = range.from.constValue(program)
val to = range.to.constValue(program)
val stepLv = range.step.constValue(program) ?: NumericLiteralValue(DataType.UBYTE, 1, range.position)
if (stepLv.type !in IntegerDatatypes || stepLv.number.toInt() == 0) {
val stepLv = range.step.constValue(program)
if(stepLv==null) {
err("range step must be a constant integer")
return
} else if (stepLv.type !in IntegerDatatypes || stepLv.number.toInt() == 0) {
err("range step must be an integer != 0")
return
}
@ -792,6 +830,13 @@ internal class AstChecker(private val program: Program,
else
printWarning("result values of subroutine call are discarded", functionCallStatement.position)
}
if(functionCallStatement.target.nameInSource.last() in setOf("lsl", "lsr", "rol", "ror", "rol2", "ror2", "swap")) {
// in-place modification, can't be done on literals
if(functionCallStatement.arglist.any { it !is IdentifierReference && it !is RegisterExpr && it !is ArrayIndexedExpression && it !is DirectMemoryRead }) {
checkResult.add(ExpressionError("can't use that as argument to a in-place modifying function", functionCallStatement.position))
}
}
super.visit(functionCallStatement)
}
@ -902,12 +947,14 @@ internal class AstChecker(private val program: Program,
if(index!=null && (index<0 || index>=arraysize))
checkResult.add(ExpressionError("array index out of bounds", arrayIndexedExpression.arrayspec.position))
} else if(target.datatype in StringDatatypes) {
// check string lengths
val heapId = (target.value as ReferenceLiteralValue).heapId!!
val stringLen = program.heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if(index!=null && (index<0 || index>=stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
if(target.value is ReferenceLiteralValue) {
// check string lengths for non-memory mapped strings
val heapId = (target.value as ReferenceLiteralValue).heapId!!
val stringLen = program.heap.get(heapId).str!!.length
val index = (arrayIndexedExpression.arrayspec.index as? NumericLiteralValue)?.number?.toInt()
if (index != null && (index < 0 || index >= stringLen))
checkResult.add(ExpressionError("index out of bounds", arrayIndexedExpression.arrayspec.position))
}
}
} else
checkResult.add(SyntaxError("indexing requires a variable to act upon", arrayIndexedExpression.position))
@ -930,6 +977,9 @@ internal class AstChecker(private val program: Program,
tally.filter { it.value>1 }.forEach {
checkResult.add(SyntaxError("choice value occurs multiple times", it.key.position))
}
if(whenStatement.choices.isEmpty())
checkResult.add(SyntaxError("empty when statement", whenStatement.position))
super.visit(whenStatement)
}
@ -937,7 +987,7 @@ internal class AstChecker(private val program: Program,
val whenStmt = whenChoice.parent as WhenStatement
if(whenChoice.values!=null) {
val conditionType = whenStmt.condition.inferType(program)
val constvalues = whenChoice.values.map { it.constValue(program) }
val constvalues = whenChoice.values!!.map { it.constValue(program) }
for(constvalue in constvalues) {
when {
constvalue == null -> checkResult.add(SyntaxError("choice value must be a constant", whenChoice.position))

View File

@ -7,10 +7,13 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.compiler.HeapValues
import prog8.compiler.target.c64.AssemblyProgram
import prog8.functions.BuiltinFunctions
internal class AstIdentifiersChecker(private val program: Program) : IAstModifyingVisitor {
private val checkResult: MutableList<AstException> = mutableListOf()
private var blocks = mutableMapOf<String, Block>()
@ -64,6 +67,9 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
// the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", decl.position))
if(decl.name in AssemblyProgram.opcodeNames)
checkResult.add(NameError("can't use a cpu opcode name as a symbol", decl.position))
// is it a struct variable? then define all its struct members as mangled names,
// and include the original decl as well.
if(decl.datatype==DataType.STRUCT) {
@ -98,17 +104,28 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
override fun visit(subroutine: Subroutine): Statement {
if(subroutine.name in BuiltinFunctions) {
if(subroutine.name in AssemblyProgram.opcodeNames) {
checkResult.add(NameError("can't use a cpu opcode name as a symbol", subroutine.position))
} else if(subroutine.name in BuiltinFunctions) {
// the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
} else {
if (subroutine.parameters.any { it.name in BuiltinFunctions })
checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
// already reported elsewhere:
// if (subroutine.parameters.any { it.name in BuiltinFunctions })
// checkResult.add(NameError("builtin function name cannot be used as parameter", subroutine.position))
val existing = program.namespace.lookup(listOf(subroutine.name), subroutine)
if (existing != null && existing !== subroutine)
nameError(subroutine.name, subroutine.position, existing)
// does the parameter redefine a variable declared elsewhere?
for(param in subroutine.parameters) {
val existingVar = subroutine.lookup(listOf(param.name), subroutine)
if (existingVar != null && existingVar.parent !== subroutine) {
nameError(param.name, param.position, existingVar)
}
}
// check that there are no local variables, labels, or other subs that redefine the subroutine's parameters
val symbolsInSub = subroutine.allDefinedSymbols()
val namesInSub = symbolsInSub.map{ it.first }.toSet()
@ -133,7 +150,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
subroutine.parameters
.filter { it.name !in namesInSub }
.forEach {
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.DONTCARE, null, it.name, null, null,
val vardecl = VarDecl(VarDeclType.VAR, it.type, ZeropageWish.NOT_IN_ZEROPAGE, null, it.name, null, null,
isArray = false, autogeneratedDontRemove = true, position = subroutine.position)
vardecl.linkParents(subroutine)
subroutine.statements.add(0, vardecl)
@ -145,6 +162,9 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
override fun visit(label: Label): Statement {
if(label.name in AssemblyProgram.opcodeNames)
checkResult.add(NameError("can't use a cpu opcode name as a symbol", label.position))
if(label.name in BuiltinFunctions) {
// the builtin functions can't be redefined
checkResult.add(NameError("builtin function cannot be redefined", label.position))
@ -166,30 +186,36 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
checkResult.add(SyntaxError("register loop variables have a fixed implicit datatype", forLoop.position))
if(forLoop.loopRegister == Register.X)
printWarning("writing to the X register is dangerous, because it's used as an internal pointer", forLoop.position)
} else if(forLoop.loopVar!=null) {
val varName = forLoop.loopVar.nameInSource.last()
if(forLoop.decltype!=null) {
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(forLoop.loopVar.nameInSource, forLoop.body.statements.first())
if(existing==null) {
// create the local scoped for loop variable itself
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, null,
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
} else {
val loopVar = forLoop.loopVar
if (loopVar != null) {
val varName = loopVar.nameInSource.last()
if (forLoop.decltype != null) {
val existing = if (forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(loopVar.nameInSource, forLoop.body.statements.first())
if (existing == null) {
// create the local scoped for loop variable itself
val vardecl = VarDecl(VarDeclType.VAR, forLoop.decltype, forLoop.zeropage, null, varName, null, null,
isArray = false, autogeneratedDontRemove = true, position = loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
loopVar.parent = forLoop.body // loopvar 'is defined in the body'
} else if(existing.parent!==forLoop && existing.parent.parent!==forLoop) {
checkResult.add(NameError("for loop var was already defined at ${existing.position}", loopVar.position))
}
}
}
if(forLoop.iterable !is RangeExpr) {
val existing = if(forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(ForLoop.iteratorLoopcounterVarname), forLoop.body.statements.first())
if(existing==null) {
// create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, ForLoop.iteratorLoopcounterVarname, null, null,
isArray = false, autogeneratedDontRemove = true, position = forLoop.loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
forLoop.loopVar.parent = forLoop.body // loopvar 'is defined in the body'
val validName = forLoop.body.name.replace("<", "").replace(">", "").replace("-", "")
val loopvarName = "prog8_loopvar_$validName"
if (forLoop.iterable !is RangeExpr) {
val existing = if (forLoop.body.containsNoCodeNorVars()) null else forLoop.body.lookup(listOf(loopvarName), forLoop.body.statements.first())
if (existing == null) {
// create loop iteration counter variable (without value, to avoid an assignment)
val vardecl = VarDecl(VarDeclType.VAR, DataType.UBYTE, ZeropageWish.PREFER_ZEROPAGE, null, loopvarName, null, null,
isArray = false, autogeneratedDontRemove = true, position = loopVar.position)
vardecl.linkParents(forLoop.body)
forLoop.body.statements.add(0, vardecl)
loopVar.parent = forLoop.body // loopvar 'is defined in the body'
}
}
}
}
@ -223,18 +249,62 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
}
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
if(refLiteral.parent !is VarDecl) {
return makeIdentifierFromRefLv(refLiteral)
val litval = super.visit(refLiteral)
if(litval is ReferenceLiteralValue) {
val vardecl = litval.parent as? VarDecl
if (litval.isString) {
// intern the string; move it into the heap
if (litval.str!!.length !in 1..255)
checkResult.add(ExpressionError("string literal length must be between 1 and 255", litval.position))
else {
litval.addToHeap(program.heap)
}
return if(vardecl!=null)
litval
else
makeIdentifierFromRefLv(litval) // replace the literal string by a identifier reference.
} else if (litval.isArray) {
if (vardecl!=null) {
return fixupArrayDatatype(litval, vardecl, program.heap)
} else {
// fix the datatype of the array (also on the heap) to the 'biggest' datatype in the array
// (we don't know the desired datatype here exactly so we guess)
val datatype = determineArrayDt(litval.array!!) ?: return litval
val litval2 = litval.cast(datatype)!!
litval2.parent = litval.parent
// finally, replace the literal array by a identifier reference.
return makeIdentifierFromRefLv(litval2)
}
}
}
return super.visit(refLiteral)
return litval
}
private fun determineArrayDt(array: Array<Expression>): DataType? {
val datatypesInArray = array.mapNotNull { it.inferType(program) }
if(datatypesInArray.isEmpty())
return null
if(DataType.FLOAT in datatypesInArray)
return DataType.ARRAY_F
if(DataType.WORD in datatypesInArray)
return DataType.ARRAY_W
if(DataType.UWORD in datatypesInArray)
return DataType.ARRAY_UW
if(DataType.BYTE in datatypesInArray)
return DataType.ARRAY_B
if(DataType.UBYTE in datatypesInArray)
return DataType.ARRAY_UB
return null
}
private fun makeIdentifierFromRefLv(refLiteral: ReferenceLiteralValue): IdentifierReference {
// a referencetype literal value that's not declared as a variable
// we need to introduce an auto-generated variable for this to be able to refer to the value
refLiteral.addToHeap(program.heap)
val variable = VarDecl.createAuto(refLiteral, program.heap)
addVarDecl(refLiteral.definingScope(), variable)
val scope = refLiteral.definingScope()
var variable = VarDecl.createAuto(refLiteral, program.heap)
variable = addVarDecl(scope, variable)
// replace the reference literal by a identifier reference
val identifier = IdentifierReference(listOf(variable.name), variable.position)
identifier.parent = refLiteral.parent
@ -290,12 +360,34 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
return expr
}
private fun addVarDecl(scope: INameScope, variable: VarDecl) {
private fun addVarDecl(scope: INameScope, variable: VarDecl): VarDecl {
if(scope !in vardeclsToAdd)
vardeclsToAdd[scope] = mutableListOf()
val declList = vardeclsToAdd.getValue(scope)
if(declList.all{it.name!=variable.name})
val existing = declList.singleOrNull { it.name==variable.name }
return if(existing!=null) {
existing
} else {
declList.add(variable)
variable
}
}
}
internal fun fixupArrayDatatype(array: ReferenceLiteralValue, vardecl: VarDecl, heap: HeapValues): ReferenceLiteralValue {
if(array.heapId!=null) {
val arrayDt = array.type
if(arrayDt!=vardecl.datatype) {
// fix the datatype of the array (also on the heap) to match the vardecl
val litval2 = array.cast(vardecl.datatype)!!
vardecl.value = litval2
litval2.linkParents(vardecl)
litval2.addToHeap(heap) // TODO is the previous array discarded from the resulting asm code?
return litval2
}
} else {
array.addToHeap(heap)
}
return array
}

View File

@ -2,6 +2,7 @@ package prog8.ast.processing
import prog8.ast.Module
import prog8.ast.Program
import prog8.ast.base.FatalAstException
import prog8.ast.expressions.*
import prog8.ast.statements.*
@ -11,7 +12,7 @@ interface IAstModifyingVisitor {
}
fun visit(module: Module) {
module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList()
module.statements = module.statements.map { it.accept(this) }.toMutableList()
}
fun visit(expr: PrefixExpression): Expression {
@ -30,7 +31,7 @@ interface IAstModifyingVisitor {
}
fun visit(block: Block): Statement {
block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList()
block.statements = block.statements.map { it.accept(this) }.toMutableList()
return block
}
@ -41,7 +42,7 @@ interface IAstModifyingVisitor {
}
fun visit(subroutine: Subroutine): Statement {
subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList()
subroutine.statements = subroutine.statements.map { it.accept(this) }.toMutableList()
return subroutine
}
@ -49,6 +50,8 @@ interface IAstModifyingVisitor {
val newtarget = functionCall.target.accept(this)
if(newtarget is IdentifierReference)
functionCall.target = newtarget
else
throw FatalAstException("cannot change class of function call target")
functionCall.arglist = functionCall.arglist.map { it.accept(this) }.toMutableList()
return functionCall
}
@ -57,6 +60,8 @@ interface IAstModifyingVisitor {
val newtarget = functionCallStatement.target.accept(this)
if(newtarget is IdentifierReference)
functionCallStatement.target = newtarget
else
throw FatalAstException("cannot change class of function call target")
functionCallStatement.arglist = functionCallStatement.arglist.map { it.accept(this) }.toMutableList()
return functionCallStatement
}
@ -135,7 +140,12 @@ interface IAstModifyingVisitor {
}
fun visit(forLoop: ForLoop): Statement {
forLoop.loopVar?.accept(this)
val newloopvar = forLoop.loopVar?.accept(this)
when(newloopvar) {
is IdentifierReference -> forLoop.loopVar = newloopvar
null -> forLoop.loopVar = null
else -> throw FatalAstException("can't change class of loopvar")
}
forLoop.iterable = forLoop.iterable.accept(this)
forLoop.body = forLoop.body.accept(this) as AnonymousScope
return forLoop
@ -158,21 +168,28 @@ interface IAstModifyingVisitor {
return returnStmt
}
fun visit(arrayIndexedExpression: ArrayIndexedExpression): Expression {
arrayIndexedExpression.identifier.accept(this)
fun visit(arrayIndexedExpression: ArrayIndexedExpression): ArrayIndexedExpression {
val ident = arrayIndexedExpression.identifier.accept(this)
if(ident is IdentifierReference)
arrayIndexedExpression.identifier = ident
arrayIndexedExpression.arrayspec.accept(this)
return arrayIndexedExpression
}
fun visit(assignTarget: AssignTarget): AssignTarget {
assignTarget.arrayindexed?.accept(this)
assignTarget.identifier?.accept(this)
val ident = assignTarget.identifier?.accept(this)
when (ident) {
is IdentifierReference -> assignTarget.identifier = ident
null -> assignTarget.identifier = null
else -> throw FatalAstException("can't change class of assign target identifier")
}
assignTarget.arrayindexed = assignTarget.arrayindexed?.accept(this)
assignTarget.memoryAddress?.let { visit(it) }
return assignTarget
}
fun visit(scope: AnonymousScope): Statement {
scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList()
scope.statements = scope.statements.map { it.accept(this) }.toMutableList()
return scope
}
@ -191,7 +208,11 @@ interface IAstModifyingVisitor {
}
fun visit(addressOf: AddressOf): Expression {
addressOf.identifier.accept(this)
val ident = addressOf.identifier.accept(this)
if(ident is IdentifierReference)
addressOf.identifier = ident
else
throw FatalAstException("can't change class of addressof identifier")
return addressOf
}
@ -212,14 +233,20 @@ interface IAstModifyingVisitor {
}
fun visit(whenStatement: WhenStatement): Statement {
whenStatement.condition.accept(this)
whenStatement.condition = whenStatement.condition.accept(this)
whenStatement.choices.forEach { it.accept(this) }
return whenStatement
}
fun visit(whenChoice: WhenChoice) {
whenChoice.values?.forEach { it.accept(this) }
whenChoice.statements.accept(this)
whenChoice.values = whenChoice.values?.map { it.accept(this) }
val stmt = whenChoice.statements.accept(this)
if(stmt is AnonymousScope)
whenChoice.statements = stmt
else {
whenChoice.statements = AnonymousScope(mutableListOf(stmt), stmt.position)
whenChoice.statements.linkParents(whenChoice)
}
}
fun visit(structDecl: StructDecl): Statement {

View File

@ -187,26 +187,29 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
}
override fun visit(expr: BinaryExpression): Expression {
val leftDt = expr.left.inferType(program)
val rightDt = expr.right.inferType(program)
val expr2 = super.visit(expr)
if(expr2 !is BinaryExpression)
return expr2
val leftDt = expr2.left.inferType(program)
val rightDt = expr2.right.inferType(program)
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
// determine common datatype and add typecast as required to make left and right equal types
val (commonDt, toFix) = expr.commonDatatype(leftDt, rightDt, expr.left, expr.right)
val (commonDt, toFix) = BinaryExpression.commonDatatype(leftDt, rightDt, expr2.left, expr2.right)
if(toFix!=null) {
when {
toFix===expr.left -> {
expr.left = TypecastExpression(expr.left, commonDt, true, expr.left.position)
expr.left.linkParents(expr)
toFix===expr2.left -> {
expr2.left = TypecastExpression(expr2.left, commonDt, true, expr2.left.position)
expr2.left.linkParents(expr2)
}
toFix===expr.right -> {
expr.right = TypecastExpression(expr.right, commonDt, true, expr.right.position)
expr.right.linkParents(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")
}
}
}
return super.visit(expr)
return expr2
}
override fun visit(assignment: Assignment): Statement {
@ -329,26 +332,6 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
return super.visit(typecast)
}
override fun visit(whenStatement: WhenStatement): Statement {
// make sure all choices are just for one single value
val choices = whenStatement.choices.toList()
for(choice in choices) {
if(choice.values==null || choice.values.size==1)
continue
for(v in choice.values) {
val newchoice=WhenChoice(listOf(v), choice.statements, choice.position)
newchoice.parent = choice.parent
whenStatement.choices.add(newchoice)
}
whenStatement.choices.remove(choice)
}
// sort the choices in low-to-high value order (nulls last)
whenStatement.choices
.sortWith(compareBy<WhenChoice, Int?>(nullsLast(), {it.values?.single()?.constValue(program)?.number?.toInt()}))
return super.visit(whenStatement)
}
override fun visit(memread: DirectMemoryRead): Expression {
// make sure the memory address is an uword
val dt = memread.addressExpression.inferType(program)

View File

@ -49,7 +49,7 @@ class BuiltinFunctionStatementPlaceholder(val name: String, override val positio
}
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean?)
data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusflag: Statusflag?, val stack: Boolean)
class Block(override val name: String,
@ -198,7 +198,6 @@ class VarDecl(val type: VarDeclType,
throw FatalAstException("can only create autovar for a ref lv that has a heapid $refLv")
val autoVarName = "$autoHeapValuePrefix${refLv.heapId}"
return if(refLv.isArray) {
val declaredType = ArrayElementTypes.getValue(refLv.type)
val arraysize = ArrayIndex.forArray(refLv, heap)
@ -281,6 +280,12 @@ class VarDecl(val type: VarDeclType,
structHasBeenFlattened = true
return result
}
fun withPrefixedName(nameprefix: String): Statement {
val new = VarDecl(type, declaredDatatype, zeropage, arraysize, nameprefix+name, structName, value, isArray, autogeneratedDontRemove, position)
new.parent = parent
return new
}
}
class ArrayIndex(var index: Expression, override val position: Position) : Node {
@ -301,6 +306,7 @@ class ArrayIndex(var index: Expression, override val position: Position) : Node
fun accept(visitor: IAstModifyingVisitor) {
index = index.accept(visitor)
}
fun accept(visitor: IAstVisitor) {
index.accept(visitor)
}
@ -337,9 +343,9 @@ class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, va
: Assignment(target, aug_op, value, position)
data class AssignTarget(val register: Register?,
val identifier: IdentifierReference?,
val arrayindexed: ArrayIndexedExpression?,
var memoryAddress: DirectMemoryWrite?,
var identifier: IdentifierReference?,
var arrayindexed: ArrayIndexedExpression?,
val memoryAddress: DirectMemoryWrite?,
override val position: Position) : Node {
override lateinit var parent: Node
@ -370,12 +376,12 @@ data class AssignTarget(val register: Register?,
return DataType.UBYTE
if(identifier!=null) {
val symbol = program.namespace.lookup(identifier.nameInSource, stmt) ?: return null
val symbol = program.namespace.lookup(identifier!!.nameInSource, stmt) ?: return null
if (symbol is VarDecl) return symbol.datatype
}
if(arrayindexed!=null) {
val dt = arrayindexed.inferType(program)
val dt = arrayindexed!!.inferType(program)
if(dt!=null)
return dt
}
@ -390,12 +396,12 @@ data class AssignTarget(val register: Register?,
return when {
this.memoryAddress!=null -> false
this.register!=null -> value is RegisterExpr && value.register==register
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier.nameInSource
this.identifier!=null -> value is IdentifierReference && value.nameInSource==identifier!!.nameInSource
this.arrayindexed!=null -> value is ArrayIndexedExpression &&
value.identifier.nameInSource==arrayindexed.identifier.nameInSource &&
value.identifier.nameInSource==arrayindexed!!.identifier.nameInSource &&
value.arrayspec.size()!=null &&
arrayindexed.arrayspec.size()!=null &&
value.arrayspec.size()==arrayindexed.arrayspec.size()
arrayindexed!!.arrayspec.size()!=null &&
value.arrayspec.size()==arrayindexed!!.arrayspec.size()
else -> false
}
}
@ -406,16 +412,16 @@ data class AssignTarget(val register: Register?,
if(this.register!=null && other.register!=null)
return this.register==other.register
if(this.identifier!=null && other.identifier!=null)
return this.identifier.nameInSource==other.identifier.nameInSource
return this.identifier!!.nameInSource==other.identifier!!.nameInSource
if(this.memoryAddress!=null && other.memoryAddress!=null) {
val addr1 = this.memoryAddress!!.addressExpression.constValue(program)
val addr2 = other.memoryAddress!!.addressExpression.constValue(program)
val addr1 = this.memoryAddress.addressExpression.constValue(program)
val addr2 = other.memoryAddress.addressExpression.constValue(program)
return addr1!=null && addr2!=null && addr1==addr2
}
if(this.arrayindexed!=null && other.arrayindexed!=null) {
if(this.arrayindexed.identifier.nameInSource == other.arrayindexed.identifier.nameInSource) {
val x1 = this.arrayindexed.arrayspec.index.constValue(program)
val x2 = other.arrayindexed.arrayspec.index.constValue(program)
if(this.arrayindexed!!.identifier.nameInSource == other.arrayindexed!!.identifier.nameInSource) {
val x1 = this.arrayindexed!!.arrayspec.index.constValue(program)
val x2 = other.arrayindexed!!.arrayspec.index.constValue(program)
return x1!=null && x2!=null && x1==x2
}
}
@ -428,12 +434,12 @@ data class AssignTarget(val register: Register?,
if(this.memoryAddress!=null)
return false
if(this.arrayindexed!=null) {
val targetStmt = this.arrayindexed.identifier.targetVarDecl(namespace)
val targetStmt = this.arrayindexed!!.identifier.targetVarDecl(namespace)
if(targetStmt!=null)
return targetStmt.type!= VarDeclType.MEMORY
}
if(this.identifier!=null) {
val targetStmt = this.identifier.targetVarDecl(namespace)
val targetStmt = this.identifier!!.targetVarDecl(namespace)
if(targetStmt!=null)
return targetStmt.type!= VarDeclType.MEMORY
}
@ -684,7 +690,7 @@ class BranchStatement(var condition: BranchCondition,
class ForLoop(val loopRegister: Register?,
val decltype: DataType?,
val zeropage: ZeropageWish,
val loopVar: IdentifierReference?,
var loopVar: IdentifierReference?,
var iterable: Expression,
var body: AnonymousScope,
override val position: Position) : Statement() {
@ -704,10 +710,6 @@ class ForLoop(val loopRegister: Register?,
override fun toString(): String {
return "ForLoop(loopVar: $loopVar, loopReg: $loopRegister, iterable: $iterable, pos=$position)"
}
companion object {
const val iteratorLoopcounterVarname = "prog8forloopcounter"
}
}
class WhileLoop(var condition: Expression,
@ -742,8 +744,8 @@ class RepeatLoop(var body: AnonymousScope,
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
}
class WhenStatement(val condition: Expression,
val choices: MutableList<WhenChoice>,
class WhenStatement(var condition: Expression,
var choices: MutableList<WhenChoice>,
override val position: Position): Statement() {
override lateinit var parent: Node
override val expensiveToInline: Boolean = true
@ -761,7 +763,7 @@ class WhenStatement(val condition: Expression,
if(choice.values==null)
result.add(null to choice)
else {
val values = choice.values.map { it.constValue(program)?.number?.toInt() }
val values = choice.values!!.map { it.constValue(program)?.number?.toInt() }
if(values.contains(null))
result.add(null to choice)
else
@ -775,8 +777,8 @@ class WhenStatement(val condition: Expression,
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
}
class WhenChoice(val values: List<Expression>?, // if null, this is the 'else' part
val statements: AnonymousScope,
class WhenChoice(var values: List<Expression>?, // if null, this is the 'else' part
var statements: AnonymousScope,
override val position: Position) : Node {
override lateinit var parent: Node

File diff suppressed because it is too large Load Diff

View File

@ -4,7 +4,6 @@ import prog8.ast.AstToSourceCode
import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.statements.Directive
import prog8.compiler.target.c64.codegen.AsmGen
import prog8.compiler.target.c64.MachineDefinition
import prog8.compiler.target.c64.codegen2.AsmGen2
import prog8.optimizer.constantFold
@ -14,16 +13,13 @@ import prog8.parser.ParsingFailedError
import prog8.parser.importLibraryModule
import prog8.parser.importModule
import prog8.parser.moduleName
import java.io.File
import java.io.PrintStream
import java.nio.file.Path
import kotlin.system.exitProcess
import kotlin.system.measureTimeMillis
fun compileProgram(filepath: Path,
optimize: Boolean, optimizeInlining: Boolean,
generateVmCode: Boolean, writeVmCode: Boolean,
writeAssembly: Boolean, asm2: Boolean): Pair<Program, String?> {
writeAssembly: Boolean): Pair<Program, String?> {
lateinit var programAst: Program
var programName: String? = null
@ -64,7 +60,7 @@ fun compileProgram(filepath: Path,
programAst.removeNopsFlattenAnonScopes()
// if you want to print the AST, do it before shuffling the statements around below
//printAst(programAst)
printAst(programAst)
programAst.reorderStatements() // reorder statements and add type casts, to please the compiler later
@ -92,33 +88,13 @@ fun compileProgram(filepath: Path,
programAst.checkValid(compilerOptions) // check if final tree is valid
programAst.checkRecursion() // check if there are recursive subroutine calls
if(generateVmCode) {
// compile the syntax tree into stackvmProg form, and optimize that
val compiler = Compiler(programAst)
val intermediate = compiler.compile(compilerOptions)
if (optimize)
intermediate.optimize()
printAst(programAst)
if (writeVmCode) {
val stackVmFilename = intermediate.name + ".vm.txt"
val stackvmFile = PrintStream(File(stackVmFilename), "utf-8")
intermediate.writeCode(stackvmFile)
stackvmFile.close()
println("StackVM program code written to '$stackVmFilename'")
}
if (writeAssembly && !asm2) {
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
intermediate.allocateZeropage(zeropage)
val assembly = AsmGen(compilerOptions, intermediate, programAst.heap, zeropage).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
programName = assembly.name
}
}
if(asm2 && writeAssembly) {
if(writeAssembly) {
// asm generation directly from the Ast, no need for intermediate code
val assembly = AsmGen2(programAst, compilerOptions, MachineDefinition.C64Zeropage(compilerOptions)).compileToAssembly(optimize)
val zeropage = MachineDefinition.C64Zeropage(compilerOptions)
programAst.anonscopeVarsCleanup()
val assembly = AsmGen2(programAst, compilerOptions, zeropage).compileToAssembly(optimize)
assembly.assemble(compilerOptions)
programName = assembly.name
}

View File

@ -18,6 +18,9 @@ abstract class Zeropage(protected val options: CompilationOptions) {
fun allocate(scopedname: String, datatype: DataType, position: Position?): Int {
assert(scopedname.isEmpty() || !allocations.values.any { it.first==scopedname } ) {"isSameAs scopedname can't be allocated twice"}
if(options.zeropage==ZeropageType.DONTUSE)
throw CompilerException("zero page usage has been disabled")
val size =
when (datatype) {
in ByteDatatypes -> 1

View File

@ -9,10 +9,25 @@ class AssemblyProgram(val name: String) {
private val assemblyFile = "$name.asm"
private val viceMonListFile = "$name.vice-mon-list"
companion object {
// 6502 opcodes (including aliases and illegal opcodes), these cannot be used as variable or label names
val opcodeNames = setOf("adc", "ahx", "alr", "anc", "and", "ane", "arr", "asl", "asr", "axs", "bcc", "bcs",
"beq", "bge", "bit", "blt", "bmi", "bne", "bpl", "brk", "bvc", "bvs", "clc",
"cld", "cli", "clv", "cmp", "cpx", "cpy", "dcm", "dcp", "dec", "dex", "dey",
"eor", "gcc", "gcs", "geq", "gge", "glt", "gmi", "gne", "gpl", "gvc", "gvs",
"inc", "ins", "inx", "iny", "isb", "isc", "jam", "jmp", "jsr", "lae", "las",
"lax", "lda", "lds", "ldx", "ldy", "lsr", "lxa", "nop", "ora", "pha", "php",
"pla", "plp", "rla", "rol", "ror", "rra", "rti", "rts", "sax", "sbc", "sbx",
"sec", "sed", "sei", "sha", "shl", "shr", "shs", "shx", "shy", "slo", "sre",
"sta", "stx", "sty", "tas", "tax", "tay", "tsx", "txa", "txs", "tya", "xaa")
}
fun assemble(options: CompilationOptions) {
// add "-Wlong-branch" to see warnings about conversion of branch instructions to jumps
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch", "-Wall", "-Wno-strict-bool",
"-Werror", "-Wno-error=long-branch", "--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
val command = mutableListOf("64tass", "--ascii", "--case-sensitive", "--long-branch",
"-Wall", "-Wno-strict-bool", "-Wno-shadow", "-Werror", "-Wno-error=long-branch",
"--dump-labels", "--vice-labels", "-l", viceMonListFile, "--no-monitor")
val outFile = when(options.output) {
OutputType.PRG -> {
@ -41,7 +56,7 @@ class AssemblyProgram(val name: String) {
private fun generateBreakpointList() {
// builds list of breakpoints, appends to monitor list file
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 File(viceMonListFile).readLines()) {
val match = pattern.matchEntire(line)
if(match!=null)

View File

@ -42,14 +42,14 @@ object MachineDefinition {
}
override val exitProgramStrategy: ExitProgramStrategy = when (options.zeropage) {
ZeropageType.BASICSAFE -> ExitProgramStrategy.CLEAN_EXIT
ZeropageType.BASICSAFE, ZeropageType.DONTUSE -> ExitProgramStrategy.CLEAN_EXIT
ZeropageType.FLOATSAFE, ZeropageType.KERNALSAFE, ZeropageType.FULL -> ExitProgramStrategy.SYSTEM_RESET
}
init {
if (options.floats && options.zeropage != ZeropageType.FLOATSAFE && options.zeropage != ZeropageType.BASICSAFE)
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe'")
if (options.floats && options.zeropage !in setOf(ZeropageType.FLOATSAFE, ZeropageType.BASICSAFE, ZeropageType.DONTUSE ))
throw CompilerException("when floats are enabled, zero page type should be 'floatsafe' or 'basicsafe' or 'dontuse'")
if (options.zeropage == ZeropageType.FULL) {
free.addAll(0x04..0xf9)
@ -84,11 +84,16 @@ object MachineDefinition {
))
}
// add the other free Zp addresses,
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
free.addAll(listOf(0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0d, 0x0e,
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
if(options.zeropage!=ZeropageType.DONTUSE) {
// add the other free Zp addresses,
// these are valid for the C-64 (when no RS232 I/O is performed) but to keep BASIC running fully:
free.addAll(listOf(0x04, 0x05, 0x06, 0x0a, 0x0e,
0x94, 0x95, 0xa7, 0xa8, 0xa9, 0xaa,
0xb5, 0xb6, 0xf7, 0xf8, 0xf9))
} else {
// don't use the zeropage at all
free.clear()
}
}
assert(SCRATCH_B1 !in free)
assert(SCRATCH_REG !in free)

View File

@ -0,0 +1,48 @@
package prog8.compiler.target.c64.codegen2
import prog8.ast.Program
import prog8.ast.base.AstException
import prog8.ast.base.NameError
import prog8.ast.processing.IAstModifyingVisitor
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>().associate { it.name to it }
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

View File

@ -1,4 +1,4 @@
package prog8.compiler.target.c64.codegen
package prog8.compiler.target.c64.codegen2
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX

View File

@ -0,0 +1,238 @@
package prog8.compiler.target.c64.codegen2
import prog8.ast.IFunctionCall
import prog8.ast.Program
import prog8.ast.base.ByteDatatypes
import prog8.ast.base.DataType
import prog8.ast.base.Register
import prog8.ast.base.WordDatatypes
import prog8.ast.expressions.*
import prog8.ast.statements.FunctionCallStatement
import prog8.compiler.CompilationOptions
import prog8.compiler.Zeropage
import prog8.compiler.target.c64.MachineDefinition.ESTACK_HI_PLUS1_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_HEX
import prog8.compiler.target.c64.MachineDefinition.ESTACK_LO_PLUS1_HEX
import prog8.compiler.toHex
import prog8.functions.FunctionSignature
internal class BuiltinFunctionsAsmGen(private val program: Program,
private val options: CompilationOptions,
private val zeropage: Zeropage,
private val asmgen: AsmGen2) {
internal fun translateFunctioncallExpression(fcall: FunctionCall, func: FunctionSignature) {
translateFunctioncall(fcall, func, false)
}
internal fun translateFunctioncallStatement(fcall: FunctionCallStatement, func: FunctionSignature) {
translateFunctioncall(fcall, func, true)
}
private fun translateFunctioncall(fcall: IFunctionCall, func: FunctionSignature, discardResult: Boolean) {
val functionName = fcall.target.nameInSource.last()
if(discardResult) {
if(func.pure)
return // can just ignore the whole function call altogether
else if(func.returntype!=null)
throw AssemblyError("discarding result of non-pure function $fcall")
}
when(functionName) {
"msb" -> {
val arg = fcall.arglist.single()
if(arg.inferType(program) !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")
}
}
"mkword" -> {
translateFunctionArguments(fcall.arglist)
asmgen.out(" inx | lda $ESTACK_LO_HEX,x | sta $ESTACK_HI_PLUS1_HEX,x")
}
"abs" -> {
translateFunctionArguments(fcall.arglist)
val dt = fcall.arglist.single().inferType(program)!!
when (dt) {
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")
}
}
// TODO: any(f), all(f), max(f), min(f), sum(f)
"sin", "cos", "tan", "atan",
"ln", "log2", "sqrt", "rad",
"deg", "round", "floor", "ceil",
"rdnf" -> {
translateFunctionArguments(fcall.arglist)
asmgen.out(" jsr c64flt.func_$functionName")
}
/*
TODO this was the old code for bit rotations:
Opcode.SHL_BYTE -> AsmFragment(" asl $variable+$index", 8)
Opcode.SHR_UBYTE -> AsmFragment(" lsr $variable+$index", 8)
Opcode.SHR_SBYTE -> AsmFragment(" lda $variable+$index | asl a | ror $variable+$index")
Opcode.SHL_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.SHR_UWORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.SHR_SWORD -> AsmFragment(" lda $variable+${index * 2 + 1} | asl a | ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL_BYTE -> AsmFragment(" rol $variable+$index", 8)
Opcode.ROR_BYTE -> AsmFragment(" ror $variable+$index", 8)
Opcode.ROL_WORD -> AsmFragment(" rol $variable+${index * 2 + 1} | rol $variable+${index * 2}", 8)
Opcode.ROR_WORD -> AsmFragment(" ror $variable+${index * 2 + 1} | ror $variable+${index * 2}", 8)
Opcode.ROL2_BYTE -> AsmFragment(" lda $variable+$index | cmp #\$80 | rol $variable+$index", 8)
Opcode.ROR2_BYTE -> AsmFragment(" lda $variable+$index | lsr a | bcc + | ora #\$80 |+ | sta $variable+$index", 10)
Opcode.ROL2_WORD -> AsmFragment(" asl $variable+${index * 2 + 1} | rol $variable+${index * 2} | bcc + | inc $variable+${index * 2 + 1} |+", 20)
Opcode.ROR2_WORD -> AsmFragment(" lsr $variable+${index * 2 + 1} | ror $variable+${index * 2} | bcc + | lda $variable+${index * 2 + 1} | ora #\$80 | sta $variable+${index * 2 + 1} |+", 30)
*/
"lsl" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
in ByteDatatypes -> {
when(what) {
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" asl a")
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 -> {
if(what.addressExpression is NumericLiteralValue) {
asmgen.out(" asl ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
} else {
TODO("lsl memory byte $what")
}
}
is ArrayIndexedExpression -> {
TODO("lsl byte array $what")
}
else -> throw AssemblyError("weird type")
}
}
in WordDatatypes -> {
TODO("lsl word $what")
}
else -> throw AssemblyError("weird type")
}
}
"lsr" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
DataType.UBYTE -> {
when(what) {
is RegisterExpr -> {
when(what.register) {
Register.A -> asmgen.out(" lsr a")
Register.X -> asmgen.out(" txa | lsr a | tax")
Register.Y -> asmgen.out(" tya | lsr a | tay")
}
}
is IdentifierReference -> asmgen.out(" lsr ${asmgen.asmIdentifierName(what)}")
is DirectMemoryRead -> {
if(what.addressExpression is NumericLiteralValue) {
asmgen.out(" lsr ${(what.addressExpression as NumericLiteralValue).number.toHex()}")
} else {
TODO("lsr memory byte $what")
}
}
is ArrayIndexedExpression -> {
TODO("lsr byte array $what")
}
else -> throw AssemblyError("weird type")
}
}
DataType.BYTE -> {
TODO("lsr sbyte $what")
}
DataType.UWORD -> {
TODO("lsr sword $what")
}
DataType.WORD -> {
TODO("lsr word $what")
}
else -> throw AssemblyError("weird type")
}
}
"rol" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
DataType.UBYTE -> {
TODO("rol ubyte")
}
DataType.UWORD -> {
TODO("rol uword")
}
else -> throw AssemblyError("weird type")
}
}
"rol2" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
DataType.UBYTE -> {
TODO("rol2 ubyte")
}
DataType.UWORD -> {
TODO("rol2 uword")
}
else -> throw AssemblyError("weird type")
}
}
"ror" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
DataType.UBYTE -> {
TODO("ror ubyte")
}
DataType.UWORD -> {
TODO("ror uword")
}
else -> throw AssemblyError("weird type")
}
}
"ror2" -> {
// in-place
val what = fcall.arglist.single()
val dt = what.inferType(program)!!
when(dt) {
DataType.UBYTE -> {
TODO("ror2 ubyte")
}
DataType.UWORD -> {
TODO("ror2 uword")
}
else -> throw AssemblyError("weird type")
}
}
else -> {
translateFunctionArguments(fcall.arglist)
asmgen.out(" jsr prog8_lib.func_$functionName")
}
}
}
private fun translateFunctionArguments(args: MutableList<Expression>) {
args.forEach { asmgen.translateExpression(it) }
}
}

View File

@ -145,12 +145,11 @@ fun builtinFunctionReturnType(function: String, args: List<Expression>, program:
return when (function) {
"abs" -> {
when(val dt = args.single().inferType(program)) {
in ByteDatatypes -> DataType.UBYTE
in WordDatatypes -> DataType.UWORD
DataType.FLOAT -> DataType.FLOAT
else -> throw FatalAstException("weird datatype passed to abs $dt")
}
val dt = args.single().inferType(program)
if(dt in NumericDatatypes)
return dt
else
throw FatalAstException("weird datatype passed to abs $dt")
}
"max", "min" -> {
when(val dt = datatypeFromIterableArg(args.single())) {

View File

@ -5,11 +5,11 @@ import prog8.ast.Program
import prog8.ast.base.*
import prog8.ast.expressions.*
import prog8.ast.processing.IAstModifyingVisitor
import prog8.ast.processing.fixupArrayDatatype
import prog8.ast.statements.*
import prog8.compiler.HeapValues
import prog8.compiler.IntegerOrAddressOf
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_NEGATIVE
import prog8.compiler.target.c64.MachineDefinition.FLOAT_MAX_POSITIVE
import prog8.functions.BuiltinFunctions
import kotlin.math.floor
@ -35,13 +35,9 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
if(decl.type==VarDeclType.CONST || decl.type==VarDeclType.VAR) {
val refLv = decl.value as? ReferenceLiteralValue
if(refLv!=null && refLv.isArray && refLv.heapId!=null)
fixupArrayTypeOnHeap(decl, refLv)
if(decl.isArray){
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
if(decl.arraysize==null) {
// for arrays that have no size specifier (or a non-constant one) attempt to deduce the size
val arrayval = (decl.value as? ReferenceLiteralValue)?.array
if(arrayval!=null) {
decl.arraysize = ArrayIndex(NumericLiteralValue.optimalInteger(arrayval.size, decl.position), decl.position)
@ -78,7 +74,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
val numericLv = decl.value as? NumericLiteralValue
val rangeExpr = decl.value as? RangeExpr
if(rangeExpr!=null) {
// convert the initializer range expression to an actual array (will be put on heap later)
// convert the initializer range expression to an actual array
val declArraySize = decl.arraysize?.size()
if(declArraySize!=null && declArraySize!=rangeExpr.size())
errors.add(ExpressionError("range expression size doesn't match declared array size", decl.value?.position!!))
@ -124,8 +120,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
else -> {}
}
val heapId = program.heap.addIntegerArray(decl.datatype, Array(size) { IntegerOrAddressOf(fillvalue, null) })
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = numericLv.position)
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue.optimalInteger(it, numericLv.position) as Expression}.toTypedArray()
val refValue = ReferenceLiteralValue(decl.datatype, array = array, position = numericLv.position)
refValue.addToHeap(program.heap)
decl.value = refValue
refValue.parent=decl
optimizationsDone++
return super.visit(decl)
}
@ -142,8 +142,12 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
if (fillvalue < FLOAT_MAX_NEGATIVE || fillvalue > FLOAT_MAX_POSITIVE)
errors.add(ExpressionError("float value overflow", litval.position))
else {
val heapId = program.heap.addDoublesArray(DoubleArray(size) { fillvalue })
decl.value = ReferenceLiteralValue(DataType.ARRAY_F, initHeapId = heapId, position = litval.position)
// create the array itself, filled with the fillvalue.
val array = Array(size) {fillvalue}.map { NumericLiteralValue(DataType.FLOAT, it, litval.position) as Expression}.toTypedArray()
val refValue = ReferenceLiteralValue(DataType.ARRAY_F, array = array, position = litval.position)
refValue.addToHeap(program.heap)
decl.value = refValue
refValue.parent=decl
optimizationsDone++
return super.visit(decl)
}
@ -158,36 +162,6 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
return super.visit(decl)
}
private fun fixupArrayTypeOnHeap(decl: VarDecl, litval: ReferenceLiteralValue) {
// fix the type of the array value that's on the heap, to match the vardecl.
// notice that checking the bounds of the actual values is not done here, but in the AstChecker later.
if(decl.datatype==litval.type)
return // already correct datatype
val heapId = litval.heapId ?: throw FatalAstException("expected array to be on heap $litval")
val array = program.heap.get(heapId)
when(decl.datatype) {
DataType.ARRAY_UB, DataType.ARRAY_B, DataType.ARRAY_UW, DataType.ARRAY_W -> {
if(array.array!=null) {
program.heap.update(heapId, HeapValues.HeapValue(decl.datatype, null, array.array, null))
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
}
}
DataType.ARRAY_F -> {
if(array.array!=null) {
// convert a non-float array to floats
val doubleArray = array.array.map { it.integer!!.toDouble() }.toDoubleArray()
program.heap.update(heapId, HeapValues.HeapValue(DataType.ARRAY_F, null, null, doubleArray))
decl.value = ReferenceLiteralValue(decl.datatype, initHeapId = heapId, position = litval.position)
}
}
DataType.STRUCT -> {
// leave it alone for structs.
}
else -> throw FatalAstException("invalid array vardecl type ${decl.datatype}")
}
}
/**
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
*/
@ -227,6 +201,25 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
private fun typeCastConstArguments(functionCall: IFunctionCall) {
if(functionCall.target.nameInSource.size==1) {
val builtinFunction = BuiltinFunctions[functionCall.target.nameInSource.single()]
if(builtinFunction!=null) {
// match the arguments of a builtin function signature.
for(arg in functionCall.arglist.withIndex().zip(builtinFunction.parameters)) {
val possibleDts = arg.second.possibleDatatypes
val argConst = arg.first.value.constValue(program)
if(argConst!=null && argConst.type !in possibleDts) {
val convertedValue = argConst.cast(possibleDts.first())
if(convertedValue!=null) {
functionCall.arglist[arg.first.index] = convertedValue
optimizationsDone++
}
}
}
return
}
}
// match the arguments of a subroutine.
val subroutine = functionCall.target.targetSubroutine(program.namespace)
if(subroutine!=null) {
// if types differ, try to typecast constant arguments to the function call to the desired data type of the parameter
@ -259,14 +252,16 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
*/
override fun visit(expr: PrefixExpression): Expression {
return try {
super.visit(expr)
val prefixExpr=super.visit(expr)
if(prefixExpr !is PrefixExpression)
return prefixExpr
val subexpr = expr.expression
val subexpr = prefixExpr.expression
if (subexpr is NumericLiteralValue) {
// accept prefixed literal values (such as -3, not true)
return when {
expr.operator == "+" -> subexpr
expr.operator == "-" -> when {
prefixExpr.operator == "+" -> subexpr
prefixExpr.operator == "-" -> when {
subexpr.type in IntegerDatatypes -> {
optimizationsDone++
NumericLiteralValue.optimalNumeric(-subexpr.number.toInt(), subexpr.position)
@ -277,21 +272,21 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
}
else -> throw ExpressionError("can only take negative of int or float", subexpr.position)
}
expr.operator == "~" -> when {
prefixExpr.operator == "~" -> when {
subexpr.type in IntegerDatatypes -> {
optimizationsDone++
NumericLiteralValue.optimalNumeric(subexpr.number.toInt().inv(), subexpr.position)
}
else -> throw ExpressionError("can only take bitwise inversion of int", subexpr.position)
}
expr.operator == "not" -> {
prefixExpr.operator == "not" -> {
optimizationsDone++
NumericLiteralValue.fromBoolean(subexpr.number.toDouble() == 0.0, subexpr.position)
}
else -> throw ExpressionError(expr.operator, subexpr.position)
else -> throw ExpressionError(prefixExpr.operator, subexpr.position)
}
}
return expr
return prefixExpr
} catch (ax: AstException) {
addError(ax)
expr
@ -606,68 +601,16 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
val litval = super.visit(refLiteral)
if(litval is ReferenceLiteralValue) {
if (litval.isString) {
// intern the string; move it into the heap
if (litval.str!!.length !in 1..255)
addError(ExpressionError("string literal length must be between 1 and 255", litval.position))
else {
litval.addToHeap(program.heap) // TODO: we don't know the actual string type yet, STR != STR_S etc...
if (litval.isArray) {
val vardecl = litval.parent as? VarDecl
if (vardecl!=null) {
return fixupArrayDatatype(litval, vardecl, program.heap)
}
} else if (litval.isArray) {
// first, adjust the array datatype
val litval2 = adjustArrayValDatatype(litval)
litval2.addToHeap(program.heap)
return litval2
}
}
return litval
}
private fun adjustArrayValDatatype(litval: ReferenceLiteralValue): ReferenceLiteralValue {
if(litval.array==null) {
if(litval.heapId!=null)
return litval // thing is already on the heap, assume it's the right type
throw FatalAstException("missing array value")
}
val typesInArray = litval.array.mapNotNull { it.inferType(program) }.toSet()
val arrayDt =
when {
litval.array.any { it is AddressOf } -> DataType.ARRAY_UW
DataType.FLOAT in typesInArray -> DataType.ARRAY_F
DataType.WORD in typesInArray -> DataType.ARRAY_W
else -> {
val allElementsAreConstantOrAddressOf = litval.array.fold(true) { c, expr-> c and (expr is NumericLiteralValue|| expr is AddressOf)}
if(!allElementsAreConstantOrAddressOf) {
addError(ExpressionError("array literal can only consist of constant primitive numerical values or memory pointers", litval.position))
return litval
} else {
val integerArray = litval.array.map { it.constValue(program)!!.number.toInt() }
val maxValue = integerArray.max()!!
val minValue = integerArray.min()!!
if (minValue >= 0) {
// unsigned
if (maxValue <= 255)
DataType.ARRAY_UB
else
DataType.ARRAY_UW
} else {
// signed
if (maxValue <= 127)
DataType.ARRAY_B
else
DataType.ARRAY_W
}
}
}
}
if(arrayDt!=litval.type) {
return ReferenceLiteralValue(arrayDt, array = litval.array, position = litval.position)
}
return litval
}
override fun visit(assignment: Assignment): Statement {
super.visit(assignment)
val lv = assignment.value as? NumericLiteralValue

View File

@ -241,8 +241,6 @@ internal class StatementOptimizer(private val program: Program, private val opti
if(functionCallStatement.target.nameInSource==listOf("c64scr", "print") ||
functionCallStatement.target.nameInSource==listOf("c64scr", "print_p")) {
// printing a literal string of just 2 or 1 characters is replaced by directly outputting those characters
if(functionCallStatement.arglist.single() is NumericLiteralValue)
throw AstException("string argument should be on heap already")
val stringVar = functionCallStatement.arglist.single() as? IdentifierReference
if(stringVar!=null) {
val heapId = stringVar.heapId(program.namespace)
@ -379,10 +377,10 @@ internal class StatementOptimizer(private val program: Program, private val opti
printWarning("condition is always true", whileLoop.position)
if(hasContinueOrBreak(whileLoop.body))
return whileLoop
val label = Label("__back", whileLoop.condition.position)
val label = Label("_prog8_back", whileLoop.condition.position)
whileLoop.body.statements.add(0, label)
whileLoop.body.statements.add(Jump(null,
IdentifierReference(listOf("__back"), whileLoop.condition.position),
IdentifierReference(listOf("_prog8_back"), whileLoop.condition.position),
null, whileLoop.condition.position))
optimizationsDone++
return whileLoop.body
@ -489,8 +487,10 @@ internal class StatementOptimizer(private val program: Program, private val opti
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
if(assignment.target isSameAs assignment.value) {
optimizationsDone++
return NopStatement.insteadOf(assignment)
if(assignment.target.isNotMemory(program.namespace)) {
optimizationsDone++
return NopStatement.insteadOf(assignment)
}
}
val targetDt = assignment.target.inferType(program, assignment)
val bexpr=assignment.value as? BinaryExpression

View File

@ -107,8 +107,6 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
asBoolean = floatval != 0.0
}
else -> {
if(heapId==null)
throw IllegalArgumentException("for non-numeric types, a heapId should be given")
byteval = null
wordval = null
floatval = null
@ -628,7 +626,7 @@ open class RuntimeValue(val type: DataType, num: Number?=null, val str: String?=
}
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValue(type, 0) {
class RuntimeValueRange(type: DataType, val range: IntProgression): RuntimeValue(type, array=range.toList().toTypedArray()) {
override fun iterator(): Iterator<Number> {
return range.iterator()
}

View File

@ -472,7 +472,7 @@ class AstVm(val program: Program) {
loopvar = IdentifierReference(listOf(stmt.loopRegister.name), stmt.position)
} else {
loopvarDt = stmt.loopVar!!.inferType(program)!!
loopvar = stmt.loopVar
loopvar = stmt.loopVar!!
}
val iterator = iterable.iterator()
for (loopvalue in iterator) {
@ -518,7 +518,7 @@ class AstVm(val program: Program) {
executeAnonymousScope(choice.statements)
break
} else {
val value = choice.values.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val value = choice.values!!.single().constValue(evalCtx.program) ?: throw VmExecutionException("can only use const values in when choices ${choice.position}")
val rtval = RuntimeValue.fromLv(value)
if(condition==rtval) {
executeAnonymousScope(choice.statements)
@ -545,10 +545,12 @@ class AstVm(val program: Program) {
}
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: Statement, evalCtx: EvalContext) {
val targetIdent = target.identifier
val targetArrayIndexed = target.arrayindexed
when {
target.identifier != null -> {
val decl = contextStmt.definingScope().lookup(target.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
targetIdent != null -> {
val decl = contextStmt.definingScope().lookup(targetIdent.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target $targetIdent")
if (decl.type == VarDeclType.MEMORY) {
val address = runtimeVariables.getMemoryAddress(decl.definingScope(), decl.name)
when (decl.datatype) {
@ -565,14 +567,14 @@ class AstVm(val program: Program) {
runtimeVariables.set(decl.definingScope(), decl.name, value)
}
target.memoryAddress != null -> {
val address = evaluate(target.memoryAddress!!.addressExpression, evalCtx).wordval!!
val address = evaluate(target.memoryAddress.addressExpression, evalCtx).wordval!!
evalCtx.mem.setUByte(address, value.byteval!!)
}
target.arrayindexed != null -> {
val vardecl = target.arrayindexed.identifier.targetVarDecl(program.namespace)!!
targetArrayIndexed != null -> {
val vardecl = targetArrayIndexed.identifier.targetVarDecl(program.namespace)!!
if(vardecl.type==VarDeclType.VAR) {
val array = evaluate(target.arrayindexed.identifier, evalCtx)
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx)
val array = evaluate(targetArrayIndexed.identifier, evalCtx)
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx)
when (array.type) {
DataType.ARRAY_UB -> {
if (value.type != DataType.UBYTE)
@ -606,7 +608,7 @@ class AstVm(val program: Program) {
val indexInt = index.integerValue()
val newchr = Petscii.decodePetscii(listOf(value.numericValue().toShort()), true)
val newstr = array.str!!.replaceRange(indexInt, indexInt + 1, newchr)
val ident = contextStmt.definingScope().lookup(target.arrayindexed.identifier.nameInSource, contextStmt) as? VarDecl
val ident = contextStmt.definingScope().lookup(targetArrayIndexed.identifier.nameInSource, contextStmt) as? VarDecl
?: throw VmExecutionException("can't find assignment target ${target.identifier}")
val identScope = ident.definingScope()
program.heap.update(array.heapId!!, newstr)
@ -615,8 +617,8 @@ class AstVm(val program: Program) {
}
else {
val address = (vardecl.value as NumericLiteralValue).number.toInt()
val index = evaluate(target.arrayindexed.arrayspec.index, evalCtx).integerValue()
val elementType = target.arrayindexed.inferType(program)!!
val index = evaluate(targetArrayIndexed.arrayspec.index, evalCtx).integerValue()
val elementType = targetArrayIndexed.inferType(program)!!
when(elementType) {
DataType.UBYTE -> mem.setUByte(address+index, value.byteval!!)
DataType.BYTE -> mem.setSByte(address+index, value.byteval!!)
@ -673,23 +675,23 @@ class AstVm(val program: Program) {
dialog.canvas.printText(args[0].wordval!!.toString(), true)
}
"c64scr.print_ubhex" -> {
val prefix = if (args[0].asBoolean) "$" else ""
val number = args[1].byteval!!
val number = args[0].byteval!!
val prefix = if (args[1].asBoolean) "$" else ""
dialog.canvas.printText("$prefix${number.toString(16).padStart(2, '0')}", true)
}
"c64scr.print_uwhex" -> {
val prefix = if (args[0].asBoolean) "$" else ""
val number = args[1].wordval!!
val number = args[0].wordval!!
val prefix = if (args[1].asBoolean) "$" else ""
dialog.canvas.printText("$prefix${number.toString(16).padStart(4, '0')}", true)
}
"c64scr.print_uwbin" -> {
val prefix = if (args[0].asBoolean) "%" else ""
val number = args[1].wordval!!
val number = args[0].wordval!!
val prefix = if (args[1].asBoolean) "%" else ""
dialog.canvas.printText("$prefix${number.toString(2).padStart(16, '0')}", true)
}
"c64scr.print_ubbin" -> {
val prefix = if (args[0].asBoolean) "%" else ""
val number = args[1].byteval!!
val number = args[0].byteval!!
val prefix = if (args[1].asBoolean) "%" else ""
dialog.canvas.printText("$prefix${number.toString(2).padStart(8, '0')}", true)
}
"c64scr.clear_screenchars" -> {
@ -731,7 +733,7 @@ class AstVm(val program: Program) {
result = RuntimeValue(DataType.UBYTE, paddedStr.indexOf('\u0000'))
}
"c64flt.print_f" -> {
dialog.canvas.printText(args[0].floatval.toString(), true)
dialog.canvas.printText(args[0].floatval.toString(), false)
}
"c64.CHROUT" -> {
dialog.canvas.printPetscii(args[0].byteval!!)
@ -952,4 +954,3 @@ class AstVm(val program: Program) {
}
}

View File

@ -116,18 +116,6 @@ class TestRuntimeValue {
assertFalse(sameValueAndType(RuntimeValue(DataType.FLOAT, 9.99), RuntimeValue(DataType.FLOAT, 9.0)))
}
@Test
fun testRequireHeap()
{
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.STR, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.STR_S, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.ARRAY_F, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.ARRAY_W, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.ARRAY_UW, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.ARRAY_B, num = 999) }
assertFailsWith<IllegalArgumentException> { RuntimeValue(DataType.ARRAY_UB, num = 999) }
}
@Test
fun testEqualityHeapTypes()
{

View File

@ -145,8 +145,12 @@ class TestZeropage {
assertFailsWith<CompilerException> {
zp.allocate("", DataType.FLOAT, null)
}
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
zp2.allocate("", DataType.FLOAT, null)
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true))
assertFailsWith<CompilerException> {
zp2.allocate("", DataType.FLOAT, null)
}
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true))
zp3.allocate("", DataType.FLOAT, null)
}
@Test
@ -165,14 +169,16 @@ class TestZeropage {
}
}
// TODO test dontuse option
@Test
fun testFreeSpaces() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
assertEquals(20, zp1.available())
assertEquals(16, zp1.available())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false))
assertEquals(95, zp2.available())
assertEquals(91, zp2.available())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false))
assertEquals(129, zp3.available())
assertEquals(125, zp3.available())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false))
assertEquals(238, zp4.available())
}
@ -202,11 +208,10 @@ class TestZeropage {
@Test
fun testBasicsafeAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
assertEquals(20, zp.available())
assertEquals(16, zp.available())
zp.allocate("", DataType.FLOAT, null)
assertFailsWith<ZeropageDepletedError> {
// in regular zp there aren't 5 sequential bytes free after we take the first sequence
// in regular zp there aren't 5 sequential bytes free
zp.allocate("", DataType.FLOAT, null)
}
@ -256,16 +261,16 @@ class TestZeropage {
@Test
fun testEfficientAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true))
assertEquals(20, zp.available())
assertEquals(0x04, zp.allocate("", DataType.FLOAT, null))
assertEquals(0x09, zp.allocate("", DataType.UBYTE, null))
assertEquals(0x0d, zp.allocate("", DataType.UWORD, null))
assertEquals(16, zp.available())
assertEquals(0x04, zp.allocate("", DataType.WORD, null))
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null))
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null))
assertEquals(0x94, zp.allocate("", DataType.UWORD, null))
assertEquals(0xa7, zp.allocate("", DataType.UWORD, null))
assertEquals(0xa9, zp.allocate("", DataType.UWORD, null))
assertEquals(0xb5, zp.allocate("", DataType.UWORD, null))
assertEquals(0xf7, zp.allocate("", DataType.UWORD, null))
assertEquals(0x0a, zp.allocate("", DataType.UBYTE, null))
assertEquals(0x0e, zp.allocate("", DataType.UBYTE, null))
assertEquals(0xf9, zp.allocate("", DataType.UBYTE, null))
assertEquals(0, zp.available())
}

View File

@ -45,7 +45,7 @@ This code calculates prime numbers using the Sieve of Eratosthenes algorithm::
%import c64utils
%zeropage basicsafe
~ main {
main {
ubyte[256] sieve
ubyte candidate_prime = 2
@ -101,7 +101,7 @@ The following programs shows a use of the high level ``struct`` type::
%import c64utils
%zeropage basicsafe
~ main {
main {
struct Color {
ubyte red

View File

@ -93,7 +93,7 @@ Blocks, Scopes, and accessing Symbols
**Blocks** are the top level separate pieces of code and data of your program. They are combined
into a single output program. No code or data can occur outside a block. Here's an example::
~ main $c000 {
main $c000 {
; this is code inside the block...
}
@ -127,12 +127,20 @@ The address must be >= ``$0200`` (because ``$00``--``$ff`` is the ZP and ``$100`
to them by their 'short' name directly. If the symbol is not found in the same scope,
the enclosing scope is searched for it, and so on, until the symbol is found.
Scopes are created using several statements:
Scopes are created using either of these two statements:
- blocks (top-level named scope)
- subroutines (nested named scopes)
- for, while, repeat loops (anonymous scope)
- if statements and branching conditionals (anonymous scope)
- subroutines (nested named scope)
.. note::
In contrast to many other programming languages, a new scope is *not* created inside
for, while and repeat statements, nor for the if statement and branching conditionals.
This is a bit restrictive because you have to think harder about what variables you
want to use inside a subroutine. But it is done precisely for this reason; memory in the
target system is very limited and it would be a waste to allocate a lot of variables.
Right now the prog8 compiler is not advanced enough to be able to 'share' or 'overlap'
variables intelligently by itself. So for now, it's something the programmer has to think about.
Program Start and Entry Point
@ -151,7 +159,7 @@ taking no parameters and having no return value.
As any subroutine, it has to end with a ``return`` statement (or a ``goto`` call)::
~ main {
main {
sub start () {
; program entrypoint code here
return
@ -400,7 +408,9 @@ You can also create loops by using the ``goto`` statement, but this should usual
The value of the loop variable or register after executing the loop *is undefined*. Don't use it immediately
after the loop without first assigning a new value to it!
(this is an optimization issue to avoid having to deal with mostly useless post-loop logic to adjust the loop variable's value)
Loop variables that are declared inline are scoped in the loop body so they're not accessible at all after the loop finishes.
Loop variables that are declared inline are not different to them being
defined in a separate var declaration in the subroutine, it's just a readability convenience.
(this may change in the future if the compiler gets more advanced with additional sub-scopes)
Conditional Execution

View File

@ -74,6 +74,7 @@ Directives
As with ``kernalsafe``, it is not possible to cleanly exit the program, other than to reset the machine.
This option makes programs smaller and faster because even more variables can
be stored in the ZP (which allows for more efficient assembly code).
- style ``dontuse`` -- don't use *any* location in the zeropage.
Also read :ref:`zeropage`.
@ -173,7 +174,7 @@ Code blocks
A named block of actual program code. Itefines a *scope* (also known as 'namespace') and
can contain Prog8 *code*, *directives*, *variable declarations* and *subroutines*::
~ <blockname> [<address>] {
<blockname> [<address>] {
<directives>
<variables>
<statements>
@ -185,7 +186,7 @@ The <address> is optional. If specified it must be a valid memory address such a
It's used to tell the compiler to put the block at a certain position in memory.
Also read :ref:`blocks`. Here is an example of a code block, to be loaded at ``$c000``::
~ main $c000 {
main $c000 {
; this is code inside the block...
}
@ -387,7 +388,7 @@ arithmetic: ``+`` ``-`` ``*`` ``/`` ``**`` ``%``
``+``, ``-``, ``*``, ``/`` are the familiar arithmetic operations.
``/`` is division (will result in integer division when using on integer operands, and a floating point division when at least one of the operands is a float)
``**`` is the power operator: ``3 ** 5`` is equal to 3*3*3*3*3 and is 243. (it only works on floating point variables)
``%`` is the remainder operator: ``25 % 7`` is 4. Be careful: without a space, %10 will be parsed as the binary number 2
``%`` is the remainder operator: ``25 % 7`` is 4. Be careful: without a space, %10 will be parsed as the binary number 2.
Remainder is only supported on integer operands (not floats).
bitwise arithmetic: ``&`` ``|`` ``^`` ``~`` ``<<`` ``>>``

View File

@ -168,7 +168,7 @@ These routines are::
If you activate an IRQ handler with one of these, it expects the handler to be defined
as a subroutine ``irq`` in the module ``irq`` so like this::
~ irq {
irq {
sub irq() {
; ... irq handling here ...
}

View File

@ -2,6 +2,28 @@
TODO
====
Fixes
^^^^^
variable naming issue::
main {
sub start() {
for A in 0 to 10 {
ubyte note1 = 44
Y+=note1
}
delay(1)
sub delay(ubyte note1) { ; TODO: redef of note1 above, conflicts because that one was moved to the zeropage
A= note1
}
}
}
Memory Block Operations integrated in language?
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^

View File

@ -0,0 +1,75 @@
%import c64utils
%zeropage basicsafe
main {
byte bb
ubyte ub
word ww
uword uw
&ubyte membyte=9999
&uword memword=9999
ubyte[10] barray
sub start() {
lsr(A)
lsl(A)
ror(A)
rol(A)
ror2(A)
rol2(A)
lsr(membyte)
lsl(membyte)
ror(membyte)
rol(membyte)
ror2(membyte)
rol2(membyte)
lsr(memword)
lsl(memword)
ror(memword)
rol(memword)
ror2(memword)
rol2(memword)
lsl(@(9999))
lsr(@(9999))
ror(@(9999))
rol(@(9999))
ror2(@(9999))
rol2(@(9999))
lsr(barray[1])
lsl(barray[1])
ror(barray[1])
rol(barray[1])
ror2(barray[1])
rol2(barray[1])
bb /= 2
bb >>= 1
bb *= 4
bb <<= 2
ub /= 2
ub >>= 1
ub *= 4
ub <<= 2
rol(ub)
ror(ub)
rol2(ub)
ror2(ub)
ww /= 2
ww >>= 1
ww *= 4
ww <<= 2
uw /= 2
uw >>= 1
uw *= 4
uw <<= 2
rol(uw)
ror(uw)
rol2(uw)
ror2(uw)
}
}

105
examples/arithmetic/div.p8 Normal file
View File

@ -0,0 +1,105 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
div_ubyte(0, 1, 0)
div_ubyte(100, 6, 16)
div_ubyte(255, 2, 127)
div_byte(0, 1, 0)
div_byte(100, -6, -16)
div_byte(127, -2, -63)
div_uword(0,1,0)
div_uword(40000,500,80)
div_uword(43211,2,21605)
div_word(0,1,0)
div_word(-20000,500,-40)
div_word(-2222,2,-1111)
div_float(0,1,0)
div_float(999.9,111.0,9.008108108108107)
}
sub div_ubyte(ubyte a1, ubyte a2, ubyte c) {
ubyte r = a1/a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("ubyte ")
c64scr.print_ub(a1)
c64scr.print(" / ")
c64scr.print_ub(a2)
c64scr.print(" = ")
c64scr.print_ub(r)
c64.CHROUT('\n')
}
sub div_byte(byte a1, byte a2, byte c) {
byte r = a1/a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("byte ")
c64scr.print_b(a1)
c64scr.print(" / ")
c64scr.print_b(a2)
c64scr.print(" = ")
c64scr.print_b(r)
c64.CHROUT('\n')
}
sub div_uword(uword a1, uword a2, uword c) {
uword r = a1/a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("uword ")
c64scr.print_uw(a1)
c64scr.print(" / ")
c64scr.print_uw(a2)
c64scr.print(" = ")
c64scr.print_uw(r)
c64.CHROUT('\n')
}
sub div_word(word a1, word a2, word c) {
word r = a1/a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("word ")
c64scr.print_w(a1)
c64scr.print(" / ")
c64scr.print_w(a2)
c64scr.print(" = ")
c64scr.print_w(r)
c64.CHROUT('\n')
}
sub div_float(float a1, float a2, float c) {
float r = a1/a2
if abs(r-c)<0.00001
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("float ")
c64flt.print_f(a1)
c64scr.print(" / ")
c64flt.print_f(a2)
c64scr.print(" = ")
c64flt.print_f(r)
c64.CHROUT('\n')
}
}

View File

@ -0,0 +1,113 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
minus_ubyte(0, 0, 0)
minus_ubyte(200, 0, 200)
minus_ubyte(200, 100, 100)
minus_ubyte(100, 200, 156)
minus_byte(0, 0, 0)
minus_byte(100, 100, 0)
minus_byte(50, -50, 100)
minus_byte(0, -30, 30)
minus_byte(-30, 0, -30)
minus_uword(0,0,0)
minus_uword(50000,0, 50000)
minus_uword(50000,20000,30000)
minus_uword(20000,50000,35536)
minus_word(0,0,0)
minus_word(1000,1000,0)
minus_word(-1000,1000,-2000)
minus_word(1000,500,500)
minus_word(0,-3333,3333)
minus_word(-3333,0,-3333)
minus_float(0,0,0)
minus_float(2.5,1.5,1.0)
minus_float(-1.5,3.5,-5.0)
}
sub minus_ubyte(ubyte a1, ubyte a2, ubyte c) {
ubyte r = a1-a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("ubyte ")
c64scr.print_ub(a1)
c64scr.print(" - ")
c64scr.print_ub(a2)
c64scr.print(" = ")
c64scr.print_ub(r)
c64.CHROUT('\n')
}
sub minus_byte(byte a1, byte a2, byte c) {
byte r = a1-a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("byte ")
c64scr.print_b(a1)
c64scr.print(" - ")
c64scr.print_b(a2)
c64scr.print(" = ")
c64scr.print_b(r)
c64.CHROUT('\n')
}
sub minus_uword(uword a1, uword a2, uword c) {
uword r = a1-a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("uword ")
c64scr.print_uw(a1)
c64scr.print(" - ")
c64scr.print_uw(a2)
c64scr.print(" = ")
c64scr.print_uw(r)
c64.CHROUT('\n')
}
sub minus_word(word a1, word a2, word c) {
word r = a1-a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("word ")
c64scr.print_w(a1)
c64scr.print(" - ")
c64scr.print_w(a2)
c64scr.print(" = ")
c64scr.print_w(r)
c64.CHROUT('\n')
}
sub minus_float(float a1, float a2, float c) {
float r = a1-a2
if abs(r-c)<0.00001
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("float ")
c64flt.print_f(a1)
c64scr.print(" - ")
c64flt.print_f(a2)
c64scr.print(" = ")
c64flt.print_f(r)
c64.CHROUT('\n')
}
}

107
examples/arithmetic/mult.p8 Normal file
View File

@ -0,0 +1,107 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
mul_ubyte(0, 0, 0)
mul_ubyte(20, 1, 20)
mul_ubyte(20, 10, 200)
mul_byte(0, 0, 0)
mul_byte(10, 10, 100)
mul_byte(5, -5, -25)
mul_byte(0, -30, 0)
mul_uword(0,0,0)
mul_uword(50000,1, 50000)
mul_uword(500,100,50000)
mul_word(0,0,0)
mul_word(-10,1000,-10000)
mul_word(1,-3333,-3333)
mul_float(0,0,0)
mul_float(2.5,10,25)
mul_float(-1.5,10,-15)
}
sub mul_ubyte(ubyte a1, ubyte a2, ubyte c) {
ubyte r = a1*a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("ubyte ")
c64scr.print_ub(a1)
c64scr.print(" * ")
c64scr.print_ub(a2)
c64scr.print(" = ")
c64scr.print_ub(r)
c64.CHROUT('\n')
}
sub mul_byte(byte a1, byte a2, byte c) {
byte r = a1*a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("byte ")
c64scr.print_b(a1)
c64scr.print(" * ")
c64scr.print_b(a2)
c64scr.print(" = ")
c64scr.print_b(r)
c64.CHROUT('\n')
}
sub mul_uword(uword a1, uword a2, uword c) {
uword r = a1*a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("uword ")
c64scr.print_uw(a1)
c64scr.print(" * ")
c64scr.print_uw(a2)
c64scr.print(" = ")
c64scr.print_uw(r)
c64.CHROUT('\n')
}
sub mul_word(word a1, word a2, word c) {
word r = a1*a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("word ")
c64scr.print_w(a1)
c64scr.print(" * ")
c64scr.print_w(a2)
c64scr.print(" = ")
c64scr.print_w(r)
c64.CHROUT('\n')
}
sub mul_float(float a1, float a2, float c) {
float r = a1*a2
if abs(r-c)<0.00001
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("float ")
c64flt.print_f(a1)
c64scr.print(" * ")
c64flt.print_f(a2)
c64scr.print(" = ")
c64flt.print_f(r)
c64.CHROUT('\n')
}
}

111
examples/arithmetic/plus.p8 Normal file
View File

@ -0,0 +1,111 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
plus_ubyte(0, 0, 0)
plus_ubyte(0, 200, 200)
plus_ubyte(100, 200, 44)
plus_byte(0, 0, 0)
plus_byte(-100, 100, 0)
plus_byte(-50, 100, 50)
plus_byte(0, -30, -30)
plus_byte(-30, 0, -30)
plus_uword(0,0,0)
plus_uword(0,50000,50000)
plus_uword(50000,20000,4464)
plus_word(0,0,0)
plus_word(-1000,1000,0)
plus_word(-500,1000,500)
plus_word(0,-3333,-3333)
plus_word(-3333,0,-3333)
plus_float(0,0,0)
plus_float(1.5,2.5,4.0)
plus_float(-1.5,3.5,2.0)
plus_float(-1.1,3.3,2.2)
}
sub plus_ubyte(ubyte a1, ubyte a2, ubyte c) {
ubyte r = a1+a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("ubyte ")
c64scr.print_ub(a1)
c64scr.print(" + ")
c64scr.print_ub(a2)
c64scr.print(" = ")
c64scr.print_ub(r)
c64.CHROUT('\n')
}
sub plus_byte(byte a1, byte a2, byte c) {
byte r = a1+a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("byte ")
c64scr.print_b(a1)
c64scr.print(" + ")
c64scr.print_b(a2)
c64scr.print(" = ")
c64scr.print_b(r)
c64.CHROUT('\n')
}
sub plus_uword(uword a1, uword a2, uword c) {
uword r = a1+a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("uword ")
c64scr.print_uw(a1)
c64scr.print(" + ")
c64scr.print_uw(a2)
c64scr.print(" = ")
c64scr.print_uw(r)
c64.CHROUT('\n')
}
sub plus_word(word a1, word a2, word c) {
word r = a1+a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("word ")
c64scr.print_w(a1)
c64scr.print(" + ")
c64scr.print_w(a2)
c64scr.print(" = ")
c64scr.print_w(r)
c64.CHROUT('\n')
}
sub plus_float(float a1, float a2, float c) {
float r = a1+a2
if abs(r-c)<0.00001
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("float ")
c64flt.print_f(a1)
c64scr.print(" + ")
c64flt.print_f(a2)
c64scr.print(" = ")
c64flt.print_f(r)
c64.CHROUT('\n')
}
}

View File

@ -0,0 +1,142 @@
%import c64utils
%import c64flt
%option enable_floats
%zeropage basicsafe
main {
sub start() {
c64scr.plot(0,24)
ubyte ub=200
byte bb=-100
uword uw = 2000
word ww = -1000
float fl = 999.99
ubyte[3] ubarr = 200
byte[3] barr = -100
uword[3] uwarr = 2000
word[3] warr = -1000
float[3] flarr = 999.99
c64scr.print("++\n")
ub++
bb++
uw++
ww++
fl++
ubarr[1]++
barr[1]++
uwarr[1]++
warr[1]++
flarr[1] ++
check_ub(ub, 201)
Y=100
Y++
check_ub(Y, 101)
check_fl(fl, 1000.99)
check_b(bb, -99)
check_uw(uw, 2001)
check_w(ww, -999)
check_ub(ubarr[0], 200)
check_fl(flarr[0], 999.99)
check_b(barr[0], -100)
check_uw(uwarr[0], 2000)
check_w(warr[0], -1000)
check_ub(ubarr[1], 201)
check_fl(flarr[1], 1000.99)
check_b(barr[1], -99)
check_uw(uwarr[1], 2001)
check_w(warr[1], -999)
c64scr.print("--\n")
ub--
bb--
uw--
ww--
fl--
ubarr[1]--
barr[1]--
uwarr[1]--
warr[1]--
flarr[1] --
check_ub(ub, 200)
Y=100
Y--
check_ub(Y, 99)
check_fl(fl, 999.99)
check_b(bb, -100)
check_uw(uw, 2000)
check_w(ww, -1000)
check_ub(ubarr[1], 200)
check_fl(flarr[1], 999.99)
check_b(barr[1], -100)
check_uw(uwarr[1], 2000)
check_w(warr[1], -1000)
@($0400+400-1) = X
}
sub check_ub(ubyte value, ubyte expected) {
if value==expected
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print(" ubyte ")
c64scr.print_ub(value)
c64.CHROUT(',')
c64scr.print_ub(expected)
c64.CHROUT('\n')
}
sub check_b(byte value, byte expected) {
if value==expected
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print(" byte ")
c64scr.print_b(value)
c64.CHROUT(',')
c64scr.print_b(expected)
c64.CHROUT('\n')
}
sub check_uw(uword value, uword expected) {
if value==expected
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print(" uword ")
c64scr.print_uw(value)
c64.CHROUT(',')
c64scr.print_uw(expected)
c64.CHROUT('\n')
}
sub check_w(word value, word expected) {
if value==expected
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print(" word ")
c64scr.print_w(value)
c64.CHROUT(',')
c64scr.print_w(expected)
c64.CHROUT('\n')
}
sub check_fl(float value, float expected) {
if value==expected
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print(" float ")
c64flt.print_f(value)
c64.CHROUT(',')
c64flt.print_f(expected)
c64.CHROUT('\n')
}
}

View File

@ -0,0 +1,49 @@
%import c64lib
%import c64utils
%import c64flt
%zeropage basicsafe
main {
sub start() {
remainder_ubyte(0, 1, 0)
remainder_ubyte(100, 6, 4)
remainder_ubyte(255, 2, 1)
remainder_ubyte(255, 20, 15)
remainder_uword(0,1,0)
remainder_uword(40000,511,142)
remainder_uword(40000,500,0)
remainder_uword(43211,12,11)
}
sub remainder_ubyte(ubyte a1, ubyte a2, ubyte c) {
ubyte r = a1%a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("ubyte ")
c64scr.print_ub(a1)
c64scr.print(" % ")
c64scr.print_ub(a2)
c64scr.print(" = ")
c64scr.print_ub(r)
c64.CHROUT('\n')
}
sub remainder_uword(uword a1, uword a2, uword c) {
uword r = a1%a2
if r==c
c64scr.print(" ok ")
else
c64scr.print("err! ")
c64scr.print("uword ")
c64scr.print_uw(a1)
c64scr.print(" % ")
c64scr.print_uw(a2)
c64scr.print(" = ")
c64scr.print_uw(r)
c64.CHROUT('\n')
}
}

View File

@ -1,6 +1,6 @@
%import c64lib
~ main {
main {
sub start() {
@ -18,10 +18,10 @@ sub start() {
while(true) {
for uword note in notes {
ubyte n1 = lsb(note)
ubyte n2 = msb(note)
c64.FREQ1 = music_freq_table[n1] ; set lo+hi freq of voice 1
c64.FREQ2 = music_freq_table[n2] ; set lo+hi freq of voice 2
ubyte note1 = lsb(note)
ubyte note2 = msb(note)
c64.FREQ1 = music_freq_table[note1] ; set lo+hi freq of voice 1
c64.FREQ2 = music_freq_table[note2] ; set lo+hi freq of voice 2
; retrigger voice 1 and 2 ADSR
c64.CR1 = waveform <<4 | 0
@ -29,7 +29,7 @@ sub start() {
c64.CR1 = waveform <<4 | 1
c64.CR2 = waveform <<4 | 1
print_notes(n1, n2)
print_notes(note1, note2)
delay()
}
}

View File

@ -2,7 +2,7 @@
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%import c64flt
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%import c64flt
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%import c64utils
%import c64flt
~ main {
main {
const uword width = 40
const uword height = 25
@ -66,23 +66,28 @@
; plot the points of the 3d cube
; first the points on the back, then the points on the front (painter algorithm)
ubyte i
float rz
float persp
ubyte sx
ubyte sy
for ubyte i in 0 to len(xcoor)-1 {
float rz = rotatedz[i]
for i in 0 to len(xcoor)-1 {
rz = rotatedz[i]
if rz >= 0.1 {
float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
persp = (5.0+rz)/height
sx = rotatedx[i] / persp + width/2.0 as ubyte
sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 46, i+2)
}
}
for ubyte i in 0 to len(xcoor)-1 {
float rz = rotatedz[i]
for i in 0 to len(xcoor)-1 {
rz = rotatedz[i]
if rz < 0.1 {
float persp = (5.0+rz)/height
ubyte sx = rotatedx[i] / persp + width/2.0 as ubyte
ubyte sy = rotatedy[i] / persp + height/2.0 as ubyte
persp = (5.0+rz)/height
sx = rotatedx[i] / persp + width/2.0 as ubyte
sy = rotatedy[i] / persp + height/2.0 as ubyte
c64scr.setcc(sx, sy, 81, i+2)
}
}

View File

@ -1,7 +1,7 @@
%import c64lib
%import c64utils
~ spritedata $2000 {
spritedata $2000 {
; 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
@ -58,7 +58,7 @@
}
~ main {
main {
const uword width = 255
const uword height = 200

View File

@ -2,7 +2,7 @@
%launcher none
%import c64flt
~ irq {
irq {
uword global_time
ubyte time_changed
@ -14,7 +14,7 @@
}
~ main {
main {
const uword width = 320
const uword height = 200

View File

@ -1,7 +1,7 @@
%import c64lib
%import c64utils
~ main {
main {
const uword width = 40
const uword height = 25
@ -75,22 +75,28 @@
; plot the points of the 3d cube
; first the points on the back, then the points on the front (painter algorithm)
for ubyte i in 0 to len(xcoor)-1 {
word rz = rotatedz[i]
ubyte i
word rz
word persp
byte sx
byte sy
for i in 0 to len(xcoor)-1 {
rz = rotatedz[i]
if rz >= 10 {
word persp = (rz+200) / height
byte sx = rotatedx[i] / persp as byte + width/2
byte sy = rotatedy[i] / persp as byte + height/2
persp = (rz+200) / height
sx = rotatedx[i] / persp as byte + width/2
sy = rotatedy[i] / persp as byte + height/2
c64scr.setcc(sx as ubyte, sy as ubyte, 46, vertexcolors[(rz as byte >>5) + 3])
}
}
for ubyte i in 0 to len(xcoor)-1 {
word rz = rotatedz[i]
for i in 0 to len(xcoor)-1 {
rz = rotatedz[i]
if rz < 10 {
word persp = (rz+200) / height
byte sx = rotatedx[i] / persp as byte + width/2
byte sy = rotatedy[i] / persp as byte + height/2
persp = (rz+200) / height
sx = rotatedx[i] / persp as byte + width/2
sy = rotatedy[i] / persp as byte + height/2
c64scr.setcc(sx as ubyte, sy as ubyte, 81, vertexcolors[(rz as byte >>5) + 3])
}
}

View File

@ -7,11 +7,11 @@
; This is extremely handy for the Fibonacci sequence because it is defined
; in terms of 'the next value is the sum of the previous two values'
~ main {
main {
sub start() {
c64scr.print("fibonacci sequence\n")
fib_setup()
for ubyte i in 0 to 20 {
for A in 0 to 20 {
c64scr.print_uw(fib_next())
c64.CHROUT('\n')
}

View File

@ -4,7 +4,7 @@
%zeropage basicsafe
~ main {
main {
sub start() {

View File

@ -2,7 +2,7 @@
%launcher none
%import c64flt
~ main {
main {
const uword width = 320 / 2
const uword height = 256 / 2
const uword xoffset = 40

View File

@ -4,7 +4,7 @@
%zeropage basicsafe
~ main {
main {
const uword width = 30
const uword height = 20
const ubyte max_iter = 16

View File

@ -4,7 +4,7 @@
; The classic number guessing game.
~ main {
main {
sub start() {
str name = "????????????????????????????????????????"

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
ubyte[256] sieve
ubyte candidate_prime = 2 ; is increased in the loop

View File

@ -2,7 +2,7 @@
%import c64lib
~ main {
main {
sub start() {
c64.SCROLY &= %11101111 ; blank the screen
@ -16,7 +16,7 @@
}
~ irq {
irq {
const ubyte barheight = 4
ubyte[] colors = [6,2,4,5,15,7,1,13,3,12,8,11,9]

56
examples/romfloats.p8 Normal file
View File

@ -0,0 +1,56 @@
%import c64flt
%zeropage basicsafe
%option enable_floats
main {
sub start() {
; these are all floating point constants defined in the ROM so no allocation required
c64flt.print_f(3.141592653589793)
c64.CHROUT('\n')
c64flt.print_f(-32768.0)
c64.CHROUT('\n')
c64flt.print_f( 1.0)
c64.CHROUT('\n')
c64flt.print_f(0.7071067811865476)
c64.CHROUT('\n')
c64flt.print_f(1.4142135623730951)
c64.CHROUT('\n')
c64flt.print_f( -0.5)
c64.CHROUT('\n')
c64flt.print_f(0.6931471805599453)
c64.CHROUT('\n')
c64flt.print_f(10.0)
c64.CHROUT('\n')
c64flt.print_f(1.0e9)
c64.CHROUT('\n')
c64flt.print_f(0.5)
c64.CHROUT('\n')
c64flt.print_f(1.4426950408889634)
c64.CHROUT('\n')
c64flt.print_f(1.5707963267948966)
c64.CHROUT('\n')
c64flt.print_f(6.283185307179586)
c64.CHROUT('\n')
c64flt.print_f(0.25)
c64.CHROUT('\n')
c64flt.print_f(0.0)
c64.CHROUT('\n')
}
}

View File

@ -3,7 +3,7 @@
%zeropage basicsafe
~ spritedata $0a00 {
spritedata $0a00 {
; 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
@ -31,7 +31,7 @@
%00000000,%00011100,%00000000 ]
}
~ main {
main {
const uword SP0X = $d000
const uword SP0Y = $d001
@ -52,7 +52,7 @@
}
~ irq {
irq {
sub irq() {
c64.EXTCOL--

View File

@ -1,7 +1,7 @@
%import c64utils
%zeropage basicsafe
~ main {
main {
struct Color {
ubyte red

View File

@ -1,7 +1,7 @@
%import c64utils
%import c64flt
~ main {
main {
const uword width = 40
const uword height = 25

View File

@ -2,7 +2,7 @@
%launcher none
%import c64flt
~ main {
main {
const uword width = 320
const uword height = 200

View File

@ -1,6 +1,6 @@
%import c64utils
~ main {
main {
const uword width = 40
const uword height = 25

View File

@ -10,7 +10,7 @@
; @todo show ghost?
~ main {
main {
const ubyte boardOffsetX = 14
const ubyte boardOffsetY = 3
@ -387,7 +387,7 @@ waitkey:
}
~ blocklogic {
blocklogic {
ubyte currentBlockNum
ubyte[16] currentBlock
@ -561,7 +561,7 @@ waitkey:
}
~ sound {
sound {
sub init() {
c64.MVOL = 15

View File

@ -1,16 +1,26 @@
%import c64utils
%import c64flt
%zeropage basicsafe
%option enable_floats
%zeropage basicsafe
~ main {
main {
sub start() {
byte bb
ubyte ub
word ww
uword uw
float fl
bb = 10*bb
ub = 12*ub
ww = 15*ww
uw = 20*uw
fl = 20*fl
sub start() {
if_z goto start
if_pos goto start
if_cc goto start
if_nz goto start
}

144
examples/testarrays.p8 Normal file
View File

@ -0,0 +1,144 @@
%import c64flt
%zeropage basicsafe
%option enable_floats
main {
sub start() {
str s1 = "irmen"
str_s s2 = "hello"
&str ms1 = $c000
byte[4] barray
ubyte[4] ubarray
word[4] warray
uword[4] uwarray
float[4] flarray
&byte[4] mbarray = $c000
&ubyte[4] mubarray = $c000
&word[4] mwarray = $c000
&uword[4] muwarray = $c000
&float[4] mflarray = $c000
byte bb
ubyte ub
word ww
uword uw
float fl
; read array
@($d020) = ub
A=s1[2]
ub=s1[2]
ub=s2[2]
bb=barray[2]
ub=ubarray[2]
ww=warray[2]
uw=uwarray[2]
fl=flarray[2]
A=ms1[2]
ub=ms1[2]
bb=mbarray[2]
ub=mubarray[2]
ww=mwarray[2]
uw=muwarray[2]
fl=mflarray[2]
A=s1[A]
ub=s2[A]
ub=s2[A]
bb=barray[A]
ub=ubarray[A]
ww=warray[A]
uw=uwarray[A]
fl=flarray[A]
A=ms1[A]
ub=ms1[A]
bb=mbarray[A]
ub=mubarray[A]
ww=mwarray[A]
uw=muwarray[A]
fl=mflarray[A]
A=s1[bb]
ub=s1[bb]
ub=s2[bb]
bb=barray[bb]
ub=ubarray[bb]
ww=warray[bb]
uw=uwarray[bb]
fl=flarray[bb]
A=ms1[bb]
ub=ms1[bb]
bb=mbarray[bb]
ub=mubarray[bb]
ww=mwarray[bb]
uw=muwarray[bb]
fl=mflarray[bb]
A=s1[bb*3]
ub=s1[bb*3]
ub=s2[bb*3]
bb=barray[bb*3]
ub=ubarray[bb*3]
ww=warray[bb*3]
uw=uwarray[bb*3]
fl=flarray[bb*3]
A=ms1[bb*3]
ub=ms1[bb*3]
bb=mbarray[bb*3]
ub=mubarray[bb*3]
ww=mwarray[bb*3]
uw=muwarray[bb*3]
fl=mflarray[bb*3]
; write array
barray[2]++
barray[2]--
s1[2] = A
s1[2] = ub
s2[2] = ub
barray[2] = bb
ubarray[2] = ub
warray[2] = ww
uwarray[2] = uw
flarray[2] = fl
ms1[2] = A
ms1[2] = ub
mbarray[2]++
mbarray[2] = bb
mbarray[2] = bb
mubarray[2] = ub
mwarray[2] = ww
muwarray[2] = uw
mflarray[2] = fl
; s1[A] = ub
; s2[A] = ub
; barray[A] = bb
; ubarray[A] = ub
; warray[A] = ww
; uwarray[A] = uw
; flarray[A] = fl
;
; s1[bb] = ub
; s2[bb] = ub
; barray[bb] = bb
; ubarray[bb] = ub
; warray[bb] = ww
; uwarray[bb] = uw
; flarray[bb] = fl
;
; s1[bb*3] = ub
; s2[bb*3] = ub
; barray[bb*3] = bb
; ubarray[bb*3] = ub
; warray[bb*3] = ww
; uwarray[bb*3] = uw
; flarray[bb*3] = fl
}
}

135
examples/testforloops.p8 Normal file
View File

@ -0,0 +1,135 @@
%zeropage basicsafe
main {
sub start() {
byte bvar
ubyte var2
ubyte[] barr = [22,33,44,55,66]
word[] warr = [-111,222,-333,444]
for A in "hello" {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in [1,3,5,99] {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in 10 to 20 {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in 20 to 10 step -1 {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in 10 to 21 step 3 {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in 24 to 10 step -3 {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for A in barr {
c64scr.print_ub(A)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64.CHROUT('\n')
for ubyte cc in "hello" {
c64scr.print_ub(cc)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc2 in [1,3,5,99] {
c64scr.print_ub(cc2)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc3 in 10 to 20 {
c64scr.print_ub(cc3)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc4 in 20 to 10 step -1 {
c64scr.print_ub(cc4)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc5 in 10 to 21 step 3 {
c64scr.print_ub(cc5)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc6 in 24 to 10 step -3 {
c64scr.print_ub(cc6)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for ubyte cc7 in barr {
c64scr.print_ub(cc7)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64.CHROUT('\n')
for uword ww1 in [1111, 2222, 3333] {
c64scr.print_uw(ww1)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for word ww2 in warr {
c64scr.print_w(ww2)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for uword ww3 in 1111 to 1117 {
c64scr.print_uw(ww3)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for uword ww3b in 2000 to 1995 step -1 {
c64scr.print_uw(ww3b)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for uword ww3c in 1111 to 50000 step 4444 {
c64scr.print_uw(ww3c)
c64.CHROUT(',')
}
c64.CHROUT('\n')
for word ww4 in 999 to -999 step -500 {
c64scr.print_w(ww4)
c64.CHROUT(',')
}
c64.CHROUT('\n')
c64.CHROUT('\n')
}
}

View File

@ -3,7 +3,7 @@
%zeropage basicsafe
~ spritedata $0a00 {
spritedata $0a00 {
; 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
@ -31,7 +31,7 @@
%00000000,%00011100,%00000000 ]
}
~ main {
main {
sub start() {
@ -44,7 +44,7 @@
}
~ irq {
irq {
sub irq() {
ubyte angle ; no initialization value so it keeps the previous one.

View File

@ -68,7 +68,7 @@ module : (modulestatement | EOL)* EOF ;
modulestatement: directive | block ;
block: '~' identifier integerliteral? statement_block EOL ;
block: identifier integerliteral? statement_block EOL ;
statement :
directive

View File

@ -0,0 +1,515 @@
// Generated from prog8.g4 by ANTLR 4.7.2
package prog8.parser;
import org.antlr.v4.runtime.Lexer;
import org.antlr.v4.runtime.CharStream;
import org.antlr.v4.runtime.Token;
import org.antlr.v4.runtime.TokenStream;
import org.antlr.v4.runtime.*;
import org.antlr.v4.runtime.atn.*;
import org.antlr.v4.runtime.dfa.DFA;
import org.antlr.v4.runtime.misc.*;
@SuppressWarnings({"all", "warnings", "unchecked", "unused", "cast"})
public class prog8Lexer extends Lexer {
static { RuntimeMetaData.checkVersion("4.7.2", RuntimeMetaData.VERSION); }
protected static final DFA[] _decisionToDFA;
protected static final PredictionContextCache _sharedContextCache =
new PredictionContextCache();
public static final int
T__0=1, T__1=2, T__2=3, T__3=4, T__4=5, T__5=6, T__6=7, T__7=8, T__8=9,
T__9=10, T__10=11, T__11=12, T__12=13, T__13=14, T__14=15, T__15=16, T__16=17,
T__17=18, T__18=19, T__19=20, T__20=21, T__21=22, T__22=23, T__23=24,
T__24=25, T__25=26, T__26=27, T__27=28, T__28=29, T__29=30, T__30=31,
T__31=32, T__32=33, T__33=34, T__34=35, T__35=36, T__36=37, T__37=38,
T__38=39, T__39=40, T__40=41, T__41=42, T__42=43, T__43=44, T__44=45,
T__45=46, T__46=47, T__47=48, T__48=49, T__49=50, T__50=51, T__51=52,
T__52=53, T__53=54, T__54=55, T__55=56, T__56=57, T__57=58, T__58=59,
T__59=60, T__60=61, T__61=62, T__62=63, T__63=64, T__64=65, T__65=66,
T__66=67, T__67=68, T__68=69, T__69=70, T__70=71, T__71=72, T__72=73,
T__73=74, T__74=75, T__75=76, T__76=77, T__77=78, T__78=79, T__79=80,
T__80=81, T__81=82, T__82=83, T__83=84, T__84=85, T__85=86, T__86=87,
T__87=88, T__88=89, T__89=90, T__90=91, T__91=92, T__92=93, T__93=94,
T__94=95, T__95=96, T__96=97, T__97=98, T__98=99, T__99=100, T__100=101,
T__101=102, T__102=103, T__103=104, T__104=105, T__105=106, T__106=107,
T__107=108, T__108=109, T__109=110, LINECOMMENT=111, COMMENT=112, WS=113,
EOL=114, NAME=115, DEC_INTEGER=116, HEX_INTEGER=117, BIN_INTEGER=118,
ADDRESS_OF=119, FLOAT_NUMBER=120, STRING=121, INLINEASMBLOCK=122, SINGLECHAR=123,
ZEROPAGE=124, ARRAYSIG=125;
public static String[] channelNames = {
"DEFAULT_TOKEN_CHANNEL", "HIDDEN"
};
public static String[] modeNames = {
"DEFAULT_MODE"
};
private static String[] makeRuleNames() {
return new String[] {
"T__0", "T__1", "T__2", "T__3", "T__4", "T__5", "T__6", "T__7", "T__8",
"T__9", "T__10", "T__11", "T__12", "T__13", "T__14", "T__15", "T__16",
"T__17", "T__18", "T__19", "T__20", "T__21", "T__22", "T__23", "T__24",
"T__25", "T__26", "T__27", "T__28", "T__29", "T__30", "T__31", "T__32",
"T__33", "T__34", "T__35", "T__36", "T__37", "T__38", "T__39", "T__40",
"T__41", "T__42", "T__43", "T__44", "T__45", "T__46", "T__47", "T__48",
"T__49", "T__50", "T__51", "T__52", "T__53", "T__54", "T__55", "T__56",
"T__57", "T__58", "T__59", "T__60", "T__61", "T__62", "T__63", "T__64",
"T__65", "T__66", "T__67", "T__68", "T__69", "T__70", "T__71", "T__72",
"T__73", "T__74", "T__75", "T__76", "T__77", "T__78", "T__79", "T__80",
"T__81", "T__82", "T__83", "T__84", "T__85", "T__86", "T__87", "T__88",
"T__89", "T__90", "T__91", "T__92", "T__93", "T__94", "T__95", "T__96",
"T__97", "T__98", "T__99", "T__100", "T__101", "T__102", "T__103", "T__104",
"T__105", "T__106", "T__107", "T__108", "T__109", "LINECOMMENT", "COMMENT",
"WS", "EOL", "NAME", "DEC_INTEGER", "HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF",
"FLOAT_NUMBER", "FNUMBER", "STRING_ESCAPE_SEQ", "STRING", "INLINEASMBLOCK",
"SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
};
}
public static final String[] ruleNames = makeRuleNames();
private static String[] makeLiteralNames() {
return new String[] {
null, "':'", "'goto'", "'%output'", "'%launcher'", "'%zeropage'", "'%zpreserved'",
"'%address'", "'%import'", "'%breakpoint'", "'%asminclude'", "'%asmbinary'",
"'%option'", "','", "'='", "'const'", "'struct'", "'{'", "'}'", "'ubyte'",
"'byte'", "'uword'", "'word'", "'float'", "'str'", "'str_s'", "'['",
"']'", "'+='", "'-='", "'/='", "'*='", "'**='", "'&='", "'|='", "'^='",
"'%='", "'<<='", "'>>='", "'++'", "'--'", "'+'", "'-'", "'~'", "'**'",
"'*'", "'/'", "'%'", "'<<'", "'>>'", "'<'", "'>'", "'<='", "'>='", "'=='",
"'!='", "'^'", "'|'", "'to'", "'step'", "'and'", "'or'", "'xor'", "'not'",
"'('", "')'", "'as'", "'@'", "'return'", "'break'", "'continue'", "'.'",
"'A'", "'X'", "'Y'", "'AX'", "'AY'", "'XY'", "'Pc'", "'Pz'", "'Pn'",
"'Pv'", "'.w'", "'true'", "'false'", "'%asm'", "'sub'", "'->'", "'asmsub'",
"'stack'", "'clobbers'", "'if'", "'else'", "'if_cs'", "'if_cc'", "'if_eq'",
"'if_z'", "'if_ne'", "'if_nz'", "'if_pl'", "'if_pos'", "'if_mi'", "'if_neg'",
"'if_vs'", "'if_vc'", "'for'", "'in'", "'while'", "'repeat'", "'until'",
"'when'", null, null, null, null, null, null, null, null, "'&'", null,
null, null, null, "'@zp'", "'[]'"
};
}
private static final String[] _LITERAL_NAMES = makeLiteralNames();
private static String[] makeSymbolicNames() {
return new String[] {
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, null, null, null, null, null, null, null, null, null,
null, null, null, "LINECOMMENT", "COMMENT", "WS", "EOL", "NAME", "DEC_INTEGER",
"HEX_INTEGER", "BIN_INTEGER", "ADDRESS_OF", "FLOAT_NUMBER", "STRING",
"INLINEASMBLOCK", "SINGLECHAR", "ZEROPAGE", "ARRAYSIG"
};
}
private static final String[] _SYMBOLIC_NAMES = makeSymbolicNames();
public static final Vocabulary VOCABULARY = new VocabularyImpl(_LITERAL_NAMES, _SYMBOLIC_NAMES);
/**
* @deprecated Use {@link #VOCABULARY} instead.
*/
@Deprecated
public static final String[] tokenNames;
static {
tokenNames = new String[_SYMBOLIC_NAMES.length];
for (int i = 0; i < tokenNames.length; i++) {
tokenNames[i] = VOCABULARY.getLiteralName(i);
if (tokenNames[i] == null) {
tokenNames[i] = VOCABULARY.getSymbolicName(i);
}
if (tokenNames[i] == null) {
tokenNames[i] = "<INVALID>";
}
}
}
@Override
@Deprecated
public String[] getTokenNames() {
return tokenNames;
}
@Override
public Vocabulary getVocabulary() {
return VOCABULARY;
}
public prog8Lexer(CharStream input) {
super(input);
_interp = new LexerATNSimulator(this,_ATN,_decisionToDFA,_sharedContextCache);
}
@Override
public String getGrammarFileName() { return "prog8.g4"; }
@Override
public String[] getRuleNames() { return ruleNames; }
@Override
public String getSerializedATN() { return _serializedATN; }
@Override
public String[] getChannelNames() { return channelNames; }
@Override
public String[] getModeNames() { return modeNames; }
@Override
public ATN getATN() { return _ATN; }
@Override
public void action(RuleContext _localctx, int ruleIndex, int actionIndex) {
switch (ruleIndex) {
case 122:
STRING_action((RuleContext)_localctx, actionIndex);
break;
case 123:
INLINEASMBLOCK_action((RuleContext)_localctx, actionIndex);
break;
case 124:
SINGLECHAR_action((RuleContext)_localctx, actionIndex);
break;
}
}
private void STRING_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 0:
// get rid of the enclosing quotes
String s = getText();
setText(s.substring(1, s.length() - 1));
break;
}
}
private void INLINEASMBLOCK_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 1:
// get rid of the enclosing double braces
String s = getText();
setText(s.substring(2, s.length() - 2));
break;
}
}
private void SINGLECHAR_action(RuleContext _localctx, int actionIndex) {
switch (actionIndex) {
case 2:
// get rid of the enclosing quotes
String s = getText();
setText(s.substring(1, s.length() - 1));
break;
}
}
public static final String _serializedATN =
"\3\u608b\ua72a\u8133\ub9ed\u417c\u3be7\u7786\u5964\2\177\u036e\b\1\4\2"+
"\t\2\4\3\t\3\4\4\t\4\4\5\t\5\4\6\t\6\4\7\t\7\4\b\t\b\4\t\t\t\4\n\t\n\4"+
"\13\t\13\4\f\t\f\4\r\t\r\4\16\t\16\4\17\t\17\4\20\t\20\4\21\t\21\4\22"+
"\t\22\4\23\t\23\4\24\t\24\4\25\t\25\4\26\t\26\4\27\t\27\4\30\t\30\4\31"+
"\t\31\4\32\t\32\4\33\t\33\4\34\t\34\4\35\t\35\4\36\t\36\4\37\t\37\4 \t"+
" \4!\t!\4\"\t\"\4#\t#\4$\t$\4%\t%\4&\t&\4\'\t\'\4(\t(\4)\t)\4*\t*\4+\t"+
"+\4,\t,\4-\t-\4.\t.\4/\t/\4\60\t\60\4\61\t\61\4\62\t\62\4\63\t\63\4\64"+
"\t\64\4\65\t\65\4\66\t\66\4\67\t\67\48\t8\49\t9\4:\t:\4;\t;\4<\t<\4=\t"+
"=\4>\t>\4?\t?\4@\t@\4A\tA\4B\tB\4C\tC\4D\tD\4E\tE\4F\tF\4G\tG\4H\tH\4"+
"I\tI\4J\tJ\4K\tK\4L\tL\4M\tM\4N\tN\4O\tO\4P\tP\4Q\tQ\4R\tR\4S\tS\4T\t"+
"T\4U\tU\4V\tV\4W\tW\4X\tX\4Y\tY\4Z\tZ\4[\t[\4\\\t\\\4]\t]\4^\t^\4_\t_"+
"\4`\t`\4a\ta\4b\tb\4c\tc\4d\td\4e\te\4f\tf\4g\tg\4h\th\4i\ti\4j\tj\4k"+
"\tk\4l\tl\4m\tm\4n\tn\4o\to\4p\tp\4q\tq\4r\tr\4s\ts\4t\tt\4u\tu\4v\tv"+
"\4w\tw\4x\tx\4y\ty\4z\tz\4{\t{\4|\t|\4}\t}\4~\t~\4\177\t\177\4\u0080\t"+
"\u0080\3\2\3\2\3\3\3\3\3\3\3\3\3\3\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\4\3\5"+
"\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\5\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3\6\3"+
"\6\3\6\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\7\3\b\3\b\3\b\3\b"+
"\3\b\3\b\3\b\3\b\3\b\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\t\3\n\3\n\3\n\3\n\3"+
"\n\3\n\3\n\3\n\3\n\3\n\3\n\3\n\3\13\3\13\3\13\3\13\3\13\3\13\3\13\3\13"+
"\3\13\3\13\3\13\3\13\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\f\3\r\3"+
"\r\3\r\3\r\3\r\3\r\3\r\3\r\3\16\3\16\3\17\3\17\3\20\3\20\3\20\3\20\3\20"+
"\3\20\3\21\3\21\3\21\3\21\3\21\3\21\3\21\3\22\3\22\3\23\3\23\3\24\3\24"+
"\3\24\3\24\3\24\3\24\3\25\3\25\3\25\3\25\3\25\3\26\3\26\3\26\3\26\3\26"+
"\3\26\3\27\3\27\3\27\3\27\3\27\3\30\3\30\3\30\3\30\3\30\3\30\3\31\3\31"+
"\3\31\3\31\3\32\3\32\3\32\3\32\3\32\3\32\3\33\3\33\3\34\3\34\3\35\3\35"+
"\3\35\3\36\3\36\3\36\3\37\3\37\3\37\3 \3 \3 \3!\3!\3!\3!\3\"\3\"\3\"\3"+
"#\3#\3#\3$\3$\3$\3%\3%\3%\3&\3&\3&\3&\3\'\3\'\3\'\3\'\3(\3(\3(\3)\3)\3"+
")\3*\3*\3+\3+\3,\3,\3-\3-\3-\3.\3.\3/\3/\3\60\3\60\3\61\3\61\3\61\3\62"+
"\3\62\3\62\3\63\3\63\3\64\3\64\3\65\3\65\3\65\3\66\3\66\3\66\3\67\3\67"+
"\3\67\38\38\38\39\39\3:\3:\3;\3;\3;\3<\3<\3<\3<\3<\3=\3=\3=\3=\3>\3>\3"+
">\3?\3?\3?\3?\3@\3@\3@\3@\3A\3A\3B\3B\3C\3C\3C\3D\3D\3E\3E\3E\3E\3E\3"+
"E\3E\3F\3F\3F\3F\3F\3F\3G\3G\3G\3G\3G\3G\3G\3G\3G\3H\3H\3I\3I\3J\3J\3"+
"K\3K\3L\3L\3L\3M\3M\3M\3N\3N\3N\3O\3O\3O\3P\3P\3P\3Q\3Q\3Q\3R\3R\3R\3"+
"S\3S\3S\3T\3T\3T\3T\3T\3U\3U\3U\3U\3U\3U\3V\3V\3V\3V\3V\3W\3W\3W\3W\3"+
"X\3X\3X\3Y\3Y\3Y\3Y\3Y\3Y\3Y\3Z\3Z\3Z\3Z\3Z\3Z\3[\3[\3[\3[\3[\3[\3[\3"+
"[\3[\3\\\3\\\3\\\3]\3]\3]\3]\3]\3^\3^\3^\3^\3^\3^\3_\3_\3_\3_\3_\3_\3"+
"`\3`\3`\3`\3`\3`\3a\3a\3a\3a\3a\3b\3b\3b\3b\3b\3b\3c\3c\3c\3c\3c\3c\3"+
"d\3d\3d\3d\3d\3d\3e\3e\3e\3e\3e\3e\3e\3f\3f\3f\3f\3f\3f\3g\3g\3g\3g\3"+
"g\3g\3g\3h\3h\3h\3h\3h\3h\3i\3i\3i\3i\3i\3i\3j\3j\3j\3j\3k\3k\3k\3l\3"+
"l\3l\3l\3l\3l\3m\3m\3m\3m\3m\3m\3m\3n\3n\3n\3n\3n\3n\3o\3o\3o\3o\3o\3"+
"p\3p\7p\u02f4\np\fp\16p\u02f7\13p\3p\3p\3p\3p\3q\3q\7q\u02ff\nq\fq\16"+
"q\u0302\13q\3q\3q\3r\3r\3r\3r\3s\6s\u030b\ns\rs\16s\u030c\3t\3t\7t\u0311"+
"\nt\ft\16t\u0314\13t\3u\3u\3u\6u\u0319\nu\ru\16u\u031a\5u\u031d\nu\3v"+
"\3v\6v\u0321\nv\rv\16v\u0322\3w\3w\6w\u0327\nw\rw\16w\u0328\3x\3x\3y\3"+
"y\3y\5y\u0330\ny\3y\5y\u0333\ny\3z\6z\u0336\nz\rz\16z\u0337\3z\3z\6z\u033c"+
"\nz\rz\16z\u033d\5z\u0340\nz\3{\3{\3{\3{\5{\u0346\n{\3|\3|\3|\7|\u034b"+
"\n|\f|\16|\u034e\13|\3|\3|\3|\3}\3}\3}\3}\6}\u0357\n}\r}\16}\u0358\3}"+
"\3}\3}\3}\3}\3~\3~\3~\5~\u0363\n~\3~\3~\3~\3\177\3\177\3\177\3\177\3\u0080"+
"\3\u0080\3\u0080\3\u0358\2\u0081\3\3\5\4\7\5\t\6\13\7\r\b\17\t\21\n\23"+
"\13\25\f\27\r\31\16\33\17\35\20\37\21!\22#\23%\24\'\25)\26+\27-\30/\31"+
"\61\32\63\33\65\34\67\359\36;\37= ?!A\"C#E$G%I&K\'M(O)Q*S+U,W-Y.[/]\60"+
"_\61a\62c\63e\64g\65i\66k\67m8o9q:s;u<w=y>{?}@\177A\u0081B\u0083C\u0085"+
"D\u0087E\u0089F\u008bG\u008dH\u008fI\u0091J\u0093K\u0095L\u0097M\u0099"+
"N\u009bO\u009dP\u009fQ\u00a1R\u00a3S\u00a5T\u00a7U\u00a9V\u00abW\u00ad"+
"X\u00afY\u00b1Z\u00b3[\u00b5\\\u00b7]\u00b9^\u00bb_\u00bd`\u00bfa\u00c1"+
"b\u00c3c\u00c5d\u00c7e\u00c9f\u00cbg\u00cdh\u00cfi\u00d1j\u00d3k\u00d5"+
"l\u00d7m\u00d9n\u00dbo\u00ddp\u00dfq\u00e1r\u00e3s\u00e5t\u00e7u\u00e9"+
"v\u00ebw\u00edx\u00efy\u00f1z\u00f3\2\u00f5\2\u00f7{\u00f9|\u00fb}\u00fd"+
"~\u00ff\177\3\2\n\4\2\f\f\17\17\4\2\13\13\"\"\5\2C\\aac|\6\2\62;C\\aa"+
"c|\5\2\62;CHch\4\2GGgg\4\2--//\6\2\f\f\16\17$$^^\2\u037d\2\3\3\2\2\2\2"+
"\5\3\2\2\2\2\7\3\2\2\2\2\t\3\2\2\2\2\13\3\2\2\2\2\r\3\2\2\2\2\17\3\2\2"+
"\2\2\21\3\2\2\2\2\23\3\2\2\2\2\25\3\2\2\2\2\27\3\2\2\2\2\31\3\2\2\2\2"+
"\33\3\2\2\2\2\35\3\2\2\2\2\37\3\2\2\2\2!\3\2\2\2\2#\3\2\2\2\2%\3\2\2\2"+
"\2\'\3\2\2\2\2)\3\2\2\2\2+\3\2\2\2\2-\3\2\2\2\2/\3\2\2\2\2\61\3\2\2\2"+
"\2\63\3\2\2\2\2\65\3\2\2\2\2\67\3\2\2\2\29\3\2\2\2\2;\3\2\2\2\2=\3\2\2"+
"\2\2?\3\2\2\2\2A\3\2\2\2\2C\3\2\2\2\2E\3\2\2\2\2G\3\2\2\2\2I\3\2\2\2\2"+
"K\3\2\2\2\2M\3\2\2\2\2O\3\2\2\2\2Q\3\2\2\2\2S\3\2\2\2\2U\3\2\2\2\2W\3"+
"\2\2\2\2Y\3\2\2\2\2[\3\2\2\2\2]\3\2\2\2\2_\3\2\2\2\2a\3\2\2\2\2c\3\2\2"+
"\2\2e\3\2\2\2\2g\3\2\2\2\2i\3\2\2\2\2k\3\2\2\2\2m\3\2\2\2\2o\3\2\2\2\2"+
"q\3\2\2\2\2s\3\2\2\2\2u\3\2\2\2\2w\3\2\2\2\2y\3\2\2\2\2{\3\2\2\2\2}\3"+
"\2\2\2\2\177\3\2\2\2\2\u0081\3\2\2\2\2\u0083\3\2\2\2\2\u0085\3\2\2\2\2"+
"\u0087\3\2\2\2\2\u0089\3\2\2\2\2\u008b\3\2\2\2\2\u008d\3\2\2\2\2\u008f"+
"\3\2\2\2\2\u0091\3\2\2\2\2\u0093\3\2\2\2\2\u0095\3\2\2\2\2\u0097\3\2\2"+
"\2\2\u0099\3\2\2\2\2\u009b\3\2\2\2\2\u009d\3\2\2\2\2\u009f\3\2\2\2\2\u00a1"+
"\3\2\2\2\2\u00a3\3\2\2\2\2\u00a5\3\2\2\2\2\u00a7\3\2\2\2\2\u00a9\3\2\2"+
"\2\2\u00ab\3\2\2\2\2\u00ad\3\2\2\2\2\u00af\3\2\2\2\2\u00b1\3\2\2\2\2\u00b3"+
"\3\2\2\2\2\u00b5\3\2\2\2\2\u00b7\3\2\2\2\2\u00b9\3\2\2\2\2\u00bb\3\2\2"+
"\2\2\u00bd\3\2\2\2\2\u00bf\3\2\2\2\2\u00c1\3\2\2\2\2\u00c3\3\2\2\2\2\u00c5"+
"\3\2\2\2\2\u00c7\3\2\2\2\2\u00c9\3\2\2\2\2\u00cb\3\2\2\2\2\u00cd\3\2\2"+
"\2\2\u00cf\3\2\2\2\2\u00d1\3\2\2\2\2\u00d3\3\2\2\2\2\u00d5\3\2\2\2\2\u00d7"+
"\3\2\2\2\2\u00d9\3\2\2\2\2\u00db\3\2\2\2\2\u00dd\3\2\2\2\2\u00df\3\2\2"+
"\2\2\u00e1\3\2\2\2\2\u00e3\3\2\2\2\2\u00e5\3\2\2\2\2\u00e7\3\2\2\2\2\u00e9"+
"\3\2\2\2\2\u00eb\3\2\2\2\2\u00ed\3\2\2\2\2\u00ef\3\2\2\2\2\u00f1\3\2\2"+
"\2\2\u00f7\3\2\2\2\2\u00f9\3\2\2\2\2\u00fb\3\2\2\2\2\u00fd\3\2\2\2\2\u00ff"+
"\3\2\2\2\3\u0101\3\2\2\2\5\u0103\3\2\2\2\7\u0108\3\2\2\2\t\u0110\3\2\2"+
"\2\13\u011a\3\2\2\2\r\u0124\3\2\2\2\17\u0130\3\2\2\2\21\u0139\3\2\2\2"+
"\23\u0141\3\2\2\2\25\u014d\3\2\2\2\27\u0159\3\2\2\2\31\u0164\3\2\2\2\33"+
"\u016c\3\2\2\2\35\u016e\3\2\2\2\37\u0170\3\2\2\2!\u0176\3\2\2\2#\u017d"+
"\3\2\2\2%\u017f\3\2\2\2\'\u0181\3\2\2\2)\u0187\3\2\2\2+\u018c\3\2\2\2"+
"-\u0192\3\2\2\2/\u0197\3\2\2\2\61\u019d\3\2\2\2\63\u01a1\3\2\2\2\65\u01a7"+
"\3\2\2\2\67\u01a9\3\2\2\29\u01ab\3\2\2\2;\u01ae\3\2\2\2=\u01b1\3\2\2\2"+
"?\u01b4\3\2\2\2A\u01b7\3\2\2\2C\u01bb\3\2\2\2E\u01be\3\2\2\2G\u01c1\3"+
"\2\2\2I\u01c4\3\2\2\2K\u01c7\3\2\2\2M\u01cb\3\2\2\2O\u01cf\3\2\2\2Q\u01d2"+
"\3\2\2\2S\u01d5\3\2\2\2U\u01d7\3\2\2\2W\u01d9\3\2\2\2Y\u01db\3\2\2\2["+
"\u01de\3\2\2\2]\u01e0\3\2\2\2_\u01e2\3\2\2\2a\u01e4\3\2\2\2c\u01e7\3\2"+
"\2\2e\u01ea\3\2\2\2g\u01ec\3\2\2\2i\u01ee\3\2\2\2k\u01f1\3\2\2\2m\u01f4"+
"\3\2\2\2o\u01f7\3\2\2\2q\u01fa\3\2\2\2s\u01fc\3\2\2\2u\u01fe\3\2\2\2w"+
"\u0201\3\2\2\2y\u0206\3\2\2\2{\u020a\3\2\2\2}\u020d\3\2\2\2\177\u0211"+
"\3\2\2\2\u0081\u0215\3\2\2\2\u0083\u0217\3\2\2\2\u0085\u0219\3\2\2\2\u0087"+
"\u021c\3\2\2\2\u0089\u021e\3\2\2\2\u008b\u0225\3\2\2\2\u008d\u022b\3\2"+
"\2\2\u008f\u0234\3\2\2\2\u0091\u0236\3\2\2\2\u0093\u0238\3\2\2\2\u0095"+
"\u023a\3\2\2\2\u0097\u023c\3\2\2\2\u0099\u023f\3\2\2\2\u009b\u0242\3\2"+
"\2\2\u009d\u0245\3\2\2\2\u009f\u0248\3\2\2\2\u00a1\u024b\3\2\2\2\u00a3"+
"\u024e\3\2\2\2\u00a5\u0251\3\2\2\2\u00a7\u0254\3\2\2\2\u00a9\u0259\3\2"+
"\2\2\u00ab\u025f\3\2\2\2\u00ad\u0264\3\2\2\2\u00af\u0268\3\2\2\2\u00b1"+
"\u026b\3\2\2\2\u00b3\u0272\3\2\2\2\u00b5\u0278\3\2\2\2\u00b7\u0281\3\2"+
"\2\2\u00b9\u0284\3\2\2\2\u00bb\u0289\3\2\2\2\u00bd\u028f\3\2\2\2\u00bf"+
"\u0295\3\2\2\2\u00c1\u029b\3\2\2\2\u00c3\u02a0\3\2\2\2\u00c5\u02a6\3\2"+
"\2\2\u00c7\u02ac\3\2\2\2\u00c9\u02b2\3\2\2\2\u00cb\u02b9\3\2\2\2\u00cd"+
"\u02bf\3\2\2\2\u00cf\u02c6\3\2\2\2\u00d1\u02cc\3\2\2\2\u00d3\u02d2\3\2"+
"\2\2\u00d5\u02d6\3\2\2\2\u00d7\u02d9\3\2\2\2\u00d9\u02df\3\2\2\2\u00db"+
"\u02e6\3\2\2\2\u00dd\u02ec\3\2\2\2\u00df\u02f1\3\2\2\2\u00e1\u02fc\3\2"+
"\2\2\u00e3\u0305\3\2\2\2\u00e5\u030a\3\2\2\2\u00e7\u030e\3\2\2\2\u00e9"+
"\u031c\3\2\2\2\u00eb\u031e\3\2\2\2\u00ed\u0324\3\2\2\2\u00ef\u032a\3\2"+
"\2\2\u00f1\u032c\3\2\2\2\u00f3\u0335\3\2\2\2\u00f5\u0345\3\2\2\2\u00f7"+
"\u0347\3\2\2\2\u00f9\u0352\3\2\2\2\u00fb\u035f\3\2\2\2\u00fd\u0367\3\2"+
"\2\2\u00ff\u036b\3\2\2\2\u0101\u0102\7<\2\2\u0102\4\3\2\2\2\u0103\u0104"+
"\7i\2\2\u0104\u0105\7q\2\2\u0105\u0106\7v\2\2\u0106\u0107\7q\2\2\u0107"+
"\6\3\2\2\2\u0108\u0109\7\'\2\2\u0109\u010a\7q\2\2\u010a\u010b\7w\2\2\u010b"+
"\u010c\7v\2\2\u010c\u010d\7r\2\2\u010d\u010e\7w\2\2\u010e\u010f\7v\2\2"+
"\u010f\b\3\2\2\2\u0110\u0111\7\'\2\2\u0111\u0112\7n\2\2\u0112\u0113\7"+
"c\2\2\u0113\u0114\7w\2\2\u0114\u0115\7p\2\2\u0115\u0116\7e\2\2\u0116\u0117"+
"\7j\2\2\u0117\u0118\7g\2\2\u0118\u0119\7t\2\2\u0119\n\3\2\2\2\u011a\u011b"+
"\7\'\2\2\u011b\u011c\7|\2\2\u011c\u011d\7g\2\2\u011d\u011e\7t\2\2\u011e"+
"\u011f\7q\2\2\u011f\u0120\7r\2\2\u0120\u0121\7c\2\2\u0121\u0122\7i\2\2"+
"\u0122\u0123\7g\2\2\u0123\f\3\2\2\2\u0124\u0125\7\'\2\2\u0125\u0126\7"+
"|\2\2\u0126\u0127\7r\2\2\u0127\u0128\7t\2\2\u0128\u0129\7g\2\2\u0129\u012a"+
"\7u\2\2\u012a\u012b\7g\2\2\u012b\u012c\7t\2\2\u012c\u012d\7x\2\2\u012d"+
"\u012e\7g\2\2\u012e\u012f\7f\2\2\u012f\16\3\2\2\2\u0130\u0131\7\'\2\2"+
"\u0131\u0132\7c\2\2\u0132\u0133\7f\2\2\u0133\u0134\7f\2\2\u0134\u0135"+
"\7t\2\2\u0135\u0136\7g\2\2\u0136\u0137\7u\2\2\u0137\u0138\7u\2\2\u0138"+
"\20\3\2\2\2\u0139\u013a\7\'\2\2\u013a\u013b\7k\2\2\u013b\u013c\7o\2\2"+
"\u013c\u013d\7r\2\2\u013d\u013e\7q\2\2\u013e\u013f\7t\2\2\u013f\u0140"+
"\7v\2\2\u0140\22\3\2\2\2\u0141\u0142\7\'\2\2\u0142\u0143\7d\2\2\u0143"+
"\u0144\7t\2\2\u0144\u0145\7g\2\2\u0145\u0146\7c\2\2\u0146\u0147\7m\2\2"+
"\u0147\u0148\7r\2\2\u0148\u0149\7q\2\2\u0149\u014a\7k\2\2\u014a\u014b"+
"\7p\2\2\u014b\u014c\7v\2\2\u014c\24\3\2\2\2\u014d\u014e\7\'\2\2\u014e"+
"\u014f\7c\2\2\u014f\u0150\7u\2\2\u0150\u0151\7o\2\2\u0151\u0152\7k\2\2"+
"\u0152\u0153\7p\2\2\u0153\u0154\7e\2\2\u0154\u0155\7n\2\2\u0155\u0156"+
"\7w\2\2\u0156\u0157\7f\2\2\u0157\u0158\7g\2\2\u0158\26\3\2\2\2\u0159\u015a"+
"\7\'\2\2\u015a\u015b\7c\2\2\u015b\u015c\7u\2\2\u015c\u015d\7o\2\2\u015d"+
"\u015e\7d\2\2\u015e\u015f\7k\2\2\u015f\u0160\7p\2\2\u0160\u0161\7c\2\2"+
"\u0161\u0162\7t\2\2\u0162\u0163\7{\2\2\u0163\30\3\2\2\2\u0164\u0165\7"+
"\'\2\2\u0165\u0166\7q\2\2\u0166\u0167\7r\2\2\u0167\u0168\7v\2\2\u0168"+
"\u0169\7k\2\2\u0169\u016a\7q\2\2\u016a\u016b\7p\2\2\u016b\32\3\2\2\2\u016c"+
"\u016d\7.\2\2\u016d\34\3\2\2\2\u016e\u016f\7?\2\2\u016f\36\3\2\2\2\u0170"+
"\u0171\7e\2\2\u0171\u0172\7q\2\2\u0172\u0173\7p\2\2\u0173\u0174\7u\2\2"+
"\u0174\u0175\7v\2\2\u0175 \3\2\2\2\u0176\u0177\7u\2\2\u0177\u0178\7v\2"+
"\2\u0178\u0179\7t\2\2\u0179\u017a\7w\2\2\u017a\u017b\7e\2\2\u017b\u017c"+
"\7v\2\2\u017c\"\3\2\2\2\u017d\u017e\7}\2\2\u017e$\3\2\2\2\u017f\u0180"+
"\7\177\2\2\u0180&\3\2\2\2\u0181\u0182\7w\2\2\u0182\u0183\7d\2\2\u0183"+
"\u0184\7{\2\2\u0184\u0185\7v\2\2\u0185\u0186\7g\2\2\u0186(\3\2\2\2\u0187"+
"\u0188\7d\2\2\u0188\u0189\7{\2\2\u0189\u018a\7v\2\2\u018a\u018b\7g\2\2"+
"\u018b*\3\2\2\2\u018c\u018d\7w\2\2\u018d\u018e\7y\2\2\u018e\u018f\7q\2"+
"\2\u018f\u0190\7t\2\2\u0190\u0191\7f\2\2\u0191,\3\2\2\2\u0192\u0193\7"+
"y\2\2\u0193\u0194\7q\2\2\u0194\u0195\7t\2\2\u0195\u0196\7f\2\2\u0196."+
"\3\2\2\2\u0197\u0198\7h\2\2\u0198\u0199\7n\2\2\u0199\u019a\7q\2\2\u019a"+
"\u019b\7c\2\2\u019b\u019c\7v\2\2\u019c\60\3\2\2\2\u019d\u019e\7u\2\2\u019e"+
"\u019f\7v\2\2\u019f\u01a0\7t\2\2\u01a0\62\3\2\2\2\u01a1\u01a2\7u\2\2\u01a2"+
"\u01a3\7v\2\2\u01a3\u01a4\7t\2\2\u01a4\u01a5\7a\2\2\u01a5\u01a6\7u\2\2"+
"\u01a6\64\3\2\2\2\u01a7\u01a8\7]\2\2\u01a8\66\3\2\2\2\u01a9\u01aa\7_\2"+
"\2\u01aa8\3\2\2\2\u01ab\u01ac\7-\2\2\u01ac\u01ad\7?\2\2\u01ad:\3\2\2\2"+
"\u01ae\u01af\7/\2\2\u01af\u01b0\7?\2\2\u01b0<\3\2\2\2\u01b1\u01b2\7\61"+
"\2\2\u01b2\u01b3\7?\2\2\u01b3>\3\2\2\2\u01b4\u01b5\7,\2\2\u01b5\u01b6"+
"\7?\2\2\u01b6@\3\2\2\2\u01b7\u01b8\7,\2\2\u01b8\u01b9\7,\2\2\u01b9\u01ba"+
"\7?\2\2\u01baB\3\2\2\2\u01bb\u01bc\7(\2\2\u01bc\u01bd\7?\2\2\u01bdD\3"+
"\2\2\2\u01be\u01bf\7~\2\2\u01bf\u01c0\7?\2\2\u01c0F\3\2\2\2\u01c1\u01c2"+
"\7`\2\2\u01c2\u01c3\7?\2\2\u01c3H\3\2\2\2\u01c4\u01c5\7\'\2\2\u01c5\u01c6"+
"\7?\2\2\u01c6J\3\2\2\2\u01c7\u01c8\7>\2\2\u01c8\u01c9\7>\2\2\u01c9\u01ca"+
"\7?\2\2\u01caL\3\2\2\2\u01cb\u01cc\7@\2\2\u01cc\u01cd\7@\2\2\u01cd\u01ce"+
"\7?\2\2\u01ceN\3\2\2\2\u01cf\u01d0\7-\2\2\u01d0\u01d1\7-\2\2\u01d1P\3"+
"\2\2\2\u01d2\u01d3\7/\2\2\u01d3\u01d4\7/\2\2\u01d4R\3\2\2\2\u01d5\u01d6"+
"\7-\2\2\u01d6T\3\2\2\2\u01d7\u01d8\7/\2\2\u01d8V\3\2\2\2\u01d9\u01da\7"+
"\u0080\2\2\u01daX\3\2\2\2\u01db\u01dc\7,\2\2\u01dc\u01dd\7,\2\2\u01dd"+
"Z\3\2\2\2\u01de\u01df\7,\2\2\u01df\\\3\2\2\2\u01e0\u01e1\7\61\2\2\u01e1"+
"^\3\2\2\2\u01e2\u01e3\7\'\2\2\u01e3`\3\2\2\2\u01e4\u01e5\7>\2\2\u01e5"+
"\u01e6\7>\2\2\u01e6b\3\2\2\2\u01e7\u01e8\7@\2\2\u01e8\u01e9\7@\2\2\u01e9"+
"d\3\2\2\2\u01ea\u01eb\7>\2\2\u01ebf\3\2\2\2\u01ec\u01ed\7@\2\2\u01edh"+
"\3\2\2\2\u01ee\u01ef\7>\2\2\u01ef\u01f0\7?\2\2\u01f0j\3\2\2\2\u01f1\u01f2"+
"\7@\2\2\u01f2\u01f3\7?\2\2\u01f3l\3\2\2\2\u01f4\u01f5\7?\2\2\u01f5\u01f6"+
"\7?\2\2\u01f6n\3\2\2\2\u01f7\u01f8\7#\2\2\u01f8\u01f9\7?\2\2\u01f9p\3"+
"\2\2\2\u01fa\u01fb\7`\2\2\u01fbr\3\2\2\2\u01fc\u01fd\7~\2\2\u01fdt\3\2"+
"\2\2\u01fe\u01ff\7v\2\2\u01ff\u0200\7q\2\2\u0200v\3\2\2\2\u0201\u0202"+
"\7u\2\2\u0202\u0203\7v\2\2\u0203\u0204\7g\2\2\u0204\u0205\7r\2\2\u0205"+
"x\3\2\2\2\u0206\u0207\7c\2\2\u0207\u0208\7p\2\2\u0208\u0209\7f\2\2\u0209"+
"z\3\2\2\2\u020a\u020b\7q\2\2\u020b\u020c\7t\2\2\u020c|\3\2\2\2\u020d\u020e"+
"\7z\2\2\u020e\u020f\7q\2\2\u020f\u0210\7t\2\2\u0210~\3\2\2\2\u0211\u0212"+
"\7p\2\2\u0212\u0213\7q\2\2\u0213\u0214\7v\2\2\u0214\u0080\3\2\2\2\u0215"+
"\u0216\7*\2\2\u0216\u0082\3\2\2\2\u0217\u0218\7+\2\2\u0218\u0084\3\2\2"+
"\2\u0219\u021a\7c\2\2\u021a\u021b\7u\2\2\u021b\u0086\3\2\2\2\u021c\u021d"+
"\7B\2\2\u021d\u0088\3\2\2\2\u021e\u021f\7t\2\2\u021f\u0220\7g\2\2\u0220"+
"\u0221\7v\2\2\u0221\u0222\7w\2\2\u0222\u0223\7t\2\2\u0223\u0224\7p\2\2"+
"\u0224\u008a\3\2\2\2\u0225\u0226\7d\2\2\u0226\u0227\7t\2\2\u0227\u0228"+
"\7g\2\2\u0228\u0229\7c\2\2\u0229\u022a\7m\2\2\u022a\u008c\3\2\2\2\u022b"+
"\u022c\7e\2\2\u022c\u022d\7q\2\2\u022d\u022e\7p\2\2\u022e\u022f\7v\2\2"+
"\u022f\u0230\7k\2\2\u0230\u0231\7p\2\2\u0231\u0232\7w\2\2\u0232\u0233"+
"\7g\2\2\u0233\u008e\3\2\2\2\u0234\u0235\7\60\2\2\u0235\u0090\3\2\2\2\u0236"+
"\u0237\7C\2\2\u0237\u0092\3\2\2\2\u0238\u0239\7Z\2\2\u0239\u0094\3\2\2"+
"\2\u023a\u023b\7[\2\2\u023b\u0096\3\2\2\2\u023c\u023d\7C\2\2\u023d\u023e"+
"\7Z\2\2\u023e\u0098\3\2\2\2\u023f\u0240\7C\2\2\u0240\u0241\7[\2\2\u0241"+
"\u009a\3\2\2\2\u0242\u0243\7Z\2\2\u0243\u0244\7[\2\2\u0244\u009c\3\2\2"+
"\2\u0245\u0246\7R\2\2\u0246\u0247\7e\2\2\u0247\u009e\3\2\2\2\u0248\u0249"+
"\7R\2\2\u0249\u024a\7|\2\2\u024a\u00a0\3\2\2\2\u024b\u024c\7R\2\2\u024c"+
"\u024d\7p\2\2\u024d\u00a2\3\2\2\2\u024e\u024f\7R\2\2\u024f\u0250\7x\2"+
"\2\u0250\u00a4\3\2\2\2\u0251\u0252\7\60\2\2\u0252\u0253\7y\2\2\u0253\u00a6"+
"\3\2\2\2\u0254\u0255\7v\2\2\u0255\u0256\7t\2\2\u0256\u0257\7w\2\2\u0257"+
"\u0258\7g\2\2\u0258\u00a8\3\2\2\2\u0259\u025a\7h\2\2\u025a\u025b\7c\2"+
"\2\u025b\u025c\7n\2\2\u025c\u025d\7u\2\2\u025d\u025e\7g\2\2\u025e\u00aa"+
"\3\2\2\2\u025f\u0260\7\'\2\2\u0260\u0261\7c\2\2\u0261\u0262\7u\2\2\u0262"+
"\u0263\7o\2\2\u0263\u00ac\3\2\2\2\u0264\u0265\7u\2\2\u0265\u0266\7w\2"+
"\2\u0266\u0267\7d\2\2\u0267\u00ae\3\2\2\2\u0268\u0269\7/\2\2\u0269\u026a"+
"\7@\2\2\u026a\u00b0\3\2\2\2\u026b\u026c\7c\2\2\u026c\u026d\7u\2\2\u026d"+
"\u026e\7o\2\2\u026e\u026f\7u\2\2\u026f\u0270\7w\2\2\u0270\u0271\7d\2\2"+
"\u0271\u00b2\3\2\2\2\u0272\u0273\7u\2\2\u0273\u0274\7v\2\2\u0274\u0275"+
"\7c\2\2\u0275\u0276\7e\2\2\u0276\u0277\7m\2\2\u0277\u00b4\3\2\2\2\u0278"+
"\u0279\7e\2\2\u0279\u027a\7n\2\2\u027a\u027b\7q\2\2\u027b\u027c\7d\2\2"+
"\u027c\u027d\7d\2\2\u027d\u027e\7g\2\2\u027e\u027f\7t\2\2\u027f\u0280"+
"\7u\2\2\u0280\u00b6\3\2\2\2\u0281\u0282\7k\2\2\u0282\u0283\7h\2\2\u0283"+
"\u00b8\3\2\2\2\u0284\u0285\7g\2\2\u0285\u0286\7n\2\2\u0286\u0287\7u\2"+
"\2\u0287\u0288\7g\2\2\u0288\u00ba\3\2\2\2\u0289\u028a\7k\2\2\u028a\u028b"+
"\7h\2\2\u028b\u028c\7a\2\2\u028c\u028d\7e\2\2\u028d\u028e\7u\2\2\u028e"+
"\u00bc\3\2\2\2\u028f\u0290\7k\2\2\u0290\u0291\7h\2\2\u0291\u0292\7a\2"+
"\2\u0292\u0293\7e\2\2\u0293\u0294\7e\2\2\u0294\u00be\3\2\2\2\u0295\u0296"+
"\7k\2\2\u0296\u0297\7h\2\2\u0297\u0298\7a\2\2\u0298\u0299\7g\2\2\u0299"+
"\u029a\7s\2\2\u029a\u00c0\3\2\2\2\u029b\u029c\7k\2\2\u029c\u029d\7h\2"+
"\2\u029d\u029e\7a\2\2\u029e\u029f\7|\2\2\u029f\u00c2\3\2\2\2\u02a0\u02a1"+
"\7k\2\2\u02a1\u02a2\7h\2\2\u02a2\u02a3\7a\2\2\u02a3\u02a4\7p\2\2\u02a4"+
"\u02a5\7g\2\2\u02a5\u00c4\3\2\2\2\u02a6\u02a7\7k\2\2\u02a7\u02a8\7h\2"+
"\2\u02a8\u02a9\7a\2\2\u02a9\u02aa\7p\2\2\u02aa\u02ab\7|\2\2\u02ab\u00c6"+
"\3\2\2\2\u02ac\u02ad\7k\2\2\u02ad\u02ae\7h\2\2\u02ae\u02af\7a\2\2\u02af"+
"\u02b0\7r\2\2\u02b0\u02b1\7n\2\2\u02b1\u00c8\3\2\2\2\u02b2\u02b3\7k\2"+
"\2\u02b3\u02b4\7h\2\2\u02b4\u02b5\7a\2\2\u02b5\u02b6\7r\2\2\u02b6\u02b7"+
"\7q\2\2\u02b7\u02b8\7u\2\2\u02b8\u00ca\3\2\2\2\u02b9\u02ba\7k\2\2\u02ba"+
"\u02bb\7h\2\2\u02bb\u02bc\7a\2\2\u02bc\u02bd\7o\2\2\u02bd\u02be\7k\2\2"+
"\u02be\u00cc\3\2\2\2\u02bf\u02c0\7k\2\2\u02c0\u02c1\7h\2\2\u02c1\u02c2"+
"\7a\2\2\u02c2\u02c3\7p\2\2\u02c3\u02c4\7g\2\2\u02c4\u02c5\7i\2\2\u02c5"+
"\u00ce\3\2\2\2\u02c6\u02c7\7k\2\2\u02c7\u02c8\7h\2\2\u02c8\u02c9\7a\2"+
"\2\u02c9\u02ca\7x\2\2\u02ca\u02cb\7u\2\2\u02cb\u00d0\3\2\2\2\u02cc\u02cd"+
"\7k\2\2\u02cd\u02ce\7h\2\2\u02ce\u02cf\7a\2\2\u02cf\u02d0\7x\2\2\u02d0"+
"\u02d1\7e\2\2\u02d1\u00d2\3\2\2\2\u02d2\u02d3\7h\2\2\u02d3\u02d4\7q\2"+
"\2\u02d4\u02d5\7t\2\2\u02d5\u00d4\3\2\2\2\u02d6\u02d7\7k\2\2\u02d7\u02d8"+
"\7p\2\2\u02d8\u00d6\3\2\2\2\u02d9\u02da\7y\2\2\u02da\u02db\7j\2\2\u02db"+
"\u02dc\7k\2\2\u02dc\u02dd\7n\2\2\u02dd\u02de\7g\2\2\u02de\u00d8\3\2\2"+
"\2\u02df\u02e0\7t\2\2\u02e0\u02e1\7g\2\2\u02e1\u02e2\7r\2\2\u02e2\u02e3"+
"\7g\2\2\u02e3\u02e4\7c\2\2\u02e4\u02e5\7v\2\2\u02e5\u00da\3\2\2\2\u02e6"+
"\u02e7\7w\2\2\u02e7\u02e8\7p\2\2\u02e8\u02e9\7v\2\2\u02e9\u02ea\7k\2\2"+
"\u02ea\u02eb\7n\2\2\u02eb\u00dc\3\2\2\2\u02ec\u02ed\7y\2\2\u02ed\u02ee"+
"\7j\2\2\u02ee\u02ef\7g\2\2\u02ef\u02f0\7p\2\2\u02f0\u00de\3\2\2\2\u02f1"+
"\u02f5\t\2\2\2\u02f2\u02f4\t\3\2\2\u02f3\u02f2\3\2\2\2\u02f4\u02f7\3\2"+
"\2\2\u02f5\u02f3\3\2\2\2\u02f5\u02f6\3\2\2\2\u02f6\u02f8\3\2\2\2\u02f7"+
"\u02f5\3\2\2\2\u02f8\u02f9\5\u00e1q\2\u02f9\u02fa\3\2\2\2\u02fa\u02fb"+
"\bp\2\2\u02fb\u00e0\3\2\2\2\u02fc\u0300\7=\2\2\u02fd\u02ff\n\2\2\2\u02fe"+
"\u02fd\3\2\2\2\u02ff\u0302\3\2\2\2\u0300\u02fe\3\2\2\2\u0300\u0301\3\2"+
"\2\2\u0301\u0303\3\2\2\2\u0302\u0300\3\2\2\2\u0303\u0304\bq\2\2\u0304"+
"\u00e2\3\2\2\2\u0305\u0306\t\3\2\2\u0306\u0307\3\2\2\2\u0307\u0308\br"+
"\3\2\u0308\u00e4\3\2\2\2\u0309\u030b\t\2\2\2\u030a\u0309\3\2\2\2\u030b"+
"\u030c\3\2\2\2\u030c\u030a\3\2\2\2\u030c\u030d\3\2\2\2\u030d\u00e6\3\2"+
"\2\2\u030e\u0312\t\4\2\2\u030f\u0311\t\5\2\2\u0310\u030f\3\2\2\2\u0311"+
"\u0314\3\2\2\2\u0312\u0310\3\2\2\2\u0312\u0313\3\2\2\2\u0313\u00e8\3\2"+
"\2\2\u0314\u0312\3\2\2\2\u0315\u031d\4\62;\2\u0316\u0318\4\63;\2\u0317"+
"\u0319\4\62;\2\u0318\u0317\3\2\2\2\u0319\u031a\3\2\2\2\u031a\u0318\3\2"+
"\2\2\u031a\u031b\3\2\2\2\u031b\u031d\3\2\2\2\u031c\u0315\3\2\2\2\u031c"+
"\u0316\3\2\2\2\u031d\u00ea\3\2\2\2\u031e\u0320\7&\2\2\u031f\u0321\t\6"+
"\2\2\u0320\u031f\3\2\2\2\u0321\u0322\3\2\2\2\u0322\u0320\3\2\2\2\u0322"+
"\u0323\3\2\2\2\u0323\u00ec\3\2\2\2\u0324\u0326\7\'\2\2\u0325\u0327\4\62"+
"\63\2\u0326\u0325\3\2\2\2\u0327\u0328\3\2\2\2\u0328\u0326\3\2\2\2\u0328"+
"\u0329\3\2\2\2\u0329\u00ee\3\2\2\2\u032a\u032b\7(\2\2\u032b\u00f0\3\2"+
"\2\2\u032c\u0332\5\u00f3z\2\u032d\u032f\t\7\2\2\u032e\u0330\t\b\2\2\u032f"+
"\u032e\3\2\2\2\u032f\u0330\3\2\2\2\u0330\u0331\3\2\2\2\u0331\u0333\5\u00f3"+
"z\2\u0332\u032d\3\2\2\2\u0332\u0333\3\2\2\2\u0333\u00f2\3\2\2\2\u0334"+
"\u0336\4\62;\2\u0335\u0334\3\2\2\2\u0336\u0337\3\2\2\2\u0337\u0335\3\2"+
"\2\2\u0337\u0338\3\2\2\2\u0338\u033f\3\2\2\2\u0339\u033b\7\60\2\2\u033a"+
"\u033c\4\62;\2\u033b\u033a\3\2\2\2\u033c\u033d\3\2\2\2\u033d\u033b\3\2"+
"\2\2\u033d\u033e\3\2\2\2\u033e\u0340\3\2\2\2\u033f\u0339\3\2\2\2\u033f"+
"\u0340\3\2\2\2\u0340\u00f4\3\2\2\2\u0341\u0342\7^\2\2\u0342\u0346\13\2"+
"\2\2\u0343\u0344\7^\2\2\u0344\u0346\5\u00e5s\2\u0345\u0341\3\2\2\2\u0345"+
"\u0343\3\2\2\2\u0346\u00f6\3\2\2\2\u0347\u034c\7$\2\2\u0348\u034b\5\u00f5"+
"{\2\u0349\u034b\n\t\2\2\u034a\u0348\3\2\2\2\u034a\u0349\3\2\2\2\u034b"+
"\u034e\3\2\2\2\u034c\u034a\3\2\2\2\u034c\u034d\3\2\2\2\u034d\u034f\3\2"+
"\2\2\u034e\u034c\3\2\2\2\u034f\u0350\7$\2\2\u0350\u0351\b|\4\2\u0351\u00f8"+
"\3\2\2\2\u0352\u0353\7}\2\2\u0353\u0354\7}\2\2\u0354\u0356\3\2\2\2\u0355"+
"\u0357\13\2\2\2\u0356\u0355\3\2\2\2\u0357\u0358\3\2\2\2\u0358\u0359\3"+
"\2\2\2\u0358\u0356\3\2\2\2\u0359\u035a\3\2\2\2\u035a\u035b\7\177\2\2\u035b"+
"\u035c\7\177\2\2\u035c\u035d\3\2\2\2\u035d\u035e\b}\5\2\u035e\u00fa\3"+
"\2\2\2\u035f\u0362\7)\2\2\u0360\u0363\5\u00f5{\2\u0361\u0363\n\t\2\2\u0362"+
"\u0360\3\2\2\2\u0362\u0361\3\2\2\2\u0363\u0364\3\2\2\2\u0364\u0365\7)"+
"\2\2\u0365\u0366\b~\6\2\u0366\u00fc\3\2\2\2\u0367\u0368\7B\2\2\u0368\u0369"+
"\7|\2\2\u0369\u036a\7r\2\2\u036a\u00fe\3\2\2\2\u036b\u036c\7]\2\2\u036c"+
"\u036d\7_\2\2\u036d\u0100\3\2\2\2\26\2\u02f5\u0300\u030c\u0312\u031a\u031c"+
"\u0320\u0322\u0328\u032f\u0332\u0337\u033d\u033f\u0345\u034a\u034c\u0358"+
"\u0362\7\2\3\2\b\2\2\3|\2\3}\3\3~\4";
public static final ATN _ATN =
new ATNDeserializer().deserialize(_serializedATN.toCharArray());
static {
_decisionToDFA = new DFA[_ATN.getNumberOfDecisions()];
for (int i = 0; i < _ATN.getNumberOfDecisions(); i++) {
_decisionToDFA[i] = new DFA(_ATN.getDecisionState(i), i);
}
}
}

File diff suppressed because it is too large Load Diff