mirror of
https://github.com/irmen/prog8.git
synced 2025-01-13 10:29:52 +00:00
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:
parent
eaa22a9d13
commit
5731b79554
@ -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,
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -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> {
|
||||
|
@ -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.
|
||||
|
||||
|
@ -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?)
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user