From 334e6dca28859eec52ce90ca1fe3c130e21b9a03 Mon Sep 17 00:00:00 2001 From: Irmen de Jong Date: Fri, 5 Jan 2024 20:46:26 +0100 Subject: [PATCH] added string.contains(). fixed string and array containment check for length 1. --- .../cpu6502/assignment/AssignmentAsmGen.kt | 10 +- compiler/res/prog8lib/string.p8 | 42 ++++---- compiler/res/prog8lib/virtual/string.p8 | 7 ++ .../compiler/astprocessing/VariousCleanups.kt | 13 --- docs/source/libraries.rst | 4 + examples/test.p8 | 97 +++++++++---------- 6 files changed, 82 insertions(+), 91 deletions(-) diff --git a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt index 8a3b62727..f3aa1383b 100644 --- a/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt +++ b/codeGenCpu6502/src/prog8/codegen/cpu6502/assignment/AssignmentAsmGen.kt @@ -1898,33 +1898,29 @@ internal class AssignmentAsmGen(private val program: PtProgram, } when(dt) { DataType.STR -> { - // use subroutine assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) - asmgen.out(" pha") + asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position,"P8ZP_SCRATCH_W1"), symbolName, null, null) asmgen.out(" pla") asmgen.out(" ldy #${numElements-1}") asmgen.out(" jsr prog8_lib.containment_bytearray") - return } DataType.ARRAY_F -> { - throw AssemblyError("containment check of floats not supported") + TODO("containment check of floats") } DataType.ARRAY_B, DataType.ARRAY_UB -> { assignExpressionToRegister(containment.element, RegisterOrPair.A, elementDt == DataType.BYTE) - asmgen.out(" pha") + asmgen.out(" pha") // need to keep the scratch var safe so we have to do it in this order assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W1"), symbolName, null, null) asmgen.out(" pla") asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_bytearray") - return } DataType.ARRAY_W, DataType.ARRAY_UW -> { assignExpressionToVariable(containment.element, "P8ZP_SCRATCH_W1", elementDt) assignAddressOf(AsmAssignTarget(TargetStorageKind.VARIABLE, asmgen, DataType.UWORD, containment.definingISub(), containment.position, "P8ZP_SCRATCH_W2"), symbolName, null, null) asmgen.out(" ldy #$numElements") asmgen.out(" jsr prog8_lib.containment_wordarray") - return } else -> throw AssemblyError("invalid dt") } diff --git a/compiler/res/prog8lib/string.p8 b/compiler/res/prog8lib/string.p8 index 2cf7d3148..bd4739f2a 100644 --- a/compiler/res/prog8lib/string.p8 +++ b/compiler/res/prog8lib/string.p8 @@ -20,22 +20,19 @@ string { }} } - asmsub left(uword source @R0, ubyte length @A, uword target @R1) clobbers(A, Y) { + asmsub left(uword source @AX, ubyte length @Y, uword target @R1) clobbers(A, Y) { ; Copies the left side of the source string of the given length to target string. ; It is assumed the target string buffer is large enough to contain the result. ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. ; Modifies in-place, doesn’t return a value (so can’t be used in an expression). %asm {{ - ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... - ldy cx16.r0 - sty P8ZP_SCRATCH_W1 - ldy cx16.r0+1 - sty P8ZP_SCRATCH_W1+1 - ldy cx16.r1 - sty P8ZP_SCRATCH_W2 - ldy cx16.r1+1 - sty P8ZP_SCRATCH_W2+1 - tay + ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... + sta P8ZP_SCRATCH_W1 + stx P8ZP_SCRATCH_W1+1 + lda cx16.r1 + sta P8ZP_SCRATCH_W2 + lda cx16.r1+1 + sta P8ZP_SCRATCH_W2+1 lda #0 sta (P8ZP_SCRATCH_W2),y cpy #0 @@ -51,16 +48,16 @@ _loop dey ; asmgen.out(" jsr prog8_lib.func_leftstr") } - asmsub right(uword source @R0, ubyte length @A, uword target @R1) clobbers(A,Y) { + asmsub right(uword source @AY, ubyte length @X, uword target @R1) clobbers(A,Y) { ; Copies the right side of the source string of the given length to target string. ; It is assumed the target string buffer is large enough to contain the result. ; Also, you have to make sure yourself that length is smaller or equal to the length of the source string. ; Modifies in-place, doesn’t return a value (so can’t be used in an expression). %asm {{ ; need to copy the the cx16 virtual registers to zeropage to be compatible with C64... - sta P8ZP_SCRATCH_B1 - lda cx16.r0 - ldy cx16.r0+1 + stx P8ZP_SCRATCH_B1 + sta cx16.r0 + sty cx16.r0+1 jsr string.length tya sec @@ -128,16 +125,14 @@ _startloop dey }} } - asmsub find(uword string @R0, ubyte character @A) -> ubyte @A, bool @Pc { + asmsub find(uword string @AY, ubyte character @X) -> ubyte @A, bool @Pc { ; Locates the first position of the given character in the string, ; returns Carry set if found + index in A, or A=0 + Carry clear if not found. %asm {{ - ; need to copy the the cx16 virtual registers to zeropage to make this run on C64... - sta P8ZP_SCRATCH_B1 - lda cx16.r0 - ldy cx16.r0+1 + ; need to copy the the cx16 virtual registers to zeropage to make this run on C64... sta P8ZP_SCRATCH_W1 sty P8ZP_SCRATCH_W1+1 + stx P8ZP_SCRATCH_B1 ldy #0 - lda (P8ZP_SCRATCH_W1),y beq _notfound @@ -154,6 +149,13 @@ _found tya }} } + asmsub contains(uword string @AY, ubyte character @X) -> bool @Pc { + ; Just return true/false if the character is in the given string or not. + %asm {{ + jmp find + }} + } + asmsub copy(uword source @R0, uword target @AY) clobbers(A) -> ubyte @Y { ; Copy a string to another, overwriting that one. ; Returns the length of the string that was copied. diff --git a/compiler/res/prog8lib/virtual/string.p8 b/compiler/res/prog8lib/virtual/string.p8 index 9fa2e9988..c02ae5873 100644 --- a/compiler/res/prog8lib/virtual/string.p8 +++ b/compiler/res/prog8lib/virtual/string.p8 @@ -65,6 +65,13 @@ string { return 0 } + sub contains(str st, ubyte character) -> bool { + void find(st, character) + if_cs + return true + return false + } + sub copy(str source, str target) -> ubyte { ; Copy a string to another, overwriting that one. ; Returns the length of the string that was copied. diff --git a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt index 03f62020f..cb4518342 100644 --- a/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt +++ b/compiler/src/prog8/compiler/astprocessing/VariousCleanups.kt @@ -225,19 +225,6 @@ internal class VariousCleanups(val program: Program, val errors: IErrorReporter, val array = (containment.iterable as ArrayLiteral).value return checkArray(array) } - is IdentifierReference -> { - val variable = (containment.iterable as IdentifierReference).targetVarDecl(program) - when(variable?.datatype) { - DataType.STR -> { - val stringVal = (variable.value as StringLiteral) - return checkString(stringVal) - } - in ArrayDatatypes -> { - return checkArray(variable!!) - } - else -> {} - } - } is RangeExpression -> { val constValues = (containment.iterable as RangeExpression).toConstantIntegerRange() if(constValues!=null) { diff --git a/docs/source/libraries.rst b/docs/source/libraries.rst index cb9a03227..939bcf443 100644 --- a/docs/source/libraries.rst +++ b/docs/source/libraries.rst @@ -258,6 +258,10 @@ Provides string manipulation routines. Simply call this and only act on the carry status with ``if_cc`` for example. Much like the difference between len(str) and length(str). +``contains (string, char) -> bool`` + Just returns true if the character is in the given string, or false if it's not. + For string literals, you can use a containment check expression instead: ``char in "hello world"``. + ``compare (string1, string2) -> ubyte result`` Returns -1, 0 or 1 depending on whether string1 sorts before, equal or after string2. Note that you can also directly compare strings and string values with each other diff --git a/examples/test.p8 b/examples/test.p8 index af6dac46a..cebce3eb3 100644 --- a/examples/test.p8 +++ b/examples/test.p8 @@ -1,66 +1,61 @@ %import textio +%import string %zeropage basicsafe %option no_sysinit main { sub start() { - ; is optimizing this useful? : not a1 or not a2 -> not(a1 and a2) likewise for and. - bool @shared a1 = true - bool @shared a2 - bool @shared a - bool @shared b + str s = "?" + s[0] = 's' + txt.print(s) + txt.nl() - ; absorbption opt: -; if a or (a and b) -; cx16.r0 ++ -; if a or (b and a) -; cx16.r0 ++ -; if a and (a or b) -; cx16.r0 ++ -; if a and (b or a) -; cx16.r0 ++ -; -; ; no opt: -; if a and (b and a) -; cx16.r0 ++ -; if a or (b or a) -; cx16.r0 ++ + if 's' in s { + txt.print("ok1\n") + } else { + txt.print("fail1\n") + } - bool @shared iteration_in_progress = false - ubyte @shared num_bytes = 99 + void string.find(s, 's') + if_cs { + txt.print("ok2\n") + } else { + txt.print("fail2\n") + } - if not iteration_in_progress or not num_bytes - txt.print("yep1") - else - txt.print("nope1") + if string.contains(s, 's') { + txt.print("ok3\n") + } else { + txt.print("fail3\n") + } - iteration_in_progress = true - if not iteration_in_progress or not num_bytes - txt.print("yep2") - else - txt.print("nope2") + if 'q' in s { + txt.print("ok1\n") + } else { + txt.print("fail1\n") + } -; -; if a1==0 and a2==0 -; cx16.r0++ -; -; if (a1!=0 or a2!=0)==0 -; cx16.r0++ -; -; if a1==0 or a2==0 -; cx16.r0++ -; -; if (a1!=0 and a2!=0)==0 -; cx16.r0++ + void string.find(s, 'q') + if_cs { + txt.print("ok2\n") + } else { + txt.print("fail2\n") + } + if string.contains(s, 'q') { + txt.print("ok3\n") + } else { + txt.print("fail3\n") + } + + str buffer="?" * 20 + str name = "irmen de jong" + string.left(name, 5, buffer) + txt.print(buffer) + txt.nl() + string.right(name, 4, buffer) + txt.print(buffer) + txt.nl() -; if not a1 or not a2 -; cx16.r0++ -; if not (a1 and a2) -; cx16.r0++ -; if not a1 and not a2 -; cx16.r0++ -; if not (a1 or a2) -; cx16.r0++ } }