mirror of
https://github.com/irmen/prog8.git
synced 2025-11-03 04:17:16 +00:00
error messages and trying to improve support for struct allocs in arrays
added sorting example
This commit is contained in:
@@ -44,7 +44,6 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"setlsb" -> funcSetLsbMsb(fcall, false)
|
||||
"setmsb" -> funcSetLsbMsb(fcall, true)
|
||||
"memory" -> funcMemory(fcall, discardResult, resultRegister)
|
||||
"structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"peekw" -> funcPeekW(fcall, resultRegister)
|
||||
"peekf" -> funcPeekF(fcall, resultRegister)
|
||||
"peek" -> throw AssemblyError("peek() should have been replaced by @()")
|
||||
@@ -67,6 +66,7 @@ internal class BuiltinFunctionsAsmGen(private val program: PtProgram,
|
||||
"callfar" -> funcCallFar(fcall, resultRegister)
|
||||
"callfar2" -> funcCallFar2(fcall, resultRegister)
|
||||
"call" -> funcCall(fcall)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(fcall, discardResult, resultRegister)
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(fcall, resultRegister)
|
||||
"prog8_lib_square_byte" -> funcSquare(fcall, BaseDataType.UBYTE, resultRegister)
|
||||
"prog8_lib_square_word" -> funcSquare(fcall, BaseDataType.UWORD, resultRegister)
|
||||
|
||||
@@ -50,7 +50,7 @@ internal class BuiltinFuncGen(private val codeGen: IRCodeGen, private val exprGe
|
||||
"prog8_lib_stringcompare" -> funcStringCompare(call)
|
||||
"prog8_lib_square_byte" -> funcSquare(call, IRDataType.BYTE)
|
||||
"prog8_lib_square_word" -> funcSquare(call, IRDataType.WORD)
|
||||
"structalloc" -> funcStructAlloc(call)
|
||||
"prog8_lib_structalloc" -> funcStructAlloc(call)
|
||||
"sizeof" -> throw AssemblyError("sizeof must have been replaced with a constant")
|
||||
else -> throw AssemblyError("missing builtinfunc for ${call.name}")
|
||||
}
|
||||
|
||||
@@ -1082,7 +1082,10 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(decl.datatype.isStructInstance && decl.origin!=VarDeclOrigin.SUBROUTINEPARAM) {
|
||||
errors.err("struct instances cannot be declared directly, use pointer and allocation call instead", decl.position)
|
||||
if(decl.type==VarDeclType.MEMORY)
|
||||
errors.err("cannot declare memory mapped struct instances, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
else
|
||||
errors.err("struct instances cannot be declared directly, use a pointer and memory allocation call or direct address assignment instead", decl.position)
|
||||
}
|
||||
|
||||
if (decl.dirty) {
|
||||
@@ -1261,8 +1264,15 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
|
||||
if(array.parent is VarDecl) {
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf })
|
||||
if (!array.value.all { it is NumericLiteral || it is AddressOf || (it is FunctionCallExpression && it.target.targetStructDecl()!=null) }) {
|
||||
errors.err("initialization value contains non-constant elements", array.value[0].position)
|
||||
}
|
||||
|
||||
if(array.value.any { it is FunctionCallExpression }) {
|
||||
errors.err("it is not yet possible to use struct initializations in an array, you have to do it one by one for now", array.value[0].position)
|
||||
// TODO this is because later in the simplified AST the allocate struct variable is still missing somehow
|
||||
}
|
||||
|
||||
} else if(array.parent is ForLoop) {
|
||||
if (!array.value.all { it.constValue(program) != null })
|
||||
errors.err("array literal for iteration must contain constants. Try using a separate array variable instead?", array.position)
|
||||
|
||||
@@ -410,7 +410,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
val call =
|
||||
if(targetStruct!=null) {
|
||||
// a call to a struct yields a pointer to a struct instance and means: allocate a statically initialized struct instance of that type
|
||||
PtBuiltinFunctionCall("structalloc", false, true, DataType.pointer(targetStruct), srcCall.position)
|
||||
PtBuiltinFunctionCall("prog8_lib_structalloc", false, true, DataType.pointer(targetStruct), srcCall.position)
|
||||
} else {
|
||||
// regular function call
|
||||
val (target, _) = srcCall.target.targetNameAndType(program)
|
||||
@@ -740,7 +740,7 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
||||
val arr = PtArray(srcArr.inferType(program).getOrElse { throw FatalAstException("array must know its type") }, srcArr.position)
|
||||
for (elt in srcArr.value) {
|
||||
val child = transformExpression(elt)
|
||||
require(child is PtAddressOf || child is PtBool || child is PtNumber) { "array element invalid type $child" }
|
||||
require(child is PtAddressOf || child is PtBool || child is PtNumber || (child is PtBuiltinFunctionCall && child.name=="prog8_lib_structalloc")) {"array element invalid type $child" }
|
||||
arr.add(child)
|
||||
}
|
||||
return arr
|
||||
|
||||
@@ -128,13 +128,24 @@ internal class VerifyFunctionArgTypes(val program: Program, val options: Compila
|
||||
if(mismatch>=0) {
|
||||
val actual = argtypes[mismatch]
|
||||
val expected = consideredParamTypes[mismatch]
|
||||
if(expected.isPointer && expected.sub?.isWord==true) {
|
||||
val arg = call.args[mismatch]
|
||||
val argArray = if(arg is AddressOf) arg.identifier else arg
|
||||
return if(argArray?.inferType(program)?.getOrUndef()?.isSplitWordArray==true)
|
||||
Pair("argument ${mismatch + 1} cannot pass address to a split words array where a word pointer argument is expected, use a @nosplit word array instead", call.args[mismatch].position)
|
||||
else
|
||||
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
if (expected.isPointer) {
|
||||
if (expected.sub?.isWord == true) {
|
||||
val arg = call.args[mismatch]
|
||||
val argArray = if(arg is AddressOf) arg.identifier else arg
|
||||
return if(argArray?.inferType(program)?.getOrUndef()?.isSplitWordArray==true)
|
||||
Pair("argument ${mismatch + 1} cannot pass address to a split words array where a word pointer argument is expected, use a @nosplit word array instead", call.args[mismatch].position)
|
||||
else
|
||||
Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
else if(actual.isPointer && actual.sub?.isByte==true) {
|
||||
val addrOf = call.args[mismatch] as? AddressOf
|
||||
if(addrOf!=null) {
|
||||
val identType = addrOf.identifier?.inferType(program)?.getOrUndef()
|
||||
if(identType?.isSplitWordArray==true) {
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual (because arg is a @split word array) expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return Pair("argument ${mismatch + 1} type mismatch, was: $actual expected: $expected", call.args[mismatch].position)
|
||||
}
|
||||
|
||||
@@ -246,7 +246,8 @@ class TestCompilerOnExamplesVirtual: FunSpec({
|
||||
"pointers/animalgame",
|
||||
"pointers/binarytree", // TODO add to "c64" later as well
|
||||
"pointers/sortedlist", // TODO add to "c64" later as well
|
||||
"pointers/fountain" // TODO add to "c64" later as well
|
||||
"pointers/fountain", // TODO add to "c64" later as well
|
||||
"pointers/sorting" // TODO add to "c64" later as well
|
||||
),
|
||||
listOf(false, true)
|
||||
)
|
||||
|
||||
@@ -1551,8 +1551,8 @@ main {
|
||||
errors.warnings.size shouldBe 0
|
||||
errors.infos.size shouldBe 0
|
||||
errors.errors[0] shouldContain "pointer arrays can only be @split"
|
||||
errors.errors[1] shouldContain "was: ^^ubyte expected: ^^main.Node"
|
||||
errors.errors[2] shouldContain "was: ^^ubyte expected: ^^main.Node"
|
||||
errors.errors[1] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
|
||||
errors.errors[2] shouldContain "was: ^^ubyte (because arg is a @split word array) expected: ^^main.Node"
|
||||
}
|
||||
|
||||
test("passing split array of structpointers to a subroutine in various forms should be param type ptr to ubyte (the lsb part of the split array)") {
|
||||
|
||||
@@ -62,7 +62,9 @@ STRUCTS and TYPED POINTERS
|
||||
- DONE: fixed support for (assigntarget) array index dereferencing "array[2]^^" where array contains pointers to primitives: replace with poke()
|
||||
- DONE: replace str or ubyte[] param and returnvalue type into ^^ubyte rather than uword
|
||||
- DONE: allow sizeof(^^type) to return the size of a pointer
|
||||
- TODO: allow initializing a pointer array with initialized structs: ^^Node[] nodes = [ Node(), Node(), Node() ]
|
||||
|
||||
- TODO: allow initializing a pointer array with initialized structs: ^^Node[] nodes = [ Node(), Node(), Node() ] (fix missing variable error) (update sorting example)
|
||||
|
||||
- try to add support for array index dereferencing as assign target "array[2]^^.value = 99" where array is struct pointers (currently a 'no support' error)
|
||||
- try to add support for array index dereferencing as assign target "array[2].value = 99" where array is struct pointers (currently a parser error)
|
||||
- try to fix parse error l1^^.s[0] = 4242 (equivalent to l1.s[0]=4242 , which does parse correctly)
|
||||
|
||||
130
examples/pointers/sorting.p8
Normal file
130
examples/pointers/sorting.p8
Normal file
@@ -0,0 +1,130 @@
|
||||
%import floats
|
||||
%import strings
|
||||
%import textio
|
||||
|
||||
main{
|
||||
struct Country {
|
||||
str name
|
||||
float population ; millions
|
||||
uword area ; 1000 km^2
|
||||
}
|
||||
|
||||
^^Country[100] countries ; won't be fully filled
|
||||
ubyte num_countries
|
||||
|
||||
sub start() {
|
||||
; because pointer array initialization is not supported yet, we have to add the countries in separate statements for now
|
||||
add(Country("Indonesia", 285.72, 1904))
|
||||
add(Country("Congo", 112.83, 2344))
|
||||
add(Country("Vietnam", 101.60, 331))
|
||||
add(Country("United States", 347.28, 9372))
|
||||
add(Country("Iran", 92.42, 1648))
|
||||
add(Country("Turkey", 87.69, 783))
|
||||
add(Country("Brazil", 212.81, 8515))
|
||||
add(Country("Bangladesh", 175.69, 147))
|
||||
add(Country("Germany", 84.08, 357))
|
||||
add(Country("Japan", 123.10, 377))
|
||||
add(Country("India", 1463.87, 3287))
|
||||
add(Country("China", 1416.10, 9596))
|
||||
add(Country("Philippines", 116.79, 300))
|
||||
add(Country("Russia", 143.99, 17098))
|
||||
add(Country("Pakistan", 255.22, 881))
|
||||
add(Country("Nigeria", 237.53, 923))
|
||||
add(Country("Ethiopia", 135.47, 1104))
|
||||
add(Country("Mexico", 131.95, 1964))
|
||||
add(Country("Thailand", 71.62, 513))
|
||||
add(Country("Egypt", 118.37, 1002))
|
||||
|
||||
txt.print("UNSORTED:\n")
|
||||
dump()
|
||||
|
||||
sort_by_population()
|
||||
txt.print("\nSORTED BY POPULATION:\n")
|
||||
dump()
|
||||
|
||||
sort_by_area()
|
||||
txt.print("\nSORTED BY AREA:\n")
|
||||
dump()
|
||||
|
||||
sort_by_name()
|
||||
txt.print("\nSORTED BY NAME:\n")
|
||||
dump()
|
||||
}
|
||||
|
||||
sub sort_by_name() {
|
||||
; stupid slow bubble sort
|
||||
ubyte n = num_countries
|
||||
do {
|
||||
ubyte newn=0
|
||||
ubyte i
|
||||
for i in 1 to n-1 {
|
||||
if strings.compare(countries[i-1].name, countries[i].name) > 0 {
|
||||
swap(i, i-1)
|
||||
newn = i
|
||||
}
|
||||
}
|
||||
n = newn
|
||||
} until n<=1
|
||||
}
|
||||
|
||||
sub sort_by_population() {
|
||||
; stupid slow bubble sort
|
||||
ubyte n = num_countries
|
||||
do {
|
||||
ubyte newn=0
|
||||
ubyte i
|
||||
for i in 1 to n-1 {
|
||||
if countries[i-1].population < countries[i].population {
|
||||
swap(i, i-1)
|
||||
newn = i
|
||||
}
|
||||
}
|
||||
n = newn
|
||||
} until n<=1
|
||||
}
|
||||
|
||||
sub sort_by_area() {
|
||||
; stupid slow bubble sort
|
||||
ubyte n = num_countries
|
||||
do {
|
||||
ubyte newn=0
|
||||
ubyte i
|
||||
for i in 1 to n-1 {
|
||||
if countries[i-1].area < countries[i].area {
|
||||
swap(i, i-1)
|
||||
newn = i
|
||||
}
|
||||
}
|
||||
n = newn
|
||||
} until n<=1
|
||||
}
|
||||
|
||||
sub swap(ubyte i, ubyte j) {
|
||||
^^Country temp = countries[i]
|
||||
countries[i] = countries[j]
|
||||
countries[j] = temp
|
||||
}
|
||||
|
||||
sub dump() {
|
||||
txt.print("name pop.(millions) area (1000 km^2)\n")
|
||||
txt.print("-------------- --------------- ----------------\n")
|
||||
|
||||
^^Country c
|
||||
for c in countries {
|
||||
if c==0
|
||||
break
|
||||
txt.print(c.name)
|
||||
txt.column(15)
|
||||
txt.print_f(c.population)
|
||||
txt.column(31)
|
||||
txt.print_uw(c.area)
|
||||
txt.nl()
|
||||
}
|
||||
}
|
||||
|
||||
sub add(^^Country c) {
|
||||
countries[num_countries] = c
|
||||
num_countries++
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,92 +6,11 @@ main {
|
||||
}
|
||||
|
||||
sub start() {
|
||||
txt.print_ub(sizeof(^^Node))
|
||||
txt.spc()
|
||||
txt.print_ub(sizeof(^^bool))
|
||||
txt.spc()
|
||||
txt.print_ub(sizeof(^^uword))
|
||||
txt.spc()
|
||||
^^Node[] nodes = [ Node(), Node(), Node() ]
|
||||
^^Node n1 = Node()
|
||||
txt.print_uw(n1)
|
||||
txt.print_uw(nodes[0])
|
||||
txt.print_uw(nodes[1])
|
||||
txt.print_uw(nodes[2])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
;%import floats
|
||||
;%import textio
|
||||
;
|
||||
;main{
|
||||
; struct Country {
|
||||
; str name
|
||||
; float population ; millions
|
||||
; uword area ; 1000 km^2
|
||||
; }
|
||||
;
|
||||
; ^^Country[100] countries
|
||||
; ubyte num_countries
|
||||
;
|
||||
; sub start() {
|
||||
;
|
||||
; str[2] @shared names = [ "aaa", "bb"]
|
||||
;
|
||||
; add(Country("India", 1463.87, 3287))
|
||||
; add(Country("China", 1416.10, 9596))
|
||||
; add(Country("United States", 347.28, 9372))
|
||||
; add(Country("Indonesia", 285.72, 1904))
|
||||
; add(Country("Pakistan", 255.22, 881))
|
||||
; add(Country("Nigeria", 237.53, 923))
|
||||
; add(Country("Brazil", 212.81, 8515))
|
||||
; add(Country("Bangladesh", 175.69, 147))
|
||||
; add(Country("Russia", 143.99, 17098))
|
||||
; add(Country("Ethiopia", 135.47, 1104))
|
||||
; add(Country("Mexico", 131.95, 1964))
|
||||
; add(Country("Japan", 123.10, 377))
|
||||
; add(Country("Egypt", 118.37, 1002))
|
||||
; add(Country("Philippines", 116.79, 300))
|
||||
; add(Country("Congo", 112.83, 2344))
|
||||
; add(Country("Vietnam", 101.60, 331))
|
||||
; add(Country("Iran", 92.42, 1648))
|
||||
; add(Country("Turkey", 87.69, 783))
|
||||
; add(Country("Germany", 84.08, 357))
|
||||
; add(Country("Thailand", 71.62, 513))
|
||||
;
|
||||
; txt.print("UNSORTED:\n")
|
||||
; dump()
|
||||
;
|
||||
; sort_by_population(countries, num_countries)
|
||||
; txt.print("SORTED BY POPULATION:\n")
|
||||
; dump()
|
||||
;
|
||||
; sort_by_area(countries, num_countries)
|
||||
; txt.print("SORTED BY AREA:\n")
|
||||
; dump()
|
||||
; }
|
||||
;
|
||||
; sub sort_by_population(^^Country cs, ubyte length) {
|
||||
;
|
||||
; }
|
||||
;
|
||||
; sub sort_by_area(^^Country cs, ubyte length) {
|
||||
;
|
||||
; }
|
||||
;
|
||||
; sub dump() {
|
||||
; txt.print("name pop.(millions) area (1000 km^2)\n")
|
||||
; txt.print("-------------- --------------- ----------------\n")
|
||||
; ubyte ci
|
||||
; for ci in 0 to num_countries-1 {
|
||||
; ^^Country cc = countries[ci]
|
||||
; txt.print(cc.name)
|
||||
; txt.column(15)
|
||||
; txt.print_f(cc.population)
|
||||
; txt.column(31)
|
||||
; txt.print_uw(cc.area)
|
||||
; txt.nl()
|
||||
; }
|
||||
; }
|
||||
;
|
||||
; sub add(^^Country c) {
|
||||
; countries[num_countries] = c
|
||||
; num_countries++
|
||||
; }
|
||||
;}
|
||||
;
|
||||
|
||||
@@ -109,7 +109,7 @@ class SymbolTable(astProgram: PtProgram) : StNode(astProgram.name, StNodeType.GL
|
||||
|
||||
companion object {
|
||||
fun labelnameForStructInstance(call: PtBuiltinFunctionCall): String {
|
||||
require(call.name == "structalloc")
|
||||
require(call.name == "prog8_lib_structalloc")
|
||||
val structname = call.type.subType!!.scopedNameString
|
||||
// each individual call to the pseudo function structalloc(),
|
||||
// needs to generate a separate unique struct instance label.
|
||||
|
||||
@@ -121,7 +121,7 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
// don't add memory slabs in nested scope, just put them in the top level of the ST
|
||||
scope.first().add(StMemorySlab("prog8_memoryslab_$slabname", size, align, node))
|
||||
}
|
||||
else if(node.name=="structalloc") {
|
||||
else if(node.name=="prog8_lib_structalloc") {
|
||||
val struct = node.type.subType!!
|
||||
if(struct is StStruct) {
|
||||
val label = SymbolTable.labelnameForStructInstance(node)
|
||||
@@ -163,6 +163,10 @@ class SymbolTableMaker(private val program: PtProgram, private val options: Comp
|
||||
}
|
||||
is PtNumber -> StArrayElement(it.number, null, null)
|
||||
is PtBool -> StArrayElement(null, null, it.value)
|
||||
is PtBuiltinFunctionCall -> {
|
||||
val labelname = SymbolTable.labelnameForStructInstance(it)
|
||||
StArrayElement(null, labelname, null)
|
||||
}
|
||||
else -> throw AssemblyError("invalid array element $it")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -38,6 +38,10 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
else
|
||||
"& ${txt(it.dereference!!)}"
|
||||
}
|
||||
is PtBuiltinFunctionCall -> {
|
||||
require(it.name=="prog8_lib_structalloc")
|
||||
txt(it)
|
||||
}
|
||||
else -> "invalid array element $it"
|
||||
}
|
||||
}
|
||||
@@ -46,7 +50,7 @@ fun printAst(root: PtNode, skipLibraries: Boolean, output: (text: String) -> Uni
|
||||
is PtArrayIndexer -> "<arrayindexer> ${type(node.type)} ${if(node.splitWords) "[splitwords]" else ""}"
|
||||
is PtBinaryExpression -> "<expr> ${node.operator} ${type(node.type)}"
|
||||
is PtBuiltinFunctionCall -> {
|
||||
if(node.name=="structalloc") {
|
||||
if(node.name=="prog8_lib_structalloc") {
|
||||
node.type.subType!!.scopedNameString+"() <structalloc>"
|
||||
} else {
|
||||
val str = if (node.void) "void " else ""
|
||||
|
||||
Reference in New Issue
Block a user