added compiler option to choose string literal deduplication yes/no -- default changed to NO

This commit is contained in:
Irmen de Jong 2021-05-13 00:35:22 +02:00
parent 74f918d911
commit 25d80f4df1
13 changed files with 115 additions and 97 deletions

View File

@ -42,6 +42,7 @@ private fun compileMain(args: Array<String>) {
val compilationTarget by cli.option(ArgType.String, fullName = "target", description = "target output of the compiler, currently '${C64Target.name}' and '${Cx16Target.name}' available").default(C64Target.name)
val moduleFiles by cli.argument(ArgType.String, fullName = "modules", description = "main module file(s) to compile").multiple(999)
val libDirs by cli.option(ArgType.String, fullName="libdirs", description = "list of extra paths to search in for imported modules").multiple().delimiter(File.pathSeparator)
val stringDedup by cli.option(ArgType.Boolean, fullName="strdedup", description = "deduplicate identical string literals").default(false)
try {
cli.parse(args)
@ -69,7 +70,7 @@ private fun compileMain(args: Array<String>) {
val results = mutableListOf<CompilationResult>()
for(filepathRaw in moduleFiles) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
val compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, stringDedup, compilationTarget, libdirs, outputPath)
results.add(compilationResult)
}
@ -106,7 +107,7 @@ private fun compileMain(args: Array<String>) {
val filepath = pathFrom(filepathRaw).normalize()
val compilationResult: CompilationResult
try {
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, compilationTarget, libdirs, outputPath)
compilationResult = compileProgram(filepath, dontOptimize!=true, dontWriteAssembly!=true, slowCodegenWarnings==true, stringDedup, compilationTarget, libdirs, outputPath)
if(!compilationResult.success)
exitProcess(1)
} catch (x: ParsingFailedError) {

View File

@ -50,6 +50,7 @@ data class CompilationOptions(val output: OutputType,
val zpReserved: List<IntRange>,
val floats: Boolean,
val noSysInit: Boolean,
val stringDedup: Boolean,
val compTarget: ICompilationTarget) {
var slowCodegenWarnings = false
var optimize = false
@ -69,6 +70,7 @@ fun compileProgram(filepath: Path,
optimize: Boolean,
writeAssembly: Boolean,
slowCodegenWarnings: Boolean,
stringDedup: Boolean,
compilationTarget: String,
libdirs: List<String>,
outputDir: Path): CompilationResult {
@ -90,7 +92,7 @@ fun compileProgram(filepath: Path,
try {
val totalTime = measureTimeMillis {
// import main module and everything it needs
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, libdirs)
val (ast, compilationOptions, imported) = parseImports(filepath, errors, compTarget, stringDedup, libdirs)
compilationOptions.slowCodegenWarnings = slowCodegenWarnings
compilationOptions.optimize = optimize
programAst = ast
@ -166,7 +168,11 @@ private class BuiltinFunctionsFacade(functions: Map<String, FSignature>): IBuilt
builtinFunctionReturnType(name, args, program)
}
private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICompilationTarget, libdirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
private fun parseImports(filepath: Path,
errors: IErrorReporter,
compTarget: ICompilationTarget,
stringDedup: Boolean,
libdirs: List<String>): Triple<Program, CompilationOptions, List<Path>> {
val compilationTargetName = compTarget.name
println("Compiler target: $compilationTargetName. Parsing...")
val bf = BuiltinFunctionsFacade(BuiltinFunctions)
@ -178,7 +184,7 @@ private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICo
errors.report()
val importedFiles = programAst.modules.filter { !it.source.startsWith("@embedded@") }.map { it.source }
val compilerOptions = determineCompilationOptions(programAst, compTarget)
val compilerOptions = determineCompilationOptions(programAst, stringDedup, compTarget)
if (compilerOptions.launcher == LauncherType.BASIC && compilerOptions.output != OutputType.PRG)
throw ParsingFailedError("${programAst.modules.first().position} BASIC launcher requires output type PRG.")
@ -193,7 +199,7 @@ private fun parseImports(filepath: Path, errors: IErrorReporter, compTarget: ICo
return Triple(programAst, compilerOptions, importedFiles)
}
private fun determineCompilationOptions(program: Program, compTarget: ICompilationTarget): CompilationOptions {
private fun determineCompilationOptions(program: Program, stringDedup: Boolean, compTarget: ICompilationTarget): CompilationOptions {
val mainModule = program.mainModule
val outputType = (mainModule.statements.singleOrNull { it is Directive && it.directive == "%output" }
as? Directive)?.args?.single()?.name?.uppercase()
@ -240,7 +246,7 @@ private fun determineCompilationOptions(program: Program, compTarget: ICompilati
return CompilationOptions(
if (outputType == null) OutputType.PRG else OutputType.valueOf(outputType),
if (launcherType == null) LauncherType.BASIC else LauncherType.valueOf(launcherType),
zpType, zpReserved, floatsEnabled, noSysInit,
zpType, zpReserved, floatsEnabled, noSysInit, stringDedup,
compTarget
)
}
@ -248,7 +254,7 @@ private fun determineCompilationOptions(program: Program, compTarget: ICompilati
private fun processAst(programAst: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
// perform initial syntax checks and processings
println("Processing for target ${compilerOptions.compTarget.name}...")
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
programAst.checkIdentifiers(errors, compilerOptions)
errors.report()
programAst.constantFold(errors, compilerOptions.compTarget)
errors.report()
@ -260,7 +266,7 @@ private fun processAst(programAst: Program, errors: IErrorReporter, compilerOpti
errors.report()
programAst.checkValid(compilerOptions, errors, compilerOptions.compTarget)
errors.report()
programAst.checkIdentifiers(errors, compilerOptions.compTarget)
programAst.checkIdentifiers(errors, compilerOptions)
errors.report()
}

View File

@ -44,16 +44,16 @@ internal fun Program.verifyFunctionArgTypes() {
fixer.visit(this)
}
internal fun Program.checkIdentifiers(errors: IErrorReporter, compTarget: ICompilationTarget) {
internal fun Program.checkIdentifiers(errors: IErrorReporter, options: CompilationOptions) {
val checker2 = AstIdentifiersChecker(this, errors, compTarget)
val checker2 = AstIdentifiersChecker(this, errors, options.compTarget)
checker2.visit(this)
if(errors.noErrors()) {
val transforms = AstVariousTransforms(this)
transforms.visit(this)
transforms.applyModifications()
val lit2decl = LiteralsToAutoVars(this)
val lit2decl = LiteralsToAutoVars(this, options)
lit2decl.visit(this)
lit2decl.applyModifications()
}

View File

@ -10,14 +10,15 @@ import prog8.ast.statements.VarDecl
import prog8.ast.statements.WhenChoice
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstModification
import prog8.compiler.CompilationOptions
internal class LiteralsToAutoVars(private val program: Program) : AstWalker() {
internal class LiteralsToAutoVars(private val program: Program, private val options: CompilationOptions) : AstWalker() {
override fun after(string: StringLiteralValue, parent: Node): Iterable<IAstModification> {
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
// replace the literal string by a identifier reference to the interned string
val scopedName = program.internString(string)
val scopedName = program.internString(string, options.stringDedup)
val identifier = IdentifierReference(scopedName, string.position)
return listOf(IAstModification.ReplaceNode(string, identifier, parent))
}

View File

@ -381,18 +381,14 @@ internal class AsmGen(private val program: Program,
out("\n; non-zeropage variables")
val vars = statements.filterIsInstance<VarDecl>().filter { it.type==VarDeclType.VAR }
// special treatment for string types: merge strings that are identical
val encodedstringVars = vars
.filter {it.datatype == DataType.STR }
.map {
val str = it.value as StringLiteralValue
it to compTarget.encodeString(str.value, str.altEncoding).plus(0)
}
.groupBy({it.second}, {it.first})
for((encoded, variables) in encodedstringVars) {
variables.dropLast(1).forEach { out(it.name) }
val lastvar = variables.last()
outputStringvar(lastvar, encoded)
for((decl, variables) in encodedstringVars) {
outputStringvar(decl, variables)
}
// non-string variables
@ -402,11 +398,11 @@ internal class AsmGen(private val program: Program,
}
}
private fun outputStringvar(lastvar: VarDecl, encoded: List<Short>) {
val sv = lastvar.value as StringLiteralValue
private fun outputStringvar(strdecl: VarDecl, bytes: List<Short>) {
val sv = strdecl.value as StringLiteralValue
val altEncoding = if(sv.altEncoding) "@" else ""
out("${lastvar.name}\t; ${lastvar.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
val outputBytes = encoded.map { "$" + it.toString(16).padStart(2, '0') }
out("${strdecl.name}\t; ${strdecl.datatype} $altEncoding\"${escape(sv.value).replace("\u0000", "<NULL>")}\"")
val outputBytes = bytes.map { "$" + it.toString(16).padStart(2, '0') }
for (chunk in outputBytes.chunked(16))
out(" .byte " + chunk.joinToString())
}

View File

@ -134,7 +134,7 @@ class TestC64Zeropage {
@Test
fun testNames() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, true, C64Target))
zp.allocate("", DataType.UBYTE, null, errors)
zp.allocate("", DataType.UBYTE, null, errors)
@ -147,37 +147,37 @@ class TestC64Zeropage {
@Test
fun testZpFloatEnable() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, C64Target))
assertFailsWith<CompilerException> {
zp.allocate("", DataType.FLOAT, null, errors)
}
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, C64Target))
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), true, false, true, C64Target))
assertFailsWith<CompilerException> {
zp2.allocate("", DataType.FLOAT, null, errors)
}
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, true, C64Target))
zp3.allocate("", DataType.FLOAT, null, errors)
}
@Test
fun testZpModesWithFloats() {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, true, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, true, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, true, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, true, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), true, false, true, C64Target))
assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), true, false, true, C64Target))
}
assertFailsWith<CompilerException> {
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, C64Target))
C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), true, false, true, C64Target))
}
}
@Test
fun testZpDontuse() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.DONTUSE, emptyList(), false, false, true, C64Target))
println(zp.free)
assertEquals(0, zp.available())
assertFailsWith<CompilerException> {
@ -187,19 +187,19 @@ class TestC64Zeropage {
@Test
fun testFreeSpaces() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, true, C64Target))
assertEquals(18, zp1.available())
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, C64Target))
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FLOATSAFE, emptyList(), false, false, true, C64Target))
assertEquals(85, zp2.available())
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, C64Target))
val zp3 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, true, C64Target))
assertEquals(125, zp3.available())
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
val zp4 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, C64Target))
assertEquals(238, zp4.available())
}
@Test
fun testReservedSpace() {
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
val zp1 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, C64Target))
assertEquals(238, zp1.available())
assertTrue(50 in zp1.free)
assertTrue(100 in zp1.free)
@ -208,7 +208,7 @@ class TestC64Zeropage {
assertTrue(200 in zp1.free)
assertTrue(255 in zp1.free)
assertTrue(199 in zp1.free)
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, C64Target))
val zp2 = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, listOf(50 .. 100, 200..255), false, false, true, C64Target))
assertEquals(139, zp2.available())
assertFalse(50 in zp2.free)
assertFalse(100 in zp2.free)
@ -221,7 +221,7 @@ class TestC64Zeropage {
@Test
fun testBasicsafeAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, true, C64Target))
assertEquals(18, zp.available())
assertFailsWith<ZeropageDepletedError> {
@ -244,7 +244,7 @@ class TestC64Zeropage {
@Test
fun testFullAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, C64Target))
assertEquals(238, zp.available())
val loc = zp.allocate("", DataType.UWORD, null, errors)
assertTrue(loc > 3)
@ -274,7 +274,7 @@ class TestC64Zeropage {
@Test
fun testEfficientAllocation() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, true, C64Target))
assertEquals(18, zp.available())
assertEquals(0x04, zp.allocate("", DataType.WORD, null, errors))
assertEquals(0x06, zp.allocate("", DataType.UBYTE, null, errors))
@ -293,7 +293,7 @@ class TestC64Zeropage {
@Test
fun testReservedLocations() {
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, C64Target))
val zp = C64Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, true, C64Target))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
}
}
@ -303,23 +303,23 @@ class TestC64Zeropage {
class TestCx16Zeropage {
@Test
fun testReservedLocations() {
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, Cx16Target))
val zp = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), false, false, true, Cx16Target))
assertEquals(zp.SCRATCH_REG, zp.SCRATCH_B1+1, "zp _B1 and _REG must be next to each other to create a word")
}
@Test
fun testFreeSpaces() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, Cx16Target))
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.BASICSAFE, emptyList(), true, false, true, Cx16Target))
assertEquals(88, zp1.available())
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, Cx16Target))
val zp3 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.KERNALSAFE, emptyList(), false, false, true, Cx16Target))
assertEquals(175, zp3.available())
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
val zp4 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, Cx16Target))
assertEquals(216, zp4.available())
}
@Test
fun testReservedSpace() {
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, Cx16Target))
val zp1 = CX16Zeropage(CompilationOptions(OutputType.RAW, LauncherType.NONE, ZeropageType.FULL, emptyList(), false, false, true, Cx16Target))
assertEquals(216, zp1.available())
assertTrue(0x22 in zp1.free)
assertTrue(0x80 in zp1.free)

View File

@ -7,7 +7,6 @@ import prog8.ast.base.VarDeclType
import prog8.ast.expressions.*
import prog8.ast.statements.*
import prog8.ast.walk.IAstVisitor
import java.util.*
class AstToSourceCode(val output: (text: String) -> Unit, val program: Program): IAstVisitor {

View File

@ -256,7 +256,7 @@ class Program(val name: String,
get() = mainModule.loadAddress
var actualLoadAddress: Int = 0
private val internedStrings = mutableMapOf<Pair<String, Boolean>, List<String>>()
private val internedStringsUnique = mutableMapOf<Pair<String, Boolean>, List<String>>()
val internedStringsModuleName = "prog8_interned_strings"
init {
@ -280,22 +280,40 @@ class Program(val name: String,
return mainBlocks[0].subScope("start") as Subroutine
}
fun internString(string: StringLiteralValue): List<String> {
val key = Pair(string.value, string.altEncoding)
val existing = internedStrings[key]
if(existing!=null)
return existing
fun internString(string: StringLiteralValue, dedup: Boolean): List<String> {
val decl = VarDecl(VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, "string_${internedStrings.size}", string,
isArray = false, autogeneratedDontRemove = true, position = string.position)
val internedStringsBlock = modules.first { it.name==internedStringsModuleName }.statements.first { it is Block && it.name == internedStringsModuleName}
(internedStringsBlock as Block).statements.add(decl)
decl.linkParents(internedStringsBlock)
val scopedName = listOf(internedStringsModuleName, decl.name)
internedStrings[key] = scopedName
return scopedName
fun getScopedName(string: StringLiteralValue): List<String> {
val internedStringsBlock = modules
.first { it.name == internedStringsModuleName }.statements
.first { it is Block && it.name == internedStringsModuleName } as Block
val varName = "string_${internedStringsBlock.statements.size}"
val decl = VarDecl(
VarDeclType.VAR, DataType.STR, ZeropageWish.NOT_IN_ZEROPAGE, null, varName, string,
isArray = false, autogeneratedDontRemove = true, position = string.position
)
internedStringsBlock.statements.add(decl)
decl.linkParents(internedStringsBlock)
val scopedName = listOf(internedStringsModuleName, decl.name)
return scopedName
}
val key = Pair(string.value, string.altEncoding)
if(dedup) {
val existing = internedStringsUnique[key]
if (existing != null)
return existing
val scopedName = getScopedName(string)
internedStringsUnique[key] = scopedName
return scopedName
} else {
// don't deduplicate string literals
return getScopedName(string)
}
}
fun allBlocks(): List<Block> = modules.flatMap { it.statements.filterIsInstance<Block>() }
override val position: Position = Position.DUMMY

View File

@ -13,7 +13,6 @@ import prog8.parser.prog8Parser
import java.io.CharConversionException
import java.io.File
import java.nio.file.Path
import java.util.*
/***************** Antlr Extension methods to create AST ****************/

View File

@ -6,7 +6,7 @@ import prog8.ast.base.*
import prog8.ast.statements.*
import prog8.ast.walk.AstWalker
import prog8.ast.walk.IAstVisitor
import java.util.Objects
import java.util.*
import kotlin.math.abs

View File

@ -5,6 +5,7 @@ TODO
- possible idea: option to mark vardecls 'shared' to indicate they should not be optimized away because they're shared with assembly code?
However: who even needs variables declared in prog8 code that are only used by assembly???
- github issue: make strings no longer immutable? Deduplication selectable via command line switch?
IMPROVE DOCUMENTATION ABOUT STRINGS AND DEDUP.
- test all examples before release of the new version

View File

@ -1,27 +1,24 @@
%import textio ; txt.*
main {
sub start() {
; ATTENTION: uncomment only one problematic line at a time!
; Normal string literals, i.e. PETSCII encoding
; ---------------------------------------------
txt.print("\"") ; fine
txt.print("\n") ; fine
txt.print("\r") ; fine
txt.print("\\") ; yields CharConversionException
txt.print("xyz\\") ; yields prog8.compiler.AssemblyError
; @-strings, i.e. translated into
; the alternate character encoding (Screencodes/pokes)
; ----------------------------------------------------
txt.print(@"\"") ; fine
txt.print(@"\n") ; yields CharConversionException
txt.print(@"xyz\n") ; yields prog8.compiler.AssemblyError
txt.print(@"\r") ; yields CharConversionException
txt.print(@"xyz\r") ; yields prog8.compiler.AssemblyError
txt.print(@"\\") ; yields CharConversionException
txt.print(@"\\") ; yields prog8.compiler.AssemblyError
; there may be more...
}
}
%import textio ; txt.*
%zeropage basicsafe
main {
sub start() {
txt.print("a")
txt.print("a")
txt.print("bb")
txt.print("bb")
txt.print("\n")
txt.print("\n\n")
; txt.print("hello\n")
; txt.print("hello\n")
; txt.print("hello\n")
; txt.print("hello\n")
; txt.print("hello22\n")
; txt.print("hello22\n")
; txt.print("hello22\n")
; txt.print("hello22\n")
; txt.print("hello666\n")
; txt.print("hello666\n")
; txt.print("hello666\n")
; txt.print("hello666\n")
}
}

View File

@ -29,7 +29,7 @@ class RequestParser : Take {
val form = RqFormBase(request)
val names = form.names()
val a = form.param("a").single()
val compilationResult = compileProgram(Path.of(a), true, true, true, "c64", emptyList<String>(), Path.of("."))
val compilationResult = compileProgram(Path.of(a), true, true, true, false, "c64", emptyList<String>(), Path.of("."))
return RsJson(Jsonding())
}
}