antlr grammar now understands underscores in identifier names

This commit is contained in:
Irmen de Jong 2024-03-31 00:20:37 +01:00
parent 3e34a3ef72
commit 788f6b44a6
5 changed files with 39 additions and 11 deletions

View File

@ -52,6 +52,8 @@ internal class AstChecker(private val program: Program,
override fun visit(module: Module) {
super.visit(module)
if(module.name.startsWith('_'))
errors.err("module names cannot start with an underscore", module.position)
val directives = module.statements.filterIsInstance<Directive>().groupBy { it.directive }
directives.filter { it.value.size > 1 }.forEach{ entry ->
when(entry.key) {
@ -62,6 +64,10 @@ internal class AstChecker(private val program: Program,
}
override fun visit(identifier: IdentifierReference) {
if(identifier.nameInSource.any { it.startsWith('_') }) {
errors.err("identifiers cannot start with an underscore", identifier.position)
}
checkLongType(identifier)
val stmt = identifier.targetStatement(program)
if(stmt==null)
@ -266,6 +272,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(block: Block) {
if(block.name.startsWith('_'))
errors.err("block names cannot start with an underscore", block.position)
val addr = block.address
if(addr!=null && addr>65535u) {
errors.err("block memory address must be valid integer 0..\$ffff", block.position)
@ -295,6 +304,9 @@ internal class AstChecker(private val program: Program,
}
override fun visit(label: Label) {
if(label.name.startsWith('_'))
errors.err("labels cannot start with an underscore", label.position)
// scope check
if(label.parent !is Block && label.parent !is Subroutine && label.parent !is AnonymousScope) {
errors.err("Labels can only be defined in the scope of a block, a loop body, or within another subroutine", label.position)
@ -336,6 +348,9 @@ internal class AstChecker(private val program: Program,
override fun visit(subroutine: Subroutine) {
fun err(msg: String) = errors.err(msg, subroutine.position)
if(subroutine.name.startsWith('_'))
errors.err("subroutine names cannot start with an underscore", subroutine.position)
if(subroutine.name in BuiltinFunctions)
err("cannot redefine a built-in function")
@ -474,6 +489,9 @@ internal class AstChecker(private val program: Program,
// Non-string and non-ubytearray Pass-by-reference datatypes can not occur as parameters to a subroutine directly
// Instead, their reference (address) should be passed (as an UWORD).
for(p in subroutine.parameters) {
if(p.name.startsWith('_'))
errors.err("parameter names cannot start with an underscore", p.position)
if(p.type in PassByReferenceDatatypes && p.type !in listOf(DataType.STR, DataType.ARRAY_UB)) {
errors.err("this pass-by-reference type can't be used as a parameter type. Instead, use an uword to receive the address, or access the variable from the outer scope directly.", p.position)
}

View File

@ -246,7 +246,8 @@ private fun Asmsub_paramsContext.toAst(): List<AsmSubroutineParameter>
val identifiers = vardecl.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
AsmSubroutineParameter(identifiers[0].NAME().text, datatype, registerorpair, statusregister, toPosition())
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER()
AsmSubroutineParameter(identifiername.text, datatype, registerorpair, statusregister, toPosition())
}
private fun Functioncall_stmtContext.toAst(): Statement {
@ -316,7 +317,8 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
val identifiers = it.identifier()
if(identifiers.size>1)
throw SyntaxError("parameter name must be singular", identifiers[0].toPosition())
SubroutineParameter(identifiers[0].NAME().text, datatype, it.toPosition())
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER()
SubroutineParameter(identifiername.text, datatype, it.toPosition())
}
private fun Assign_targetContext.toAst() : AssignTarget {
@ -556,8 +558,9 @@ private fun StringliteralContext.toAst(): StringLiteral {
private fun Expression_listContext.toAst() = expression().map{ it.toAst() }
private fun Scoped_identifierContext.toAst() : IdentifierReference =
IdentifierReference(NAME().map { it.text }, toPosition())
private fun Scoped_identifierContext.toAst() : IdentifierReference {
return IdentifierReference(identifier().map { it.text }, toPosition())
}
private fun FloatliteralContext.toAst() = text.replace("_","").toDouble()
@ -669,7 +672,8 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
else -> ZeropageWish.DONTCARE
}
val identifiers = identifier()
val name = if(identifiers.size==1) identifiers[0].NAME().text else "<multiple>"
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME() ?: identifiers[0].UNDERSCOREPLACEHOLDER()
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null
val split = options.SPLIT().isNotEmpty()
val origDt = datatype()?.toAst() ?: DataType.UNDEFINED
@ -690,7 +694,10 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
zp,
arrayindex()?.toAst(),
name,
if(identifiers.size==1) emptyList() else identifiers.map { it.NAME().text },
if(identifiers.size==1) emptyList() else identifiers.map {
val idname = it.NAME() ?: it.UNDERSCORENAME() ?: it.UNDERSCOREPLACEHOLDER()
idname.text
},
value,
options.SHARED().isNotEmpty(),
split,

View File

@ -906,12 +906,12 @@ class ArrayLiteral(val type: InferredTypes.InferredType, // inferred because
else if(elementType in WordDatatypes && value.all { it is NumericLiteral || it is AddressOf || it is IdentifierReference}) {
val castArray = value.map {
when(it) {
is AddressOf -> it as Expression
is IdentifierReference -> it as Expression
is AddressOf -> it
is IdentifierReference -> it
is NumericLiteral -> {
val numcast = it.cast(elementType, true)
if(numcast.isValid)
numcast.valueOrZero() as Expression
numcast.valueOrZero()
else
return null // abort
}

View File

@ -2,6 +2,7 @@ TODO
====
try to replace the variable number of assignment targets by allowing placeholder '_' target
or try to do this with 'void' instead, is more prog8-like?
check souce code of examples and library, for void calls that could now be turned into multi-assign calls.

View File

@ -25,6 +25,8 @@ WS : [ \t] -> skip ;
// WS2 : '\\' EOL -> skip;
VOID: 'void';
NAME : [\p{Letter}][\p{Letter}\p{Mark}\p{Digit}_]* ; // match unicode properties
UNDERSCORENAME : '_' NAME ; // match unicode properties
UNDERSCOREPLACEHOLDER: '_' ;
DEC_INTEGER : DEC_DIGIT (DEC_DIGIT | '_')* ;
HEX_INTEGER : '$' HEX_DIGIT (HEX_DIGIT | '_')* ;
BIN_INTEGER : '%' BIN_DIGIT (BIN_DIGIT | '_')* ;
@ -220,9 +222,9 @@ breakstmt : 'break';
continuestmt: 'continue';
identifier : NAME ;
identifier : NAME | UNDERSCORENAME | UNDERSCOREPLACEHOLDER;
scoped_identifier : NAME ('.' NAME)* ;
scoped_identifier : identifier ('.' identifier)* ;
integerliteral : intpart=(DEC_INTEGER | HEX_INTEGER | BIN_INTEGER) ;