mirror of
https://github.com/irmen/prog8.git
synced 2025-09-12 00:24:34 +00:00
Compare commits
4 Commits
v11.4.1
...
languageSe
Author | SHA1 | Date | |
---|---|---|---|
|
357be82446 | ||
|
7ea80babfc | ||
|
67fb45a55b | ||
|
11186f1dbe |
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" />
|
||||||
|
@@ -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
|
||||||
|
@@ -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.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" }
|
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
|
||||||
}
|
}
|
||||||
|
@@ -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()
|
||||||
|
@@ -11,9 +11,11 @@ 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.PtAssignment
|
||||||
|
import prog8.code.ast.PtBool
|
||||||
import prog8.code.ast.PtNumber
|
import prog8.code.ast.PtNumber
|
||||||
import prog8.code.target.C64Target
|
import prog8.code.target.C64Target
|
||||||
import prog8.code.target.Cx16Target
|
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
|
||||||
|
|
||||||
@@ -229,27 +231,40 @@ main {
|
|||||||
val src="""
|
val src="""
|
||||||
main {
|
main {
|
||||||
ubyte @shared @requirezp zpvar
|
ubyte @shared @requirezp zpvar
|
||||||
|
bool @shared @requirezp zpbool
|
||||||
ubyte @shared @requirezp @dirty dirtyzpvar
|
ubyte @shared @requirezp @dirty dirtyzpvar
|
||||||
|
bool @shared @requirezp @dirty dirtyzpbool
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
ubyte @shared @requirezp zpvar2
|
ubyte @shared @requirezp zpvar2
|
||||||
|
bool @shared @requirezp zpbool2
|
||||||
ubyte @shared @requirezp @dirty dirtyzpvar2
|
ubyte @shared @requirezp @dirty dirtyzpvar2
|
||||||
|
bool @shared @requirezp @dirty dirtyzpbool2
|
||||||
}
|
}
|
||||||
}"""
|
}"""
|
||||||
|
|
||||||
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = true)!!.codegenAst
|
val result = compileText(Cx16Target(), false, src, outputDir, writeAssembly = true)!!.codegenAst
|
||||||
|
|
||||||
val main = result!!.allBlocks().first { it.name=="p8b_main" }
|
val main = result!!.allBlocks().first { it.name=="p8b_main" }
|
||||||
main.children.size shouldBe 4
|
main.children.size shouldBe 7
|
||||||
val zeroassignlobal = main.children.single { it is PtAssignment } as PtAssignment
|
val zeroassignsglobal = main.children.filterIsInstance<PtAssignment>()
|
||||||
(zeroassignlobal.value as PtNumber).number shouldBe 0.0
|
zeroassignsglobal.size shouldBe 2
|
||||||
zeroassignlobal.target.identifier!!.name shouldBe "p8b_main.p8v_zpvar"
|
(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
|
val st = result.entrypoint()!!.children
|
||||||
st.size shouldBe 4
|
st.size shouldBe 7
|
||||||
val zeroassign = st.single { it is PtAssignment } as PtAssignment
|
val zeroassigns = st.filterIsInstance<PtAssignment>()
|
||||||
(zeroassign.value as PtNumber).number shouldBe 0.0
|
zeroassigns.size shouldBe 2
|
||||||
zeroassign.target.identifier!!.name shouldBe "p8b_main.p8s_start.p8v_zpvar2"
|
(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)") {
|
test("nondirty non zp variables in block scope should not be explicitly initialized to 0 (bss clear takes care of it)") {
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
Prog8 compiler v11.4.1 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
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
Prog8 compiler v11.4.1 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
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
Prog8 compiler v11.4.1 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)
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
Prog8 compiler v11.4.1 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
|
||||||
|
@@ -1,5 +1,6 @@
|
|||||||
|
|
||||||
Prog8 compiler v11.4.1 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
|
||||||
|
@@ -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.
|
||||||
|
@@ -1,8 +1,7 @@
|
|||||||
TODO
|
TODO
|
||||||
====
|
====
|
||||||
|
|
||||||
Since fixing the missing zp-var initialization, programs grew in size again (assem)
|
Port benchmarks from https://thred.github.io/c-bench-64/ to prog8 and see how it stacks up.
|
||||||
Are there any redundant block-level variable initializations to 0 that we can remove in peephole optimization for example?
|
|
||||||
|
|
||||||
|
|
||||||
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".
|
||||||
@@ -72,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)
|
||||||
|
@@ -1,84 +1,13 @@
|
|||||||
%option no_sysinit
|
|
||||||
%zeropage kernalsafe
|
|
||||||
%import textio
|
|
||||||
%zpallowed 224,255
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
uword @shared @requirezp zpvar1
|
ubyte @shared @requirezp zpvar
|
||||||
uword @shared @requirezp zpvar2
|
bool @shared @requirezp zpbool
|
||||||
uword @shared @requirezp zpvar3
|
ubyte @shared @requirezp @dirty dirtyzpvar
|
||||||
uword @shared @requirezp zpvar4
|
bool @shared @requirezp @dirty dirtyzpbool
|
||||||
uword @shared @requirezp zpvar5
|
|
||||||
uword @shared @requirezp @dirty dzpvar1
|
|
||||||
uword @shared @requirezp @dirty dzpvar2
|
|
||||||
uword @shared @requirezp @dirty dzpvar3
|
|
||||||
uword @shared @requirezp @dirty dzpvar4
|
|
||||||
uword @shared @requirezp @dirty dzpvar5
|
|
||||||
uword @shared @nozp var1
|
|
||||||
uword @shared @nozp var2
|
|
||||||
uword @shared @nozp var3
|
|
||||||
uword @shared @nozp var4
|
|
||||||
uword @shared @nozp var5
|
|
||||||
uword @shared @nozp @dirty dvar1
|
|
||||||
uword @shared @nozp @dirty dvar2
|
|
||||||
uword @shared @nozp @dirty dvar3
|
|
||||||
uword @shared @nozp @dirty dvar4
|
|
||||||
uword @shared @nozp @dirty dvar5
|
|
||||||
|
|
||||||
sub start() {
|
sub start() {
|
||||||
txt.print("address start of zpvars: ")
|
ubyte @shared @requirezp zpvar2
|
||||||
txt.print_uw(&zpvar1)
|
bool @shared @requirezp zpbool2
|
||||||
txt.nl()
|
ubyte @shared @requirezp @dirty dirtyzpvar2
|
||||||
txt.print("address start of normal vars: ")
|
bool @shared @requirezp @dirty dirtyzpbool2
|
||||||
txt.print_uw(&var1)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("non-dirty zp should all be 0: ")
|
|
||||||
txt.print_uw(zpvar1)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(zpvar2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(zpvar3)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(zpvar4)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(zpvar5)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("non-dirty should all be 0: ")
|
|
||||||
txt.print_uw(var1)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(var2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(var3)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(var4)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(var5)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
txt.print("dirty zp may be random: ")
|
|
||||||
txt.print_uw(dzpvar1)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dzpvar2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dzpvar3)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dzpvar4)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dzpvar5)
|
|
||||||
txt.nl()
|
|
||||||
txt.print("dirty may be random: ")
|
|
||||||
txt.print_uw(dvar1)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dvar2)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dvar3)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dvar4)
|
|
||||||
txt.spc()
|
|
||||||
txt.print_uw(dvar5)
|
|
||||||
txt.nl()
|
|
||||||
|
|
||||||
repeat {}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@@ -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.1
|
version=11.5-SNAPSHOT
|
||||||
|
103
languageServer/build.gradle.kts
Normal file
103
languageServer/build.gradle.kts
Normal 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")
|
||||||
|
}
|
13
languageServer/languageServer.iml
Normal file
13
languageServer/languageServer.iml
Normal 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>
|
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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
19
languageServer/src/prog8lsp/Main.kt
Normal file
19
languageServer/src/prog8lsp/Main.kt
Normal 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()
|
||||||
|
}
|
46
languageServer/src/prog8lsp/Prog8LanguageServer.kt
Normal file
46
languageServer/src/prog8lsp/Prog8LanguageServer.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
62
languageServer/src/prog8lsp/Prog8TextDocumentService.kt
Normal file
62
languageServer/src/prog8lsp/Prog8TextDocumentService.kt
Normal 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
|
||||||
|
}
|
80
languageServer/src/prog8lsp/Prog8WorkspaceService.kt
Normal file
80
languageServer/src/prog8lsp/Prog8WorkspaceService.kt
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
@@ -10,5 +10,6 @@ include(
|
|||||||
':codeGenCpu6502',
|
':codeGenCpu6502',
|
||||||
':codeGenExperimental',
|
':codeGenExperimental',
|
||||||
':compiler',
|
':compiler',
|
||||||
':beanshell'
|
':beanshell',
|
||||||
|
':languageServer'
|
||||||
)
|
)
|
||||||
|
Reference in New Issue
Block a user