mirror of
https://github.com/irmen/prog8.git
synced 2026-04-20 11:17:01 +00:00
improve docs about recursion
This commit is contained in:
@@ -691,11 +691,10 @@ internal class AstChecker(private val program: Program,
|
||||
// unfortunately the AST regarding pointer dereferencing is a bit of a mess, and we cannot do precise type checking on elements inside such expressions yet.
|
||||
if(assignment.value.inferType(program).isUnknown) {
|
||||
val binexpr = assignment.value as? BinaryExpression
|
||||
if(binexpr?.operator == ".") {
|
||||
errors.err("invalid pointer dereference (can't determine type)", assignment.value.position)
|
||||
if (binexpr?.operator != ".") {
|
||||
if(assignment.value !is PtrDereference && assignment.target.multi==null)
|
||||
errors.err("invalid assignment value", assignment.value.position)
|
||||
}
|
||||
else if(assignment.value !is PtrDereference && assignment.target.multi==null)
|
||||
errors.err("invalid assignment value", assignment.value.position)
|
||||
}
|
||||
|
||||
super.visit(assignment)
|
||||
@@ -1396,6 +1395,16 @@ internal class AstChecker(private val program: Program,
|
||||
)
|
||||
}
|
||||
}
|
||||
} else if(leftDt.isStructInstance) {
|
||||
val struct = leftDt.getOrUndef().subType as StructDecl
|
||||
if (rightIdentifier.nameInSource.size == 1) {
|
||||
val fieldDt = struct.getFieldType(rightIdentifier.nameInSource.single())
|
||||
if (fieldDt == null)
|
||||
errors.err(
|
||||
"no such field '${rightIdentifier.nameInSource.single()}' in struct '${struct.scopedNameString}'",
|
||||
rightIdentifier.position
|
||||
)
|
||||
}
|
||||
} else
|
||||
errors.err("cannot find struct type", expr.left.position)
|
||||
}
|
||||
@@ -2108,7 +2117,21 @@ internal class AstChecker(private val program: Program,
|
||||
if((deref.parent as? BinaryExpression)?.operator==".") {
|
||||
throw FatalAstException("binexpr with '.' operator should have been converted into PtrDereference or something else ${deref.position}")
|
||||
}
|
||||
if(deref.inferType(program).isUnknown)
|
||||
if(deref.chain.size>1) {
|
||||
val field = deref.chain.last()
|
||||
val structname = deref.chain.dropLast(1)
|
||||
val variable = deref.definingScope.lookup(structname) as? VarDecl
|
||||
if (variable != null) {
|
||||
if(variable.datatype.isStructInstance || variable.datatype.isPointer) {
|
||||
val struct = variable.datatype.subType!! as StructDecl
|
||||
if(struct.getFieldType(field)==null) {
|
||||
errors.err("no such field '$field' in struct '${struct.name}'", deref.position)
|
||||
return
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
if (deref.inferType(program).isUnknown)
|
||||
errors.err("unable to determine type of dereferenced pointer expression", deref.position)
|
||||
}
|
||||
|
||||
|
||||
@@ -206,8 +206,10 @@ class AstPreprocessor(val program: Program,
|
||||
val node = decl.definingScope.lookup(antlrTypeName)
|
||||
if(node==null) {
|
||||
errors.err("cannot find struct type ${antlrTypeName.joinToString(".")}", decl.position)
|
||||
} else {
|
||||
decl.datatype.setActualSubType(node as StructDecl)
|
||||
} else if(node is StructDecl) {
|
||||
decl.datatype.setActualSubType(node)
|
||||
} else if(antlrTypeName.size==1 && antlrTypeName[0] in program.builtinFunctions.names) {
|
||||
errors.err("builtin function can only be called, not used as a type name", decl.position)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -598,6 +598,28 @@ main {
|
||||
err[0] shouldContain("15:16: incompatible value type, can only assign uword or correct pointer")
|
||||
}
|
||||
|
||||
test("unknown field") {
|
||||
val src="""
|
||||
main {
|
||||
sub start() {
|
||||
struct Node {
|
||||
ubyte weight
|
||||
}
|
||||
^^Node nodes
|
||||
nodes^^.zzz1 = 99
|
||||
cx16.r0L = nodes^^.zzz2
|
||||
cx16.r0L = nodes[2].zzz3
|
||||
}
|
||||
}"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(VMTarget(), false, src, outputDir, errors=errors, writeAssembly = false) shouldBe null
|
||||
val err = errors.errors
|
||||
err.size shouldBe 3
|
||||
err[0] shouldContain("no such field 'zzz1'")
|
||||
err[1] shouldContain("no such field 'zzz2'")
|
||||
err[2] shouldContain("no such field 'zzz3'")
|
||||
}
|
||||
|
||||
|
||||
class Struct(override val scopedNameString: String) : ISubType {
|
||||
override fun memsize(sizer: IMemSizer): Int {
|
||||
@@ -975,7 +997,6 @@ main {
|
||||
compileText(VMTarget(), true, src, outputDir) shouldNotBe null
|
||||
}
|
||||
|
||||
|
||||
test("local struct var deref type") {
|
||||
val src="""
|
||||
main {
|
||||
|
||||
@@ -64,7 +64,7 @@ Subroutines
|
||||
-----------
|
||||
- Subroutines can be nested. Inner subroutines can directly access variables from their parent.
|
||||
- Subroutine parameters are just local variables in the subroutine. (you can access them directly as such via their scoped name, if you want)
|
||||
- There is no call stack for subroutine arguments: subroutine parameters are overwritten when called again. Thus recursion is not easily possible, but you can do it with manual stack manipulations.
|
||||
- There is no call stack for subroutine arguments: subroutine parameters are overwritten when called again. Thus recursion is not easily possible, but you can still do it with manual stack handling.
|
||||
There are a couple of example programs that show how to solve this in different ways, among which are fractal-tree.p8, maze.p8 and queens.p8
|
||||
- There is no function overloading (except for a couple of builtin functions).
|
||||
- Subroutines can return multiple return values, and you can multi-assign those in a single statement.
|
||||
|
||||
@@ -1185,9 +1185,14 @@ Otherwise the compiler will warn you about discarding the result of the call.
|
||||
|
||||
.. caution::
|
||||
Note that due to the way parameters are processed by the compiler,
|
||||
subroutines are *non-reentrant*. This means you cannot create recursive calls.
|
||||
If you do need a recursive algorithm, you'll have to hand code it in embedded assembly for now,
|
||||
or rewrite it into an iterative algorithm.
|
||||
subroutines are *non-reentrant*. This means you cannot create *recursive calls* (routines calling themselves)
|
||||
without doing some manual work to save and restore the variables that need to retain their value between calls.
|
||||
If you really need a recursive algorithm, there are a few options:
|
||||
|
||||
- hand code it in embedded assembly
|
||||
- rewrite it as an iterative algorithm if possible
|
||||
- use manual stack handling of the variables that need to retain their values, perhaps using a manual stack or using `push()` and `pop()`
|
||||
|
||||
Also, subroutines used in the main program should not be used from an IRQ handler. This is because
|
||||
the subroutine may be interrupted, and will then call itself from the IRQ handler. Results are
|
||||
then undefined because the variables will get overwritten.
|
||||
|
||||
@@ -45,7 +45,7 @@ So the syntax for declaring typed pointers looks like this:
|
||||
So for example; ``^^word[100] values`` declares values to be an array of 100 pointers to words.
|
||||
Note that an array of pointers (regardless of the type they point to) is always a @split word array at this time.
|
||||
(this is the most efficient way to access the pointers, and they need to be copied to zeropage first to
|
||||
be able to use them anyway. It also allows for arrays of up to 256 pointers instead of 128.)
|
||||
be able to use them anyway. It also allows for arrays of up to 256 pointers instead of 128.)
|
||||
|
||||
It is not possible to define pointers to *arrays*; ``^^(type[])`` is invalid syntax.
|
||||
|
||||
|
||||
@@ -139,6 +139,8 @@ Calling a subroutine requires three steps:
|
||||
#. calling the subroutine
|
||||
#. preparing the return value (if any) and returning that from the call.
|
||||
|
||||
*There is no stack handling involved: Prog8 doesn't have call stack frames.*
|
||||
|
||||
|
||||
Regular subroutines
|
||||
^^^^^^^^^^^^^^^^^^^
|
||||
|
||||
@@ -1,11 +1,13 @@
|
||||
TODO
|
||||
====
|
||||
|
||||
make more use of ISubType interface itself rather than casting it to StructDecl all the time
|
||||
|
||||
fix ^^Node nodes / cx16.r0L = nodes[2].weight
|
||||
don't write pointer types into P8IR files, just write uword as the type? (actually breaks the VARIABLESWITHINIT now that zp vars get initialized to 0 again; all the pointer examples won't compile anymore)
|
||||
fix countries[2]^^ = 0 compiler crash
|
||||
fix ^^Node nodes / cx16.r0L = nodes[2].weight
|
||||
fix passing array of structptrs to subroutine , arg type mismatches
|
||||
disallow ^^str entirely??
|
||||
disallow ^^str
|
||||
|
||||
|
||||
STRUCTS and TYPED POINTERS
|
||||
@@ -54,7 +56,7 @@ STRUCTS and TYPED POINTERS
|
||||
- DONE: @(ptr) complains that ptr is not uword when ptr is ^^ubyte (should be allowed)
|
||||
- DONE: pointer[0] should be replaced with @(pointer) if pointer is ^^ubyte, so these are now all identical: ptr[0], ptr^^, @(ptr) if ptr is ^^ubyte
|
||||
- DONE: STR should be asssignment compatible with UBYTE^^ but local scoped STR should still be accessed directly using LDA str,Y instead of through the pointer, like arrays.
|
||||
- DONE: replace ^^str by ^^ubyte
|
||||
- DONE: disallow ^^str
|
||||
- DONE: allow return ubyte/uword when pointer type is expected as return value type
|
||||
- DONE: fix _msb/_lsb storage of the split-words pointer-arrays
|
||||
- DONE: what about static initialization of an array of struct pointers? -> impossible right now because the pointer values are not constants.
|
||||
|
||||
Reference in New Issue
Block a user