don't allow problematic string and array assignments anymore, improve error messages.

In certain cases you will need to use string.copy() explicitly to overwrite strings with new strings.
This commit is contained in:
Irmen de Jong 2024-10-09 00:51:05 +02:00
parent eaa22a9d13
commit 5731b79554
6 changed files with 51 additions and 60 deletions

View File

@ -2,6 +2,8 @@
compression {
%option no_symbol_prefixing, ignore_unused
sub encode_rle_outfunc(uword data, uword size, uword output_function, bool is_last_block) {
; -- Compress the given data block using ByteRun1 aka PackBits RLE encoding.
; output_function = address of a routine that gets a byte arg in A,

View File

@ -550,10 +550,8 @@ internal class AstChecker(private val program: Program,
fun checkType(target: AssignTarget, value: Expression, augmentable: Boolean) {
val targetDt = target.inferType(program)
val valueDt = value.inferType(program)
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt)) {
if(targetDt.isIterable)
errors.err("cannot assign value to string or array", value.position)
else if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD)) {
if(valueDt.isKnown && !(valueDt isAssignableTo targetDt) && !targetDt.isIterable) {
if(!(valueDt istype DataType.STR && targetDt istype DataType.UWORD)) {
if(targetDt.isUnknown) {
if(target.identifier?.targetStatement(program)!=null)
errors.err("target datatype is unknown", target.position)
@ -1844,6 +1842,12 @@ internal class AstChecker(private val program: Program,
else if(targetDatatype==DataType.BOOL && sourceDatatype!=DataType.BOOL) {
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
else if(targetDatatype==DataType.STR) {
if(sourceDatatype==DataType.UWORD)
errors.err("can't assign UWORD to STR. If the source is a string and you actually want to overwrite the target string, use an explicit string.copy(src,tgt) instead.", position)
else
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}
else {
errors.err("type of value $sourceDatatype doesn't match target $targetDatatype", position)
}

View File

@ -210,15 +210,11 @@ internal class StatementReorderer(
val targetType = assignment.target.inferType(program)
if(targetType.isArray && valueType.isArray) {
if (assignment.value is ArrayLiteral) {
errors.err("cannot assign array literal here, use separate assignment per element", assignment.position)
} else {
return copyArrayValue(assignment)
}
checkCopyArrayValue(assignment)
}
if(!assignment.isAugmentable) {
if (valueType.isString && (targetType istype DataType.STR || targetType istype DataType.ARRAY_B || targetType istype DataType.ARRAY_UB)) {
if (valueType istype DataType.STR && targetType istype DataType.STR) {
// replace string assignment by a call to stringcopy
return copyStringValue(assignment)
}
@ -247,18 +243,22 @@ internal class StatementReorderer(
return noModifications
}
private fun copyArrayValue(assign: Assignment): List<IAstModification> {
private fun checkCopyArrayValue(assign: Assignment) {
val identifier = assign.target.identifier!!
val targetVar = identifier.targetVarDecl(program)!!
if(targetVar.arraysize==null) {
errors.err("array has no defined size", assign.position)
return noModifications
return
}
if(assign.value is ArrayLiteral) {
return // invalid assignment of literals will be reported elsewhere
}
if(assign.value !is IdentifierReference) {
errors.err("invalid array value to assign to other array", assign.value.position)
return noModifications
return
}
val sourceIdent = assign.value as IdentifierReference
val sourceVar = sourceIdent.targetVarDecl(program)!!
@ -273,7 +273,6 @@ internal class StatementReorderer(
errors.err("element size mismatch", assign.position)
}
}
return noModifications
}
private fun copyStringValue(assign: Assignment): List<IAstModification> {

View File

@ -286,8 +286,7 @@ Arrays
^^^^^^
Array types are also supported. They can be formed from a list of booleans, bytes, words, floats, or addresses of other variables
(such as explicit address-of expressions, strings, or other array variables) - values in an array literal
always have to be constants. Putting variables inside an array has to be done on a value-by-value basis.
Here are some examples of arrays::
always have to be constants. Here are some examples of arrays::
byte[10] array ; array of 10 bytes, initially set to 0
byte[] array = [1, 2, 3, 4] ; initialize the array, size taken from value
@ -300,6 +299,7 @@ Here are some examples of arrays::
value = array[3] ; the fourth value in the array (index is 0-based)
char = string[4] ; the fifth character (=byte) in the string
char = string[-2] ; the second-to-last character in the string (Python-style indexing from the end)
flags = [false, true] ; reset all flags in the array
.. note::
Right now, the array should be small enough to be indexable by a single byte index.
@ -312,9 +312,8 @@ Note that the various keywords for the data type and variable type (``byte``, ``
can't be used as *identifiers* elsewhere. You can't make a variable, block or subroutine with the name ``byte``
for instance.
It's possible to assign a new array to another array, this will overwrite all elements in the original
array with those in the value array. The number and types of elements have to match.
It's possible to assign an array to another array; this will overwrite all elements in the target
array with those in the source array. The number and types of elements have to match for this to work!
For large arrays this is a slow operation because every element is copied over. It should probably be avoided.
Using the ``in`` operator you can easily check if a value is present in an array,
@ -650,10 +649,6 @@ Assignment statements assign a single value to a target variable or memory locat
Augmented assignments (such as ``aa += xx``) are also available, but these are just shorthands
for normal assignments (``aa = aa + xx``).
Only variables of type byte, word and float can be assigned a new value.
It's not possible to set a new value to string or array variables etc, because they get allocated
a fixed amount of memory which will not change. (You *can* change the value of elements in a string or array though).
It is possible to "chain" assignments: ``x = y = z = 42``, this is just a shorthand
for the three individual assignments with the same value 42.

View File

@ -1,6 +1,12 @@
TODO
====
Don't allow assigning str to array!
Don't allow assigning array to str!
Don't allow assigning a word to an array or string!
Put palette fade to white / black in.
Regenerate skeleton doc files.
Improve register load order in subroutine call args assignments:
@ -12,6 +18,7 @@ Maybe this routine can be made more intelligent. See usesOtherRegistersWhileEva
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- The string assignment footgun should be removed in favor of just calling string.copy explicitly. Get rid of sys.internal_stringcopy asm routine. Fix docs too.
- Improve the SublimeText syntax file for prog8, you can also install this for 'bat': https://github.com/sharkdp/bat?tab=readme-ov-file#adding-new-syntaxes--language-definitions
- Can we support signed % (remainder) somehow?
- Don't add "random" rts to %asm blocks but instead give a warning about it? (but this breaks existing behavior that others already depend on... command line switch? block directive?)

View File

@ -1,46 +1,30 @@
%import textio
%import string
%zeropage basicsafe
main {
sub start() {
routine(11,22,33)
txt.nl()
cx16.r0 = callfar2(0, &routine, 11, 22, 33, true)
txt.nl()
txt.print_uwhex(cx16.r0, true)
txt.nl()
cx16.r0 = callfar(0, &routine, 11*256 + 22)
txt.nl()
txt.print_uwhex(cx16.r0, true)
txt.nl()
}
str name1 = "irmen"
str name2 = "other"
bool[2] flags = [true, false]
asmsub routine(ubyte v1 @A, ubyte v2 @X, ubyte v3 @Y) -> uword @AY {
%asm {{
sta cx16.r8L
stx cx16.r9L
sty cx16.r10L
lda #0
rol a
sta cx16.r11L
txt.print(name1)
txt.nl()
name1 = name2
txt.print(name1)
txt.nl()
flags = [false, true]
lda cx16.r8L
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda cx16.r9L
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda cx16.r10L
jsr txt.print_ub
lda #' '
jsr txt.chrout
lda cx16.r11L
jsr txt.print_ub
lda #$31
ldy #$ea
rts
}}
ubyte[10] array
ubyte[10] array2
void string.copy(name2, name1)
array = array2
name2 = "zzz"
array = [1,2,3,4,5,6,7,8,9,10]
;; array = cx16.r0
;; array = name1
;; name1 = array
;; name1 = cx16.r0
}
}