mirror of
https://github.com/irmen/prog8.git
synced 2024-11-22 00:31:56 +00:00
added alias statement
This commit is contained in:
parent
504c80cddf
commit
a82f211f9a
@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<module type="WEB_MODULE" version="4">
|
||||
<component name="NewModuleRootManager" inherit-compiler-output="true">
|
||||
<exclude-output />
|
||||
<content url="file://$MODULE_DIR$">
|
||||
<excludeFolder url="file://$MODULE_DIR$/compiled" />
|
||||
</content>
|
||||
<orderEntry type="sourceFolder" forTests="false" />
|
||||
</component>
|
||||
</module>
|
@ -285,7 +285,7 @@ internal class ConstantIdentifierReplacer(
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
// replace identifiers that refer to const value, with the value itself
|
||||
// if it's a simple type and if it's not a left hand side variable
|
||||
if(identifier.parent is AssignTarget)
|
||||
if(identifier.parent is AssignTarget || identifier.parent is Alias)
|
||||
return noModifications
|
||||
var forloop = identifier.parent as? ForLoop
|
||||
if(forloop==null)
|
||||
|
@ -290,6 +290,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
for (statement in block.statements) {
|
||||
val ok = when (statement) {
|
||||
is Alias,
|
||||
is Block,
|
||||
is Directive,
|
||||
is Label,
|
||||
|
@ -193,6 +193,9 @@ internal fun IdentifierReference.checkFunctionOrLabelExists(program: Program, st
|
||||
else
|
||||
errors.err("cannot call that: ${this.nameInSource.joinToString(".")}", this.position)
|
||||
}
|
||||
is Alias -> {
|
||||
return targetStatement
|
||||
}
|
||||
null -> {
|
||||
errors.undefined(this.nameInSource, this.position)
|
||||
}
|
||||
|
@ -36,6 +36,11 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
errors.err("invalid number of arguments: expected ${params.size} got $numArgs", pos)
|
||||
}
|
||||
|
||||
override fun visit(alias: Alias) {
|
||||
if(alias.target.targetStatement(program)==null)
|
||||
errors.err("undefined symbol: ${alias.target.nameInSource.joinToString(".") }", alias.target.position)
|
||||
}
|
||||
|
||||
override fun visit(block: Block) {
|
||||
val existing = blocks[block.name]
|
||||
if(existing!=null) {
|
||||
@ -210,6 +215,7 @@ internal class AstIdentifiersChecker(private val errors: IErrorReporter,
|
||||
if(target.type!=VarDeclType.VAR || target.datatype!=DataType.UWORD)
|
||||
errors.err("wrong address variable datatype, expected uword", call.target.position)
|
||||
}
|
||||
is Alias -> {}
|
||||
null -> {}
|
||||
else -> errors.err("cannot call this as a subroutine or function", call.target.position)
|
||||
}
|
||||
|
@ -249,6 +249,14 @@ class AstPreprocessor(val program: Program,
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
val tgt = alias.target.targetStatement(program)
|
||||
if(tgt is Block) {
|
||||
errors.err("cannot alias blocks", alias.target.position)
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
private fun checkStringParam(call: IFunctionCall, stmt: Statement) {
|
||||
val targetStatement = call.target.checkFunctionOrLabelExists(program, stmt, errors)
|
||||
if(targetStatement!=null) {
|
||||
|
@ -23,6 +23,11 @@ internal class CodeDesugarer(val program: Program, private val errors: IErrorRep
|
||||
// - pointer[word] replaced by @(pointer+word)
|
||||
// - @(&var) and @(&var+1) replaced by lsb(var) and msb(var) if var is a word
|
||||
// - flatten chained assignments
|
||||
// - remove alias nodes
|
||||
|
||||
override fun after(alias: Alias, parent: Node): Iterable<IAstModification> {
|
||||
return listOf(IAstModification.Remove(alias, parent as IStatementContainer))
|
||||
}
|
||||
|
||||
override fun before(breakStmt: Break, parent: Node): Iterable<IAstModification> {
|
||||
fun jumpAfter(stmt: Statement): Iterable<IAstModification> {
|
||||
|
@ -41,6 +41,7 @@ class IntermediateAstMaker(private val program: Program, private val errors: IEr
|
||||
is ChainedAssignment -> throw FatalAstException("ChainedAssignment should have been flattened")
|
||||
is Assignment -> transform(statement)
|
||||
is Block -> transform(statement)
|
||||
is Alias -> throw FatalAstException("alias should have been desugared")
|
||||
is Break -> throw FatalAstException("break should have been replaced by Goto")
|
||||
is Continue -> throw FatalAstException("continue should have been replaced by Goto")
|
||||
is BuiltinFunctionCallStatement -> transform(statement)
|
||||
|
@ -5,9 +5,7 @@ import prog8.ast.IStatementContainer
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.WhenChoice
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.walk.AstWalker
|
||||
import prog8.ast.walk.IAstModification
|
||||
import prog8.code.ast.PtContainmentCheck
|
||||
@ -108,4 +106,24 @@ internal class LiteralsToAutoVars(private val program: Program, private val erro
|
||||
}
|
||||
return noModifications
|
||||
}
|
||||
|
||||
override fun after(identifier: IdentifierReference, parent: Node): Iterable<IAstModification> {
|
||||
val target = identifier.targetStatement(program)
|
||||
if(target is Alias) {
|
||||
return listOf(IAstModification.ReplaceNode(identifier, target.target.copy(position = identifier.position), parent))
|
||||
}
|
||||
|
||||
// experimental code to be able to alias blocks too:
|
||||
// if(target is INamedStatement) {
|
||||
// if (identifier.nameInSource != target.scopedName) {
|
||||
// val blockAlias = identifier.definingScope.lookup(identifier.nameInSource.take(1))
|
||||
// if(blockAlias is Alias) {
|
||||
// val newname = mutableListOf(blockAlias.target.nameInSource.single())
|
||||
// newname.addAll(identifier.nameInSource.drop(1))
|
||||
// return listOf(IAstModification.ReplaceNode(identifier, IdentifierReference(newname, position = identifier.position), parent))
|
||||
// }
|
||||
// }
|
||||
// }
|
||||
return noModifications
|
||||
}
|
||||
}
|
||||
|
@ -727,5 +727,69 @@ main {
|
||||
val handler = sub.children[7] as PtSub
|
||||
handler.name shouldBe "p8s_prog8_invoke_defers"
|
||||
}
|
||||
|
||||
test("aliases ok") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false) shouldNotBe null
|
||||
}
|
||||
|
||||
test("wrong alias gives correct error") {
|
||||
val src="""
|
||||
main {
|
||||
alias print = txt.print2222
|
||||
alias width = txt.DEFAULT_WIDTH
|
||||
|
||||
sub start() {
|
||||
alias print2 = txt.print
|
||||
alias width2 = txt.DEFAULT_WIDTH_XXX
|
||||
print("one")
|
||||
print2("two")
|
||||
txt.print_ub(width)
|
||||
txt.print_ub(width2)
|
||||
}
|
||||
}
|
||||
|
||||
txt {
|
||||
const ubyte DEFAULT_WIDTH = 80
|
||||
sub print_ub(ubyte value) {
|
||||
; nothing
|
||||
}
|
||||
sub print(str msg) {
|
||||
; nothing
|
||||
}
|
||||
}
|
||||
|
||||
"""
|
||||
val errors = ErrorReporterForTests()
|
||||
compileText(C64Target(), optimize=false, src, writeAssembly=false, errors=errors) shouldBe null
|
||||
errors.errors.size shouldBe 2
|
||||
errors.errors[0] shouldContain "undefined symbol: txt.print2222"
|
||||
errors.errors[1] shouldContain "undefined symbol: txt.DEFAULT_WIDTH_XXX"
|
||||
}
|
||||
})
|
||||
|
||||
|
@ -126,6 +126,7 @@ interface IStatementContainer {
|
||||
return found
|
||||
}
|
||||
}
|
||||
is Alias -> if(stmt.alias==name) return stmt
|
||||
else -> {
|
||||
// NOTE: when other nodes containing AnonymousScope are introduced,
|
||||
// these should be added here as well to look into!
|
||||
@ -155,6 +156,19 @@ interface INameScope: IStatementContainer, INamedStatement {
|
||||
|
||||
private fun lookupQualified(scopedName: List<String>): Statement? {
|
||||
// a scoped name refers to a name in another namespace, and stars from the root.
|
||||
|
||||
// experimental code to be able to alias blocks too:
|
||||
// val stmt = this.lookup(listOf(scopedName[0])) ?: return null
|
||||
// if(stmt is Alias) {
|
||||
// val block = this.lookup(stmt.target.nameInSource) ?: return null
|
||||
// var statement = block as Statement?
|
||||
// for(name in scopedName.drop(1)) {
|
||||
// statement = (statement as? IStatementContainer)?.searchSymbol(name)
|
||||
// if(statement==null)
|
||||
// return null
|
||||
// }
|
||||
// return statement
|
||||
// }
|
||||
for(module in (this as Node).definingModule.program.modules) {
|
||||
val block = module.searchSymbol(scopedName[0])
|
||||
if(block!=null) {
|
||||
|
@ -42,6 +42,7 @@ internal fun BlockContext.toAst(isInLibrary: Boolean) : Block {
|
||||
it.inlineasm()!=null -> it.inlineasm().toAst()
|
||||
it.inlineir()!=null -> it.inlineir().toAst()
|
||||
it.labeldef()!=null -> it.labeldef().toAst()
|
||||
it.alias()!=null -> it.alias().toAst()
|
||||
else -> throw FatalAstException("weird block node $it")
|
||||
}
|
||||
}
|
||||
@ -159,6 +160,9 @@ private fun StatementContext.toAst() : Statement {
|
||||
val deferstmt = defer()?.toAst()
|
||||
if(deferstmt!=null) return deferstmt
|
||||
|
||||
val aliasstmt = alias()?.toAst()
|
||||
if(aliasstmt!=null) return aliasstmt
|
||||
|
||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||
}
|
||||
|
||||
@ -291,7 +295,10 @@ private fun UnconditionaljumpContext.toAst(): Jump {
|
||||
}
|
||||
|
||||
private fun LabeldefContext.toAst(): Statement =
|
||||
Label(children[0].text, toPosition())
|
||||
Label(children[0].text, toPosition())
|
||||
|
||||
private fun AliasContext.toAst(): Statement =
|
||||
Alias(identifier().text, scoped_identifier().toAst(), toPosition())
|
||||
|
||||
private fun SubroutineContext.toAst() : Subroutine {
|
||||
// non-asm subroutine
|
||||
|
@ -138,6 +138,22 @@ data class DirectiveArg(val str: String?, val name: String?, val int: UInt?, ove
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = false
|
||||
}
|
||||
|
||||
data class Alias(val alias: String, val target: IdentifierReference, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
target.parent = this
|
||||
}
|
||||
|
||||
override fun copy(): Statement = Alias(alias, target.copy(), position)
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
override fun accept(visitor: AstWalker, parent: Node) = visitor.visit(this, parent)
|
||||
override fun replaceChildNode(node: Node, replacement: Node) = throw FatalAstException("can't replace here")
|
||||
override fun referencesIdentifier(nameInSource: List<String>): Boolean = (nameInSource.size==1 && nameInSource[0]==alias) || target.referencesIdentifier(nameInSource)
|
||||
}
|
||||
|
||||
|
||||
data class Label(override val name: String, override val position: Position) : Statement(), INamedStatement {
|
||||
override lateinit var parent: Node
|
||||
|
||||
|
@ -122,6 +122,7 @@ abstract class AstWalker {
|
||||
open fun before(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(label: Label, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(alias: Alias, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun before(module: Module, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@ -168,6 +169,7 @@ abstract class AstWalker {
|
||||
open fun after(inlineAssembly: InlineAssembly, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(jump: Jump, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(label: Label, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(alias: Alias, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(memread: DirectMemoryRead, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(memwrite: DirectMemoryWrite, parent: Node): Iterable<IAstModification> = noModifications
|
||||
open fun after(module: Module, parent: Node): Iterable<IAstModification> = noModifications
|
||||
@ -344,6 +346,12 @@ abstract class AstWalker {
|
||||
track(after(label, parent), label, parent)
|
||||
}
|
||||
|
||||
fun visit(alias: Alias, parent: Node) {
|
||||
track(before(alias, parent), alias, parent)
|
||||
alias.target.accept(this, alias)
|
||||
track(after(alias, parent), alias, parent)
|
||||
}
|
||||
|
||||
fun visit(numLiteral: NumericLiteral, parent: Node) {
|
||||
track(before(numLiteral, parent), numLiteral, parent)
|
||||
track(after(numLiteral, parent), numLiteral, parent)
|
||||
|
@ -26,6 +26,10 @@ interface IAstVisitor {
|
||||
fun visit(directive: Directive) {
|
||||
}
|
||||
|
||||
fun visit(alias: Alias) {
|
||||
alias.target.accept(this)
|
||||
}
|
||||
|
||||
fun visit(containment: ContainmentCheck) {
|
||||
containment.element.accept(this)
|
||||
containment.iterable.accept(this)
|
||||
|
@ -145,6 +145,14 @@ to access a variable from a nested subroutine::
|
||||
}
|
||||
}
|
||||
|
||||
**Aliases** make it easier to refer to symbols from other places. They save
|
||||
you from having to type the fully scoped name everytime you need to access that symbol.
|
||||
Aliases can be created in any scope except at the module level.
|
||||
You can create and use an alias with the ``alias`` statement like so::
|
||||
|
||||
alias prn = txt.print_ub
|
||||
...
|
||||
prn(score)
|
||||
|
||||
|
||||
.. important::
|
||||
|
@ -296,6 +296,15 @@ scoped names always need to be fully scoped (because they always start in the gl
|
||||
main.start ; the entrypoint subroutine
|
||||
main.start.variable ; a variable in the entrypoint subroutine
|
||||
|
||||
**Aliases**
|
||||
|
||||
The ``alias`` statement makes it easier to refer to symbols from other places, and they can save
|
||||
you from having to type the fully scoped name everytime you need to access that symbol.
|
||||
Aliases can be created in any scope except at the module level.
|
||||
An alias is created with ``alias <name> = <target>`` and then you can use ``<name>`` as if it were ``<target>``.
|
||||
It is possible to alias variables, labels and subroutines, but not whole blocks.
|
||||
The name has to be an unscoped identifier name, the target can be any symbol.
|
||||
|
||||
|
||||
Code blocks
|
||||
-----------
|
||||
|
@ -1,24 +1,18 @@
|
||||
%import textio
|
||||
%option no_sysinit
|
||||
%zeropage dontuse
|
||||
|
||||
%zeropage basicsafe
|
||||
|
||||
main {
|
||||
sub start() {
|
||||
derp()
|
||||
derp2()
|
||||
}
|
||||
alias prn = txt.print_ub
|
||||
alias spc = txt.spc
|
||||
alias nl = txt.nl
|
||||
|
||||
asmsub derp() {
|
||||
%asm {{
|
||||
nop
|
||||
nop
|
||||
}}
|
||||
}
|
||||
|
||||
inline asmsub derp2() {
|
||||
%asm {{
|
||||
nop
|
||||
nop
|
||||
}}
|
||||
prn(10)
|
||||
spc()
|
||||
prn(20)
|
||||
spc()
|
||||
prn(30)
|
||||
nl()
|
||||
}
|
||||
}
|
||||
|
@ -89,6 +89,7 @@ block_statement:
|
||||
| inlineasm
|
||||
| inlineir
|
||||
| labeldef
|
||||
| alias
|
||||
;
|
||||
|
||||
|
||||
@ -116,6 +117,7 @@ statement :
|
||||
| continuestmt
|
||||
| labeldef
|
||||
| defer
|
||||
| alias
|
||||
;
|
||||
|
||||
|
||||
@ -133,6 +135,8 @@ subroutinedeclaration :
|
||||
| romsubroutine
|
||||
;
|
||||
|
||||
alias: 'alias' identifier '=' scoped_identifier ;
|
||||
|
||||
defer: 'defer' (statement | statement_block) ;
|
||||
|
||||
labeldef : identifier ':' ;
|
||||
|
Loading…
Reference in New Issue
Block a user