simplify grammar of @tags, also improving their error message

This commit is contained in:
Irmen de Jong 2024-12-21 01:05:08 +01:00
parent 131d5ceb4f
commit 5482ac0302
7 changed files with 41 additions and 61 deletions

View File

@ -414,7 +414,7 @@ internal fun determineCompilationOptions(program: Program, compTarget: ICompilat
private fun processAst(program: Program, errors: IErrorReporter, compilerOptions: CompilationOptions) {
program.preprocessAst(errors, compilerOptions)
if(compilerOptions.dumpSymbols) {
if(errors.noErrors() && compilerOptions.dumpSymbols) {
printSymbols(program)
exitProcess(0)
}

View File

@ -56,6 +56,12 @@ class AstPreprocessor(val program: Program,
vardecl.value = NumericLiteral(oldAddr.type, address.toDouble(), oldAddr.position)
}
override fun before(directive: Directive, parent: Node): Iterable<IAstModification> {
if(directive.parent is Expression)
errors.err("${directive.directive} is ambiguous here as an operand for the % operator and a directive. Add spaces around the operator % to distinguish it.", directive.position)
return noModifications
}
override fun before(char: CharLiteral, parent: Node): Iterable<IAstModification> {
if(char.encoding== Encoding.DEFAULT)
char.encoding = char.definingModule.textEncoding

View File

@ -756,9 +756,9 @@ main {
val src="""
main {
sub start() {
ubyte bb1 = 199
ubyte bb2 = 12
ubyte @shared bb3 = bb1%bb2
ubyte @shared bb = 199
ubyte @shared cc = 12
ubyte @shared bb2 = bb%cc
}
}"""

View File

@ -332,12 +332,13 @@ private fun SubroutineContext.toAst() : Subroutine {
private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
sub_param().map {
val decl = it.vardecl()
val options = decl.decloptions()
if(options.ALIGNPAGE().isNotEmpty() || options.ALIGNWORD().isNotEmpty())
throw SyntaxError("cannot use alignments on parameters", it.toPosition())
if(options.DIRTY().isNotEmpty())
throw SyntaxError("cannot use @dirty on parameters", it.toPosition())
val zp = getZpOption(options)
val tags = decl.TAG().map { it.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid parameter tag '$tag'", toPosition())
}
val zp = getZpOption(tags)
var baseDt = decl.datatype()?.toAst() ?: BaseDataType.UNDEFINED
var datatype = DataType.forDt(baseDt)
if(decl.ARRAYSIG()!=null || decl.arrayindex()!=null)
@ -355,23 +356,17 @@ private fun Sub_paramsContext.toAst(): List<SubroutineParameter> =
SubroutineParameter(identifiername.text, datatype, zp, registerorpair, it.toPosition())
}
private fun getZpOption(options: DecloptionsContext?): ZeropageWish {
if(options==null)
return ZeropageWish.DONTCARE
return when {
options.ZEROPAGEREQUIRE().isNotEmpty() -> ZeropageWish.REQUIRE_ZEROPAGE
options.ZEROPAGE().isNotEmpty() -> ZeropageWish.PREFER_ZEROPAGE
options.ZEROPAGENOT().isNotEmpty() -> ZeropageWish.NOT_IN_ZEROPAGE
else -> ZeropageWish.DONTCARE
}
private fun getZpOption(tags: List<String>): ZeropageWish = when {
"@requirezp" in tags -> ZeropageWish.REQUIRE_ZEROPAGE
"@zp" in tags -> ZeropageWish.PREFER_ZEROPAGE
"@nozp" in tags -> ZeropageWish.NOT_IN_ZEROPAGE
else -> ZeropageWish.DONTCARE
}
private fun getSplitOption(options: DecloptionsContext?): SplitWish {
if(options==null)
return SplitWish.DONTCARE
private fun getSplitOption(tags: List<String>): SplitWish {
return when {
options.NOSPLIT().isNotEmpty() -> SplitWish.NOSPLIT
options.SPLIT().isNotEmpty() -> SplitWish.SPLIT
"@nosplit" in tags -> SplitWish.NOSPLIT
"@split" in tags -> SplitWish.SPLIT
else -> SplitWish.DONTCARE
}
}
@ -773,16 +768,21 @@ private fun When_choiceContext.toAst(): WhenChoice {
}
private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl {
val options = decloptions()
val zp = getZpOption(options)
val split = getSplitOption(options)
val tags = TAG().map { it.text }
val validTags = arrayOf("@zp", "@requirezp", "@nozp", "@split", "@nosplit", "@shared", "@alignword", "@alignpage", "@align64", "@dirty")
for(tag in tags) {
if(tag !in validTags)
throw SyntaxError("invalid variable tag '$tag'", toPosition())
}
val zp = getZpOption(tags)
val split = getSplitOption(tags)
val identifiers = identifier()
val identifiername = identifiers[0].NAME() ?: identifiers[0].UNDERSCORENAME()
val name = if(identifiers.size==1) identifiername.text else "<multiple>"
val isArray = ARRAYSIG() != null || arrayindex() != null
val alignword = options.ALIGNWORD().isNotEmpty()
val align64 = options.ALIGN64().isNotEmpty()
val alignpage = options.ALIGNPAGE().isNotEmpty()
val alignword = "@alignword" in tags
val align64 = "@align64" in tags
val alignpage = "@alignpage" in tags
if(alignpage && alignword)
throw SyntaxError("choose a single alignment option", toPosition())
val baseDt = datatype()?.toAst() ?: BaseDataType.UNDEFINED
@ -800,9 +800,9 @@ private fun VardeclContext.toAst(type: VarDeclType, value: Expression?): VarDecl
idname.text
},
value,
options.SHARED().isNotEmpty(),
"@shared" in tags,
if(alignword) 2u else if(align64) 64u else if(alignpage) 256u else 0u,
options.DIRTY().isNotEmpty(),
"@dirty" in tags,
toPosition()
)
}

View File

@ -20,7 +20,6 @@ TODO
Future Things and Ideas
^^^^^^^^^^^^^^^^^^^^^^^
- SourceLineCache should be removed as a separate thing, it reloads all source files again and splits them by line. It should re-use the already loaded Sources. Wrap it all in a ImporterFileSystem?
- Compiling Libraries: improve ability to create library files in prog8; for instance there's still stuff injected into the start of the start() routine AND there is separate setup logic going on before calling it.
Make up our mind! Maybe all setup does need to be put into start() ? because the program cannot function correctly when the variables aren't initialized properly bss is not cleared etc. etc.
Add a -library $xxxx command line option (and/or some directive) to preselect every setting that is required to make a library at $xxxx rather than a normal loadable and runnable program?

View File

@ -1,9 +1,4 @@
%import compression
main {
sub start() {
; compression.decode_rle(0,0,0)
; compression.decode_zx0(0,0)
compression.decode_tscrunch(0,0)
}
}

View File

@ -55,25 +55,7 @@ SINGLECHAR :
'\'' ( STRING_ESCAPE_SEQ | ~[\\\r\n\f'] ) '\''
;
ZEROPAGE : '@zp' ;
ZEROPAGEREQUIRE : '@requirezp' ;
ZEROPAGENOT: '@nozp' ;
SHARED : '@shared' ;
SPLIT: '@split' ;
NOSPLIT: '@nosplit' ;
ALIGNWORD: '@alignword' ;
ALIGN64: '@align64' ;
ALIGNPAGE: '@alignpage' ;
DIRTY: '@dirty' ;
TAG: '@' ('a'..'z' | '0'..'9')+ ;
ARRAYSIG : '[' [ \t]* ']' ;
@ -161,9 +143,7 @@ directive :
directivearg : stringliteral | identifier | integerliteral ;
vardecl: datatype (arrayindex | ARRAYSIG)? decloptions identifier (',' identifier)* ;
decloptions: (SHARED | ZEROPAGE | ZEROPAGEREQUIRE | ZEROPAGENOT | NOSPLIT | SPLIT | ALIGNWORD | ALIGN64 | ALIGNPAGE | DIRTY)* ;
vardecl: datatype (arrayindex | ARRAYSIG)? TAG* identifier (',' identifier)* ;
varinitializer : vardecl '=' expression ;
@ -302,7 +282,7 @@ asmsubroutine :
;
extsubroutine :
'extsub' ('@bank' (constbank=integerliteral | varbank=scoped_identifier))? address=expression '=' asmsub_decl
'extsub' (TAG (constbank=integerliteral | varbank=scoped_identifier))? address=expression '=' asmsub_decl
;
asmsub_decl : identifier '(' asmsub_params? ')' asmsub_clobbers? asmsub_returns? ;