Compare commits

...

16 Commits

Author SHA1 Message Date
Irmen de Jong
357be82446 first setup of LSP languageserver 2025-08-01 23:39:10 +02:00
Irmen de Jong
7ea80babfc todo 2025-08-01 23:38:49 +02:00
Irmen de Jong
67fb45a55b don't produce invalid boolean initalization error. Fixes #173 2025-07-26 12:35:23 +02:00
Irmen de Jong
11186f1dbe make sure that the virtual target -emu (vm) only runs the actual .p8ir file (fixes #172)
added emudbg.console_nl()
2025-07-26 11:19:01 +02:00
Irmen de Jong
0116fac201 release 11.4.1 2025-07-24 23:00:10 +02:00
Irmen de Jong
866313209b fixed zp vars 0 initialization 2025-07-24 00:17:31 +02:00
Irmen de Jong
e2901cca1b fix virtual diskio save_raw() 2025-07-21 22:10:50 +02:00
Irmen de Jong
dd7adde387 fix virtual diskio.f_write 2025-07-21 20:52:11 +02:00
Irmen de Jong
2f90c53ad0 started changing libs to typed pointers 2025-07-20 23:59:59 +02:00
Irmen de Jong
720988ae72 proper warnings for using pure builtin functions as a statement (discarding the result)
swallow a defer warning for a very common use case
2025-07-18 22:37:07 +02:00
Irmen de Jong
ea5deeefbd new links to Codebase64 website 2025-07-17 23:06:39 +02:00
Irmen de Jong
054c98da7c add link to extra prog8 compilation targets 2025-07-15 00:17:04 +02:00
Irmen de Jong
a4a1b563aa sdks 2025-07-14 21:54:56 +02:00
Irmen de Jong
7dd64b4f13 fix 'cpa' instruction generated in certain boolean assignment, must be 'cmp' 2025-07-06 22:43:19 +02:00
Irmen de Jong
edc353cc24 more kotlin 2.2 settings 2025-07-06 00:37:15 +02:00
Irmen de Jong
0a16dcafc0 update kotlin and gradle wrapper 2025-07-06 00:01:24 +02:00
47 changed files with 624 additions and 102 deletions

8
.idea/kotlinc.xml generated
View File

@@ -7,13 +7,13 @@
<option name="jvmTarget" value="11" /> <option name="jvmTarget" value="11" />
</component> </component>
<component name="KotlinCommonCompilerArguments"> <component name="KotlinCommonCompilerArguments">
<option name="apiVersion" value="2.1" /> <option name="apiVersion" value="2.2" />
<option name="languageVersion" value="2.1" /> <option name="languageVersion" value="2.2" />
</component> </component>
<component name="KotlinCompilerSettings"> <component name="KotlinCompilerSettings">
<option name="additionalArguments" value="-Xwhen-guards" /> <option name="additionalArguments" value="-Xwhen-guards -jvm-default=no-compatibility" />
</component> </component>
<component name="KotlinJpsPluginSettings"> <component name="KotlinJpsPluginSettings">
<option name="version" value="2.1.10" /> <option name="version" value="2.2.0" />
</component> </component>
</project> </project>

View File

@@ -1,23 +1,23 @@
<component name="libraryTable"> <component name="libraryTable">
<library name="KotlinJavaRuntime" type="repository"> <library name="KotlinJavaRuntime" type="repository">
<properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.1.20" /> <properties maven-id="org.jetbrains.kotlin:kotlin-stdlib-jdk8:2.2.0" />
<CLASSES> <CLASSES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0.jar!/" />
</CLASSES> </CLASSES>
<JAVADOC> <JAVADOC>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-javadoc.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-javadoc.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-javadoc.jar!/" />
</JAVADOC> </JAVADOC>
<SOURCES> <SOURCES>
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.1.20/kotlin-stdlib-jdk8-2.1.20-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk8/2.2.0/kotlin-stdlib-jdk8-2.2.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.1.20/kotlin-stdlib-2.1.20-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib/2.2.0/kotlin-stdlib-2.2.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/annotations/13.0/annotations-13.0-sources.jar!/" />
<root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.1.20/kotlin-stdlib-jdk7-2.1.20-sources.jar!/" /> <root url="jar://$MAVEN_REPOSITORY$/org/jetbrains/kotlin/kotlin-stdlib-jdk7/2.2.0/kotlin-stdlib-jdk7-2.2.0-sources.jar!/" />
</SOURCES> </SOURCES>
</library> </library>
</component> </component>

1
.idea/modules.xml generated
View File

@@ -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" />

View File

@@ -1,9 +1,10 @@
import org.jetbrains.kotlin.gradle.dsl.JvmTarget import org.jetbrains.kotlin.gradle.dsl.JvmTarget
import org.jetbrains.kotlin.gradle.dsl.JvmDefaultMode
import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature import org.jetbrains.kotlin.gradle.internal.config.LanguageFeature
plugins { plugins {
kotlin("jvm") version "2.1.20" kotlin("jvm") version "2.2.0"
} }
allprojects { allprojects {
@@ -18,6 +19,7 @@ allprojects {
compilerOptions { compilerOptions {
freeCompilerArgs = listOf("-Xwhen-guards") freeCompilerArgs = listOf("-Xwhen-guards")
jvmTarget = JvmTarget.JVM_11 jvmTarget = JvmTarget.JVM_11
jvmDefault = JvmDefaultMode.NO_COMPATIBILITY
} }
sourceSets.all { sourceSets.all {
languageSettings { languageSettings {

View File

@@ -3,6 +3,7 @@ package prog8.code.target
import prog8.code.core.* import prog8.code.core.*
import prog8.code.target.encodings.Encoder import prog8.code.target.encodings.Encoder
import java.nio.file.Path import java.nio.file.Path
import kotlin.io.path.extension
import kotlin.io.path.isReadable import kotlin.io.path.isReadable
import kotlin.io.path.name import kotlin.io.path.name
import kotlin.io.path.readText import kotlin.io.path.readText
@@ -73,16 +74,11 @@ class VMTarget: ICompilationTarget,
// to not have external module dependencies in our own module, we launch the virtual machine via reflection // to not have external module dependencies in our own module, we launch the virtual machine via reflection
val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner val vm = Class.forName("prog8.vm.VmRunner").getDeclaredConstructor().newInstance() as IVirtualMachineRunner
val filename = programNameWithPath.name val withExt = if(programNameWithPath.extension=="p8ir") programNameWithPath else programNameWithPath.resolveSibling("${programNameWithPath.name}.p8ir")
if(programNameWithPath.isReadable()) { if(withExt.isReadable())
vm.runProgram(programNameWithPath.readText(), quiet) vm.runProgram(withExt.readText(), quiet)
} else { else
val withExt = programNameWithPath.resolveSibling("$filename.p8ir") throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
if(withExt.isReadable())
vm.runProgram(withExt.readText(), quiet)
else
throw java.nio.file.NoSuchFileException(withExt.name, null, "not a .p8ir file")
}
} }
override fun isIOAddress(address: UInt): Boolean = false override fun isIOAddress(address: UInt): Boolean = false

View File

@@ -328,10 +328,9 @@ internal class ProgramAndVarsGen(
if (initializers.isNotEmpty()) { if (initializers.isNotEmpty()) {
asmgen.out("prog8_init_vars\t.block") asmgen.out("prog8_init_vars\t.block")
initializers.forEach { assign -> initializers.forEach { assign ->
if((assign.value as? PtNumber)?.number != 0.0 || allocator.isZpVar(assign.target.identifier!!.name)) val constvalue = assign.value as? PtNumber
if(constvalue==null || constvalue.number!=0.0 || allocator.isZpVar(assign.target.identifier!!.name))
asmgen.translate(assign) asmgen.translate(assign)
else
throw AssemblyError("non-zp variable should not be initialized to zero; it will be zeroed as part of BSS clear")
// the other variables that should be set to zero are done so as part of the BSS section clear. // the other variables that should be set to zero are done so as part of the BSS section clear.
} }
asmgen.out(" rts\n .bend") asmgen.out(" rts\n .bend")

View File

@@ -2491,8 +2491,9 @@ $endLabel""")
BaseDataType.UBYTE -> { BaseDataType.UBYTE -> {
when(targetDt) { when(targetDt) {
BaseDataType.BOOL -> { BaseDataType.BOOL -> {
val compare = if(regs==RegisterOrPair.A) "cmp" else "cp${regs.toString().lowercase()}"
asmgen.out(""" asmgen.out("""
cp${regs.toString().lowercase()} #0 $compare #0
beq + beq +
ld${regs.toString().lowercase()} #1 ld${regs.toString().lowercase()} #1
+ st${regs.toString().lowercase()} $targetAsmVarName""") + st${regs.toString().lowercase()} $targetAsmVarName""")

View File

@@ -70,12 +70,12 @@ class IRCodeGen(
val initValue = initialization?.value val initValue = initialization?.value
when(initValue){ when(initValue){
is PtBool -> { is PtBool -> {
require(initValue.asInt()!=0) { "boolean var should not be initialized with false, it wil be set to false as part of BSS clear, initializer=$initialization" } require(initValue.asInt()!=0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.asInt().toDouble()) variable.setOnetimeInitNumeric(initValue.asInt().toDouble())
initsToRemove += block to initialization initsToRemove += block to initialization
} }
is PtNumber -> { is PtNumber -> {
require(initValue.number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" } require(initValue.number!=0.0 || variable.zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear, initializer=$initialization" }
variable.setOnetimeInitNumeric(initValue.number) variable.setOnetimeInitNumeric(initValue.number)
initsToRemove += block to initialization initsToRemove += block to initialization
} }

View File

@@ -19,7 +19,7 @@ class StatementOptimizer(private val program: Program,
val functionName = functionCallStatement.target.nameInSource[0] val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in functions.purefunctionNames) { if (functionName in functions.purefunctionNames) {
if("ignore_unused" !in parent.definingBlock.options()) if("ignore_unused" !in parent.definingBlock.options())
errors.info("statement has no effect (function return value is discarded)", functionCallStatement.position) errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer)) return listOf(IAstModification.Remove(functionCallStatement, parent as IStatementContainer))
} }
} }

View File

@@ -376,7 +376,7 @@ hline_filled_right .byte 0, %10000000, %11000000, %11100000, %11110000, %1111
_ormask .byte 128, 64, 32, 16, 8, 4, 2, 1 _ormask .byte 128, 64, 32, 16, 8, 4, 2, 1
; note: this can be even faster if we also have a 320 word x-lookup table, but hey, that's a lot of memory. ; note: this can be even faster if we also have a 320 word x-lookup table, but hey, that's a lot of memory.
; see http://codebase64.org/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics ; see https://codebase64.net/doku.php?id=base:various_techniques_to_calculate_adresses_fast_common_screen_formats_for_pixel_graphics
; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199) ; the y lookup tables encodes this formula: BITMAP_ADDRESS + 320*(py>>3) + (py & 7) (y from 0..199)
; We use the 64tass syntax for range expressions to calculate this table on assembly time. ; We use the 64tass syntax for range expressions to calculate this table on assembly time.

View File

@@ -47,6 +47,12 @@ emudbg {
} }
} }
sub console_nl() {
; write a newline to the debug output console.
; because '\n' gets encoded in the x16 value for it (13) which is different than what the shell expects (10).
console_chrout(10)
}
sub console_chrout(ubyte char) { sub console_chrout(ubyte char) {
; note: make sure the character is in Iso encoding. ; note: make sure the character is in Iso encoding.
if is_emulator() if is_emulator()

View File

@@ -3,7 +3,7 @@
; ;
; some more interesting routines can be found here: ; some more interesting routines can be found here:
; http://6502org.wikidot.com/software-math ; http://6502org.wikidot.com/software-math
; http://codebase64.org/doku.php?id=base:6502_6510_maths ; https://codebase64.net/doku.php?id=base:6502_6510_maths
; https://github.com/TobyLobster/multiply_test ; https://github.com/TobyLobster/multiply_test
; https://github.com/TobyLobster/sqrt_test ; https://github.com/TobyLobster/sqrt_test
@@ -353,7 +353,7 @@ _divisor .word ?
randword .proc randword .proc
; -- 16 bit pseudo random number generator into AY ; -- 16 bit pseudo random number generator into AY
; default seed = $00c2 $1137. NOTE: uses self-modifying code so won't work in ROM (use randword_rom instead) ; default seed = $00c2 $1137. NOTE: uses self-modifying code so won't work in ROM (use randword_rom instead)
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit ; routine from https://codebase64.net/doku.php?id=6502_6510_maths:x_abc_random_number_generator_8_16_bit
inc x1 inc x1
clc clc
x1=*+1 x1=*+1
@@ -377,7 +377,7 @@ b1=*+1
randword_rom .proc randword_rom .proc
; -- 16 bit pseudo random number generator into AY. Can run from ROM. ; -- 16 bit pseudo random number generator into AY. Can run from ROM.
; NOTE: you have to set the initial seed using randseed_rom! (a good default seed = $00c2 $1137) ; NOTE: you have to set the initial seed using randseed_rom! (a good default seed = $00c2 $1137)
; routine from https://codebase64.org/doku.php?id=base:x_abc_random_number_generator_8_16_bit ; routine from https://codebase64.net/doku.php?id=6502_6510_maths:x_abc_random_number_generator_8_16_bit
inc _x1 inc _x1
clc clc
lda _x1 lda _x1

View File

@@ -380,7 +380,7 @@ _quadrant_region_to_direction:
asmsub atan2(ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A { asmsub atan2(ubyte x1 @R0, ubyte y1 @R1, ubyte x2 @R2, ubyte y2 @R3) -> ubyte @A {
;; Calculate the angle, in a 256-degree circle, between two points into A. ;; Calculate the angle, in a 256-degree circle, between two points into A.
;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane! ;; The points (x1, y1) and (x2, y2) have to use *unsigned coordinates only* from the positive quadrant in the carthesian plane!
;; https://www.codebase64.org/doku.php?id=base:8bit_atan2_8-bit_angle ;; http://codebase64.net/doku.php?id=base:8bit_atan2_8-bit_angle
;; This uses 2 large lookup tables so uses a lot of memory but is super fast. ;; This uses 2 large lookup tables so uses a lot of memory but is super fast.
%asm {{ %asm {{

View File

@@ -138,8 +138,8 @@ diskio {
; you can call this multiple times to append more data ; you can call this multiple times to append more data
repeat num_bytes { repeat num_bytes {
%ir {{ %ir {{
loadm.w r0,diskio.f_write.bufferpointer loadm.w r99000,diskio.f_write.bufferpointer
loadi.b r99100,r0 loadi.b r99100,r99000
syscall 55 (r99100.b): r99100.b syscall 55 (r99100.b): r99100.b
storem.b r99100,$ff02 storem.b r99100,$ff02
}} }}
@@ -216,12 +216,12 @@ diskio {
} }
; like save() but omits the 2 byte prg header. ; like save() but omits the 2 byte prg header.
sub save_raw(uword filenameptr, uword startaddress, uword savesize) -> bool { sub save_raw(uword filenameptr, uword start_address, uword savesize) -> bool {
%ir {{ %ir {{
load.b r99100,1 load.b r99100,1
loadm.w r99000,diskio.save.filenameptr loadm.w r99000,diskio.save_raw.filenameptr
loadm.w r99001,diskio.save.start_address loadm.w r99001,diskio.save_raw.start_address
loadm.w r99002,diskio.save.savesize loadm.w r99002,diskio.save_raw.savesize
syscall 42 (r99100.b, r99000.w, r99001.w, r99002.w): r99100.b syscall 42 (r99100.b, r99000.w, r99001.w, r99002.w): r99100.b
returnr.b r99100 returnr.b r99100
}} }}

View File

@@ -468,7 +468,7 @@ private fun processAst(program: Program, errors: IErrorReporter, compilerOptions
errors.report() errors.report()
program.constantFold(errors, compilerOptions) program.constantFold(errors, compilerOptions)
errors.report() errors.report()
program.reorderStatements(errors) program.reorderStatements(compilerOptions, errors)
errors.report() errors.report()
program.desugaring(errors, compilerOptions) program.desugaring(errors, compilerOptions)
errors.report() errors.report()

View File

@@ -1470,6 +1470,17 @@ internal class AstChecker(private val program: Program,
} }
override fun visit(functionCallStatement: FunctionCallStatement) { override fun visit(functionCallStatement: FunctionCallStatement) {
if(functionCallStatement.target.nameInSource.size==1) {
val functionName = functionCallStatement.target.nameInSource[0]
if (functionName in program.builtinFunctions.purefunctionNames) {
if("ignore_unused" !in functionCallStatement.parent.definingBlock.options()) {
errors.warn("statement has no effect (function return value is discarded)", functionCallStatement.position)
return
}
}
}
// most function calls, even to builtin functions, are still regular FunctionCall nodes here. // most function calls, even to builtin functions, are still regular FunctionCall nodes here.
// they get converted to the more specialized node type in BeforeAsmTypecastCleaner // they get converted to the more specialized node type in BeforeAsmTypecastCleaner
val targetStatement = functionCallStatement.target.checkFunctionOrLabelExists(program, functionCallStatement, errors) val targetStatement = functionCallStatement.target.checkFunctionOrLabelExists(program, functionCallStatement, errors)

View File

@@ -20,8 +20,8 @@ internal fun Program.checkValid(errors: IErrorReporter, compilerOptions: Compila
checker.visit(this) checker.visit(this)
} }
internal fun Program.reorderStatements(errors: IErrorReporter) { internal fun Program.reorderStatements(options: CompilationOptions, errors: IErrorReporter) {
val reorder = StatementReorderer(this, errors) val reorder = StatementReorderer(this, options, errors)
reorder.visit(this) reorder.visit(this)
if(errors.noErrors()) { if(errors.noErrors()) {
reorder.applyModifications() reorder.applyModifications()

View File

@@ -165,8 +165,11 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
} }
} }
if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0) if(srcAssign.origin == AssignmentOrigin.VARINIT && srcAssign.parent is Block && srcAssign.value.constValue(program)?.number==0.0) {
throw FatalAstException("should not have a redundant block-level variable=0 assignment; it will be zeroed as part of BSS clear") val zeropages = srcAssign.target.targetIdentifiers().mapNotNull { it.targetVarDecl()?.zeropage }
if(zeropages.any {it==ZeropageWish.NOT_IN_ZEROPAGE})
throw FatalAstException("should not have a redundant block-level variable=0 assignment for a non-ZP variable; it will be zeroed as part of BSS clear")
}
val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT) val assign = PtAssignment(srcAssign.position, srcAssign.origin==AssignmentOrigin.VARINIT)
val multi = srcAssign.target.multi val multi = srcAssign.target.multi

View File

@@ -133,7 +133,7 @@ private fun integrateDefers(subdefers: Map<PtSub, List<PtDefer>>, program: PtPro
is PtNumber, is PtNumber,
is PtRange, is PtRange,
is PtString -> true is PtString -> true
// note that unlike most other times, PtIdentifier IS "complex" this time (it's a variable that might change) is PtIdentifier -> true // actually PtIdentifier IS "complex" this time (it's a variable that might change) but it's kinda annoying to give a warning message for this very common case
else -> false else -> false
} }

View File

@@ -5,13 +5,11 @@ import prog8.ast.expressions.*
import prog8.ast.statements.* import prog8.ast.statements.*
import prog8.ast.walk.AstWalker import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification import prog8.ast.walk.IAstModification
import prog8.code.core.AssociativeOperators import prog8.code.core.*
import prog8.code.core.BaseDataType
import prog8.code.core.DataType
import prog8.code.core.IErrorReporter
internal class StatementReorderer( internal class StatementReorderer(
val program: Program, val program: Program,
val options: CompilationOptions,
val errors: IErrorReporter val errors: IErrorReporter
) : AstWalker() { ) : AstWalker() {
// Reorders the statements in a way the compiler needs. // Reorders the statements in a way the compiler needs.
@@ -114,10 +112,17 @@ internal class StatementReorderer(
} }
private fun canSkipInitializationWith0(decl: VarDecl): Boolean { private fun canSkipInitializationWith0(decl: VarDecl): Boolean {
// if the variable is declared in a block, we can omit the init with 0 because if(decl.parent is Block) {
// the variable will be initialized to zero when the BSS section is cleared as a whole. // if the variable is declared in a block and is NOT in ZEROPAGE, we can omit the init with 0 because
if(decl.parent is Block) // the variable will be initialized to zero when the BSS section is cleared as a whole.
return true if (decl.zeropage == ZeropageWish.NOT_IN_ZEROPAGE)
return true
// block level zp var that is not in zeropage, doesn't have to be cleared (will be done as part of bss clear at startup)
// note: subroutine level var HAS to be cleared because it needs to be zero at every subroutine call!
if (decl.zeropage == ZeropageWish.DONTCARE && options.zeropage == ZeropageType.DONTUSE)
return true
}
// if there is an assignment to the variable below it (regular assign, or For loop), // if there is an assignment to the variable below it (regular assign, or For loop),
// and there is nothing important in between, we can skip the initialization. // and there is nothing important in between, we can skip the initialization.

View File

@@ -5,6 +5,7 @@ import io.kotest.engine.spec.tempdir
import io.kotest.inspectors.shouldForAll import io.kotest.inspectors.shouldForAll
import io.kotest.matchers.shouldBe import io.kotest.matchers.shouldBe
import io.kotest.matchers.shouldNotBe import io.kotest.matchers.shouldNotBe
import io.kotest.matchers.string.shouldContain
import prog8.ast.expressions.NumericLiteral import prog8.ast.expressions.NumericLiteral
import prog8.ast.statements.Assignment import prog8.ast.statements.Assignment
import prog8.ast.statements.FunctionCallStatement import prog8.ast.statements.FunctionCallStatement
@@ -13,6 +14,7 @@ import prog8.code.core.BuiltinFunctions
import prog8.code.core.RegisterOrPair import prog8.code.core.RegisterOrPair
import prog8.code.core.isNumeric import prog8.code.core.isNumeric
import prog8.code.target.Cx16Target import prog8.code.target.Cx16Target
import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
class TestBuiltinFunctions: FunSpec({ class TestBuiltinFunctions: FunSpec({
@@ -103,5 +105,31 @@ main {
compileText(Cx16Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null compileText(Cx16Target(), true, src, outputDir, writeAssembly = true) shouldNotBe null
} }
test("warning for return value discarding of pure functions") {
val src="""
main {
sub start() {
word @shared ww = 2222
abs(ww)
sgn(ww)
sqrt(ww)
min(ww, 0)
max(ww, 0)
clamp(ww, 0, 319)
}
}"""
val errors = ErrorReporterForTests(keepMessagesAfterReporting = true)
compileText(Cx16Target(), true, src, outputDir, errors=errors, writeAssembly = false) shouldNotBe null
errors.warnings.size shouldBe 6
errors.warnings[0] shouldContain "statement has no effect"
errors.warnings[1] shouldContain "statement has no effect"
errors.warnings[2] shouldContain "statement has no effect"
errors.warnings[3] shouldContain "statement has no effect"
errors.warnings[4] shouldContain "statement has no effect"
errors.warnings[5] shouldContain "statement has no effect"
}
}) })

View File

@@ -10,7 +10,12 @@ import prog8.ast.statements.Assignment
import prog8.ast.statements.AssignmentOrigin import prog8.ast.statements.AssignmentOrigin
import prog8.ast.statements.ForLoop import prog8.ast.statements.ForLoop
import prog8.ast.statements.VarDecl import prog8.ast.statements.VarDecl
import prog8.code.ast.PtAssignment
import prog8.code.ast.PtBool
import prog8.code.ast.PtNumber
import prog8.code.target.C64Target import prog8.code.target.C64Target
import prog8.code.target.Cx16Target
import prog8.code.target.VMTarget
import prog8tests.helpers.ErrorReporterForTests import prog8tests.helpers.ErrorReporterForTests
import prog8tests.helpers.compileText import prog8tests.helpers.compileText
@@ -106,6 +111,7 @@ class TestVariables: FunSpec({
test("global var init with array lookup should sometimes be const") { test("global var init with array lookup should sometimes be const") {
val src=""" val src="""
%zeropage dontuse
main { main {
bool[] barray = [true, false, true, false] bool[] barray = [true, false, true, false]
@@ -220,4 +226,98 @@ main {
(st[5] as Assignment).target.identifier?.nameInSource shouldBe listOf("v1") (st[5] as Assignment).target.identifier?.nameInSource shouldBe listOf("v1")
(st[6] as Assignment).target.identifier?.nameInSource shouldBe listOf("v0") (st[6] as Assignment).target.identifier?.nameInSource shouldBe listOf("v0")
} }
test("nondirty zp variables should be explicitly initialized to 0") {
val src="""
main {
ubyte @shared @requirezp zpvar
bool @shared @requirezp zpbool
ubyte @shared @requirezp @dirty dirtyzpvar
bool @shared @requirezp @dirty dirtyzpbool
sub start() {
ubyte @shared @requirezp zpvar2
bool @shared @requirezp zpbool2
ubyte @shared @requirezp @dirty dirtyzpvar2
bool @shared @requirezp @dirty dirtyzpbool2
}
}"""
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = true)!!.codegenAst
val main = result!!.allBlocks().first { it.name=="p8b_main" }
main.children.size shouldBe 7
val zeroassignsglobal = main.children.filterIsInstance<PtAssignment>()
zeroassignsglobal.size shouldBe 2
(zeroassignsglobal[0].value as PtNumber).number shouldBe 0.0
zeroassignsglobal[0].target.identifier!!.name shouldBe "p8b_main.p8v_zpvar"
(zeroassignsglobal[1].value as PtBool).value shouldBe false
zeroassignsglobal[1].target.identifier!!.name shouldBe "p8b_main.p8v_zpbool"
val st = result.entrypoint()!!.children
st.size shouldBe 7
val zeroassigns = st.filterIsInstance<PtAssignment>()
zeroassigns.size shouldBe 2
(zeroassigns[0].value as PtNumber).number shouldBe 0.0
zeroassigns[0].target.identifier!!.name shouldBe "p8b_main.p8s_start.p8v_zpvar2"
(zeroassigns[1].value as PtBool).value shouldBe false
zeroassigns[1].target.identifier!!.name shouldBe "p8b_main.p8s_start.p8v_zpbool2"
compileText(VMTarget(), false, src, outputDir, writeAssembly = true) shouldNotBe null
}
test("nondirty non zp variables in block scope should not be explicitly initialized to 0 (bss clear takes care of it)") {
val src="""
%zeropage dontuse
main {
ubyte @shared v1
ubyte @shared @dirty dv1
sub start() {
ubyte @shared v2
ubyte @shared @dirty dv2
}
}"""
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = true)!!.codegenAst
// block level should not be intialized to 0 (will be done by BSS clear)
val main = result!!.allBlocks().first { it.name=="p8b_main" }
main.children.size shouldBe 3
main.children.any { it is PtAssignment } shouldBe false
// subroutine should be initialized to 0 because that needs to be done on every call to the subroutine
val st = result.entrypoint()!!.children
st.size shouldBe 4
val zeroassign = st.single { it is PtAssignment } as PtAssignment
(zeroassign.value as PtNumber).number shouldBe 0.0
zeroassign.target.identifier!!.name shouldBe "p8b_main.p8s_start.p8v_v2"
}
test("nondirty explicit non zp variables in block scope should not be explicitly initialized to 0 (bss clear takes care of it)") {
val src="""
main {
ubyte @shared @nozp v1
ubyte @shared @dirty @nozp dv1
sub start() {
ubyte @shared @nozp v2
ubyte @shared @dirty @nozp dv2
}
}"""
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = true)!!.codegenAst
// block level should not be intialized to 0 (will be done by BSS clear)
val main = result!!.allBlocks().first { it.name=="p8b_main" }
main.children.size shouldBe 3
main.children.any { it is PtAssignment } shouldBe false
// subroutine should be initialized to 0 because that needs to be done on every call to the subroutine
val st = result.entrypoint()!!.children
st.size shouldBe 4
val zeroassign = st.single { it is PtAssignment } as PtAssignment
(zeroassign.value as PtNumber).number shouldBe 0.0
zeroassign.target.identifier!!.name shouldBe "p8b_main.p8s_start.p8v_v2"
}
}) })

View File

@@ -5,7 +5,7 @@
<content url="file://$MODULE_DIR$"> <content url="file://$MODULE_DIR$">
<excludeFolder url="file://$MODULE_DIR$/build" /> <excludeFolder url="file://$MODULE_DIR$/build" />
</content> </content>
<orderEntry type="jdk" jdkName="Python 3.12" jdkType="Python SDK" /> <orderEntry type="jdk" jdkName="Python 3" jdkType="Python SDK" />
<orderEntry type="sourceFolder" forTests="false" /> <orderEntry type="sourceFolder" forTests="false" />
</component> </component>
</module> </module>

View File

@@ -1,5 +1,6 @@
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.5-SNAPSHOT by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit 0116fac2 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c128.p8 Compiling program import-all-c128.p8

View File

@@ -1,5 +1,6 @@
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.5-SNAPSHOT by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit 0116fac2 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-c64.p8 Compiling program import-all-c64.p8

View File

@@ -1,5 +1,6 @@
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.5-SNAPSHOT by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit 0116fac2 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-cx16.p8 Compiling program import-all-cx16.p8
@@ -262,6 +263,7 @@ emudbg {
&ubyte EMU_RECORD_WAV &ubyte EMU_RECORD_WAV
&ubyte EMU_SAVE_ON_EXIT &ubyte EMU_SAVE_ON_EXIT
console_chrout (ubyte char) console_chrout (ubyte char)
console_nl ()
console_value1 (ubyte value) console_value1 (ubyte value)
console_value2 (ubyte value) console_value2 (ubyte value)
console_write (str isoString) console_write (str isoString)

View File

@@ -1,5 +1,6 @@
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.5-SNAPSHOT by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit 0116fac2 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-pet32.p8 Compiling program import-all-pet32.p8

View File

@@ -1,5 +1,6 @@
Prog8 compiler v11.4 by Irmen de Jong (irmen@razorvine.net) Prog8 compiler v11.5-SNAPSHOT by Irmen de Jong (irmen@razorvine.net)
Prerelease version from git commit 0116fac2 in branch master
This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html This software is licensed under the GNU GPL 3.0, see https://www.gnu.org/licenses/gpl.html
Compiling program import-all-virtual.p8 Compiling program import-all-virtual.p8
@@ -129,7 +130,7 @@ diskio {
rename (uword oldfileptr, uword newfileptr) rename (uword oldfileptr, uword newfileptr)
rmdir (str name) rmdir (str name)
save (uword filenameptr, uword start_address, uword savesize) -> bool save (uword filenameptr, uword start_address, uword savesize) -> bool
save_raw (uword filenameptr, uword startaddress, uword savesize) -> bool save_raw (uword filenameptr, uword start_address, uword savesize) -> bool
status () -> str status () -> str
status_code () -> ubyte status_code () -> ubyte
} }

View File

@@ -27,7 +27,7 @@ of these library modules automatically as required.
.. note:: .. note::
Several algorithms and math routines in Prog8's assembly library files are adapted from Several algorithms and math routines in Prog8's assembly library files are adapted from
code publicly available on https://www.codebase64.org/ code publicly available on https://www.codebase64.net/
.. _builtinfunctions: .. _builtinfunctions:
@@ -488,7 +488,17 @@ emudbg (cx16 only)
X16Emu Emulator debug routines, for Cx16 only. X16Emu Emulator debug routines, for Cx16 only.
Allows you to interface with the emulator's debug routines/registers. Allows you to interface with the emulator's debug routines/registers.
There's stuff like ``is_emulator`` to detect if running in the emulator, There's stuff like ``is_emulator`` to detect if running in the emulator,
and ``console_write`` to write a (iso) string to the emulator's console (stdout) etc. and ``console_write`` to write a (iso) string to the emulator's console (stdout), etc.
*EOL (end of line) character handling:*
Writing ``iso:'\n'`` to the console doesn't produce a proper new line there, because prog8 encodes
the newline to character 13 on the X16 (this is what the X16 uses to print a newline on the screen).
You have to explicitly output a character 10 on the console to see a newline there. You can do that in several ways::
emudbg.console_nl()
emudbg.console_chrout(10)
emudbg.console_write(iso:"hello\x0a")
Read the `emudbg source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/emudbg.p8>`_ Read the `emudbg source code <https://github.com/irmen/prog8/tree/master/compiler/res/prog8lib/cx16/emudbg.p8>`_
to see what's in there. to see what's in there.

View File

@@ -65,6 +65,11 @@ Various things:
and it does it at run time. In this demo a jump table is not only created in the library, and it does it at run time. In this demo a jump table is not only created in the library,
but also in the main program and copied into the library for its use. but also in the main program and copied into the library for its use.
`Additional custom compilation targets (such as VIC-20) <https://github.com/gillham/prog8targets>`_
Various custom targets for Prog8 that are not (yet?) part of the Prog8 examples themselves.
These additional compilation targets may be in varying state of completeness.
Perhaps most recognisable at the time of adding this link, are the various VIC-20 targets.
.. image:: _static/curious.png .. image:: _static/curious.png
:align: center :align: center

View File

@@ -1,6 +1,9 @@
TODO TODO
==== ====
Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up.
STRUCTS: are being developed in their own separate branch for now, called "structs". STRUCTS: are being developed in their own separate branch for now, called "structs".
Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it. Idea is to make it feature complete in the IR/Virtual target, then merge it to master?, and then start building the 6502 code generation for it.
@@ -68,6 +71,7 @@ Libraries
Optimizations Optimizations
------------- -------------
- Since fixing the missing zp-var initialization, programs grew in size again because STZ's reappered. Can we add more intelligent (and correct!) optimizations to remove those STZs that might be redundant again?
- in Identifier: use typedarray of strings instead of listOf? Other places? - in Identifier: use typedarray of strings instead of listOf? Other places?
- Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time - Compilation speed: try to join multiple modifications in 1 result in the AST processors instead of returning it straight away every time
- Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples) - Compare output of some Oscar64 samples to what prog8 does for the equivalent code (see https://github.com/drmortalwombat/OscarTutorials/tree/main and https://github.com/drmortalwombat/oscar64/tree/main/samples)

View File

@@ -1,27 +1,13 @@
main { main {
ubyte @shared @requirezp zpvar
bool @shared @requirezp zpbool
ubyte @shared @requirezp @dirty dirtyzpvar
bool @shared @requirezp @dirty dirtyzpbool
sub start() { sub start() {
uword uw = 9999 ubyte @shared @requirezp zpvar2
word sw = -2222 bool @shared @requirezp zpbool2
ubyte ub = 42 ubyte @shared @requirezp @dirty dirtyzpvar2
byte sb = -99 bool @shared @requirezp @dirty dirtyzpbool2
bool bb = true
cx16.r0 = uw
cx16.r0s = sw
cx16.r0L = ub
cx16.r0H = ub
cx16.r0sL = sb
cx16.r0sH = sb
cx16.r0bL = bb
cx16.r0bH = bb
uw = cx16.r0
sw = cx16.r0s
ub = cx16.r0L
ub = cx16.r0H
sb = cx16.r0sL
sb = cx16.r0sH
bb = cx16.r0bL
bb = cx16.r0bH
} }
} }

View File

@@ -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=11.4 version=11.5-SNAPSHOT

Binary file not shown.

View File

@@ -1,6 +1,6 @@
distributionBase=GRADLE_USER_HOME distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-8.10.2-bin.zip distributionUrl=https\://services.gradle.org/distributions/gradle-8.14.3-bin.zip
networkTimeout=10000 networkTimeout=10000
validateDistributionUrl=true validateDistributionUrl=true
zipStoreBase=GRADLE_USER_HOME zipStoreBase=GRADLE_USER_HOME

9
gradlew vendored
View File

@@ -86,8 +86,7 @@ done
# shellcheck disable=SC2034 # shellcheck disable=SC2034
APP_BASE_NAME=${0##*/} APP_BASE_NAME=${0##*/}
# Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036) # Discard cd standard output in case $CDPATH is set (https://github.com/gradle/gradle/issues/25036)
APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s APP_HOME=$( cd -P "${APP_HOME:-./}" > /dev/null && printf '%s\n' "$PWD" ) || exit
' "$PWD" ) || exit
# Use the maximum available, or set MAX_FD != -1 to use that value. # Use the maximum available, or set MAX_FD != -1 to use that value.
MAX_FD=maximum MAX_FD=maximum
@@ -115,7 +114,7 @@ case "$( uname )" in #(
NONSTOP* ) nonstop=true ;; NONSTOP* ) nonstop=true ;;
esac esac
CLASSPATH=$APP_HOME/gradle/wrapper/gradle-wrapper.jar CLASSPATH="\\\"\\\""
# Determine the Java command to use to start the JVM. # Determine the Java command to use to start the JVM.
@@ -206,7 +205,7 @@ fi
DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"' DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
# Collect all arguments for the java command: # Collect all arguments for the java command:
# * DEFAULT_JVM_OPTS, JAVA_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments, # * DEFAULT_JVM_OPTS, JAVA_OPTS, and optsEnvironmentVar are not allowed to contain shell fragments,
# and any embedded shellness will be escaped. # and any embedded shellness will be escaped.
# * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be # * For example: A user cannot expect ${Hostname} to be expanded, as it is an environment variable and will be
# treated as '${Hostname}' itself on the command line. # treated as '${Hostname}' itself on the command line.
@@ -214,7 +213,7 @@ DEFAULT_JVM_OPTS='"-Xmx64m" "-Xms64m"'
set -- \ set -- \
"-Dorg.gradle.appname=$APP_BASE_NAME" \ "-Dorg.gradle.appname=$APP_BASE_NAME" \
-classpath "$CLASSPATH" \ -classpath "$CLASSPATH" \
org.gradle.wrapper.GradleWrapperMain \ -jar "$APP_HOME/gradle/wrapper/gradle-wrapper.jar" \
"$@" "$@"
# Stop when "xargs" is not available. # Stop when "xargs" is not available.

4
gradlew.bat vendored
View File

@@ -70,11 +70,11 @@ goto fail
:execute :execute
@rem Setup the command line @rem Setup the command line
set CLASSPATH=%APP_HOME%\gradle\wrapper\gradle-wrapper.jar set CLASSPATH=
@rem Execute Gradle @rem Execute Gradle
"%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" org.gradle.wrapper.GradleWrapperMain %* "%JAVA_EXE%" %DEFAULT_JVM_OPTS% %JAVA_OPTS% %GRADLE_OPTS% "-Dorg.gradle.appname=%APP_BASE_NAME%" -classpath "%CLASSPATH%" -jar "%APP_HOME%\gradle\wrapper\gradle-wrapper.jar" %*
:end :end
@rem End local scope for the variables with windows NT shell @rem End local scope for the variables with windows NT shell

View File

@@ -11,8 +11,10 @@ Intermediate Representation instructions for the IR Virtual machine.
Specs of the virtual machine this will run on: Specs of the virtual machine this will run on:
Program to execute is not stored in the system memory, it's just a separate list of instructions. Program to execute is not stored in the system memory, it's just a separate list of instructions.
65536 virtual registers, 16 bits wide, can also be used as 8 bits. r0-r65535 100K virtual registers, 16 bits wide, can also be used as 8 bits. r0-r99999
65536 virtual floating point registers (64 bits double precision) fr0-fr65535 reserved 99000 - 99099 : WORD registers for syscall arguments and response value(s)
reserved 99100 - 99199 : BYTE registers for syscall arguments and response value(s)
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. NOTE: status flags are only affected by the CMP instruction or explicit CLC/SEC,

View File

@@ -0,0 +1,103 @@
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")
}
configurations.forEach { config ->
config.resolutionStrategy {
preferProjectModules()
}
}
sourceSets.main {
java.srcDir("src")
resources.srcDir("resources")
}
sourceSets.test {
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>() {
testLogging {
events("failed")
exceptionFormat = org.gradle.api.tasks.testing.logging.TestExceptionFormat.FULL
}
}
tasks.installDist {
finalizedBy("fixFilePermissions")
}
tasks.build {
finalizedBy("installDist")
}

View File

@@ -0,0 +1,13 @@
<?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" />
</component>
</module>

View 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)
}
}
}

View File

@@ -0,0 +1,19 @@
package prog8lsp
import org.eclipse.lsp4j.launch.LSPLauncher
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.newSingleThreadExecutor { Thread(it, "client") }
val launcher = LSPLauncher.createServerLauncher(server, inStream, outStream, threads) { it }
server.connect(launcher.remoteProxy)
launcher.startListening()
}

View File

@@ -0,0 +1,46 @@
package prog8lsp
import org.eclipse.lsp4j.InitializeParams
import org.eclipse.lsp4j.InitializeResult
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")
InitializeResult()
}
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)
}
}

View File

@@ -0,0 +1,62 @@
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
class Prog8TextDocumentService: TextDocumentService {
private var client: LanguageClient? = null
private val async = AsyncExecutor()
private val logger = Logger.getLogger(Prog8TextDocumentService::class.simpleName)
fun connect(client: LanguageClient) {
this.client = client
}
override fun didOpen(params: DidOpenTextDocumentParams) {
logger.info("didOpen: $params")
}
override fun didChange(params: DidChangeTextDocumentParams) {
logger.info("didChange: $params")
}
override fun didClose(params: DidCloseTextDocumentParams) {
logger.info("didClose: $params")
}
override fun didSave(params: DidSaveTextDocumentParams) {
logger.info("didSave: $params")
}
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()
val range = Range(Position(1,1), Position(1,5))
val selectionRange = Range(Position(1,2), Position(1,10))
val symbol = DocumentSymbol("test-symbolName", SymbolKind.Constant, range, selectionRange)
result.add(Either.forRight(symbol))
}
logger.info("Finished in $time ms")
result
}
override fun completion(position: CompletionParams): CompletableFuture<Either<MutableList<CompletionItem>, CompletionList>> = async.compute{
logger.info("Completion for ${position}")
val result: Either<MutableList<CompletionItem>, CompletionList>
val time = measureTimeMillis {
val list = CompletionList(false, listOf(CompletionItem("test-completionItem")))
result = Either.forRight(list)
}
logger.info("Finished in $time ms")
result
}
// TODO add all other methods that get called.... :P
}

View File

@@ -0,0 +1,80 @@
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")
return super.symbol(params)
}
override fun resolveWorkspaceSymbol(workspaceSymbol: WorkspaceSymbol): CompletableFuture<WorkspaceSymbol> {
logger.info("resolveWorkspaceSymbol $workspaceSymbol")
return super.resolveWorkspaceSymbol(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)
}
}

View File

@@ -10,5 +10,6 @@ include(
':codeGenCpu6502', ':codeGenCpu6502',
':codeGenExperimental', ':codeGenExperimental',
':compiler', ':compiler',
':beanshell' ':beanshell',
':languageServer'
) )

View File

@@ -200,7 +200,7 @@ class StStaticVariable(name: String,
// Certain codegens might want to put them back into the variable directly. // Certain codegens might want to put them back into the variable directly.
// For strings and arrays this doesn't occur - these are always already specced at creation time. // For strings and arrays this doesn't occur - these are always already specced at creation time.
require(number!=0.0) { "variable should not be initialized with 0, it will already be zeroed as part of BSS clear" } require(number!=0.0 || zpwish!=ZeropageWish.NOT_IN_ZEROPAGE) { "non-zp variable should not be initialized with 0, it will already be zeroed as part of BSS clear" }
initializationNumericValue = number initializationNumericValue = number
} }