mirror of
https://github.com/irmen/prog8.git
synced 2025-10-01 03:16:26 +00:00
Compare commits
8 Commits
v12.0-beta
...
master
Author | SHA1 | Date | |
---|---|---|---|
|
87c1bbbf40 | ||
|
a7ad6abdb9 | ||
|
a611406020 | ||
|
75da38224d | ||
|
8d6f3301c8 | ||
|
86b52a1c5e | ||
|
2c8b1c2022 | ||
|
ab1f065752 |
1
.idea/modules.xml
generated
1
.idea/modules.xml
generated
@@ -15,6 +15,7 @@
|
|||||||
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
<module fileurl="file://$PROJECT_DIR$/docs/docs.iml" filepath="$PROJECT_DIR$/docs/docs.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
<module fileurl="file://$PROJECT_DIR$/examples/examples.iml" filepath="$PROJECT_DIR$/examples/examples.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
<module fileurl="file://$PROJECT_DIR$/intermediate/intermediate.iml" filepath="$PROJECT_DIR$/intermediate/intermediate.iml" />
|
||||||
|
<module fileurl="file://$PROJECT_DIR$/languageServer/languageServer.iml" filepath="$PROJECT_DIR$/languageServer/languageServer.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
<module fileurl="file://$PROJECT_DIR$/parser/parser.iml" filepath="$PROJECT_DIR$/parser/parser.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
<module fileurl="file://$PROJECT_DIR$/.idea/modules/prog8.iml" filepath="$PROJECT_DIR$/.idea/modules/prog8.iml" />
|
||||||
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
<module fileurl="file://$PROJECT_DIR$/simpleAst/simpleAst.iml" filepath="$PROJECT_DIR$/simpleAst/simpleAst.iml" />
|
||||||
|
@@ -13,6 +13,7 @@ class CompilationOptions(val output: OutputType,
|
|||||||
val noSysInit: Boolean,
|
val noSysInit: Boolean,
|
||||||
val romable: Boolean,
|
val romable: Boolean,
|
||||||
val compTarget: ICompilationTarget,
|
val compTarget: ICompilationTarget,
|
||||||
|
val compilerVersion: String,
|
||||||
// these are set later, based on command line arguments or options in the source code:
|
// these are set later, based on command line arguments or options in the source code:
|
||||||
var loadAddress: UInt,
|
var loadAddress: UInt,
|
||||||
var memtopAddress: UInt,
|
var memtopAddress: UInt,
|
||||||
|
@@ -31,6 +31,7 @@ class TestCodegen: FunSpec({
|
|||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
|
compilerVersion="99.99",
|
||||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
|
@@ -23,6 +23,7 @@ class TestIRPeepholeOpt: FunSpec({
|
|||||||
noSysInit = true,
|
noSysInit = true,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
|
compilerVersion="99.99",
|
||||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
|
@@ -25,6 +25,7 @@ class TestVmCodeGen: FunSpec({
|
|||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
|
compilerVersion="99.99",
|
||||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
|
@@ -440,7 +440,7 @@ class ConstantFoldingOptimizer(private val program: Program, private val errors:
|
|||||||
val numval = decl.value as? NumericLiteral
|
val numval = decl.value as? NumericLiteral
|
||||||
if(decl.type== VarDeclType.CONST && numval!=null) {
|
if(decl.type== VarDeclType.CONST && numval!=null) {
|
||||||
val valueDt = numval.inferType(program)
|
val valueDt = numval.inferType(program)
|
||||||
if(valueDt issimpletype BaseDataType.LONG) {
|
if(valueDt issimpletype BaseDataType.LONG || decl.datatype.isLong) {
|
||||||
return noModifications // this is handled in the numericalvalue case
|
return noModifications // this is handled in the numericalvalue case
|
||||||
}
|
}
|
||||||
if(!(valueDt istype decl.datatype)) {
|
if(!(valueDt istype decl.datatype)) {
|
||||||
|
@@ -1033,6 +1033,22 @@ _no_msb_size
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
clv
|
||||||
|
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||||
|
bvc +
|
||||||
|
lda #1
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
+ lda #0
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -1209,12 +1225,6 @@ cx16 {
|
|||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -1041,6 +1041,23 @@ _no_msb_size
|
|||||||
pla
|
pla
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
; The SuperCPU expansion for the C64/C128 contains a 65816.
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
clv
|
||||||
|
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||||
|
bvc +
|
||||||
|
lda #1
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
+ lda #0
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -1218,12 +1235,6 @@ cx16 {
|
|||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -231,12 +231,12 @@ asmsub str_w (word value @ AY) clobbers(X) -> str @AY {
|
|||||||
|
|
||||||
; ---- string conversion to numbers -----
|
; ---- string conversion to numbers -----
|
||||||
|
|
||||||
asmsub any2uword(str string @AY) clobbers(Y) -> ubyte @A {
|
asmsub any2uword(str string @AY) -> uword @AY, ubyte @X {
|
||||||
; -- parses a string into a 16 bit unsigned number. String may be in decimal, hex or binary format.
|
; -- parses a string into a 16 bit unsigned number. String may be in decimal, hex or binary format.
|
||||||
; (the latter two require a $ or % prefix to be recognised)
|
; (the latter two require a $ or % prefix to be recognised)
|
||||||
; (any non-digit character will terminate the number string that is parsed)
|
; (any non-digit character will terminate the number string that is parsed)
|
||||||
; returns amount of processed characters in A, and the parsed number will be in cx16.r15.
|
; returns the parsed number word in AY, and the number of processed characters (including the prefix symbol) in X.
|
||||||
; if the string was invalid, 0 will be returned in A.
|
; if the string was invalid, 0 will be returned as count in X (and the word value in AY will be undefined).
|
||||||
%asm {{
|
%asm {{
|
||||||
pha
|
pha
|
||||||
sta P8ZP_SCRATCH_W1
|
sta P8ZP_SCRATCH_W1
|
||||||
@@ -257,13 +257,7 @@ _hex pla
|
|||||||
_bin pla
|
_bin pla
|
||||||
jsr bin2uword
|
jsr bin2uword
|
||||||
_result
|
_result
|
||||||
pha
|
ldx cx16.r15
|
||||||
lda cx16.r15
|
|
||||||
sta P8ZP_SCRATCH_B1 ; result value
|
|
||||||
pla
|
|
||||||
sta cx16.r15
|
|
||||||
sty cx16.r15+1
|
|
||||||
lda P8ZP_SCRATCH_B1
|
|
||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
@@ -1454,22 +1454,6 @@ sub search_x16edit() -> ubyte {
|
|||||||
return 255
|
return 255
|
||||||
}
|
}
|
||||||
|
|
||||||
asmsub cpu_is_65816() -> bool @A {
|
|
||||||
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
%asm {{
|
|
||||||
php
|
|
||||||
clv
|
|
||||||
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
|
||||||
bvc +
|
|
||||||
lda #1
|
|
||||||
plp
|
|
||||||
rts
|
|
||||||
+ lda #0
|
|
||||||
plp
|
|
||||||
rts
|
|
||||||
}}
|
|
||||||
}
|
|
||||||
|
|
||||||
sub set_program_args(str args_ptr, ubyte args_size) {
|
sub set_program_args(str args_ptr, ubyte args_size) {
|
||||||
; -- Set the inter-program arguments.
|
; -- Set the inter-program arguments.
|
||||||
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
; standardized way to pass arguments between programs is in ram bank 0, address $bf00-$bfff.
|
||||||
@@ -2135,6 +2119,21 @@ save_SCRATCH_ZPWORD2 .word ?
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
asmsub cpu_is_65816() -> bool @A {
|
||||||
|
; -- Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
%asm {{
|
||||||
|
php
|
||||||
|
clv
|
||||||
|
.byte $e2, $ea ; SEP #$ea, should be interpreted as 2 NOPs by 6502. 65c816 will set the Overflow flag.
|
||||||
|
bvc +
|
||||||
|
lda #1
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
+ lda #0
|
||||||
|
plp
|
||||||
|
rts
|
||||||
|
}}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -509,6 +509,11 @@ save_SCRATCH_ZPWORD2 .word ?
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -686,11 +691,6 @@ cx16 {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -267,12 +267,15 @@ sub bin2uword(str string) -> uword {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub any2uword(str string) -> uword {
|
sub any2uword(str string) -> uword, ubyte {
|
||||||
; -- convert any number string (any prefix allowed) to uword.
|
; -- convert any number string (any prefix allowed) to uword.
|
||||||
|
; returns the parsed word value, and the number of processed characters (including the prefix symbol)
|
||||||
|
ubyte length
|
||||||
|
while string[length]!=0 length++
|
||||||
when string[0] {
|
when string[0] {
|
||||||
'$' -> return hex2uword(string)
|
'$' -> return hex2uword(string), length
|
||||||
'%' -> return bin2uword(string)
|
'%' -> return bin2uword(string), length
|
||||||
else -> return str2uword(string)
|
else -> return str2uword(string), length
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -244,6 +244,11 @@ sys {
|
|||||||
|
|
||||||
return cx16.r0L
|
return cx16.r0L
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
|
@@ -5,6 +5,7 @@ import prog8.ast.*
|
|||||||
import prog8.ast.expressions.Expression
|
import prog8.ast.expressions.Expression
|
||||||
import prog8.ast.expressions.NumericLiteral
|
import prog8.ast.expressions.NumericLiteral
|
||||||
import prog8.ast.statements.Directive
|
import prog8.ast.statements.Directive
|
||||||
|
import prog8.buildversion.VERSION
|
||||||
import prog8.code.SymbolTable
|
import prog8.code.SymbolTable
|
||||||
import prog8.code.SymbolTableMaker
|
import prog8.code.SymbolTableMaker
|
||||||
import prog8.code.ast.PtProgram
|
import prog8.code.ast.PtProgram
|
||||||
@@ -456,7 +457,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
|
|||||||
return CompilationOptions(
|
return CompilationOptions(
|
||||||
outputType, launcherType,
|
outputType, launcherType,
|
||||||
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, rombale,
|
zpType, zpReserved, zpAllowed, floatsEnabled, noSysInit, rombale,
|
||||||
compTarget, 0u, 0xffffu
|
compTarget, VERSION, 0u, 0xffffu
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -49,9 +49,11 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter,
|
|||||||
val constValue = decl.value!!.constValue(program)!!
|
val constValue = decl.value!!.constValue(program)!!
|
||||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||||
} else {
|
} else {
|
||||||
// don't make it signed if it was unsigned and vice versa
|
// don't make it signed if it was unsigned and vice versa, except when it is a long const declaration
|
||||||
if(valueDt.isSigned && decl.datatype.isUnsigned ||
|
if(!decl.datatype.isLong &&
|
||||||
valueDt.isUnsigned && decl.datatype.isSigned) {
|
(valueDt.isSigned && decl.datatype.isUnsigned ||
|
||||||
|
valueDt.isUnsigned && decl.datatype.isSigned))
|
||||||
|
{
|
||||||
val constValue = decl.value!!.constValue(program)!!
|
val constValue = decl.value!!.constValue(program)!!
|
||||||
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
errors.err("value '${constValue.number}' out of range for ${decl.datatype}", constValue.position)
|
||||||
} else {
|
} else {
|
||||||
|
@@ -21,6 +21,7 @@ class TestGoldenRam: FunSpec({
|
|||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = VMTarget(),
|
compTarget = VMTarget(),
|
||||||
|
compilerVersion="99.99",
|
||||||
loadAddress = 999u,
|
loadAddress = 999u,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
|
@@ -12,7 +12,7 @@ class TestLaunchEmu: FunSpec({
|
|||||||
val target = VMTarget()
|
val target = VMTarget()
|
||||||
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
|
val tmpfile = kotlin.io.path.createTempFile(suffix=".p8ir")
|
||||||
tmpfile.writeText("""<?xml version="1.0" encoding="utf-8"?>
|
tmpfile.writeText("""<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PROGRAM NAME="test">
|
<PROGRAM NAME="test" COMPILERVERSION="99.99">
|
||||||
<OPTIONS>
|
<OPTIONS>
|
||||||
</OPTIONS>
|
</OPTIONS>
|
||||||
|
|
||||||
|
@@ -49,6 +49,7 @@ class TestAbstractZeropage: FunSpec({
|
|||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = C64Target(),
|
compTarget = C64Target(),
|
||||||
|
compilerVersion="99.99",
|
||||||
loadAddress = 999u,
|
loadAddress = 999u,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
@@ -69,7 +70,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
floats = false,
|
floats = false,
|
||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = c64target, loadAddress = 999u, memtopAddress = 0xffffu
|
compTarget = c64target, compilerVersion="99.99", loadAddress = 999u, memtopAddress = 0xffffu
|
||||||
))
|
))
|
||||||
|
|
||||||
var result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
var result = zp.allocate("", DataType.UBYTE, null, null, errors)
|
||||||
@@ -84,33 +85,33 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testZpFloatEnable") {
|
test("testZpFloatEnable") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
var result = zp.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
result.expectError { "should be allocation error due to disabled floats" }
|
result.expectError { "should be allocation error due to disabled floats" }
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
result = zp2.allocate("", DataType.FLOAT, null, null, errors)
|
result = zp2.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
result.expectError { "should be allocation error due to disabled ZP use" }
|
result.expectError { "should be allocation error due to disabled ZP use" }
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp3.allocate("", DataType.FLOAT, null, null, errors)
|
zp3.allocate("", DataType.FLOAT, null, null, errors)
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testZpModesWithFloats") {
|
test("testZpModesWithFloats") {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
shouldThrow<InternalCompilerException> {
|
shouldThrow<InternalCompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
}
|
}
|
||||||
shouldThrow<InternalCompilerException> {
|
shouldThrow<InternalCompilerException> {
|
||||||
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testZpDontuse") {
|
test("testZpDontuse") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.DONTUSE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
println(zp.free)
|
println(zp.free)
|
||||||
zp.availableBytes() shouldBe 0
|
zp.availableBytes() shouldBe 0
|
||||||
val result = zp.allocate("", DataType.BYTE, null, null, errors)
|
val result = zp.allocate("", DataType.BYTE, null, null, errors)
|
||||||
@@ -118,13 +119,13 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testFreeSpacesBytes") {
|
test("testFreeSpacesBytes") {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp1.availableBytes() shouldBe 15
|
zp1.availableBytes() shouldBe 15
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp2.availableBytes() shouldBe 85
|
zp2.availableBytes() shouldBe 85
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp3.availableBytes() shouldBe 94
|
zp3.availableBytes() shouldBe 94
|
||||||
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp4.availableBytes() shouldBe 205
|
zp4.availableBytes() shouldBe 205
|
||||||
zp4.allocate("test", DataType.UBYTE, null, null, errors)
|
zp4.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
zp4.availableBytes() shouldBe 204
|
zp4.availableBytes() shouldBe 204
|
||||||
@@ -133,7 +134,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testReservedSpace") {
|
test("testReservedSpace") {
|
||||||
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp1.availableBytes() shouldBe 205
|
zp1.availableBytes() shouldBe 205
|
||||||
4u shouldNotBeIn zp1.free
|
4u shouldNotBeIn zp1.free
|
||||||
5u shouldNotBeIn zp1.free
|
5u shouldNotBeIn zp1.free
|
||||||
@@ -146,7 +147,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
200u shouldBeIn zp1.free
|
200u shouldBeIn zp1.free
|
||||||
255u shouldBeIn zp1.free
|
255u shouldBeIn zp1.free
|
||||||
199u shouldBeIn zp1.free
|
199u shouldBeIn zp1.free
|
||||||
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp2.availableBytes() shouldBe 105
|
zp2.availableBytes() shouldBe 105
|
||||||
4u shouldNotBeIn zp2.free
|
4u shouldNotBeIn zp2.free
|
||||||
5u shouldNotBeIn zp2.free
|
5u shouldNotBeIn zp2.free
|
||||||
@@ -159,7 +160,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
200u shouldNotBeIn zp2.free
|
200u shouldNotBeIn zp2.free
|
||||||
255u shouldNotBeIn zp2.free
|
255u shouldNotBeIn zp2.free
|
||||||
199u shouldBeIn zp2.free
|
199u shouldBeIn zp2.free
|
||||||
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FLOATSAFE, listOf(50u .. 100u, 200u..255u), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp2.availableBytes() shouldBe 105
|
zp2.availableBytes() shouldBe 105
|
||||||
4u shouldBeIn zp3.free
|
4u shouldBeIn zp3.free
|
||||||
5u shouldBeIn zp3.free
|
5u shouldBeIn zp3.free
|
||||||
@@ -168,7 +169,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testBasicsafeAllocation") {
|
test("testBasicsafeAllocation") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp.availableBytes() shouldBe 15
|
zp.availableBytes() shouldBe 15
|
||||||
zp.hasByteAvailable() shouldBe true
|
zp.hasByteAvailable() shouldBe true
|
||||||
zp.hasWordAvailable() shouldBe true
|
zp.hasWordAvailable() shouldBe true
|
||||||
@@ -190,7 +191,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testFullAllocation") {
|
test("testFullAllocation") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp.availableBytes() shouldBe 205
|
zp.availableBytes() shouldBe 205
|
||||||
zp.hasByteAvailable() shouldBe true
|
zp.hasByteAvailable() shouldBe true
|
||||||
zp.hasWordAvailable() shouldBe true
|
zp.hasWordAvailable() shouldBe true
|
||||||
@@ -222,7 +223,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testEfficientAllocation") {
|
test("testEfficientAllocation") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
zp.availableBytes() shouldBe 15
|
zp.availableBytes() shouldBe 15
|
||||||
zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u
|
zp.allocate("", DataType.WORD, null, null, errors).getOrElse{throw it}.address shouldBe 0x04u
|
||||||
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
zp.allocate("", DataType.UBYTE, null, null, errors).getOrElse{throw it}.address shouldBe 0x06u
|
||||||
@@ -239,7 +240,7 @@ class TestC64Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testReservedLocations") {
|
test("testReservedLocations") {
|
||||||
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, 999u, 0xffffu))
|
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, c64target, "99.99", 999u, 0xffffu))
|
||||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||||
}
|
}
|
||||||
@@ -252,18 +253,18 @@ class TestCx16Zeropage: FunSpec({
|
|||||||
val cx16target = Cx16Target()
|
val cx16target = Cx16Target()
|
||||||
|
|
||||||
test("testReservedLocations") {
|
test("testReservedLocations") {
|
||||||
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
withClue("zp _B1 and _REG must be next to each other to create a word") {
|
||||||
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
zp.SCRATCH_B1 + 1u shouldBe zp.SCRATCH_REG
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
test("testFreeSpacesBytes") {
|
test("testFreeSpacesBytes") {
|
||||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, cx16target, 999u, 0xffffu))
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, true, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
zp1.availableBytes() shouldBe 86
|
zp1.availableBytes() shouldBe 86
|
||||||
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
val zp2 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
zp2.availableBytes() shouldBe 173
|
zp2.availableBytes() shouldBe 173
|
||||||
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
zp3.availableBytes() shouldBe 214
|
zp3.availableBytes() shouldBe 214
|
||||||
zp3.allocate("test", DataType.UBYTE, null, null, errors)
|
zp3.allocate("test", DataType.UBYTE, null, null, errors)
|
||||||
zp3.availableBytes() shouldBe 213
|
zp3.availableBytes() shouldBe 213
|
||||||
@@ -272,7 +273,7 @@ class TestCx16Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("testReservedSpace") {
|
test("testReservedSpace") {
|
||||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
zp1.availableBytes() shouldBe 214
|
zp1.availableBytes() shouldBe 214
|
||||||
0x22u shouldNotBeIn zp1.free
|
0x22u shouldNotBeIn zp1.free
|
||||||
0x23u shouldNotBeIn zp1.free
|
0x23u shouldNotBeIn zp1.free
|
||||||
@@ -284,7 +285,7 @@ class TestCx16Zeropage: FunSpec({
|
|||||||
}
|
}
|
||||||
|
|
||||||
test("preallocated zp vars") {
|
test("preallocated zp vars") {
|
||||||
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, 999u, 0xffffu))
|
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, false, false, cx16target, "99.99", 999u, 0xffffu))
|
||||||
zp1.allocatedVariables["test"] shouldBe null
|
zp1.allocatedVariables["test"] shouldBe null
|
||||||
zp1.allocatedVariables["cx16.r0"] shouldNotBe null
|
zp1.allocatedVariables["cx16.r0"] shouldNotBe null
|
||||||
zp1.allocatedVariables["cx16.r15"] shouldNotBe null
|
zp1.allocatedVariables["cx16.r15"] shouldNotBe null
|
||||||
|
@@ -466,4 +466,15 @@ main {
|
|||||||
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
compileText(C64Target(), false, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
test("const long with small values") {
|
||||||
|
val src="""
|
||||||
|
main {
|
||||||
|
sub start() {
|
||||||
|
const long notkaputt = 42
|
||||||
|
cx16.r0L = notkaputt
|
||||||
|
}
|
||||||
|
}"""
|
||||||
|
compileText(Cx16Target(), true, src, outputDir, writeAssembly = false) shouldNotBe null
|
||||||
|
}
|
||||||
})
|
})
|
||||||
|
@@ -88,7 +88,7 @@ class TestAsmGenSymbols: StringSpec({
|
|||||||
|
|
||||||
fun createTestAsmGen6502(program: Program): AsmGen6502Internal {
|
fun createTestAsmGen6502(program: Program): AsmGen6502Internal {
|
||||||
val errors = ErrorReporterForTests()
|
val errors = ErrorReporterForTests()
|
||||||
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, false, C64Target(), 999u, 0xffffu)
|
val options = CompilationOptions(OutputType.RAW, CbmPrgLauncherType.NONE, ZeropageType.FULL, emptyList(), CompilationOptions.AllZeropageAllowed, false, true, false, C64Target(), "99.99", 999u, 0xffffu)
|
||||||
val astchecker = AstChecker(program, errors, options)
|
val astchecker = AstChecker(program, errors, options)
|
||||||
astchecker.visit(program)
|
astchecker.visit(program)
|
||||||
errors.report()
|
errors.report()
|
||||||
|
@@ -422,10 +422,6 @@ On the other targets, it only contains the definition of the 16 memory-mapped vi
|
|||||||
``restore_virtual_registers()``
|
``restore_virtual_registers()``
|
||||||
restore the values of all 16 virtual registers r0 - r15 from the buffer. Might be useful in an IRQ handler to avoid clobbering them.
|
restore the values of all 16 virtual registers r0 - r15 from the buffer. Might be useful in an IRQ handler to avoid clobbering them.
|
||||||
|
|
||||||
``cpu_is_65816()``
|
|
||||||
Returns true if the CPU in the computer is a 65816, false otherwise (6502 cpu).
|
|
||||||
Note that Prog8 itself has no support yet for this CPU other than detecting its presence.
|
|
||||||
|
|
||||||
``reset_system ()``
|
``reset_system ()``
|
||||||
Soft-reset the system back to initial power-on BASIC prompt. (same as the routine in sys)
|
Soft-reset the system back to initial power-on BASIC prompt. (same as the routine in sys)
|
||||||
|
|
||||||
@@ -1060,6 +1056,10 @@ sys (part of syslib)
|
|||||||
- 128 = Commodore 128
|
- 128 = Commodore 128
|
||||||
- 255 = Virtual machine
|
- 255 = Virtual machine
|
||||||
|
|
||||||
|
``cpu_is_65816()``
|
||||||
|
Returns true if the CPU in the computer is a 65816, false otherwise (6502 cpu).
|
||||||
|
Note that Prog8 itself has no support yet for this CPU other than detecting its presence.
|
||||||
|
|
||||||
``exit (returncode)``
|
``exit (returncode)``
|
||||||
Immediately stops the program and exits it, with the returncode in the A register.
|
Immediately stops the program and exits it, with the returncode in the A register.
|
||||||
Note: custom interrupt handlers remain active unless manually cleared first!
|
Note: custom interrupt handlers remain active unless manually cleared first!
|
||||||
|
@@ -49,7 +49,6 @@ Future Things and Ideas
|
|||||||
|
|
||||||
IR/VM
|
IR/VM
|
||||||
-----
|
-----
|
||||||
- make MSIG instruction set flags and skip cmp #0 afterwards (if msb(x)>0)
|
|
||||||
- is it possible to use LOADFIELD/STOREFIELD instructions even more?
|
- is it possible to use LOADFIELD/STOREFIELD instructions even more?
|
||||||
- make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future
|
- make multiple classes of registers and maybe also categorize by life time , to prepare for better register allocation in the future
|
||||||
SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199)
|
SYSCALL_ARGS, // Reserved for syscall arguments (r99000-99099, r99100-99199)
|
||||||
@@ -75,8 +74,8 @@ IR/VM
|
|||||||
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
|
- change the instruction format so an indirect register (a pointer) can be used more often, at least for the inplace assignment operators that operate on pointer
|
||||||
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
- getting it in shape for code generation...: the IR file should be able to encode every detail about a prog8 program (the VM doesn't have to actually be able to run all of it though!)
|
||||||
- fix call() return value handling (... what's wrong with it again?)
|
- fix call() return value handling (... what's wrong with it again?)
|
||||||
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
|
||||||
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
- proper code gen for the CALLI instruction and that it (optionally) returns a word value that needs to be assigned to a reg
|
||||||
|
- encode asmsub/extsub clobber info in the call , or maybe include these definitions in the p8ir file itself too. (return registers are already encoded in the CALL instruction)
|
||||||
- implement fast code paths for TODO("inplace split....
|
- implement fast code paths for TODO("inplace split....
|
||||||
- implement more TODOs in AssignmentGen
|
- implement more TODOs in AssignmentGen
|
||||||
- do something with the 'split' tag on split word arrays
|
- do something with the 'split' tag on split word arrays
|
||||||
@@ -86,10 +85,10 @@ IR/VM
|
|||||||
don't forget to take into account the data type of the register when it's going to be reused!
|
don't forget to take into account the data type of the register when it's going to be reused!
|
||||||
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
- idea: (but LLVM IR simply keeps the variables, so not a good idea then?...): replace all scalar variables by an allocated register. Keep a table of the variable to register mapping (including the datatype)
|
||||||
global initialization values are simply a list of LOAD instructions.
|
global initialization values are simply a list of LOAD instructions.
|
||||||
Variables replaced include all subroutine parameters! So the only variables that remain as variables are arrays and strings.
|
Variables replaced include all subroutine parameters? Or not? So the only variables that remain as variables are arrays and strings.
|
||||||
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
- the @split arrays are currently also split in _lsb/_msb arrays in the IR, and operations take multiple (byte) instructions that may lead to verbose and slow operation and machine code generation down the line.
|
||||||
maybe another representation is needed once actual codegeneration is done from the IR...?
|
maybe another representation is needed once actual codegeneration is done from the IR...? Should array operations be encoded in a more high level form in the IR?
|
||||||
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change
|
- ExpressionCodeResult: get rid of the separation between single result register and multiple result registers? maybe not, this requires hundreds of lines to change.. :(
|
||||||
- sometimes source lines end up missing in the output p8ir, for example the first assignment is gone in:
|
- sometimes source lines end up missing in the output p8ir, for example the first assignment is gone in:
|
||||||
sub start() {
|
sub start() {
|
||||||
cx16.r0L = cx16.r1 as ubyte
|
cx16.r0L = cx16.r1 as ubyte
|
||||||
@@ -170,4 +169,4 @@ Optimizations
|
|||||||
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
- VariableAllocator: can we think of a smarter strategy for allocating variables into zeropage, rather than first-come-first-served?
|
||||||
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
for instance, vars used inside loops first, then loopvars, then uwords used as pointers (or these first??), then the rest
|
||||||
This will probably need the register categorization from the IR explained there, for the old 6502 codegen there is not enough information to act on
|
This will probably need the register categorization from the IR explained there, for the old 6502 codegen there is not enough information to act on
|
||||||
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, those checks should probably be removed, or be made permanent
|
- various optimizers skip stuff if compTarget.name==VMTarget.NAME. Once 6502-codegen is done from IR code, those checks should probably all be removed, or be made permanent
|
||||||
|
@@ -411,6 +411,11 @@ save_SCRATCH_ZPWORD2 .word ?
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -584,12 +589,6 @@ cx16 {
|
|||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -672,6 +672,10 @@ save_SCRATCH_ZPWORD2 .word ?
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
return false
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -846,11 +850,6 @@ cx16 {
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -344,6 +344,11 @@ save_SCRATCH_ZPWORD2 .word ?
|
|||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub cpu_is_65816() -> bool {
|
||||||
|
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
cx16 {
|
cx16 {
|
||||||
@@ -517,12 +522,6 @@ cx16 {
|
|||||||
rts
|
rts
|
||||||
}}
|
}}
|
||||||
}
|
}
|
||||||
|
|
||||||
sub cpu_is_65816() -> bool {
|
|
||||||
; Returns true when you have a 65816 cpu, false when it's a 6502.
|
|
||||||
return false
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
p8_sys_startup {
|
p8_sys_startup {
|
||||||
|
@@ -1,15 +1,15 @@
|
|||||||
main {
|
main {
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
sprptr[2]^^.y++
|
uword @shared value
|
||||||
}
|
|
||||||
|
|
||||||
struct Sprite {
|
if msb(value)>0
|
||||||
ubyte x
|
cx16.r0++
|
||||||
uword y
|
|
||||||
}
|
|
||||||
|
|
||||||
^^Sprite[4] @shared sprites
|
if lsb(value)>0
|
||||||
^^Sprite @shared sprptr
|
cx16.r0++
|
||||||
|
|
||||||
|
value = mkword(cx16.r0L, cx16.r1L)
|
||||||
|
if_z
|
||||||
|
cx16.r0++
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@@ -3,4 +3,4 @@ org.gradle.console=rich
|
|||||||
org.gradle.parallel=true
|
org.gradle.parallel=true
|
||||||
org.gradle.daemon=true
|
org.gradle.daemon=true
|
||||||
kotlin.code.style=official
|
kotlin.code.style=official
|
||||||
version=12.0-BETA3
|
version=12.0-SNAPSHOT
|
||||||
|
@@ -46,7 +46,8 @@ class IRFileReader {
|
|||||||
val start = reader.nextEvent().asStartElement()
|
val start = reader.nextEvent().asStartElement()
|
||||||
require(start.name.localPart=="PROGRAM") { "missing PROGRAM" }
|
require(start.name.localPart=="PROGRAM") { "missing PROGRAM" }
|
||||||
val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value
|
val programName = start.attributes.asSequence().single { it.name.localPart == "NAME" }.value
|
||||||
val options = parseOptions(reader)
|
val compilerVersion = start.attributes.asSequence().single { it.name.localPart == "COMPILERVERSION" }.value
|
||||||
|
val options = parseOptions(reader, compilerVersion)
|
||||||
val asmsymbols = parseAsmSymbols(reader)
|
val asmsymbols = parseAsmSymbols(reader)
|
||||||
val varsWithoutInitClean = parseVarsWithoutInit("VARIABLESNOINIT", false, reader)
|
val varsWithoutInitClean = parseVarsWithoutInit("VARIABLESNOINIT", false, reader)
|
||||||
val varsWithoutInitDirty = parseVarsWithoutInit("VARIABLESNOINITDIRTY", true, reader)
|
val varsWithoutInitDirty = parseVarsWithoutInit("VARIABLESNOINITDIRTY", true, reader)
|
||||||
@@ -81,7 +82,7 @@ class IRFileReader {
|
|||||||
return program
|
return program
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun parseOptions(reader: XMLEventReader): CompilationOptions {
|
private fun parseOptions(reader: XMLEventReader, compilerVersion: String): CompilationOptions {
|
||||||
skipText(reader)
|
skipText(reader)
|
||||||
val start = reader.nextEvent().asStartElement()
|
val start = reader.nextEvent().asStartElement()
|
||||||
require(start.name.localPart=="OPTIONS") { "missing OPTIONS" }
|
require(start.name.localPart=="OPTIONS") { "missing OPTIONS" }
|
||||||
@@ -136,6 +137,7 @@ class IRFileReader {
|
|||||||
false,
|
false,
|
||||||
romable,
|
romable,
|
||||||
target,
|
target,
|
||||||
|
compilerVersion,
|
||||||
loadAddress,
|
loadAddress,
|
||||||
memtop,
|
memtop,
|
||||||
outputDir = outputDir,
|
outputDir = outputDir,
|
||||||
|
@@ -26,6 +26,7 @@ class IRFileWriter(private val irProgram: IRProgram, outfileOverride: Path?) {
|
|||||||
xml.writeCharacters("\n")
|
xml.writeCharacters("\n")
|
||||||
xml.writeStartElement("PROGRAM")
|
xml.writeStartElement("PROGRAM")
|
||||||
xml.writeAttribute("NAME", irProgram.name)
|
xml.writeAttribute("NAME", irProgram.name)
|
||||||
|
xml.writeAttribute("COMPILERVERSION", irProgram.options.compilerVersion)
|
||||||
xml.writeCharacters("\n")
|
xml.writeCharacters("\n")
|
||||||
writeOptions()
|
writeOptions()
|
||||||
writeAsmSymbols()
|
writeAsmSymbols()
|
||||||
|
@@ -17,11 +17,12 @@ Program to execute is not stored in the system memory, it's just a separate list
|
|||||||
100K virtual floating point registers (64 bits double precision) fr0-fr99999
|
100K virtual floating point registers (64 bits double precision) fr0-fr99999
|
||||||
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
|
65536 bytes of memory. Thus memory pointers (addresses) are limited to 16 bits.
|
||||||
Value stack, max 128 entries of 1 byte each.
|
Value stack, max 128 entries of 1 byte each.
|
||||||
Status flags: Carry, Zero, Negative. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,
|
Status flags: Carry, Zero, Negative, Overflow.
|
||||||
LOAD instructions DO affect the Z and N flags.
|
NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,
|
||||||
INC/DEC/NEG instructions DO affect the Z and N flags,
|
LOAD instructions also DO affect the Z and N flags.
|
||||||
other instructions only affect Z an N flags if the value in a result register is written.
|
INC/DEC/NEG instructions also DO affect the Z and N flags,
|
||||||
See OpcodesThatSetStatusbits
|
other instructions also only affect Z an N flags if the value in a result register is written.
|
||||||
|
See OpcodesThatSetStatusbits.
|
||||||
|
|
||||||
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
|
Instruction set is mostly a load/store architecture, there are few instructions operating on memory directly.
|
||||||
|
|
||||||
@@ -500,7 +501,10 @@ val OpcodesThatSetStatusbitsButNotCarry = arrayOf(
|
|||||||
Opcode.OR,
|
Opcode.OR,
|
||||||
Opcode.XORM,
|
Opcode.XORM,
|
||||||
Opcode.XORR,
|
Opcode.XORR,
|
||||||
Opcode.XOR
|
Opcode.XOR,
|
||||||
|
Opcode.LSIG,
|
||||||
|
Opcode.MSIG,
|
||||||
|
Opcode.CONCAT
|
||||||
)
|
)
|
||||||
|
|
||||||
val OpcodesThatDependOnCarry = arrayOf(
|
val OpcodesThatDependOnCarry = arrayOf(
|
||||||
|
@@ -23,6 +23,7 @@ class TestIRFileInOut: FunSpec({
|
|||||||
noSysInit = true,
|
noSysInit = true,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
|
compilerVersion = "99.99",
|
||||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu,
|
memtopAddress = 0xffffu,
|
||||||
outputDir = tempdir
|
outputDir = tempdir
|
||||||
@@ -32,7 +33,7 @@ class TestIRFileInOut: FunSpec({
|
|||||||
val generatedFile = writer.write()
|
val generatedFile = writer.write()
|
||||||
val lines = generatedFile.readLines()
|
val lines = generatedFile.readLines()
|
||||||
lines[0] shouldBe "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
lines[0] shouldBe "<?xml version=\"1.0\" encoding=\"utf-8\"?>"
|
||||||
lines[1] shouldBe "<PROGRAM NAME=\"unittest-irwriter\">"
|
lines[1] shouldBe "<PROGRAM NAME=\"unittest-irwriter\" COMPILERVERSION=\"99.99\">"
|
||||||
lines.last() shouldBe "</PROGRAM>"
|
lines.last() shouldBe "</PROGRAM>"
|
||||||
generatedFile.deleteExisting()
|
generatedFile.deleteExisting()
|
||||||
lines.size shouldBeGreaterThan 20
|
lines.size shouldBeGreaterThan 20
|
||||||
@@ -40,7 +41,7 @@ class TestIRFileInOut: FunSpec({
|
|||||||
|
|
||||||
test("test IR reader") {
|
test("test IR reader") {
|
||||||
val source="""<?xml version="1.0" encoding="utf-8"?>
|
val source="""<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PROGRAM NAME="test-ir-reader">
|
<PROGRAM NAME="test-ir-reader" COMPILERVERSION="99.99">
|
||||||
<OPTIONS>
|
<OPTIONS>
|
||||||
compTarget=virtual
|
compTarget=virtual
|
||||||
output=PRG
|
output=PRG
|
||||||
|
3
languageServer/README.TXT
Normal file
3
languageServer/README.TXT
Normal file
@@ -0,0 +1,3 @@
|
|||||||
|
This module is a totally not yet working skeleton implementation of a Prog8 LSP server.
|
||||||
|
|
||||||
|
https://en.wikipedia.org/wiki/Language_Server_Protocol
|
105
languageServer/build.gradle.kts
Normal file
105
languageServer/build.gradle.kts
Normal file
@@ -0,0 +1,105 @@
|
|||||||
|
plugins {
|
||||||
|
kotlin("jvm")
|
||||||
|
id("application")
|
||||||
|
}
|
||||||
|
|
||||||
|
val debugPort = 8000
|
||||||
|
val debugArgs = "-agentlib:jdwp=transport=dt_socket,server=y,address=8000,suspend=n,quiet=y"
|
||||||
|
|
||||||
|
val serverMainClassName = "prog8lsp.MainKt"
|
||||||
|
val applicationName = "prog8-language-server"
|
||||||
|
|
||||||
|
application {
|
||||||
|
mainClass.set(serverMainClassName)
|
||||||
|
description = "Code completions, diagnostics and more for Prog8"
|
||||||
|
// applicationDefaultJvmArgs = listOf("-DkotlinLanguageServer.version=$version")
|
||||||
|
applicationDistribution.into("bin") {
|
||||||
|
filePermissions {
|
||||||
|
user {
|
||||||
|
read=true
|
||||||
|
execute=true
|
||||||
|
write=true
|
||||||
|
}
|
||||||
|
other.execute = true
|
||||||
|
group.execute = true
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
repositories {
|
||||||
|
mavenCentral()
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies {
|
||||||
|
implementation("org.eclipse.lsp4j:org.eclipse.lsp4j:0.24.0")
|
||||||
|
implementation("org.eclipse.lsp4j:org.eclipse.lsp4j.jsonrpc:0.24.0")
|
||||||
|
|
||||||
|
// For JSON processing if needed
|
||||||
|
//implementation("com.google.code.gson:gson:2.10.1")
|
||||||
|
|
||||||
|
// For more advanced text processing
|
||||||
|
//implementation("org.apache.commons:commons-lang3:3.12.0")
|
||||||
|
|
||||||
|
implementation(project(":compiler"))
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
configurations.forEach { config ->
|
||||||
|
config.resolutionStrategy {
|
||||||
|
preferProjectModules()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
sourceSets.main {
|
||||||
|
java.srcDir("src")
|
||||||
|
resources.srcDir("resources")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.startScripts {
|
||||||
|
applicationName = "prog8-language-server"
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Exec>("fixFilePermissions") {
|
||||||
|
// When running on macOS or Linux the start script
|
||||||
|
// needs executable permissions to run.
|
||||||
|
|
||||||
|
onlyIf { !System.getProperty("os.name").lowercase().contains("windows") }
|
||||||
|
commandLine("chmod", "+x", "${tasks.installDist.get().destinationDir}/bin/prog8-language-server")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<JavaExec>("debugRun") {
|
||||||
|
mainClass.set(serverMainClassName)
|
||||||
|
classpath(sourceSets.main.get().runtimeClasspath)
|
||||||
|
standardInput = System.`in`
|
||||||
|
|
||||||
|
jvmArgs(debugArgs)
|
||||||
|
doLast {
|
||||||
|
println("Using debug port $debugPort")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<CreateStartScripts>("debugStartScripts") {
|
||||||
|
applicationName = "prog8-language-server"
|
||||||
|
mainClass.set(serverMainClassName)
|
||||||
|
outputDir = tasks.installDist.get().destinationDir.toPath().resolve("bin").toFile()
|
||||||
|
classpath = tasks.startScripts.get().classpath
|
||||||
|
defaultJvmOpts = listOf(debugArgs)
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.register<Sync>("installDebugDist") {
|
||||||
|
dependsOn("installDist")
|
||||||
|
finalizedBy("debugStartScripts")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.withType<Test> {
|
||||||
|
// Disable tests for now since we don't have any
|
||||||
|
enabled = false
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.installDist {
|
||||||
|
finalizedBy("fixFilePermissions")
|
||||||
|
}
|
||||||
|
|
||||||
|
tasks.build {
|
||||||
|
finalizedBy("installDist")
|
||||||
|
}
|
14
languageServer/languageServer.iml
Normal file
14
languageServer/languageServer.iml
Normal file
@@ -0,0 +1,14 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<module type="JAVA_MODULE" version="4">
|
||||||
|
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||||
|
<exclude-output />
|
||||||
|
<content url="file://$MODULE_DIR$">
|
||||||
|
<sourceFolder url="file://$MODULE_DIR$/src" isTestSource="false" />
|
||||||
|
</content>
|
||||||
|
<orderEntry type="inheritedJdk" />
|
||||||
|
<orderEntry type="sourceFolder" forTests="false" />
|
||||||
|
<orderEntry type="library" name="eclipse.lsp4j" level="project" />
|
||||||
|
<orderEntry type="library" name="KotlinJavaRuntime" level="project" />
|
||||||
|
<orderEntry type="module" module-name="compiler" />
|
||||||
|
</component>
|
||||||
|
</module>
|
34
languageServer/src/prog8lsp/AsyncExecutor.kt
Normal file
34
languageServer/src/prog8lsp/AsyncExecutor.kt
Normal file
@@ -0,0 +1,34 @@
|
|||||||
|
package prog8lsp
|
||||||
|
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.concurrent.TimeUnit
|
||||||
|
import java.util.function.Supplier
|
||||||
|
|
||||||
|
private var threadCount = 0
|
||||||
|
|
||||||
|
class AsyncExecutor {
|
||||||
|
private val workerThread = Executors.newSingleThreadExecutor { Thread(it, "async${threadCount++}") }
|
||||||
|
|
||||||
|
fun execute(task: () -> Unit) =
|
||||||
|
CompletableFuture.runAsync(Runnable(task), workerThread)
|
||||||
|
|
||||||
|
fun <R> compute(task: () -> R) =
|
||||||
|
CompletableFuture.supplyAsync(Supplier(task), workerThread)
|
||||||
|
|
||||||
|
fun <R> computeOr(defaultValue: R, task: () -> R?) =
|
||||||
|
CompletableFuture.supplyAsync(Supplier {
|
||||||
|
try {
|
||||||
|
task() ?: defaultValue
|
||||||
|
} catch (e: Exception) {
|
||||||
|
defaultValue
|
||||||
|
}
|
||||||
|
}, workerThread)
|
||||||
|
|
||||||
|
fun shutdown(awaitTermination: Boolean) {
|
||||||
|
workerThread.shutdown()
|
||||||
|
if (awaitTermination) {
|
||||||
|
workerThread.awaitTermination(Long.MAX_VALUE, TimeUnit.DAYS)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
22
languageServer/src/prog8lsp/Main.kt
Normal file
22
languageServer/src/prog8lsp/Main.kt
Normal file
@@ -0,0 +1,22 @@
|
|||||||
|
package prog8lsp
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.launch.LSPLauncher
|
||||||
|
import prog8.buildversion.VERSION
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
import java.util.logging.Level
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
fun main(args: Array<String>) {
|
||||||
|
Logger.getLogger("").level = Level.INFO
|
||||||
|
|
||||||
|
val inStream = System.`in`
|
||||||
|
val outStream = System.out
|
||||||
|
val server = Prog8LanguageServer()
|
||||||
|
val threads = Executors.newCachedThreadPool { Thread(it, "client") }
|
||||||
|
val launcher = LSPLauncher.createServerLauncher(server, inStream, outStream, threads) { it }
|
||||||
|
|
||||||
|
server.connect(launcher.remoteProxy)
|
||||||
|
launcher.startListening()
|
||||||
|
|
||||||
|
println("Prog8 Language Server started. Prog8 version: ${VERSION}")
|
||||||
|
}
|
85
languageServer/src/prog8lsp/Prog8LanguageServer.kt
Normal file
85
languageServer/src/prog8lsp/Prog8LanguageServer.kt
Normal file
@@ -0,0 +1,85 @@
|
|||||||
|
package prog8lsp
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.*
|
||||||
|
import org.eclipse.lsp4j.jsonrpc.messages.Either
|
||||||
|
import org.eclipse.lsp4j.services.*
|
||||||
|
import java.io.Closeable
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.concurrent.CompletableFuture.completedFuture
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
class Prog8LanguageServer: LanguageServer, LanguageClientAware, Closeable {
|
||||||
|
private lateinit var client: LanguageClient
|
||||||
|
private val textDocuments = Prog8TextDocumentService()
|
||||||
|
private val workspaces = Prog8WorkspaceService()
|
||||||
|
private val async = AsyncExecutor()
|
||||||
|
private val logger = Logger.getLogger(Prog8LanguageServer::class.simpleName)
|
||||||
|
|
||||||
|
override fun initialize(params: InitializeParams): CompletableFuture<InitializeResult> = async.compute {
|
||||||
|
logger.info("Initializing LanguageServer")
|
||||||
|
|
||||||
|
val result = InitializeResult()
|
||||||
|
val capabilities = ServerCapabilities()
|
||||||
|
|
||||||
|
// Text document synchronization
|
||||||
|
capabilities.textDocumentSync = Either.forLeft(TextDocumentSyncKind.Full)
|
||||||
|
|
||||||
|
// Completion support
|
||||||
|
val completionOptions = CompletionOptions()
|
||||||
|
completionOptions.resolveProvider = true
|
||||||
|
completionOptions.triggerCharacters = listOf(".", ":")
|
||||||
|
capabilities.completionProvider = completionOptions
|
||||||
|
|
||||||
|
// Document symbol support
|
||||||
|
capabilities.documentSymbolProvider = Either.forLeft(true)
|
||||||
|
|
||||||
|
// Hover support
|
||||||
|
capabilities.hoverProvider = Either.forLeft(true)
|
||||||
|
|
||||||
|
// Definition support
|
||||||
|
capabilities.definitionProvider = Either.forLeft(true)
|
||||||
|
|
||||||
|
// Code action support
|
||||||
|
val codeActionOptions = CodeActionOptions()
|
||||||
|
codeActionOptions.codeActionKinds = listOf(CodeActionKind.QuickFix)
|
||||||
|
capabilities.codeActionProvider = Either.forRight(codeActionOptions)
|
||||||
|
|
||||||
|
// Document formatting support
|
||||||
|
capabilities.documentFormattingProvider = Either.forLeft(true)
|
||||||
|
capabilities.documentRangeFormattingProvider = Either.forLeft(true)
|
||||||
|
|
||||||
|
// Rename support
|
||||||
|
val renameOptions = RenameOptions()
|
||||||
|
renameOptions.prepareProvider = true
|
||||||
|
capabilities.renameProvider = Either.forRight(renameOptions)
|
||||||
|
|
||||||
|
// Workspace symbol support
|
||||||
|
capabilities.workspaceSymbolProvider = Either.forLeft(true)
|
||||||
|
|
||||||
|
result.capabilities = capabilities
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun shutdown(): CompletableFuture<Any> {
|
||||||
|
close()
|
||||||
|
return completedFuture(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun exit() { }
|
||||||
|
|
||||||
|
override fun getTextDocumentService(): TextDocumentService = textDocuments
|
||||||
|
|
||||||
|
override fun getWorkspaceService(): WorkspaceService = workspaces
|
||||||
|
|
||||||
|
override fun connect(client: LanguageClient) {
|
||||||
|
logger.info("connecting to language client")
|
||||||
|
this.client = client
|
||||||
|
workspaces.connect(client)
|
||||||
|
textDocuments.connect(client)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun close() {
|
||||||
|
logger.info("closing down")
|
||||||
|
async.shutdown(awaitTermination = true)
|
||||||
|
}
|
||||||
|
}
|
389
languageServer/src/prog8lsp/Prog8TextDocumentService.kt
Normal file
389
languageServer/src/prog8lsp/Prog8TextDocumentService.kt
Normal file
@@ -0,0 +1,389 @@
|
|||||||
|
package prog8lsp
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.*
|
||||||
|
import org.eclipse.lsp4j.jsonrpc.messages.Either
|
||||||
|
import org.eclipse.lsp4j.services.LanguageClient
|
||||||
|
import org.eclipse.lsp4j.services.TextDocumentService
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.logging.Logger
|
||||||
|
import kotlin.system.measureTimeMillis
|
||||||
|
|
||||||
|
// Document model to maintain in memory
|
||||||
|
data class Prog8Document(
|
||||||
|
val uri: String,
|
||||||
|
var text: String,
|
||||||
|
var version: Int
|
||||||
|
)
|
||||||
|
|
||||||
|
class Prog8TextDocumentService: TextDocumentService {
|
||||||
|
private var client: LanguageClient? = null
|
||||||
|
private val async = AsyncExecutor()
|
||||||
|
private val logger = Logger.getLogger(Prog8TextDocumentService::class.simpleName)
|
||||||
|
|
||||||
|
// In-memory document store
|
||||||
|
private val documents = mutableMapOf<String, Prog8Document>()
|
||||||
|
|
||||||
|
fun connect(client: LanguageClient) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didOpen(params: DidOpenTextDocumentParams) {
|
||||||
|
logger.info("didOpen: ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Create and store document model
|
||||||
|
val document = Prog8Document(
|
||||||
|
uri = params.textDocument.uri,
|
||||||
|
text = params.textDocument.text,
|
||||||
|
version = params.textDocument.version
|
||||||
|
)
|
||||||
|
documents[params.textDocument.uri] = document
|
||||||
|
|
||||||
|
// Trigger diagnostics when a document is opened
|
||||||
|
validateDocument(document)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didChange(params: DidChangeTextDocumentParams) {
|
||||||
|
logger.info("didChange: ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Get the document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Update document version
|
||||||
|
document.version = params.textDocument.version
|
||||||
|
|
||||||
|
// Apply changes to the document text
|
||||||
|
// For simplicity, we're assuming full document sync (TextDocumentSyncKind.Full)
|
||||||
|
// In a real implementation, you might need to handle incremental changes
|
||||||
|
val text = params.contentChanges.firstOrNull()?.text
|
||||||
|
if (text != null) {
|
||||||
|
document.text = text
|
||||||
|
}
|
||||||
|
|
||||||
|
// Trigger diagnostics when a document changes
|
||||||
|
validateDocument(document)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didClose(params: DidCloseTextDocumentParams) {
|
||||||
|
logger.info("didClose: ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Remove document from our store
|
||||||
|
documents.remove(params.textDocument.uri)
|
||||||
|
|
||||||
|
// Clear diagnostics when a document is closed
|
||||||
|
client?.publishDiagnostics(PublishDiagnosticsParams(params.textDocument.uri, listOf()))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didSave(params: DidSaveTextDocumentParams) {
|
||||||
|
logger.info("didSave: ${params.textDocument.uri}")
|
||||||
|
// Handle save events if needed
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun documentSymbol(params: DocumentSymbolParams): CompletableFuture<MutableList<Either<SymbolInformation, DocumentSymbol>>> = async.compute {
|
||||||
|
logger.info("Find symbols in ${params.textDocument.uri}")
|
||||||
|
val result: MutableList<Either<SymbolInformation, DocumentSymbol>>
|
||||||
|
val time = measureTimeMillis {
|
||||||
|
result = mutableListOf()
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Parse document and extract symbols
|
||||||
|
// This is just a placeholder implementation
|
||||||
|
val range = Range(Position(0, 0), Position(0, 10))
|
||||||
|
val selectionRange = Range(Position(0, 0), Position(0, 10))
|
||||||
|
val symbol = DocumentSymbol("exampleSymbol", SymbolKind.Function, range, selectionRange)
|
||||||
|
result.add(Either.forRight(symbol))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.info("Finished in $time ms")
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun completion(params: CompletionParams): CompletableFuture<Either<MutableList<CompletionItem>, CompletionList>> = async.compute {
|
||||||
|
logger.info("Completion for ${params.textDocument.uri} at ${params.position}")
|
||||||
|
val result: Either<MutableList<CompletionItem>, CompletionList>
|
||||||
|
val time = measureTimeMillis {
|
||||||
|
val items = mutableListOf<CompletionItem>()
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Implement actual completion logic based on context
|
||||||
|
// This is just a placeholder implementation
|
||||||
|
val printItem = CompletionItem("print")
|
||||||
|
printItem.kind = CompletionItemKind.Function
|
||||||
|
printItem.detail = "Print text to console"
|
||||||
|
printItem.documentation = Either.forLeft("Outputs the given text to the console")
|
||||||
|
|
||||||
|
val forItem = CompletionItem("for")
|
||||||
|
forItem.kind = CompletionItemKind.Keyword
|
||||||
|
forItem.detail = "For loop"
|
||||||
|
forItem.documentation = Either.forLeft("Iterates over a range or collection")
|
||||||
|
|
||||||
|
val ifItem = CompletionItem("if")
|
||||||
|
ifItem.kind = CompletionItemKind.Keyword
|
||||||
|
ifItem.detail = "Conditional statement"
|
||||||
|
ifItem.documentation = Either.forLeft("Executes code based on a condition")
|
||||||
|
|
||||||
|
items.add(printItem)
|
||||||
|
items.add(forItem)
|
||||||
|
items.add(ifItem)
|
||||||
|
}
|
||||||
|
|
||||||
|
val list = CompletionList(false, items)
|
||||||
|
result = Either.forRight(list)
|
||||||
|
}
|
||||||
|
logger.info("Finished in $time ms")
|
||||||
|
result
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun hover(params: HoverParams): CompletableFuture<Hover?> = async.compute {
|
||||||
|
logger.info("Hover for ${params.textDocument.uri} at ${params.position}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Simple implementation that checks for keywords at the position
|
||||||
|
val keyword = getWordAtPosition(document, params.position)
|
||||||
|
|
||||||
|
when (keyword) {
|
||||||
|
"print" -> {
|
||||||
|
val hover = Hover()
|
||||||
|
hover.contents = Either.forLeft(listOf(Either.forLeft("**print** - Outputs text to the console\n\n```prog8\nprint \"Hello, World!\"\n```")))
|
||||||
|
return@compute hover
|
||||||
|
}
|
||||||
|
"for" -> {
|
||||||
|
val hover = Hover()
|
||||||
|
hover.contents = Either.forLeft(listOf(Either.forLeft("**for** - Loop construct\n\n```prog8\nfor i in 0..10 {\n print i\n}\n```")))
|
||||||
|
return@compute hover
|
||||||
|
}
|
||||||
|
"if" -> {
|
||||||
|
val hover = Hover()
|
||||||
|
hover.contents = Either.forLeft(listOf(Either.forLeft("**if** - Conditional statement\n\n```prog8\nif x > 5 {\n print \"x is greater than 5\"\n}\n```")))
|
||||||
|
return@compute hover
|
||||||
|
}
|
||||||
|
"sub" -> {
|
||||||
|
val hover = Hover()
|
||||||
|
hover.contents = Either.forLeft(listOf(Either.forLeft("**sub** - Defines a subroutine\n\n```prog8\nsub myFunction() {\n print \"Hello from function\"\n}\n```")))
|
||||||
|
return@compute hover
|
||||||
|
}
|
||||||
|
else -> {
|
||||||
|
// Return null for unknown symbols
|
||||||
|
return@compute null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return null if document not found
|
||||||
|
null
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun definition(params: DefinitionParams): CompletableFuture<Either<MutableList<out Location>, MutableList<out LocationLink>>> = async.compute {
|
||||||
|
logger.info("Definition request for ${params.textDocument.uri} at ${params.position}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Implement actual definition lookup
|
||||||
|
// This would involve parsing the document, finding the symbol at the position,
|
||||||
|
// and then finding where that symbol is defined
|
||||||
|
val locations = mutableListOf<Location>()
|
||||||
|
|
||||||
|
// Placeholder implementation
|
||||||
|
// locations.add(Location("file:///path/to/definition.p8", Range(Position(0, 0), Position(0, 10))))
|
||||||
|
|
||||||
|
return@compute Either.forLeft(locations)
|
||||||
|
}
|
||||||
|
|
||||||
|
Either.forLeft(mutableListOf<Location>())
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun formatting(params: DocumentFormattingParams): CompletableFuture<MutableList<out TextEdit>> = async.compute {
|
||||||
|
logger.info("Formatting document ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Implement actual code formatting
|
||||||
|
// This is just a placeholder implementation
|
||||||
|
val edits = mutableListOf<TextEdit>()
|
||||||
|
|
||||||
|
// Example of how you might implement formatting:
|
||||||
|
// 1. Parse the document
|
||||||
|
// 2. Apply formatting rules (indentation, spacing, etc.)
|
||||||
|
// 3. Generate TextEdit objects for the changes
|
||||||
|
|
||||||
|
return@compute edits
|
||||||
|
}
|
||||||
|
|
||||||
|
mutableListOf<TextEdit>()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun rangeFormatting(params: DocumentRangeFormattingParams): CompletableFuture<MutableList<out TextEdit>> = async.compute {
|
||||||
|
logger.info("Range formatting document ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Implement actual code formatting for range
|
||||||
|
// This is just a placeholder implementation
|
||||||
|
val edits = mutableListOf<TextEdit>()
|
||||||
|
|
||||||
|
// Example of how you might implement range formatting:
|
||||||
|
// 1. Parse the document range
|
||||||
|
// 2. Apply formatting rules to the selected range
|
||||||
|
// 3. Generate TextEdit objects for the changes
|
||||||
|
|
||||||
|
return@compute edits
|
||||||
|
}
|
||||||
|
|
||||||
|
mutableListOf<TextEdit>()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun rename(params: RenameParams): CompletableFuture<WorkspaceEdit> = async.compute {
|
||||||
|
logger.info("Rename symbol in ${params.textDocument.uri} at ${params.position}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
// Implement actual rename functionality
|
||||||
|
// This would involve:
|
||||||
|
// 1. Finding all references to the symbol at the given position
|
||||||
|
// 2. Creating TextEdit objects to rename each reference
|
||||||
|
// 3. Adding the edits to a WorkspaceEdit
|
||||||
|
|
||||||
|
return@compute WorkspaceEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
WorkspaceEdit()
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun codeAction(params: CodeActionParams): CompletableFuture<MutableList<Either<Command, CodeAction>>> = async.compute {
|
||||||
|
logger.info("Code actions for ${params.textDocument.uri}")
|
||||||
|
|
||||||
|
// Get document from our store
|
||||||
|
val document = documents[params.textDocument.uri]
|
||||||
|
if (document != null) {
|
||||||
|
val actions = mutableListOf<Either<Command, CodeAction>>()
|
||||||
|
|
||||||
|
// Check diagnostics to provide quick fixes
|
||||||
|
for (diagnostic in params.context.diagnostics) {
|
||||||
|
when (diagnostic.code?.left) {
|
||||||
|
"UnmatchedQuotes" -> {
|
||||||
|
val action = CodeAction()
|
||||||
|
action.title = "Add closing quote"
|
||||||
|
action.kind = CodeActionKind.QuickFix
|
||||||
|
action.diagnostics = listOf(diagnostic)
|
||||||
|
action.isPreferred = true
|
||||||
|
// TODO: Add actual TextEdit to fix the issue
|
||||||
|
actions.add(Either.forRight(action))
|
||||||
|
}
|
||||||
|
"InvalidCharacter" -> {
|
||||||
|
val action = CodeAction()
|
||||||
|
action.title = "Remove invalid characters"
|
||||||
|
action.kind = CodeActionKind.QuickFix
|
||||||
|
action.diagnostics = listOf(diagnostic)
|
||||||
|
// TODO: Add actual TextEdit to fix the issue
|
||||||
|
actions.add(Either.forRight(action))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add some general code actions
|
||||||
|
val organizeImportsAction = CodeAction()
|
||||||
|
organizeImportsAction.title = "Organize imports"
|
||||||
|
organizeImportsAction.kind = CodeActionKind.SourceOrganizeImports
|
||||||
|
actions.add(Either.forRight(organizeImportsAction))
|
||||||
|
|
||||||
|
return@compute actions
|
||||||
|
}
|
||||||
|
|
||||||
|
mutableListOf<Either<Command, CodeAction>>()
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun getWordAtPosition(document: Prog8Document, position: Position): String {
|
||||||
|
// Extract the word at the given position from the document text
|
||||||
|
val lines = document.text.lines()
|
||||||
|
if (position.line < lines.size) {
|
||||||
|
val line = lines[position.line]
|
||||||
|
// Simple word extraction - in a real implementation, you'd want a more robust solution
|
||||||
|
val words = line.split(Regex("\\s+|[^a-zA-Z0-9_]"))
|
||||||
|
var charIndex = 0
|
||||||
|
for (word in words) {
|
||||||
|
if (position.character >= charIndex && position.character <= charIndex + word.length) {
|
||||||
|
return word
|
||||||
|
}
|
||||||
|
charIndex += word.length + 1 // +1 for the separator
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return "" // Default to empty string
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun validateDocument(document: Prog8Document) {
|
||||||
|
logger.info("Validating document: ${document.uri}")
|
||||||
|
val diagnostics = mutableListOf<Diagnostic>()
|
||||||
|
|
||||||
|
// Split text into lines for easier processing
|
||||||
|
val lines = document.text.lines()
|
||||||
|
|
||||||
|
// Check for syntax errors
|
||||||
|
for ((lineNumber, line) in lines.withIndex()) {
|
||||||
|
// Check for unmatched quotes
|
||||||
|
val quoteCount = line.count { it == '"' }
|
||||||
|
if (quoteCount % 2 != 0) {
|
||||||
|
val range = Range(Position(lineNumber, 0), Position(lineNumber, line.length))
|
||||||
|
val diagnostic = Diagnostic(
|
||||||
|
range,
|
||||||
|
"Unmatched quotes",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"prog8-lsp",
|
||||||
|
"UnmatchedQuotes"
|
||||||
|
)
|
||||||
|
diagnostics.add(diagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for invalid characters
|
||||||
|
if (line.contains(Regex("[^\\u0000-\\u007F]"))) {
|
||||||
|
val range = Range(Position(lineNumber, 0), Position(lineNumber, line.length))
|
||||||
|
val diagnostic = Diagnostic(
|
||||||
|
range,
|
||||||
|
"Invalid character found",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"prog8-lsp",
|
||||||
|
"InvalidCharacter"
|
||||||
|
)
|
||||||
|
diagnostics.add(diagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for common Prog8 syntax issues
|
||||||
|
// For example, check if a line starts with a keyword but doesn't follow proper syntax
|
||||||
|
if (line.trim().startsWith("sub ") && !line.contains("(")) {
|
||||||
|
val range = Range(Position(lineNumber, 0), Position(lineNumber, line.length))
|
||||||
|
val diagnostic = Diagnostic(
|
||||||
|
range,
|
||||||
|
"Subroutine declaration missing parentheses",
|
||||||
|
DiagnosticSeverity.Error,
|
||||||
|
"prog8-lsp",
|
||||||
|
"InvalidSubroutine"
|
||||||
|
)
|
||||||
|
diagnostics.add(diagnostic)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check for other issues
|
||||||
|
if (document.text.contains("error")) {
|
||||||
|
val range = Range(Position(0, 0), Position(0, 5))
|
||||||
|
val diagnostic = Diagnostic(
|
||||||
|
range,
|
||||||
|
"This is a sample diagnostic",
|
||||||
|
DiagnosticSeverity.Warning,
|
||||||
|
"prog8-lsp",
|
||||||
|
"SampleDiagnostic"
|
||||||
|
)
|
||||||
|
diagnostics.add(diagnostic)
|
||||||
|
}
|
||||||
|
|
||||||
|
client?.publishDiagnostics(PublishDiagnosticsParams(document.uri, diagnostics))
|
||||||
|
}
|
||||||
|
}
|
89
languageServer/src/prog8lsp/Prog8WorkspaceService.kt
Normal file
89
languageServer/src/prog8lsp/Prog8WorkspaceService.kt
Normal file
@@ -0,0 +1,89 @@
|
|||||||
|
package prog8lsp
|
||||||
|
|
||||||
|
import org.eclipse.lsp4j.*
|
||||||
|
import org.eclipse.lsp4j.jsonrpc.messages.Either
|
||||||
|
import org.eclipse.lsp4j.services.LanguageClient
|
||||||
|
import org.eclipse.lsp4j.services.WorkspaceService
|
||||||
|
import java.util.concurrent.CompletableFuture
|
||||||
|
import java.util.logging.Logger
|
||||||
|
|
||||||
|
class Prog8WorkspaceService: WorkspaceService {
|
||||||
|
private var client: LanguageClient? = null
|
||||||
|
private val logger = Logger.getLogger(Prog8WorkspaceService::class.simpleName)
|
||||||
|
|
||||||
|
fun connect(client: LanguageClient) {
|
||||||
|
this.client = client
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun executeCommand(params: ExecuteCommandParams): CompletableFuture<Any> {
|
||||||
|
logger.info("executeCommand $params")
|
||||||
|
return super.executeCommand(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun symbol(params: WorkspaceSymbolParams): CompletableFuture<Either<MutableList<out SymbolInformation>, MutableList<out WorkspaceSymbol>>> {
|
||||||
|
logger.info("symbol $params")
|
||||||
|
// TODO: Implement workspace symbol search
|
||||||
|
// This is just a placeholder implementation
|
||||||
|
val symbols = mutableListOf<WorkspaceSymbol>()
|
||||||
|
val symbol = WorkspaceSymbol(
|
||||||
|
"workspaceSymbol",
|
||||||
|
SymbolKind.Function,
|
||||||
|
Either.forLeft(Location("file:///example.p8", Range(Position(0, 0), Position(0, 10))))
|
||||||
|
)
|
||||||
|
symbols.add(symbol)
|
||||||
|
return CompletableFuture.completedFuture(Either.forRight(symbols))
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun resolveWorkspaceSymbol(workspaceSymbol: WorkspaceSymbol): CompletableFuture<WorkspaceSymbol> {
|
||||||
|
logger.info("resolveWorkspaceSymbol $workspaceSymbol")
|
||||||
|
return CompletableFuture.completedFuture(workspaceSymbol)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didChangeConfiguration(params: DidChangeConfigurationParams) {
|
||||||
|
logger.info("didChangeConfiguration: $params")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didChangeWatchedFiles(params: DidChangeWatchedFilesParams) {
|
||||||
|
logger.info("didChangeWatchedFiles: $params")
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didChangeWorkspaceFolders(params: DidChangeWorkspaceFoldersParams) {
|
||||||
|
logger.info("didChangeWorkspaceFolders $params")
|
||||||
|
super.didChangeWorkspaceFolders(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willCreateFiles(params: CreateFilesParams): CompletableFuture<WorkspaceEdit> {
|
||||||
|
logger.info("willCreateFiles $params")
|
||||||
|
return super.willCreateFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didCreateFiles(params: CreateFilesParams) {
|
||||||
|
logger.info("didCreateFiles $params")
|
||||||
|
super.didCreateFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willRenameFiles(params: RenameFilesParams): CompletableFuture<WorkspaceEdit> {
|
||||||
|
logger.info("willRenameFiles $params")
|
||||||
|
return super.willRenameFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didRenameFiles(params: RenameFilesParams) {
|
||||||
|
logger.info("didRenameFiles $params")
|
||||||
|
super.didRenameFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun willDeleteFiles(params: DeleteFilesParams): CompletableFuture<WorkspaceEdit> {
|
||||||
|
logger.info("willDeleteFiles $params")
|
||||||
|
return super.willDeleteFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun didDeleteFiles(params: DeleteFilesParams) {
|
||||||
|
logger.info("didDeleteFiles $params")
|
||||||
|
super.didDeleteFiles(params)
|
||||||
|
}
|
||||||
|
|
||||||
|
override fun diagnostic(params: WorkspaceDiagnosticParams): CompletableFuture<WorkspaceDiagnosticReport> {
|
||||||
|
logger.info("diagnostic $params")
|
||||||
|
return super.diagnostic(params)
|
||||||
|
}
|
||||||
|
}
|
@@ -10,5 +10,6 @@ include(
|
|||||||
':codeGenCpu6502',
|
':codeGenCpu6502',
|
||||||
':codeGenExperimental',
|
':codeGenExperimental',
|
||||||
':compiler',
|
':compiler',
|
||||||
':beanshell'
|
':beanshell',
|
||||||
|
':languageServer'
|
||||||
)
|
)
|
||||||
|
@@ -2358,6 +2358,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
IRDataType.BYTE -> {
|
IRDataType.BYTE -> {
|
||||||
val value = registers.getUW(i.reg2!!)
|
val value = registers.getUW(i.reg2!!)
|
||||||
registers.setUB(i.reg1!!, value.toUByte())
|
registers.setUB(i.reg1!!, value.toUByte())
|
||||||
|
statusbitsNZ(value.toInt(), i.type!!)
|
||||||
}
|
}
|
||||||
IRDataType.WORD -> throw IllegalArgumentException("lsig.w not yet supported, requires 32-bits registers")
|
IRDataType.WORD -> throw IllegalArgumentException("lsig.w not yet supported, requires 32-bits registers")
|
||||||
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
@@ -2370,6 +2371,7 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
IRDataType.BYTE -> {
|
IRDataType.BYTE -> {
|
||||||
val value = registers.getUW(i.reg2!!)
|
val value = registers.getUW(i.reg2!!)
|
||||||
val newValue = value.toInt() ushr 8
|
val newValue = value.toInt() ushr 8
|
||||||
|
statusbitsNZ(newValue, i.type!!)
|
||||||
registers.setUB(i.reg1!!, newValue.toUByte())
|
registers.setUB(i.reg1!!, newValue.toUByte())
|
||||||
}
|
}
|
||||||
IRDataType.WORD -> throw IllegalArgumentException("msig.w not yet supported, requires 32-bits registers")
|
IRDataType.WORD -> throw IllegalArgumentException("msig.w not yet supported, requires 32-bits registers")
|
||||||
@@ -2383,7 +2385,9 @@ class VirtualMachine(irProgram: IRProgram) {
|
|||||||
IRDataType.BYTE -> {
|
IRDataType.BYTE -> {
|
||||||
val msb = registers.getUB(i.reg2!!)
|
val msb = registers.getUB(i.reg2!!)
|
||||||
val lsb = registers.getUB(i.reg3!!)
|
val lsb = registers.getUB(i.reg3!!)
|
||||||
registers.setUW(i.reg1!!, ((msb.toInt() shl 8) or lsb.toInt()).toUShort())
|
val value = ((msb.toInt() shl 8) or lsb.toInt())
|
||||||
|
registers.setUW(i.reg1!!, value.toUShort())
|
||||||
|
statusbitsNZ(value.toInt(), i.type!!)
|
||||||
}
|
}
|
||||||
IRDataType.WORD -> throw IllegalArgumentException("concat.w not yet supported, requires 32-bits registers")
|
IRDataType.WORD -> throw IllegalArgumentException("concat.w not yet supported, requires 32-bits registers")
|
||||||
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
IRDataType.FLOAT -> throw IllegalArgumentException("invalid float type for this instruction $i")
|
||||||
|
@@ -24,6 +24,7 @@ class TestVm: FunSpec( {
|
|||||||
noSysInit = false,
|
noSysInit = false,
|
||||||
romable = false,
|
romable = false,
|
||||||
compTarget = target,
|
compTarget = target,
|
||||||
|
compilerVersion ="99.99",
|
||||||
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
loadAddress = target.PROGRAM_LOAD_ADDRESS,
|
||||||
memtopAddress = 0xffffu
|
memtopAddress = 0xffffu
|
||||||
)
|
)
|
||||||
@@ -94,7 +95,7 @@ class TestVm: FunSpec( {
|
|||||||
test("vmrunner") {
|
test("vmrunner") {
|
||||||
val runner = VmRunner()
|
val runner = VmRunner()
|
||||||
val irSource="""<?xml version="1.0" encoding="utf-8"?>
|
val irSource="""<?xml version="1.0" encoding="utf-8"?>
|
||||||
<PROGRAM NAME="test">
|
<PROGRAM NAME="test" COMPILERVERSION="99.99">
|
||||||
<OPTIONS>
|
<OPTIONS>
|
||||||
</OPTIONS>
|
</OPTIONS>
|
||||||
|
|
||||||
|
Reference in New Issue
Block a user