mirror of
https://github.com/irmen/prog8.git
synced 2025-11-03 04:17:16 +00:00
building syntax support for ptr[x].field
attempting to do this by making '.' an expression operator
This commit is contained in:
@@ -62,6 +62,12 @@ internal class AstChecker(private val program: Program,
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(identifier: IdentifierReference) {
|
override fun visit(identifier: IdentifierReference) {
|
||||||
|
|
||||||
|
val parentExpr = identifier.parent as? BinaryExpression
|
||||||
|
if(parentExpr?.operator=="^^") {
|
||||||
|
return // identifiers will be checked over at the BinaryExpression itself
|
||||||
|
}
|
||||||
|
|
||||||
if(identifier.nameInSource.any { it.startsWith('_') }) {
|
if(identifier.nameInSource.any { it.startsWith('_') }) {
|
||||||
errors.err("identifiers cannot start with an underscore", identifier.position)
|
errors.err("identifiers cannot start with an underscore", identifier.position)
|
||||||
}
|
}
|
||||||
@@ -1228,6 +1234,39 @@ internal class AstChecker(private val program: Program,
|
|||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
super.visit(expr)
|
super.visit(expr)
|
||||||
|
|
||||||
|
if(expr.operator==".")
|
||||||
|
throw FatalAstException("temporary operator '.' should have been replaced by '^^' for 'walking the chain' at ${expr.position}")
|
||||||
|
|
||||||
|
if(expr.operator=="^^") {
|
||||||
|
val leftIdentfier = expr.left as? IdentifierReference
|
||||||
|
val leftIndexer = expr.left as? ArrayIndexedExpression
|
||||||
|
val rightIdentifier = expr.right as? IdentifierReference
|
||||||
|
val rightIndexer = expr.right as? ArrayIndexedExpression
|
||||||
|
if(rightIdentifier!=null) {
|
||||||
|
val struct: StructDecl? =
|
||||||
|
if (leftIdentfier != null) {
|
||||||
|
// PTR.FIELD
|
||||||
|
leftIdentfier.targetVarDecl()?.datatype?.subType as? StructDecl
|
||||||
|
} else if(leftIndexer!=null) {
|
||||||
|
// ARRAY[x].NAME --> maybe it's a pointer dereference
|
||||||
|
leftIndexer.arrayvar.targetVarDecl()?.datatype?.subType as? StructDecl
|
||||||
|
}
|
||||||
|
else null
|
||||||
|
if (struct != null) {
|
||||||
|
val field = struct.getFieldType(rightIdentifier.nameInSource.joinToString("."))
|
||||||
|
if (field == null)
|
||||||
|
errors.err("no such field '${rightIdentifier.nameInSource.joinToString(".")}' in struct '${struct.name}'", expr.position)
|
||||||
|
} else
|
||||||
|
errors.err("cannot find struct type", expr.position)
|
||||||
|
} else if(rightIndexer!=null) {
|
||||||
|
TODO("something.field[y] at ${expr.position}")
|
||||||
|
// TODO I don't think we can evaluate this because it could end up in as a struct instance, which we don't support yet... rewrite or just give an error?
|
||||||
|
} else
|
||||||
|
throw FatalAstException("expected identifier or arrayindexer after ^^ operator at ${expr.position})")
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
checkLongType(expr)
|
checkLongType(expr)
|
||||||
|
|
||||||
val leftIDt = expr.left.inferType(program)
|
val leftIDt = expr.left.inferType(program)
|
||||||
|
|||||||
@@ -132,7 +132,7 @@ internal fun Program.checkIdentifiers(errors: IErrorReporter, options: Compilati
|
|||||||
checker2.visit(this)
|
checker2.visit(this)
|
||||||
|
|
||||||
if(errors.noErrors()) {
|
if(errors.noErrors()) {
|
||||||
val lit2decl = LiteralsToAutoVars(this, errors)
|
val lit2decl = LiteralsToAutoVarsAndRecombineIdentifiers(this, errors)
|
||||||
lit2decl.visit(this)
|
lit2decl.visit(this)
|
||||||
while(errors.noErrors() && lit2decl.applyModifications()>0)
|
while(errors.noErrors() && lit2decl.applyModifications()>0)
|
||||||
lit2decl.visit(this)
|
lit2decl.visit(this)
|
||||||
|
|||||||
@@ -25,6 +25,7 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
|
|||||||
// - flatten chained assignments
|
// - flatten chained assignments
|
||||||
// - remove alias nodes
|
// - remove alias nodes
|
||||||
// - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
|
// - replace implicit pointer dereference chains (a.b.c.d) with explicit ones (a^^.b^^.c^^.d)
|
||||||
|
// - replace binary expression with "." operator with one that has a "^^" operator, to signify pointer scope traversal
|
||||||
|
|
||||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||||
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
||||||
@@ -267,6 +268,10 @@ _after:
|
|||||||
return listOf(IAstModification.ReplaceNode(expr, squareCall, parent))
|
return listOf(IAstModification.ReplaceNode(expr, squareCall, parent))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if(expr.operator==".") {
|
||||||
|
expr.operator = "^^"
|
||||||
|
}
|
||||||
|
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import prog8.code.ast.PtContainmentCheck
|
|||||||
import prog8.code.core.IErrorReporter
|
import prog8.code.core.IErrorReporter
|
||||||
|
|
||||||
|
|
||||||
internal class LiteralsToAutoVars(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
internal class LiteralsToAutoVarsAndRecombineIdentifiers(private val program: Program, private val errors: IErrorReporter) : AstWalker() {
|
||||||
|
|
||||||
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
|
override fun after(string: StringLiteral, parent: Node): Iterable<IAstModification> {
|
||||||
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
if(string.parent !is VarDecl && string.parent !is WhenChoice) {
|
||||||
@@ -160,4 +160,22 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
|||||||
// }
|
// }
|
||||||
return noModifications
|
return noModifications
|
||||||
}
|
}
|
||||||
|
|
||||||
|
override fun after(expr: BinaryExpression, parent: Node): Iterable<IAstModification> {
|
||||||
|
if(expr.operator==".") {
|
||||||
|
val leftIdent = expr.left as? IdentifierReference
|
||||||
|
val rightIndex = expr.right as? ArrayIndexedExpression
|
||||||
|
if (leftIdent != null && rightIndex != null) {
|
||||||
|
// maybe recombine IDENTIFIER . ARRAY[IDX] --> COMBINEDIDENTIFIER[IDX]
|
||||||
|
val leftTarget = leftIdent.targetStatement(null)
|
||||||
|
if(leftTarget==null || leftTarget !is StructDecl) {
|
||||||
|
val combinedName = leftIdent.nameInSource + rightIndex.arrayvar.nameInSource
|
||||||
|
val combined = IdentifierReference(combinedName, leftIdent.position)
|
||||||
|
val indexer = ArrayIndexedExpression(combined, rightIndex.indexer, leftIdent.position)
|
||||||
|
return listOf(IAstModification.ReplaceNode(expr, indexer, parent))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return noModifications
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -665,10 +665,17 @@ class SimplifiedAstMaker(private val program: Program, private val errors: IErro
|
|||||||
|
|
||||||
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
private fun transform(srcExpr: BinaryExpression): PtBinaryExpression {
|
||||||
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
val type = srcExpr.inferType(program).getOrElse { throw FatalAstException("unknown dt") }
|
||||||
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
if(srcExpr.operator=="^^") {
|
||||||
expr.add(transformExpression(srcExpr.left))
|
if(srcExpr.left is ArrayIndexedExpression) {
|
||||||
expr.add(transformExpression(srcExpr.right))
|
TODO("ptr[x].field dereference, field type=$type at ${srcExpr.position}")
|
||||||
return expr
|
} else
|
||||||
|
TODO("??? deref something ??? at ${srcExpr.position}")
|
||||||
|
} else {
|
||||||
|
val expr = PtBinaryExpression(srcExpr.operator, type, srcExpr.position)
|
||||||
|
expr.add(transformExpression(srcExpr.left))
|
||||||
|
expr.add(transformExpression(srcExpr.right))
|
||||||
|
return expr
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun transform(srcCheck: ContainmentCheck): PtExpression {
|
private fun transform(srcCheck: ContainmentCheck): PtExpression {
|
||||||
|
|||||||
@@ -92,14 +92,17 @@ class AstToSourceTextConverter(val output: (text: String) -> Unit, val program:
|
|||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(expr: BinaryExpression) {
|
override fun visit(expr: BinaryExpression) {
|
||||||
output("(")
|
val isValue = expr.parent is Assignment
|
||||||
|
if(!isValue) output("(")
|
||||||
expr.left.accept(this)
|
expr.left.accept(this)
|
||||||
if(expr.operator.any { it.isLetter() })
|
if(expr.operator.any { it.isLetter() })
|
||||||
output(" ${expr.operator} ")
|
output(" ${expr.operator} ")
|
||||||
|
else if(expr.operator=="^^")
|
||||||
|
output(".")
|
||||||
else
|
else
|
||||||
output(expr.operator)
|
output(expr.operator)
|
||||||
expr.right.accept(this)
|
expr.right.accept(this)
|
||||||
output(")")
|
if(!isValue) output(")")
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun visit(directive: Directive) {
|
override fun visit(directive: Directive) {
|
||||||
|
|||||||
@@ -231,6 +231,36 @@ class BinaryExpression(
|
|||||||
"<=", ">=",
|
"<=", ">=",
|
||||||
"==", "!=" -> InferredTypes.knownFor(BaseDataType.BOOL)
|
"==", "!=" -> InferredTypes.knownFor(BaseDataType.BOOL)
|
||||||
"<<", ">>" -> leftDt
|
"<<", ">>" -> leftDt
|
||||||
|
"." -> InferredTypes.unknown() // intermediate operator, will be replaced with '^^' after recombining scoped identifiers
|
||||||
|
"^^" -> {
|
||||||
|
val leftIdentfier = left as? IdentifierReference
|
||||||
|
val leftIndexer = left as? ArrayIndexedExpression
|
||||||
|
val rightIdentifier = right as? IdentifierReference
|
||||||
|
val rightIndexer = right as? ArrayIndexedExpression
|
||||||
|
if(rightIdentifier!=null) {
|
||||||
|
val struct: StructDecl? =
|
||||||
|
if (leftIdentfier != null) {
|
||||||
|
// PTR . FIELD
|
||||||
|
leftIdentfier.targetVarDecl()?.datatype?.subType as? StructDecl
|
||||||
|
} else if(leftIndexer!=null) {
|
||||||
|
// ARRAY[x].NAME --> maybe it's a pointer dereference
|
||||||
|
leftIndexer.arrayvar.targetVarDecl()?.datatype?.subType as? StructDecl
|
||||||
|
}
|
||||||
|
else null
|
||||||
|
if (struct != null) {
|
||||||
|
val field = struct.getFieldType(rightIdentifier.nameInSource.joinToString("."))
|
||||||
|
if (field != null)
|
||||||
|
InferredTypes.knownFor(field)
|
||||||
|
else
|
||||||
|
InferredTypes.unknown()
|
||||||
|
} else
|
||||||
|
InferredTypes.unknown()
|
||||||
|
} else if(rightIndexer!=null) {
|
||||||
|
TODO("something.field[x] at ${right.position}")
|
||||||
|
// TODO I don't think we can evaluate this type because it could end up in as a struct instance, which we don't support yet... rewrite or just give an error?
|
||||||
|
} else
|
||||||
|
InferredTypes.unknown()
|
||||||
|
}
|
||||||
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
else -> throw FatalAstException("resulting datatype check for invalid operator $operator")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -357,8 +387,10 @@ class ArrayIndexedExpression(var arrayvar: IdentifierReference,
|
|||||||
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
|
target.datatype.isString || target.datatype.isUnsignedWord -> InferredTypes.knownFor(BaseDataType.UBYTE)
|
||||||
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
|
target.datatype.isArray -> InferredTypes.knownFor(target.datatype.elementType())
|
||||||
target.datatype.isPointer -> {
|
target.datatype.isPointer -> {
|
||||||
if(target.datatype.subType!=null)
|
if(target.datatype.subTypeFromAntlr!=null)
|
||||||
TODO("indexing on pointer to struct would yield the struct type itself, this is not yet supported (only pointers) at $position")
|
InferredTypes.unknown()
|
||||||
|
else if(target.datatype.subType!=null)
|
||||||
|
InferredTypes.unknown() // TODO("indexing on pointer to struct would yield the struct type itself, this is not yet supported (only pointers) at $position")
|
||||||
else
|
else
|
||||||
InferredTypes.knownFor(target.datatype.sub!!)
|
InferredTypes.knownFor(target.datatype.sub!!)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,6 +28,7 @@ STRUCTS and TYPED POINTERS
|
|||||||
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
- DONE (for basic types only): allow array syntax on pointers too: ptr[2] means ptr+sizeof()*2, ptr[0] just means ptr^^ .
|
||||||
- allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
|
- allow array syntax on pointers to structs too, but what type will ptr[2] have? And it will require ptr[2].field to work as well now. Actually that will be the only thing to work for now.
|
||||||
- pointer arithmetic should follow C: ptr=ptr+10 adds 10*sizeof() instead of just 10.
|
- pointer arithmetic should follow C: ptr=ptr+10 adds 10*sizeof() instead of just 10.
|
||||||
|
- can we get rid of the '.' -> '^^' operator rewrite?
|
||||||
- add unit tests for all changes
|
- add unit tests for all changes
|
||||||
- arrays of structs? No -> Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
|
- arrays of structs? No -> Just an array of uword pointers to said structs. Can even be @split as the only representation form because that's the default for word arrays.
|
||||||
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
|
- static initialization of structs may be allowed only at block scope and then behaves like arrays; it won't reset to the original value when program is restarted, so beware. Syntax = TBD
|
||||||
|
|||||||
@@ -1,22 +1,25 @@
|
|||||||
%import textio
|
|
||||||
%import floats
|
|
||||||
%zeropage basicsafe
|
|
||||||
%option no_sysinit
|
|
||||||
|
|
||||||
main {
|
main {
|
||||||
sub start() {
|
sub start() {
|
||||||
|
; rotatedx[i] = A*shipdata.x[i] + B*shipdata.y[i] + C*shipdata.z[i]
|
||||||
|
; cx16.VERA_DATA0 = cx16.VERA_DATA0 & gfx_hires.plot.mask4c[cx16.r2L] | cx16.r12L
|
||||||
|
if cx16.r0[0]!='0' {
|
||||||
|
cx16.r0++
|
||||||
|
}
|
||||||
|
|
||||||
struct Node {
|
struct Node {
|
||||||
bool flag
|
bool flag
|
||||||
^^Node next
|
^^Node next
|
||||||
}
|
}
|
||||||
|
|
||||||
^^Node ptr = 2000
|
^^Node ptr
|
||||||
|
|
||||||
txt.print_uw(ptr)
|
bool zz1, zz2
|
||||||
txt.nl()
|
ptr = ptr[10].next
|
||||||
|
zz2 = ptr[10].next.flag ;; TODO fix
|
||||||
bool derp = ptr[2].flag
|
zz1 = ptr[10].next.next.flag ;; TODO fix
|
||||||
bool derp2 = ptr.next.next[2].flag
|
zz2 = ptr[10].next[2].flag ;; TODO fix
|
||||||
|
zz1 = ptr[10].next[2].flagz ;; TODO should complain about flagz only
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -189,6 +189,7 @@ postincrdecr : assign_target operator = ('++' | '--') ;
|
|||||||
expression :
|
expression :
|
||||||
'(' expression ')'
|
'(' expression ')'
|
||||||
| functioncall
|
| functioncall
|
||||||
|
| left = expression EOL? bop = '.' EOL? right = expression // "scope traversal operator"
|
||||||
| <assoc=right> prefix = ('+'|'-'|'~') expression
|
| <assoc=right> prefix = ('+'|'-'|'~') expression
|
||||||
| left = expression EOL? bop = ('*' | '/' | '%' ) EOL? right = expression
|
| left = expression EOL? bop = ('*' | '/' | '%' ) EOL? right = expression
|
||||||
| left = expression EOL? bop = ('+' | '-' ) EOL? right = expression
|
| left = expression EOL? bop = ('+' | '-' ) EOL? right = expression
|
||||||
|
|||||||
@@ -215,7 +215,7 @@ class PtBinaryExpression(val operator: String, type: DataType, position: Positio
|
|||||||
init {
|
init {
|
||||||
if(operator in ComparisonOperators + LogicalOperators)
|
if(operator in ComparisonOperators + LogicalOperators)
|
||||||
require(type.isBool)
|
require(type.isBool)
|
||||||
else
|
else if(operator!="^^")
|
||||||
require(!type.isBool) { "no bool allowed for this operator $operator"}
|
require(!type.isBool) { "no bool allowed for this operator $operator"}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user