mirror of
https://github.com/irmen/prog8.git
synced 2025-10-25 05:18:38 +00:00
using sealed class instead of interface
This commit is contained in:
@@ -1,18 +1,167 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.FatalAstException
|
||||
import prog8.ast.base.NameError
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.Position
|
||||
import prog8.ast.statements.Block
|
||||
import prog8.ast.statements.Label
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.functions.BuiltinFunctions
|
||||
import java.nio.file.Path
|
||||
|
||||
|
||||
interface Node {
|
||||
val position: Position
|
||||
var parent: Node // will be linked correctly later (late init)
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun definingModule(): Module {
|
||||
if(this is Module)
|
||||
return this
|
||||
return findParentNode<Module>(this)!!
|
||||
}
|
||||
|
||||
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
||||
|
||||
fun definingScope(): INameScope {
|
||||
val scope = findParentNode<INameScope>(this)
|
||||
if(scope!=null) {
|
||||
return scope
|
||||
}
|
||||
if(this is Label && this.name.startsWith("builtin::")) {
|
||||
return BuiltinFunctionScopePlaceholder
|
||||
}
|
||||
if(this is GlobalNamespace)
|
||||
return this
|
||||
throw FatalAstException("scope missing from $this")
|
||||
}
|
||||
}
|
||||
|
||||
interface IFunctionCall {
|
||||
var target: IdentifierReference
|
||||
var arglist: MutableList<Expression>
|
||||
}
|
||||
|
||||
interface INameScope {
|
||||
val name: String
|
||||
val position: Position
|
||||
val statements: MutableList<Statement>
|
||||
val parent: Node
|
||||
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun subScopes(): Map<String, INameScope> {
|
||||
val subscopes = mutableMapOf<String, INameScope>()
|
||||
for(stmt in statements) {
|
||||
when(stmt) {
|
||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
||||
is ForLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is RepeatLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is BranchStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is IfStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is WhenStatement -> {
|
||||
stmt.choices.forEach { subscopes[it.statements.name] = it.statements }
|
||||
}
|
||||
is INameScope -> subscopes[stmt.name] = stmt
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
return subscopes
|
||||
}
|
||||
|
||||
fun getLabelOrVariable(name: String): Statement? {
|
||||
// this is called A LOT and could perhaps be optimized a bit more,
|
||||
// but adding a memoization cache didn't make much of a practical runtime difference
|
||||
for (stmt in statements) {
|
||||
if (stmt is VarDecl && stmt.name==name) return stmt
|
||||
if (stmt is Label && stmt.name==name) return stmt
|
||||
if (stmt is AnonymousScope) {
|
||||
val sub = stmt.getLabelOrVariable(name)
|
||||
if(sub!=null)
|
||||
return sub
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun allDefinedSymbols(): List<Pair<String, Statement>> {
|
||||
return statements.mapNotNull {
|
||||
when (it) {
|
||||
is Label -> it.name to it
|
||||
is VarDecl -> it.name to it
|
||||
is Subroutine -> it.name to it
|
||||
is Block -> it.name to it
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lookup(scopedName: List<String>, localContext: Node) : Statement? {
|
||||
if(scopedName.size>1) {
|
||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||
// try the struct first.
|
||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||
val struct = thing?.struct
|
||||
if (struct != null) {
|
||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||
// return ref to the mangled name variable
|
||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||
return thing.definingScope().getLabelOrVariable(mangled)
|
||||
}
|
||||
}
|
||||
|
||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||
for(module in localContext.definingModule().program.modules) {
|
||||
var scope: INameScope? = module
|
||||
for(name in scopedName.dropLast(1)) {
|
||||
scope = scope?.subScopes()?.get(name)
|
||||
if(scope==null)
|
||||
break
|
||||
}
|
||||
if(scope!=null) {
|
||||
val result = scope.getLabelOrVariable(scopedName.last())
|
||||
if(result!=null)
|
||||
return result
|
||||
return scope.subScopes()[scopedName.last()] as Statement?
|
||||
}
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
// unqualified name, find the scope the localContext is in, look in that first
|
||||
var statementScope = localContext
|
||||
while(statementScope !is ParentSentinel) {
|
||||
val localScope = statementScope.definingScope()
|
||||
val result = localScope.getLabelOrVariable(scopedName[0])
|
||||
if (result != null)
|
||||
return result
|
||||
val subscope = localScope.subScopes()[scopedName[0]] as Statement?
|
||||
if (subscope != null)
|
||||
return subscope
|
||||
// not found in this scope, look one higher up
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||
|
||||
fun remove(stmt: Statement) {
|
||||
if(!statements.remove(stmt))
|
||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/*********** Everything starts from here, the Program; zero or more modules *************/
|
||||
|
||||
class Program(val name: String, val modules: MutableList<Module>) {
|
||||
@@ -35,7 +184,7 @@ class Program(val name: String, val modules: MutableList<Module>) {
|
||||
}
|
||||
|
||||
class Module(override val name: String,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override var statements: MutableList<Statement>,
|
||||
override val position: Position,
|
||||
val isLibraryModule: Boolean,
|
||||
val source: Path) : Node, INameScope {
|
||||
@@ -59,14 +208,14 @@ class Module(override val name: String,
|
||||
class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||
override val name = "<<<global>>>"
|
||||
override val position = Position("<<<global>>>", 0, 0, 0)
|
||||
override val statements = mutableListOf<IStatement>()
|
||||
override val statements = mutableListOf<Statement>()
|
||||
override var parent: Node = ParentSentinel
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
modules.forEach { it.linkParents(this) }
|
||||
}
|
||||
|
||||
override fun lookup(scopedName: List<String>, localContext: Node): IStatement? {
|
||||
override fun lookup(scopedName: List<String>, localContext: Node): Statement? {
|
||||
if (scopedName.size == 1 && scopedName[0] in BuiltinFunctions) {
|
||||
// builtin functions always exist, return a dummy localContext for them
|
||||
val builtinPlaceholder = Label("builtin::${scopedName.last()}", localContext.position)
|
||||
@@ -100,7 +249,7 @@ class GlobalNamespace(val modules: List<Module>): Node, INameScope {
|
||||
object BuiltinFunctionScopePlaceholder : INameScope {
|
||||
override val name = "<<builtin-functions-scope-placeholder>>"
|
||||
override val position = Position("<<placeholder>>", 0, 0, 0)
|
||||
override var statements = mutableListOf<IStatement>()
|
||||
override var statements = mutableListOf<Statement>()
|
||||
override var parent: Node = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
}
|
||||
|
||||
@@ -1,224 +0,0 @@
|
||||
package prog8.ast
|
||||
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
|
||||
|
||||
// TODO sealed classes instead??
|
||||
|
||||
|
||||
interface Node {
|
||||
val position: Position
|
||||
var parent: Node // will be linked correctly later (late init)
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun definingModule(): Module {
|
||||
if(this is Module)
|
||||
return this
|
||||
return findParentNode<Module>(this)!!
|
||||
}
|
||||
|
||||
fun definingSubroutine(): Subroutine? = findParentNode<Subroutine>(this)
|
||||
|
||||
fun definingScope(): INameScope {
|
||||
val scope = findParentNode<INameScope>(this)
|
||||
if(scope!=null) {
|
||||
return scope
|
||||
}
|
||||
if(this is Label && this.name.startsWith("builtin::")) {
|
||||
return BuiltinFunctionScopePlaceholder
|
||||
}
|
||||
if(this is GlobalNamespace)
|
||||
return this
|
||||
throw FatalAstException("scope missing from $this")
|
||||
}
|
||||
}
|
||||
|
||||
interface IStatement : Node {
|
||||
fun accept(visitor: IAstModifyingVisitor) : IStatement
|
||||
fun accept(visitor: IAstVisitor)
|
||||
fun makeScopedName(name: String): String {
|
||||
// easy way out is to always return the full scoped name.
|
||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||
// and like this, we can cache the name even,
|
||||
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
||||
val scope = mutableListOf<String>()
|
||||
var statementScope = this.parent
|
||||
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
||||
if(statementScope is INameScope) {
|
||||
scope.add(0, statementScope.name)
|
||||
}
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
if(name.isNotEmpty())
|
||||
scope.add(name)
|
||||
return scope.joinToString(".")
|
||||
}
|
||||
|
||||
val expensiveToInline: Boolean
|
||||
|
||||
fun definingBlock(): Block {
|
||||
if(this is Block)
|
||||
return this
|
||||
return findParentNode<Block>(this)!!
|
||||
}
|
||||
}
|
||||
|
||||
interface IFunctionCall {
|
||||
var target: IdentifierReference
|
||||
var arglist: MutableList<IExpression>
|
||||
}
|
||||
|
||||
interface INameScope {
|
||||
val name: String
|
||||
val position: Position
|
||||
val statements: MutableList<IStatement>
|
||||
val parent: Node
|
||||
|
||||
fun linkParents(parent: Node)
|
||||
|
||||
fun subScopes(): Map<String, INameScope> {
|
||||
val subscopes = mutableMapOf<String, INameScope>()
|
||||
for(stmt in statements) {
|
||||
when(stmt) {
|
||||
// NOTE: if other nodes are introduced that are a scope, or contain subscopes, they must be added here!
|
||||
is ForLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is RepeatLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is WhileLoop -> subscopes[stmt.body.name] = stmt.body
|
||||
is BranchStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is IfStatement -> {
|
||||
subscopes[stmt.truepart.name] = stmt.truepart
|
||||
if(stmt.elsepart.containsCodeOrVars())
|
||||
subscopes[stmt.elsepart.name] = stmt.elsepart
|
||||
}
|
||||
is WhenStatement -> {
|
||||
stmt.choices.forEach { subscopes[it.statements.name] = it.statements }
|
||||
}
|
||||
is INameScope -> subscopes[stmt.name] = stmt
|
||||
}
|
||||
}
|
||||
return subscopes
|
||||
}
|
||||
|
||||
fun getLabelOrVariable(name: String): IStatement? {
|
||||
// this is called A LOT and could perhaps be optimized a bit more,
|
||||
// but adding a memoization cache didn't make much of a practical runtime difference
|
||||
for (stmt in statements) {
|
||||
if (stmt is VarDecl && stmt.name==name) return stmt
|
||||
if (stmt is Label && stmt.name==name) return stmt
|
||||
if (stmt is AnonymousScope) {
|
||||
val sub = stmt.getLabelOrVariable(name)
|
||||
if(sub!=null)
|
||||
return sub
|
||||
}
|
||||
}
|
||||
return null
|
||||
}
|
||||
|
||||
fun allDefinedSymbols(): List<Pair<String, IStatement>> {
|
||||
return statements.mapNotNull {
|
||||
when (it) {
|
||||
is Label -> it.name to it
|
||||
is VarDecl -> it.name to it
|
||||
is Subroutine -> it.name to it
|
||||
is Block -> it.name to it
|
||||
else -> null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun lookup(scopedName: List<String>, localContext: Node) : IStatement? {
|
||||
if(scopedName.size>1) {
|
||||
// a scoped name can a) refer to a member of a struct, or b) refer to a name in another module.
|
||||
// try the struct first.
|
||||
val thing = lookup(scopedName.dropLast(1), localContext) as? VarDecl
|
||||
val struct = thing?.struct
|
||||
if (struct != null) {
|
||||
if(struct.statements.any { (it as VarDecl).name == scopedName.last()}) {
|
||||
// return ref to the mangled name variable
|
||||
val mangled = mangledStructMemberName(thing.name, scopedName.last())
|
||||
return thing.definingScope().getLabelOrVariable(mangled)
|
||||
}
|
||||
}
|
||||
|
||||
// it's a qualified name, look it up from the root of the module's namespace (consider all modules in the program)
|
||||
for(module in localContext.definingModule().program.modules) {
|
||||
var scope: INameScope? = module
|
||||
for(name in scopedName.dropLast(1)) {
|
||||
scope = scope?.subScopes()?.get(name)
|
||||
if(scope==null)
|
||||
break
|
||||
}
|
||||
if(scope!=null) {
|
||||
val result = scope.getLabelOrVariable(scopedName.last())
|
||||
if(result!=null)
|
||||
return result
|
||||
return scope.subScopes()[scopedName.last()] as IStatement?
|
||||
}
|
||||
}
|
||||
return null
|
||||
} else {
|
||||
// unqualified name, find the scope the localContext is in, look in that first
|
||||
var statementScope = localContext
|
||||
while(statementScope !is ParentSentinel) {
|
||||
val localScope = statementScope.definingScope()
|
||||
val result = localScope.getLabelOrVariable(scopedName[0])
|
||||
if (result != null)
|
||||
return result
|
||||
val subscope = localScope.subScopes()[scopedName[0]] as IStatement?
|
||||
if (subscope != null)
|
||||
return subscope
|
||||
// not found in this scope, look one higher up
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
return null
|
||||
}
|
||||
}
|
||||
|
||||
fun containsCodeOrVars() = statements.any { it !is Directive || it.directive == "%asminclude" || it.directive == "%asm"}
|
||||
fun containsNoCodeNorVars() = !containsCodeOrVars()
|
||||
|
||||
fun remove(stmt: IStatement) {
|
||||
if(!statements.remove(stmt))
|
||||
throw FatalAstException("stmt to remove wasn't found in scope")
|
||||
}
|
||||
}
|
||||
|
||||
interface IExpression: Node {
|
||||
fun constValue(program: Program): NumericLiteralValue?
|
||||
fun accept(visitor: IAstModifyingVisitor): IExpression
|
||||
fun accept(visitor: IAstVisitor)
|
||||
fun referencesIdentifiers(vararg name: String): Boolean // todo: remove and use calltree instead
|
||||
fun inferType(program: Program): DataType?
|
||||
|
||||
infix fun isSameAs(other: IExpression): Boolean {
|
||||
if(this===other)
|
||||
return true
|
||||
when(this) {
|
||||
is RegisterExpr ->
|
||||
return (other is RegisterExpr && other.register==register)
|
||||
is IdentifierReference ->
|
||||
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
||||
is PrefixExpression ->
|
||||
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||
is BinaryExpression ->
|
||||
return (other is BinaryExpression && other.operator==operator
|
||||
&& other.left isSameAs left
|
||||
&& other.right isSameAs right)
|
||||
is ArrayIndexedExpression -> {
|
||||
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||
}
|
||||
is NumericLiteralValue -> return other==this
|
||||
is ReferenceLiteralValue -> return other==this
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
@@ -3,8 +3,6 @@ package prog8.ast.antlr
|
||||
import org.antlr.v4.runtime.IntStream
|
||||
import org.antlr.v4.runtime.ParserRuleContext
|
||||
import org.antlr.v4.runtime.tree.TerminalNode
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
@@ -41,7 +39,7 @@ private fun ParserRuleContext.toPosition() : Position {
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : IStatement {
|
||||
private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : Statement {
|
||||
val directive = directive()?.toAst()
|
||||
if(directive!=null) return directive
|
||||
|
||||
@@ -52,15 +50,15 @@ private fun prog8Parser.ModulestatementContext.toAst(isInLibrary: Boolean) : ISt
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : IStatement =
|
||||
private fun prog8Parser.BlockContext.toAst(isInLibrary: Boolean) : Statement =
|
||||
Block(identifier().text, integerliteral()?.toAst()?.number?.toInt(), statement_block().toAst(), isInLibrary, toPosition())
|
||||
|
||||
|
||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<IStatement> =
|
||||
private fun prog8Parser.Statement_blockContext.toAst(): MutableList<Statement> =
|
||||
statement().asSequence().map { it.toAst() }.toMutableList()
|
||||
|
||||
|
||||
private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
private fun prog8Parser.StatementContext.toAst() : Statement {
|
||||
vardecl()?.let { return it.toAst() }
|
||||
|
||||
varinitializer()?.let {
|
||||
@@ -216,7 +214,7 @@ private fun prog8Parser.StatementContext.toAst() : IStatement {
|
||||
throw FatalAstException("unprocessed source text (are we missing ast conversion rules for parser elements?): $text")
|
||||
}
|
||||
|
||||
private fun prog8Parser.AsmsubroutineContext.toAst(): IStatement {
|
||||
private fun prog8Parser.AsmsubroutineContext.toAst(): Statement {
|
||||
val name = identifier().text
|
||||
val address = asmsub_address()?.address?.toAst()?.number?.toInt()
|
||||
val params = asmsub_params()?.toAst() ?: emptyList()
|
||||
@@ -265,7 +263,7 @@ private fun prog8Parser.Asmsub_paramsContext.toAst(): List<AsmSubroutineParamete
|
||||
private fun prog8Parser.StatusregisterContext.toAst() = Statusflag.valueOf(text)
|
||||
|
||||
|
||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): IStatement {
|
||||
private fun prog8Parser.Functioncall_stmtContext.toAst(): Statement {
|
||||
val location = scoped_identifier().toAst()
|
||||
return if(expression_list() == null)
|
||||
FunctionCallStatement(location, mutableListOf(), toPosition())
|
||||
@@ -298,7 +296,7 @@ private fun prog8Parser.UnconditionaljumpContext.toAst(): Jump {
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.LabeldefContext.toAst(): IStatement =
|
||||
private fun prog8Parser.LabeldefContext.toAst(): Statement =
|
||||
Label(children[0].text, toPosition())
|
||||
|
||||
|
||||
@@ -411,7 +409,7 @@ private fun prog8Parser.IntegerliteralContext.toAst(): NumericLiteral {
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ExpressionContext.toAst() : IExpression {
|
||||
private fun prog8Parser.ExpressionContext.toAst() : Expression {
|
||||
|
||||
val litval = literalvalue()
|
||||
if(litval!=null) {
|
||||
@@ -521,7 +519,7 @@ private fun prog8Parser.BooleanliteralContext.toAst() = when(text) {
|
||||
}
|
||||
|
||||
|
||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<IExpression> =
|
||||
private fun prog8Parser.ArrayliteralContext.toAst() : Array<Expression> =
|
||||
expression().map { it.toAst() }.toTypedArray()
|
||||
|
||||
private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||
@@ -534,7 +532,7 @@ private fun prog8Parser.If_stmtContext.toAst(): IfStatement {
|
||||
return IfStatement(condition, trueScope, elseScope, toPosition())
|
||||
}
|
||||
|
||||
private fun prog8Parser.Else_partContext.toAst(): MutableList<IStatement> {
|
||||
private fun prog8Parser.Else_partContext.toAst(): MutableList<Statement> {
|
||||
return statement_block()?.toAst() ?: mutableListOf(statement().toAst())
|
||||
}
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
package prog8.ast.expressions
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.antlr.escape
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.processing.IAstVisitor
|
||||
import prog8.ast.statements.*
|
||||
import prog8.ast.statements.ArrayIndex
|
||||
import prog8.ast.statements.BuiltinFunctionStatementPlaceholder
|
||||
import prog8.ast.statements.Subroutine
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.compiler.HeapValues
|
||||
import prog8.compiler.IntegerOrAddressOf
|
||||
import prog8.compiler.target.c64.Petscii
|
||||
@@ -18,7 +24,38 @@ import kotlin.math.abs
|
||||
val associativeOperators = setOf("+", "*", "&", "|", "^", "or", "and", "xor", "==", "!=")
|
||||
|
||||
|
||||
class PrefixExpression(val operator: String, var expression: IExpression, override val position: Position) : IExpression {
|
||||
sealed class Expression: Node {
|
||||
abstract fun constValue(program: Program): NumericLiteralValue?
|
||||
abstract fun accept(visitor: IAstModifyingVisitor): Expression
|
||||
abstract fun accept(visitor: IAstVisitor)
|
||||
abstract fun referencesIdentifiers(vararg name: String): Boolean // todo: remove and use calltree instead
|
||||
abstract fun inferType(program: Program): DataType?
|
||||
|
||||
infix fun isSameAs(other: Expression): Boolean {
|
||||
if(this===other)
|
||||
return true
|
||||
when(this) {
|
||||
is RegisterExpr ->
|
||||
return (other is RegisterExpr && other.register==register)
|
||||
is IdentifierReference ->
|
||||
return (other is IdentifierReference && other.nameInSource==nameInSource)
|
||||
is PrefixExpression ->
|
||||
return (other is PrefixExpression && other.operator==operator && other.expression isSameAs expression)
|
||||
is BinaryExpression ->
|
||||
return (other is BinaryExpression && other.operator==operator
|
||||
&& other.left isSameAs left
|
||||
&& other.right isSameAs right)
|
||||
is ArrayIndexedExpression -> {
|
||||
return (other is ArrayIndexedExpression && other.identifier.nameInSource == identifier.nameInSource
|
||||
&& other.arrayspec.index isSameAs arrayspec.index)
|
||||
}
|
||||
else -> return other==this
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class PrefixExpression(val operator: String, var expression: Expression, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -37,7 +74,7 @@ class PrefixExpression(val operator: String, var expression: IExpression, overri
|
||||
}
|
||||
}
|
||||
|
||||
class BinaryExpression(var left: IExpression, var operator: String, var right: IExpression, override val position: Position) : IExpression {
|
||||
class BinaryExpression(var left: Expression, var operator: String, var right: Expression, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -148,7 +185,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
}
|
||||
|
||||
fun commonDatatype(leftDt: DataType, rightDt: DataType,
|
||||
left: IExpression, right: IExpression): Pair<DataType, IExpression?> {
|
||||
left: Expression, right: Expression): Pair<DataType, Expression?> {
|
||||
// byte + byte -> byte
|
||||
// byte + word -> word
|
||||
// word + byte -> word
|
||||
@@ -212,7 +249,7 @@ class BinaryExpression(var left: IExpression, var operator: String, var right: I
|
||||
|
||||
class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
var arrayspec: ArrayIndex,
|
||||
override val position: Position) : IExpression {
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
override fun linkParents(parent: Node) {
|
||||
this.parent = parent
|
||||
@@ -243,7 +280,7 @@ class ArrayIndexedExpression(val identifier: IdentifierReference,
|
||||
}
|
||||
}
|
||||
|
||||
class TypecastExpression(var expression: IExpression, var type: DataType, val implicit: Boolean, override val position: Position) : IExpression {
|
||||
class TypecastExpression(var expression: Expression, var type: DataType, val implicit: Boolean, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -267,7 +304,7 @@ class TypecastExpression(var expression: IExpression, var type: DataType, val im
|
||||
}
|
||||
}
|
||||
|
||||
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : IExpression {
|
||||
data class AddressOf(val identifier: IdentifierReference, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -283,7 +320,7 @@ data class AddressOf(val identifier: IdentifierReference, override val position:
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class DirectMemoryRead(var addressExpression: IExpression, override val position: Position) : IExpression {
|
||||
class DirectMemoryRead(var addressExpression: Expression, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -304,7 +341,7 @@ class DirectMemoryRead(var addressExpression: IExpression, override val position
|
||||
|
||||
class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
val number: Number, // can be byte, word or float depending on the type
|
||||
override val position: Position) : IExpression {
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
companion object {
|
||||
@@ -420,8 +457,8 @@ class NumericLiteralValue(val type: DataType, // only numerical types allowed
|
||||
}
|
||||
}
|
||||
|
||||
class StructLiteralValue(var values: List<IExpression>,
|
||||
override val position: Position): IExpression {
|
||||
class StructLiteralValue(var values: List<Expression>,
|
||||
override val position: Position): Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -442,10 +479,10 @@ class StructLiteralValue(var values: List<IExpression>,
|
||||
|
||||
class ReferenceLiteralValue(val type: DataType, // only reference types allowed here
|
||||
val str: String? = null,
|
||||
val array: Array<IExpression>? = null,
|
||||
val array: Array<Expression>? = null,
|
||||
// actually, at the moment, we don't have struct literals in the language
|
||||
initHeapId: Int? =null,
|
||||
override val position: Position) : IExpression {
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun referencesIdentifiers(vararg name: String) = array?.any { it.referencesIdentifiers(*name) } ?: false
|
||||
@@ -558,10 +595,10 @@ class ReferenceLiteralValue(val type: DataType, // only reference types allo
|
||||
}
|
||||
}
|
||||
|
||||
class RangeExpr(var from: IExpression,
|
||||
var to: IExpression,
|
||||
var step: IExpression,
|
||||
override val position: Position) : IExpression {
|
||||
class RangeExpr(var from: Expression,
|
||||
var to: Expression,
|
||||
var step: Expression,
|
||||
override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -635,7 +672,7 @@ class RangeExpr(var from: IExpression,
|
||||
}
|
||||
}
|
||||
|
||||
class RegisterExpr(val register: Register, override val position: Position) : IExpression {
|
||||
class RegisterExpr(val register: Register, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -653,7 +690,7 @@ class RegisterExpr(val register: Register, override val position: Position) : IE
|
||||
override fun inferType(program: Program) = DataType.UBYTE
|
||||
}
|
||||
|
||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : IExpression {
|
||||
data class IdentifierReference(val nameInSource: List<String>, override val position: Position) : Expression() {
|
||||
override lateinit var parent: Node
|
||||
|
||||
fun targetStatement(namespace: INameScope) =
|
||||
@@ -710,8 +747,8 @@ data class IdentifierReference(val nameInSource: List<String>, override val posi
|
||||
}
|
||||
|
||||
class FunctionCall(override var target: IdentifierReference,
|
||||
override var arglist: MutableList<IExpression>,
|
||||
override val position: Position) : IExpression, IFunctionCall {
|
||||
override var arglist: MutableList<Expression>,
|
||||
override val position: Position) : Expression(), IFunctionCall {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -780,8 +817,7 @@ class FunctionCall(override var target: IdentifierReference,
|
||||
return stmt.returntypes[0]
|
||||
return null // has multiple return types... so not a single resulting datatype possible
|
||||
}
|
||||
is Label -> return null
|
||||
else -> return null
|
||||
}
|
||||
return null // calling something we don't recognise...
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
@@ -357,7 +359,7 @@ internal class AstChecker(private val program: Program,
|
||||
checkResult.add(ExpressionError("address out of range", assignTarget.position))
|
||||
}
|
||||
|
||||
val assignment = assignTarget.parent as IStatement
|
||||
val assignment = assignTarget.parent as Statement
|
||||
if (assignTarget.identifier != null) {
|
||||
val targetName = assignTarget.identifier.nameInSource
|
||||
val targetSymbol = program.namespace.lookup(targetName, assignment)
|
||||
@@ -798,7 +800,7 @@ internal class AstChecker(private val program: Program,
|
||||
|
||||
override fun visit(functionCall: FunctionCall) {
|
||||
// this function call is (part of) an expression, which should be in a statement somewhere.
|
||||
val stmtOfExpression = findParentNode<IStatement>(functionCall)
|
||||
val stmtOfExpression = findParentNode<Statement>(functionCall)
|
||||
?: throw FatalAstException("cannot determine statement scope of function call expression at ${functionCall.position}")
|
||||
|
||||
val targetStatement = checkFunctionOrLabelExists(functionCall.target, stmtOfExpression)
|
||||
@@ -820,7 +822,7 @@ internal class AstChecker(private val program: Program,
|
||||
super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
private fun checkFunctionCall(target: IStatement, args: List<IExpression>, position: Position) {
|
||||
private fun checkFunctionCall(target: Statement, args: List<Expression>, position: Position) {
|
||||
if(target is Label && args.isNotEmpty())
|
||||
checkResult.add(SyntaxError("cannot use arguments when calling a label", position))
|
||||
|
||||
@@ -995,7 +997,7 @@ internal class AstChecker(private val program: Program,
|
||||
}
|
||||
}
|
||||
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: IStatement): IStatement? {
|
||||
private fun checkFunctionOrLabelExists(target: IdentifierReference, statement: Statement): Statement? {
|
||||
val targetStatement = target.targetStatement(program.namespace)
|
||||
if(targetStatement is Label || targetStatement is Subroutine || targetStatement is BuiltinFunctionStatementPlaceholder)
|
||||
return targetStatement
|
||||
@@ -1253,7 +1255,7 @@ internal class AstChecker(private val program: Program,
|
||||
private fun checkAssignmentCompatible(targetDatatype: DataType,
|
||||
target: AssignTarget,
|
||||
sourceDatatype: DataType,
|
||||
sourceValue: IExpression,
|
||||
sourceValue: Expression,
|
||||
position: Position) : Boolean {
|
||||
|
||||
if(sourceValue is RangeExpr)
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
@@ -18,7 +21,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return checkResult
|
||||
}
|
||||
|
||||
private fun nameError(name: String, position: Position, existing: IStatement) {
|
||||
private fun nameError(name: String, position: Position, existing: Statement) {
|
||||
checkResult.add(NameError("name conflict '$name', also defined in ${existing.position.file} line ${existing.position.line}", position))
|
||||
}
|
||||
|
||||
@@ -33,7 +36,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(block: Block): IStatement {
|
||||
override fun visit(block: Block): Statement {
|
||||
val existing = blocks[block.name]
|
||||
if(existing!=null)
|
||||
nameError(block.name, block.position, existing)
|
||||
@@ -43,7 +46,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
if(functionCall.target.nameInSource.size==1 && functionCall.target.nameInSource[0]=="lsb") {
|
||||
// lsb(...) is just an alias for type cast to ubyte, so replace with "... as ubyte"
|
||||
val typecast = TypecastExpression(functionCall.arglist.single(), DataType.UBYTE, false, functionCall.position)
|
||||
@@ -53,7 +56,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): Statement {
|
||||
// first, check if there are datatype errors on the vardecl
|
||||
decl.datatypeErrors.forEach { checkResult.add(it) }
|
||||
|
||||
@@ -95,7 +98,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine): Statement {
|
||||
if(subroutine.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", subroutine.position))
|
||||
@@ -142,7 +145,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(subroutine)
|
||||
}
|
||||
|
||||
override fun visit(label: Label): IStatement {
|
||||
override fun visit(label: Label): Statement {
|
||||
if(label.name in BuiltinFunctions) {
|
||||
// the builtin functions can't be redefined
|
||||
checkResult.add(NameError("builtin function cannot be redefined", label.position))
|
||||
@@ -154,7 +157,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(label)
|
||||
}
|
||||
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): Statement {
|
||||
// If the for loop has a decltype, it means to declare the loopvar inside the loop body
|
||||
// rather than reusing an already declared loopvar from an outer scope.
|
||||
// For loops that loop over an interable variable (instead of a range of numbers) get an
|
||||
@@ -200,13 +203,13 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(assignTarget)
|
||||
}
|
||||
|
||||
override fun visit(returnStmt: Return): IStatement {
|
||||
override fun visit(returnStmt: Return): Statement {
|
||||
if(returnStmt.value!=null) {
|
||||
// possibly adjust any literal values returned, into the desired returning data type
|
||||
val subroutine = returnStmt.definingSubroutine()!!
|
||||
if(subroutine.returntypes.size!=1)
|
||||
return returnStmt // mismatch in number of return values, error will be printed later.
|
||||
val newValue: IExpression
|
||||
val newValue: Expression
|
||||
val lval = returnStmt.value as? NumericLiteralValue
|
||||
if(lval!=null) {
|
||||
val adjusted = lval.cast(subroutine.returntypes.single())
|
||||
@@ -220,7 +223,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(returnStmt)
|
||||
}
|
||||
|
||||
override fun visit(refLiteral: ReferenceLiteralValue): IExpression {
|
||||
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||
if(refLiteral.parent !is VarDecl) {
|
||||
return makeIdentifierFromRefLv(refLiteral)
|
||||
}
|
||||
@@ -240,14 +243,14 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return identifier
|
||||
}
|
||||
|
||||
override fun visit(addressOf: AddressOf): IExpression {
|
||||
override fun visit(addressOf: AddressOf): Expression {
|
||||
// register the scoped name of the referenced identifier
|
||||
val variable= addressOf.identifier.targetVarDecl(program.namespace) ?: return addressOf
|
||||
addressOf.scopedname = variable.scopedname
|
||||
return super.visit(addressOf)
|
||||
}
|
||||
|
||||
override fun visit(structDecl: StructDecl): IStatement {
|
||||
override fun visit(structDecl: StructDecl): Statement {
|
||||
for(member in structDecl.statements){
|
||||
val decl = member as? VarDecl
|
||||
if(decl!=null && decl.datatype !in NumericDatatypes)
|
||||
@@ -257,7 +260,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
return super.visit(structDecl)
|
||||
}
|
||||
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): Expression {
|
||||
return when {
|
||||
expr.left is ReferenceLiteralValue ->
|
||||
processBinaryExprWithReferenceVal(expr.left as ReferenceLiteralValue, expr.right, expr)
|
||||
@@ -267,7 +270,7 @@ internal class AstIdentifiersChecker(private val program: Program) : IAstModifyi
|
||||
}
|
||||
}
|
||||
|
||||
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: IExpression, expr: BinaryExpression): IExpression {
|
||||
private fun processBinaryExprWithReferenceVal(refLv: ReferenceLiteralValue, operand: Expression, expr: BinaryExpression): Expression {
|
||||
// expressions on strings or arrays
|
||||
if(refLv.isString) {
|
||||
val constvalue = operand.constValue(program)
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.expressions.*
|
||||
@@ -16,38 +14,38 @@ interface IAstModifyingVisitor {
|
||||
module.statements = module.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
}
|
||||
|
||||
fun visit(expr: PrefixExpression): IExpression {
|
||||
fun visit(expr: PrefixExpression): Expression {
|
||||
expr.expression = expr.expression.accept(this)
|
||||
return expr
|
||||
}
|
||||
|
||||
fun visit(expr: BinaryExpression): IExpression {
|
||||
fun visit(expr: BinaryExpression): Expression {
|
||||
expr.left = expr.left.accept(this)
|
||||
expr.right = expr.right.accept(this)
|
||||
return expr
|
||||
}
|
||||
|
||||
fun visit(directive: Directive): IStatement {
|
||||
fun visit(directive: Directive): Statement {
|
||||
return directive
|
||||
}
|
||||
|
||||
fun visit(block: Block): IStatement {
|
||||
fun visit(block: Block): Statement {
|
||||
block.statements = block.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return block
|
||||
}
|
||||
|
||||
fun visit(decl: VarDecl): IStatement {
|
||||
fun visit(decl: VarDecl): Statement {
|
||||
decl.value = decl.value?.accept(this)
|
||||
decl.arraysize?.accept(this)
|
||||
return decl
|
||||
}
|
||||
|
||||
fun visit(subroutine: Subroutine): IStatement {
|
||||
fun visit(subroutine: Subroutine): Statement {
|
||||
subroutine.statements = subroutine.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return subroutine
|
||||
}
|
||||
|
||||
fun visit(functionCall: FunctionCall): IExpression {
|
||||
fun visit(functionCall: FunctionCall): Expression {
|
||||
val newtarget = functionCall.target.accept(this)
|
||||
if(newtarget is IdentifierReference)
|
||||
functionCall.target = newtarget
|
||||
@@ -55,7 +53,7 @@ interface IAstModifyingVisitor {
|
||||
return functionCall
|
||||
}
|
||||
|
||||
fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||
val newtarget = functionCallStatement.target.accept(this)
|
||||
if(newtarget is IdentifierReference)
|
||||
functionCallStatement.target = newtarget
|
||||
@@ -63,13 +61,13 @@ interface IAstModifyingVisitor {
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
fun visit(identifier: IdentifierReference): IExpression {
|
||||
fun visit(identifier: IdentifierReference): Expression {
|
||||
// note: this is an identifier that is used in an expression.
|
||||
// other identifiers are simply part of the other statements (such as jumps, subroutine defs etc)
|
||||
return identifier
|
||||
}
|
||||
|
||||
fun visit(jump: Jump): IStatement {
|
||||
fun visit(jump: Jump): Statement {
|
||||
if(jump.identifier!=null) {
|
||||
val ident = jump.identifier.accept(this)
|
||||
if(ident is IdentifierReference && ident!==jump.identifier) {
|
||||
@@ -79,27 +77,27 @@ interface IAstModifyingVisitor {
|
||||
return jump
|
||||
}
|
||||
|
||||
fun visit(ifStatement: IfStatement): IStatement {
|
||||
fun visit(ifStatement: IfStatement): Statement {
|
||||
ifStatement.condition = ifStatement.condition.accept(this)
|
||||
ifStatement.truepart = ifStatement.truepart.accept(this) as AnonymousScope
|
||||
ifStatement.elsepart = ifStatement.elsepart.accept(this) as AnonymousScope
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
fun visit(branchStatement: BranchStatement): IStatement {
|
||||
fun visit(branchStatement: BranchStatement): Statement {
|
||||
branchStatement.truepart = branchStatement.truepart.accept(this) as AnonymousScope
|
||||
branchStatement.elsepart = branchStatement.elsepart.accept(this) as AnonymousScope
|
||||
return branchStatement
|
||||
}
|
||||
|
||||
fun visit(range: RangeExpr): IExpression {
|
||||
fun visit(range: RangeExpr): Expression {
|
||||
range.from = range.from.accept(this)
|
||||
range.to = range.to.accept(this)
|
||||
range.step = range.step.accept(this)
|
||||
return range
|
||||
}
|
||||
|
||||
fun visit(label: Label): IStatement {
|
||||
fun visit(label: Label): Statement {
|
||||
return label
|
||||
}
|
||||
|
||||
@@ -107,7 +105,7 @@ interface IAstModifyingVisitor {
|
||||
return literalValue
|
||||
}
|
||||
|
||||
fun visit(refLiteral: ReferenceLiteralValue): IExpression {
|
||||
fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||
if(refLiteral.array!=null) {
|
||||
for(av in refLiteral.array.withIndex()) {
|
||||
val newvalue = av.value.accept(this)
|
||||
@@ -117,50 +115,50 @@ interface IAstModifyingVisitor {
|
||||
return refLiteral
|
||||
}
|
||||
|
||||
fun visit(assignment: Assignment): IStatement {
|
||||
fun visit(assignment: Assignment): Statement {
|
||||
assignment.target = assignment.target.accept(this)
|
||||
assignment.value = assignment.value.accept(this)
|
||||
return assignment
|
||||
}
|
||||
|
||||
fun visit(postIncrDecr: PostIncrDecr): IStatement {
|
||||
fun visit(postIncrDecr: PostIncrDecr): Statement {
|
||||
postIncrDecr.target = postIncrDecr.target.accept(this)
|
||||
return postIncrDecr
|
||||
}
|
||||
|
||||
fun visit(contStmt: Continue): IStatement {
|
||||
fun visit(contStmt: Continue): Statement {
|
||||
return contStmt
|
||||
}
|
||||
|
||||
fun visit(breakStmt: Break): IStatement {
|
||||
fun visit(breakStmt: Break): Statement {
|
||||
return breakStmt
|
||||
}
|
||||
|
||||
fun visit(forLoop: ForLoop): IStatement {
|
||||
fun visit(forLoop: ForLoop): Statement {
|
||||
forLoop.loopVar?.accept(this)
|
||||
forLoop.iterable = forLoop.iterable.accept(this)
|
||||
forLoop.body = forLoop.body.accept(this) as AnonymousScope
|
||||
return forLoop
|
||||
}
|
||||
|
||||
fun visit(whileLoop: WhileLoop): IStatement {
|
||||
fun visit(whileLoop: WhileLoop): Statement {
|
||||
whileLoop.condition = whileLoop.condition.accept(this)
|
||||
whileLoop.body = whileLoop.body.accept(this) as AnonymousScope
|
||||
return whileLoop
|
||||
}
|
||||
|
||||
fun visit(repeatLoop: RepeatLoop): IStatement {
|
||||
fun visit(repeatLoop: RepeatLoop): Statement {
|
||||
repeatLoop.untilCondition = repeatLoop.untilCondition.accept(this)
|
||||
repeatLoop.body = repeatLoop.body.accept(this) as AnonymousScope
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
fun visit(returnStmt: Return): IStatement {
|
||||
fun visit(returnStmt: Return): Statement {
|
||||
returnStmt.value = returnStmt.value?.accept(this)
|
||||
return returnStmt
|
||||
}
|
||||
|
||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): IExpression {
|
||||
fun visit(arrayIndexedExpression: ArrayIndexedExpression): Expression {
|
||||
arrayIndexedExpression.identifier.accept(this)
|
||||
arrayIndexedExpression.arrayspec.accept(this)
|
||||
return arrayIndexedExpression
|
||||
@@ -173,17 +171,17 @@ interface IAstModifyingVisitor {
|
||||
return assignTarget
|
||||
}
|
||||
|
||||
fun visit(scope: AnonymousScope): IStatement {
|
||||
fun visit(scope: AnonymousScope): Statement {
|
||||
scope.statements = scope.statements.asSequence().map { it.accept(this) }.toMutableList()
|
||||
return scope
|
||||
}
|
||||
|
||||
fun visit(typecast: TypecastExpression): IExpression {
|
||||
fun visit(typecast: TypecastExpression): Expression {
|
||||
typecast.expression = typecast.expression.accept(this)
|
||||
return typecast
|
||||
}
|
||||
|
||||
fun visit(memread: DirectMemoryRead): IExpression {
|
||||
fun visit(memread: DirectMemoryRead): Expression {
|
||||
memread.addressExpression = memread.addressExpression.accept(this)
|
||||
return memread
|
||||
}
|
||||
@@ -192,28 +190,28 @@ interface IAstModifyingVisitor {
|
||||
memwrite.addressExpression = memwrite.addressExpression.accept(this)
|
||||
}
|
||||
|
||||
fun visit(addressOf: AddressOf): IExpression {
|
||||
fun visit(addressOf: AddressOf): Expression {
|
||||
addressOf.identifier.accept(this)
|
||||
return addressOf
|
||||
}
|
||||
|
||||
fun visit(inlineAssembly: InlineAssembly): IStatement {
|
||||
fun visit(inlineAssembly: InlineAssembly): Statement {
|
||||
return inlineAssembly
|
||||
}
|
||||
|
||||
fun visit(registerExpr: RegisterExpr): IExpression {
|
||||
fun visit(registerExpr: RegisterExpr): Expression {
|
||||
return registerExpr
|
||||
}
|
||||
|
||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): IStatement {
|
||||
fun visit(builtinFunctionStatementPlaceholder: BuiltinFunctionStatementPlaceholder): Statement {
|
||||
return builtinFunctionStatementPlaceholder
|
||||
}
|
||||
|
||||
fun visit(nopStatement: NopStatement): IStatement {
|
||||
fun visit(nopStatement: NopStatement): Statement {
|
||||
return nopStatement
|
||||
}
|
||||
|
||||
fun visit(whenStatement: WhenStatement): IStatement {
|
||||
fun visit(whenStatement: WhenStatement): Statement {
|
||||
whenStatement.condition.accept(this)
|
||||
whenStatement.choices.forEach { it.accept(this) }
|
||||
return whenStatement
|
||||
@@ -224,12 +222,12 @@ interface IAstModifyingVisitor {
|
||||
whenChoice.statements.accept(this)
|
||||
}
|
||||
|
||||
fun visit(structDecl: StructDecl): IStatement {
|
||||
fun visit(structDecl: StructDecl): Statement {
|
||||
structDecl.statements = structDecl.statements.map{ it.accept(this) }.toMutableList()
|
||||
return structDecl
|
||||
}
|
||||
|
||||
fun visit(structLv: StructLiteralValue): IExpression {
|
||||
fun visit(structLv: StructLiteralValue): Expression {
|
||||
structLv.values = structLv.values.map { it.accept(this) }
|
||||
return structLv
|
||||
}
|
||||
|
||||
@@ -1,10 +1,10 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.base.SyntaxError
|
||||
import prog8.ast.base.printWarning
|
||||
import prog8.ast.statements.Directive
|
||||
import prog8.ast.statements.Statement
|
||||
|
||||
internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
||||
private val checkResult: MutableList<SyntaxError> = mutableListOf()
|
||||
@@ -18,7 +18,7 @@ internal class ImportedModuleDirectiveRemover : IAstModifyingVisitor {
|
||||
*/
|
||||
override fun visit(module: Module) {
|
||||
super.visit(module)
|
||||
val newStatements : MutableList<IStatement> = mutableListOf()
|
||||
val newStatements : MutableList<Statement> = mutableListOf()
|
||||
|
||||
val moduleLevelDirectives = listOf("%output", "%launcher", "%zeropage", "%zpreserved", "%address")
|
||||
for (sourceStmt in module.statements) {
|
||||
|
||||
@@ -97,7 +97,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
module.statements.addAll(0, directives)
|
||||
}
|
||||
|
||||
override fun visit(block: Block): IStatement {
|
||||
override fun visit(block: Block): Statement {
|
||||
|
||||
val subroutines = block.statements.filterIsInstance<Subroutine>()
|
||||
var numSubroutinesAtEnd = 0
|
||||
@@ -161,7 +161,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine): Statement {
|
||||
super.visit(subroutine)
|
||||
|
||||
val varDecls = subroutine.statements.filterIsInstance<VarDecl>()
|
||||
@@ -186,7 +186,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return subroutine
|
||||
}
|
||||
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): Expression {
|
||||
val leftDt = expr.left.inferType(program)
|
||||
val rightDt = expr.right.inferType(program)
|
||||
if(leftDt!=null && rightDt!=null && leftDt!=rightDt) {
|
||||
@@ -209,7 +209,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): Statement {
|
||||
val assg = super.visit(assignment)
|
||||
if(assg !is Assignment)
|
||||
return assg
|
||||
@@ -246,7 +246,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
|
||||
if(assg.aug_op!=null) {
|
||||
// transform augmented assg into normal assg so we have one case less to deal with later
|
||||
val newTarget: IExpression =
|
||||
val newTarget: Expression =
|
||||
when {
|
||||
assg.target.register != null -> RegisterExpr(assg.target.register!!, assg.target.position)
|
||||
assg.target.identifier != null -> assg.target.identifier!!
|
||||
@@ -265,12 +265,12 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return assg
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||
checkFunctionCallArguments(functionCallStatement, functionCallStatement.definingScope())
|
||||
return super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
checkFunctionCallArguments(functionCall, functionCall.definingScope())
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
@@ -321,7 +321,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression): IExpression {
|
||||
override fun visit(typecast: TypecastExpression): Expression {
|
||||
// warn about any implicit type casts to Float, because that may not be intended
|
||||
if(typecast.implicit && typecast.type in setOf(DataType.FLOAT, DataType.ARRAY_F)) {
|
||||
printWarning("byte or word value implicitly converted to float. Suggestion: use explicit cast as float, a float number, or revert to integer arithmetic", typecast.position)
|
||||
@@ -329,7 +329,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return super.visit(typecast)
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
||||
override fun visit(whenStatement: WhenStatement): Statement {
|
||||
// make sure all choices are just for one single value
|
||||
val choices = whenStatement.choices.toList()
|
||||
for(choice in choices) {
|
||||
@@ -349,7 +349,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
return super.visit(whenStatement)
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
||||
override fun visit(memread: DirectMemoryRead): Expression {
|
||||
// make sure the memory address is an uword
|
||||
val dt = memread.addressExpression.inferType(program)
|
||||
if(dt!=DataType.UWORD) {
|
||||
@@ -378,7 +378,7 @@ internal class StatementReorderer(private val program: Program): IAstModifyingVi
|
||||
super.visit(memwrite)
|
||||
}
|
||||
|
||||
override fun visit(structLv: StructLiteralValue): IExpression {
|
||||
override fun visit(structLv: StructLiteralValue): Expression {
|
||||
val litval = super.visit(structLv)
|
||||
if(litval !is StructLiteralValue)
|
||||
return litval
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package prog8.ast.processing
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.statements.*
|
||||
@@ -31,7 +33,7 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): Statement {
|
||||
super.visit(decl)
|
||||
|
||||
if(decl.isArray && decl.value==null) {
|
||||
@@ -70,25 +72,25 @@ internal class VarInitValueAndAddressOfCreator(private val namespace: INameScope
|
||||
return decl
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
val targetStatement = functionCall.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null) {
|
||||
var node: Node = functionCall
|
||||
while(node !is IStatement)
|
||||
while(node !is Statement)
|
||||
node=node.parent
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCall.arglist, node)
|
||||
}
|
||||
return functionCall
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||
val targetStatement = functionCallStatement.target.targetSubroutine(namespace)
|
||||
if(targetStatement!=null)
|
||||
addAddressOfExprIfNeeded(targetStatement, functionCallStatement.arglist, functionCallStatement)
|
||||
return functionCallStatement
|
||||
}
|
||||
|
||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<IExpression>, parent: IStatement) {
|
||||
private fun addAddressOfExprIfNeeded(subroutine: Subroutine, arglist: MutableList<Expression>, parent: Statement) {
|
||||
// functions that accept UWORD and are given an array type, or string, will receive the AddressOf (memory location) of that value instead.
|
||||
for(argparam in subroutine.parameters.withIndex().zip(arglist)) {
|
||||
if(argparam.first.value.type==DataType.UWORD || argparam.first.value.type in StringDatatypes) {
|
||||
|
||||
@@ -9,7 +9,38 @@ import prog8.compiler.HeapValues
|
||||
import prog8.compiler.target.c64.MachineDefinition
|
||||
|
||||
|
||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : IStatement {
|
||||
sealed class Statement : Node {
|
||||
abstract fun accept(visitor: IAstModifyingVisitor) : Statement
|
||||
abstract fun accept(visitor: IAstVisitor)
|
||||
fun makeScopedName(name: String): String {
|
||||
// easy way out is to always return the full scoped name.
|
||||
// it would be nicer to find only the minimal prefixed scoped name, but that's too much hassle for now.
|
||||
// and like this, we can cache the name even,
|
||||
// like in a lazy property on the statement object itself (label, subroutine, vardecl)
|
||||
val scope = mutableListOf<String>()
|
||||
var statementScope = this.parent
|
||||
while(statementScope !is ParentSentinel && statementScope !is Module) {
|
||||
if(statementScope is INameScope) {
|
||||
scope.add(0, statementScope.name)
|
||||
}
|
||||
statementScope = statementScope.parent
|
||||
}
|
||||
if(name.isNotEmpty())
|
||||
scope.add(name)
|
||||
return scope.joinToString(".")
|
||||
}
|
||||
|
||||
abstract val expensiveToInline: Boolean
|
||||
|
||||
fun definingBlock(): Block {
|
||||
if(this is Block)
|
||||
return this
|
||||
return findParentNode<Block>(this)!!
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
class BuiltinFunctionStatementPlaceholder(val name: String, override val position: Position) : Statement() {
|
||||
override var parent: Node = ParentSentinel
|
||||
override fun linkParents(parent: Node) {}
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
@@ -24,9 +55,9 @@ data class RegisterOrStatusflag(val registerOrPair: RegisterOrPair?, val statusf
|
||||
|
||||
class Block(override val name: String,
|
||||
val address: Int?,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override var statements: MutableList<Statement>,
|
||||
val isInLibrary: Boolean,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
override val position: Position) : Statement(), INameScope {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = statements.any { it.expensiveToInline }
|
||||
@@ -46,7 +77,7 @@ class Block(override val name: String,
|
||||
fun options() = statements.filter { it is Directive && it.directive == "%option" }.flatMap { (it as Directive).args }.map {it.name!!}.toSet()
|
||||
}
|
||||
|
||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : IStatement {
|
||||
data class Directive(val directive: String, val args: List<DirectiveArg>, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -67,7 +98,7 @@ data class DirectiveArg(val str: String?, val name: String?, val int: Int?, over
|
||||
}
|
||||
}
|
||||
|
||||
data class Label(val name: String, override val position: Position) : IStatement {
|
||||
data class Label(val name: String, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -85,7 +116,7 @@ data class Label(val name: String, override val position: Position) : IStatement
|
||||
val scopedname: String by lazy { makeScopedName(name) }
|
||||
}
|
||||
|
||||
open class Return(var value: IExpression?, override val position: Position) : IStatement {
|
||||
open class Return(var value: Expression?, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = value!=null && value !is NumericLiteralValue
|
||||
|
||||
@@ -111,7 +142,7 @@ class ReturnFromIrq(override val position: Position) : Return(null, position) {
|
||||
}
|
||||
}
|
||||
|
||||
class Continue(override val position: Position) : IStatement {
|
||||
class Continue(override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -123,7 +154,7 @@ class Continue(override val position: Position) : IStatement {
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class Break(override val position: Position) : IStatement {
|
||||
class Break(override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -149,10 +180,10 @@ class VarDecl(val type: VarDeclType,
|
||||
var arraysize: ArrayIndex?,
|
||||
val name: String,
|
||||
private val structName: String?,
|
||||
var value: IExpression?,
|
||||
var value: Expression?,
|
||||
val isArray: Boolean,
|
||||
val autogeneratedDontRemove: Boolean,
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
var struct: StructDecl? = null // set later (because at parse time, we only know the name)
|
||||
private set
|
||||
@@ -231,7 +262,7 @@ class VarDecl(val type: VarDeclType,
|
||||
return decl
|
||||
}
|
||||
|
||||
fun flattenStructMembers(): MutableList<IStatement> {
|
||||
fun flattenStructMembers(): MutableList<Statement> {
|
||||
val result = struct!!.statements.withIndex().map {
|
||||
val member = it.value as VarDecl
|
||||
val initvalue = if(value!=null) (value as StructLiteralValue).values[it.index] else null
|
||||
@@ -246,14 +277,14 @@ class VarDecl(val type: VarDeclType,
|
||||
member.isArray,
|
||||
true,
|
||||
member.position
|
||||
) as IStatement
|
||||
) as Statement
|
||||
}.toMutableList()
|
||||
structHasBeenFlattened = true
|
||||
return result
|
||||
}
|
||||
}
|
||||
|
||||
class ArrayIndex(var index: IExpression, override val position: Position) : Node {
|
||||
class ArrayIndex(var index: Expression, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
@@ -282,7 +313,7 @@ class ArrayIndex(var index: IExpression, override val position: Position) : Node
|
||||
fun size() = (index as? NumericLiteralValue)?.number?.toInt()
|
||||
}
|
||||
|
||||
open class Assignment(var target: AssignTarget, val aug_op : String?, var value: IExpression, override val position: Position) : IStatement {
|
||||
open class Assignment(var target: AssignTarget, val aug_op : String?, var value: Expression, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = value !is NumericLiteralValue
|
||||
@@ -303,7 +334,7 @@ open class Assignment(var target: AssignTarget, val aug_op : String?, var value:
|
||||
|
||||
// This is a special class so the compiler can see if the assignments are for initializing the vars in the scope,
|
||||
// or just a regular assignment. It may optimize the initialization step from this.
|
||||
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: IExpression, position: Position)
|
||||
class VariableInitializationAssignment(target: AssignTarget, aug_op: String?, value: Expression, position: Position)
|
||||
: Assignment(target, aug_op, value, position)
|
||||
|
||||
data class AssignTarget(val register: Register?,
|
||||
@@ -324,7 +355,7 @@ data class AssignTarget(val register: Register?,
|
||||
fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
companion object {
|
||||
fun fromExpr(expr: IExpression): AssignTarget {
|
||||
fun fromExpr(expr: Expression): AssignTarget {
|
||||
return when (expr) {
|
||||
is RegisterExpr -> AssignTarget(expr.register, null, null, null, expr.position)
|
||||
is IdentifierReference -> AssignTarget(null, expr, null, null, expr.position)
|
||||
@@ -335,7 +366,7 @@ data class AssignTarget(val register: Register?,
|
||||
}
|
||||
}
|
||||
|
||||
fun inferType(program: Program, stmt: IStatement): DataType? {
|
||||
fun inferType(program: Program, stmt: Statement): DataType? {
|
||||
if(register!=null)
|
||||
return DataType.UBYTE
|
||||
|
||||
@@ -356,7 +387,7 @@ data class AssignTarget(val register: Register?,
|
||||
return null
|
||||
}
|
||||
|
||||
infix fun isSameAs(value: IExpression): Boolean {
|
||||
infix fun isSameAs(value: Expression): Boolean {
|
||||
return when {
|
||||
this.memoryAddress!=null -> false
|
||||
this.register!=null -> value is RegisterExpr && value.register==register
|
||||
@@ -411,7 +442,7 @@ data class AssignTarget(val register: Register?,
|
||||
}
|
||||
}
|
||||
|
||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : IStatement {
|
||||
class PostIncrDecr(var target: AssignTarget, val operator: String, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -431,7 +462,7 @@ class PostIncrDecr(var target: AssignTarget, val operator: String, override val
|
||||
class Jump(val address: Int?,
|
||||
val identifier: IdentifierReference?,
|
||||
val generatedLabel: String?, // used in code generation scenarios
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -449,8 +480,8 @@ class Jump(val address: Int?,
|
||||
}
|
||||
|
||||
class FunctionCallStatement(override var target: IdentifierReference,
|
||||
override var arglist: MutableList<IExpression>,
|
||||
override val position: Position) : IStatement, IFunctionCall {
|
||||
override var arglist: MutableList<Expression>,
|
||||
override val position: Position) : Statement(), IFunctionCall {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
get() = arglist.any { it !is NumericLiteralValue }
|
||||
@@ -469,7 +500,7 @@ class FunctionCallStatement(override var target: IdentifierReference,
|
||||
}
|
||||
}
|
||||
|
||||
class InlineAssembly(val assembly: String, override val position: Position) : IStatement {
|
||||
class InlineAssembly(val assembly: String, override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
@@ -481,8 +512,8 @@ class InlineAssembly(val assembly: String, override val position: Position) : IS
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class AnonymousScope(override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : INameScope, IStatement {
|
||||
class AnonymousScope(override var statements: MutableList<Statement>,
|
||||
override val position: Position) : INameScope, Statement() {
|
||||
override val name: String
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline
|
||||
@@ -506,7 +537,7 @@ class AnonymousScope(override var statements: MutableList<IStatement>,
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class NopStatement(override val position: Position): IStatement {
|
||||
class NopStatement(override val position: Position): Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = false
|
||||
|
||||
@@ -518,7 +549,7 @@ class NopStatement(override val position: Position): IStatement {
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
|
||||
companion object {
|
||||
fun insteadOf(stmt: IStatement): NopStatement {
|
||||
fun insteadOf(stmt: Statement): NopStatement {
|
||||
val nop = NopStatement(stmt.position)
|
||||
nop.parent = stmt.parent
|
||||
return nop
|
||||
@@ -537,8 +568,8 @@ class Subroutine(override val name: String,
|
||||
val asmClobbers: Set<Register>,
|
||||
val asmAddress: Int?,
|
||||
val isAsmSubroutine: Boolean,
|
||||
override var statements: MutableList<IStatement>,
|
||||
override val position: Position) : IStatement, INameScope {
|
||||
override var statements: MutableList<Statement>,
|
||||
override val position: Position) : Statement(), INameScope {
|
||||
|
||||
var keepAlways: Boolean = false
|
||||
override val expensiveToInline
|
||||
@@ -614,10 +645,10 @@ open class SubroutineParameter(val name: String,
|
||||
}
|
||||
}
|
||||
|
||||
class IfStatement(var condition: IExpression,
|
||||
class IfStatement(var condition: Expression,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean
|
||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||
@@ -636,7 +667,7 @@ class IfStatement(var condition: IExpression,
|
||||
class BranchStatement(var condition: BranchCondition,
|
||||
var truepart: AnonymousScope,
|
||||
var elsepart: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean
|
||||
get() = truepart.expensiveToInline || elsepart.expensiveToInline
|
||||
@@ -655,9 +686,9 @@ class ForLoop(val loopRegister: Register?,
|
||||
val decltype: DataType?,
|
||||
val zeropage: ZeropageWish,
|
||||
val loopVar: IdentifierReference?,
|
||||
var iterable: IExpression,
|
||||
var iterable: Expression,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
@@ -680,9 +711,9 @@ class ForLoop(val loopRegister: Register?,
|
||||
}
|
||||
}
|
||||
|
||||
class WhileLoop(var condition: IExpression,
|
||||
class WhileLoop(var condition: Expression,
|
||||
var body: AnonymousScope,
|
||||
override val position: Position) : IStatement {
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
@@ -697,8 +728,8 @@ class WhileLoop(var condition: IExpression,
|
||||
}
|
||||
|
||||
class RepeatLoop(var body: AnonymousScope,
|
||||
var untilCondition: IExpression,
|
||||
override val position: Position) : IStatement {
|
||||
var untilCondition: Expression,
|
||||
override val position: Position) : Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline = true
|
||||
|
||||
@@ -712,9 +743,9 @@ class RepeatLoop(var body: AnonymousScope,
|
||||
override fun accept(visitor: IAstVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class WhenStatement(val condition: IExpression,
|
||||
class WhenStatement(val condition: Expression,
|
||||
val choices: MutableList<WhenChoice>,
|
||||
override val position: Position): IStatement {
|
||||
override val position: Position): Statement() {
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean = true
|
||||
|
||||
@@ -745,7 +776,7 @@ class WhenStatement(val condition: IExpression,
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class WhenChoice(val values: List<IExpression>?, // if null, this is the 'else' part
|
||||
class WhenChoice(val values: List<Expression>?, // if null, this is the 'else' part
|
||||
val statements: AnonymousScope,
|
||||
override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
@@ -766,8 +797,8 @@ class WhenChoice(val values: List<IExpression>?, // if null, this is
|
||||
|
||||
|
||||
class StructDecl(override val name: String,
|
||||
override var statements: MutableList<IStatement>, // actually, only vardecls here
|
||||
override val position: Position): IStatement, INameScope {
|
||||
override var statements: MutableList<Statement>, // actually, only vardecls here
|
||||
override val position: Position): Statement(), INameScope {
|
||||
|
||||
override lateinit var parent: Node
|
||||
override val expensiveToInline: Boolean = true
|
||||
@@ -797,7 +828,7 @@ class StructDecl(override val name: String,
|
||||
override fun accept(visitor: IAstModifyingVisitor) = visitor.visit(this)
|
||||
}
|
||||
|
||||
class DirectMemoryWrite(var addressExpression: IExpression, override val position: Position) : Node {
|
||||
class DirectMemoryWrite(var addressExpression: Expression, override val position: Position) : Node {
|
||||
override lateinit var parent: Node
|
||||
|
||||
override fun linkParents(parent: Node) {
|
||||
|
||||
@@ -1,6 +1,8 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.antlr.escape
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.NumericDatatypes
|
||||
@@ -176,7 +178,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputStatements(statements: List<IStatement>) {
|
||||
private fun outputStatements(statements: List<Statement>) {
|
||||
for(stmt in statements) {
|
||||
if(stmt is VarDecl && stmt.autogeneratedDontRemove)
|
||||
continue // skip autogenerated decls (to avoid generating a newline)
|
||||
@@ -267,7 +269,7 @@ class AstToSourceCode(val output: (text: String) -> Unit, val program: Program):
|
||||
}
|
||||
}
|
||||
|
||||
private fun outputListMembers(array: Sequence<IExpression>, openchar: Char, closechar: Char) {
|
||||
private fun outputListMembers(array: Sequence<Expression>, openchar: Char, closechar: Char) {
|
||||
var counter = 0
|
||||
output(openchar.toString())
|
||||
scopelevel++
|
||||
|
||||
@@ -1,9 +1,11 @@
|
||||
package prog8.compiler
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.base.RegisterOrPair.*
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.mangledStructMemberName
|
||||
import prog8.ast.statements.*
|
||||
import prog8.compiler.intermediate.IntermediateProgram
|
||||
import prog8.compiler.intermediate.Opcode
|
||||
@@ -179,8 +181,8 @@ internal class Compiler(private val program: Program) {
|
||||
processVariables(subscope.value)
|
||||
}
|
||||
|
||||
private fun translate(statements: List<IStatement>) {
|
||||
for (stmt: IStatement in statements) {
|
||||
private fun translate(statements: List<Statement>) {
|
||||
for (stmt: Statement in statements) {
|
||||
generatedLabelSequenceNumber++
|
||||
when (stmt) {
|
||||
is Label -> translate(stmt)
|
||||
@@ -486,7 +488,7 @@ internal class Compiler(private val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun makeLabel(scopeStmt: IStatement, postfix: String): String {
|
||||
private fun makeLabel(scopeStmt: Statement, postfix: String): String {
|
||||
generatedLabelSequenceNumber++
|
||||
return "${scopeStmt.makeScopedName("")}.<s-$generatedLabelSequenceNumber-$postfix>"
|
||||
}
|
||||
@@ -551,7 +553,7 @@ internal class Compiler(private val program: Program) {
|
||||
prog.instr(Opcode.NOP)
|
||||
}
|
||||
|
||||
private fun translate(expr: IExpression) {
|
||||
private fun translate(expr: Expression) {
|
||||
when(expr) {
|
||||
is RegisterExpr -> {
|
||||
prog.instr(Opcode.PUSH_VAR_BYTE, callLabel = expr.register.name)
|
||||
@@ -716,7 +718,7 @@ internal class Compiler(private val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateBuiltinFunctionCall(funcname: String, args: List<IExpression>) {
|
||||
private fun translateBuiltinFunctionCall(funcname: String, args: List<Expression>) {
|
||||
// some builtin functions are implemented directly as vm opcodes
|
||||
|
||||
if(funcname == "swap") {
|
||||
@@ -900,7 +902,7 @@ internal class Compiler(private val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateSwap(args: List<IExpression>) {
|
||||
private fun translateSwap(args: List<Expression>) {
|
||||
// swap(x,y) is treated differently, it's not a normal function call
|
||||
if (args.size != 2)
|
||||
throw AstException("swap requires 2 arguments")
|
||||
@@ -923,7 +925,7 @@ internal class Compiler(private val program: Program) {
|
||||
return
|
||||
}
|
||||
|
||||
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position) {
|
||||
private fun translateSubroutineCall(subroutine: Subroutine, arguments: List<Expression>, callPosition: Position) {
|
||||
// evaluate the arguments and assign them into the subroutine's argument variables.
|
||||
var restoreX = Register.X in subroutine.asmClobbers
|
||||
if(restoreX)
|
||||
@@ -970,7 +972,7 @@ internal class Compiler(private val program: Program) {
|
||||
}
|
||||
}
|
||||
|
||||
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<IExpression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
||||
private fun translateAsmSubCallArguments(subroutine: Subroutine, arguments: List<Expression>, callPosition: Position, restoreXinitial: Boolean): Boolean {
|
||||
var restoreX = restoreXinitial
|
||||
if (subroutine.parameters.size != subroutine.asmParameterRegisters.size)
|
||||
TODO("no support yet for mix of register and non-register subroutine arguments")
|
||||
@@ -1009,8 +1011,8 @@ internal class Compiler(private val program: Program) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueA: IExpression
|
||||
val valueX: IExpression
|
||||
val valueA: Expression
|
||||
val valueX: Expression
|
||||
val paramDt = arg.first.inferType(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
@@ -1032,8 +1034,8 @@ internal class Compiler(private val program: Program) {
|
||||
}
|
||||
}
|
||||
AY -> {
|
||||
val valueA: IExpression
|
||||
val valueY: IExpression
|
||||
val valueA: Expression
|
||||
val valueY: Expression
|
||||
val paramDt = arg.first.inferType(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
@@ -1059,8 +1061,8 @@ internal class Compiler(private val program: Program) {
|
||||
prog.instr(Opcode.RSAVEX)
|
||||
restoreX = true
|
||||
}
|
||||
val valueX: IExpression
|
||||
val valueY: IExpression
|
||||
val valueX: Expression
|
||||
val valueY: Expression
|
||||
val paramDt = arg.first.inferType(program)
|
||||
when (paramDt) {
|
||||
DataType.UBYTE -> {
|
||||
@@ -1482,7 +1484,7 @@ internal class Compiler(private val program: Program) {
|
||||
popValueIntoTarget(stmt.target, datatype)
|
||||
}
|
||||
|
||||
private fun pushHeapVarAddress(value: IExpression, removeLastOpcode: Boolean) {
|
||||
private fun pushHeapVarAddress(value: Expression, removeLastOpcode: Boolean) {
|
||||
if (value is IdentifierReference) {
|
||||
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||
if(removeLastOpcode) prog.removeLastInstruction()
|
||||
@@ -1491,7 +1493,7 @@ internal class Compiler(private val program: Program) {
|
||||
else throw CompilerException("can only take address of a literal string value or a string/array variable")
|
||||
}
|
||||
|
||||
private fun pushFloatAddress(value: IExpression) {
|
||||
private fun pushFloatAddress(value: Expression) {
|
||||
if (value is IdentifierReference) {
|
||||
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||
prog.instr(Opcode.PUSH_ADDR_HEAPVAR, callLabel = vardecl.scopedname)
|
||||
@@ -1499,7 +1501,7 @@ internal class Compiler(private val program: Program) {
|
||||
else throw CompilerException("can only take address of a the float as constant literal or variable")
|
||||
}
|
||||
|
||||
private fun pushStructAddress(value: IExpression) {
|
||||
private fun pushStructAddress(value: Expression) {
|
||||
if (value is IdentifierReference) {
|
||||
// notice that the mangled name of the first struct member is the start address of this struct var
|
||||
val vardecl = value.targetVarDecl(program.namespace)!!
|
||||
|
||||
@@ -1,12 +1,8 @@
|
||||
package prog8.functions
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.DirectMemoryRead
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.ReferenceLiteralValue
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.compiler.CompilerException
|
||||
import kotlin.math.*
|
||||
|
||||
@@ -14,7 +10,7 @@ import kotlin.math.*
|
||||
class BuiltinFunctionParam(val name: String, val possibleDatatypes: Set<DataType>)
|
||||
|
||||
|
||||
typealias ConstExpressionCaller = (args: List<IExpression>, position: Position, program: Program) -> NumericLiteralValue
|
||||
typealias ConstExpressionCaller = (args: List<Expression>, position: Position, program: Program) -> NumericLiteralValue
|
||||
|
||||
|
||||
class FunctionSignature(val pure: Boolean, // does it have side effects?
|
||||
@@ -117,9 +113,9 @@ val BuiltinFunctions = mapOf(
|
||||
)
|
||||
|
||||
|
||||
fun builtinFunctionReturnType(function: String, args: List<IExpression>, program: Program): DataType? {
|
||||
fun builtinFunctionReturnType(function: String, args: List<Expression>, program: Program): DataType? {
|
||||
|
||||
fun datatypeFromIterableArg(arglist: IExpression): DataType {
|
||||
fun datatypeFromIterableArg(arglist: Expression): DataType {
|
||||
if(arglist is ReferenceLiteralValue) {
|
||||
if(arglist.type== DataType.ARRAY_UB || arglist.type== DataType.ARRAY_UW || arglist.type== DataType.ARRAY_F) {
|
||||
val dt = arglist.array!!.map {it.inferType(program)}
|
||||
@@ -189,7 +185,7 @@ fun builtinFunctionReturnType(function: String, args: List<IExpression>, program
|
||||
class NotConstArgumentException: AstException("not a const argument to a built-in function")
|
||||
|
||||
|
||||
private fun oneDoubleArg(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||
private fun oneDoubleArg(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -197,7 +193,7 @@ private fun oneDoubleArg(args: List<IExpression>, position: Position, program: P
|
||||
return numericLiteral(function(float), args[0].position)
|
||||
}
|
||||
|
||||
private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||
private fun oneDoubleArgOutputWord(args: List<Expression>, position: Position, program: Program, function: (arg: Double)->Number): NumericLiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one floating point argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -205,7 +201,7 @@ private fun oneDoubleArgOutputWord(args: List<IExpression>, position: Position,
|
||||
return NumericLiteralValue(DataType.WORD, function(float).toInt(), args[0].position)
|
||||
}
|
||||
|
||||
private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, program: Program, function: (arg: Int)->Number): NumericLiteralValue {
|
||||
private fun oneIntArgOutputInt(args: List<Expression>, position: Position, program: Program, function: (arg: Int)->Number): NumericLiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("built-in function requires one integer argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -216,7 +212,7 @@ private fun oneIntArgOutputInt(args: List<IExpression>, position: Position, prog
|
||||
return numericLiteral(function(integer).toInt(), args[0].position)
|
||||
}
|
||||
|
||||
private fun collectionArgNeverConst(args: List<IExpression>, position: Position): NumericLiteralValue {
|
||||
private fun collectionArgNeverConst(args: List<Expression>, position: Position): NumericLiteralValue {
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("builtin function requires one non-scalar argument", position)
|
||||
|
||||
@@ -224,7 +220,7 @@ private fun collectionArgNeverConst(args: List<IExpression>, position: Position)
|
||||
throw NotConstArgumentException()
|
||||
}
|
||||
|
||||
private fun builtinAbs(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinAbs(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
// 1 arg, type = float or int, result type= isSameAs as argument type
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("abs requires one numeric argument", position)
|
||||
@@ -237,7 +233,7 @@ private fun builtinAbs(args: List<IExpression>, position: Position, program: Pro
|
||||
}
|
||||
}
|
||||
|
||||
private fun builtinStrlen(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinStrlen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("strlen requires one argument", position)
|
||||
val argument = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -247,7 +243,7 @@ private fun builtinStrlen(args: List<IExpression>, position: Position, program:
|
||||
throw NotConstArgumentException() // this function is not considering the string argument a constant
|
||||
}
|
||||
|
||||
private fun builtinLen(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinLen(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
// note: in some cases the length is > 255 and then we have to return a UWORD type instead of a UBYTE.
|
||||
if(args.size!=1)
|
||||
throw SyntaxError("len requires one argument", position)
|
||||
@@ -288,7 +284,7 @@ private fun builtinLen(args: List<IExpression>, position: Position, program: Pro
|
||||
}
|
||||
|
||||
|
||||
private fun builtinMkword(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinMkword(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 2)
|
||||
throw SyntaxError("mkword requires lsb and msb arguments", position)
|
||||
val constLsb = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -297,7 +293,7 @@ private fun builtinMkword(args: List<IExpression>, position: Position, program:
|
||||
return NumericLiteralValue(DataType.UWORD, result, position)
|
||||
}
|
||||
|
||||
private fun builtinSin8(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinSin8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -305,7 +301,7 @@ private fun builtinSin8(args: List<IExpression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * sin(rad)).toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin8u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinSin8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -313,7 +309,7 @@ private fun builtinSin8u(args: List<IExpression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * sin(rad)).toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinCos8(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -321,7 +317,7 @@ private fun builtinCos8(args: List<IExpression>, position: Position, program: Pr
|
||||
return NumericLiteralValue(DataType.BYTE, (127.0 * cos(rad)).toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos8u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinCos8u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos8u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -329,7 +325,7 @@ private fun builtinCos8u(args: List<IExpression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.UBYTE, (128.0 + 127.5 * cos(rad)).toShort(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin16(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinSin16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -337,7 +333,7 @@ private fun builtinSin16(args: List<IExpression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.WORD, (32767.0 * sin(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinSin16u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinSin16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("sin16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -345,7 +341,7 @@ private fun builtinSin16u(args: List<IExpression>, position: Position, program:
|
||||
return NumericLiteralValue(DataType.UWORD, (32768.0 + 32767.5 * sin(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos16(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinCos16(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16 requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
@@ -353,7 +349,7 @@ private fun builtinCos16(args: List<IExpression>, position: Position, program: P
|
||||
return NumericLiteralValue(DataType.WORD, (32767.0 * cos(rad)).toInt(), position)
|
||||
}
|
||||
|
||||
private fun builtinCos16u(args: List<IExpression>, position: Position, program: Program): NumericLiteralValue {
|
||||
private fun builtinCos16u(args: List<Expression>, position: Position, program: Program): NumericLiteralValue {
|
||||
if (args.size != 1)
|
||||
throw SyntaxError("cos16u requires one argument", position)
|
||||
val constval = args[0].constValue(program) ?: throw NotConstArgumentException()
|
||||
|
||||
@@ -1,6 +1,9 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.*
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.Module
|
||||
import prog8.ast.Node
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.ParentSentinel
|
||||
import prog8.ast.base.VarDeclType
|
||||
@@ -19,7 +22,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
||||
val subroutinesCalling = mutableMapOf<INameScope, List<Subroutine>>().withDefault { mutableListOf() }
|
||||
val subroutinesCalledBy = mutableMapOf<Subroutine, List<Node>>().withDefault { mutableListOf() }
|
||||
// TODO add dataflow graph: what statements use what variables
|
||||
val usedSymbols = mutableSetOf<IStatement>()
|
||||
val usedSymbols = mutableSetOf<Statement>()
|
||||
|
||||
init {
|
||||
visit(program)
|
||||
@@ -94,11 +97,11 @@ class CallGraph(private val program: Program): IAstVisitor {
|
||||
super.visit(identifier)
|
||||
}
|
||||
|
||||
private fun addNodeAndParentScopes(stmt: IStatement) {
|
||||
private fun addNodeAndParentScopes(stmt: Statement) {
|
||||
usedSymbols.add(stmt)
|
||||
var node: Node=stmt
|
||||
do {
|
||||
if(node is INameScope && node is IStatement) {
|
||||
if(node is INameScope && node is Statement) {
|
||||
usedSymbols.add(node)
|
||||
}
|
||||
node=node.parent
|
||||
@@ -176,7 +179,7 @@ class CallGraph(private val program: Program): IAstVisitor {
|
||||
super.visit(inlineAssembly)
|
||||
}
|
||||
|
||||
private fun scanAssemblyCode(asm: String, context: IStatement, scope: INameScope) {
|
||||
private fun scanAssemblyCode(asm: String, context: Statement, scope: INameScope) {
|
||||
val asmJumpRx = Regex("""[\-+a-zA-Z0-9_ \t]+(jmp|jsr)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||
val asmRefRx = Regex("""[\-+a-zA-Z0-9_ \t]+(...)[ \t]+(\S+).*""", RegexOption.IGNORE_CASE)
|
||||
asm.lines().forEach { line ->
|
||||
|
||||
@@ -1,14 +1,14 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import kotlin.math.pow
|
||||
|
||||
|
||||
class ConstExprEvaluator {
|
||||
|
||||
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): IExpression {
|
||||
fun evaluate(left: NumericLiteralValue, operator: String, right: NumericLiteralValue): Expression {
|
||||
return when(operator) {
|
||||
"+" -> plus(left, right)
|
||||
"-" -> minus(left, right)
|
||||
@@ -34,7 +34,7 @@ class ConstExprEvaluator {
|
||||
}
|
||||
}
|
||||
|
||||
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): IExpression {
|
||||
private fun shiftedright(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left >> $amount", left.position)
|
||||
val result =
|
||||
@@ -45,7 +45,7 @@ class ConstExprEvaluator {
|
||||
return NumericLiteralValue(left.type, result, left.position)
|
||||
}
|
||||
|
||||
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): IExpression {
|
||||
private fun shiftedleft(left: NumericLiteralValue, amount: NumericLiteralValue): Expression {
|
||||
if(left.type !in IntegerDatatypes || amount.type !in IntegerDatatypes)
|
||||
throw ExpressionError("cannot compute $left << $amount", left.position)
|
||||
val result = left.number.toInt().shl(amount.number.toInt())
|
||||
|
||||
@@ -1,8 +1,6 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.IFunctionCall
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.*
|
||||
@@ -28,7 +26,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): Statement {
|
||||
// the initializer value can't refer to the variable itself (recursive definition)
|
||||
// TODO: use call tree for this?
|
||||
if(decl.value?.referencesIdentifiers(decl.name) == true || decl.arraysize?.index?.referencesIdentifiers(decl.name) == true) {
|
||||
@@ -193,7 +191,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
/**
|
||||
* replace identifiers that refer to const value, with the value itself (if it's a simple type)
|
||||
*/
|
||||
override fun visit(identifier: IdentifierReference): IExpression {
|
||||
override fun visit(identifier: IdentifierReference): Expression {
|
||||
return try {
|
||||
val cval = identifier.constValue(program) ?: return identifier
|
||||
return when {
|
||||
@@ -211,7 +209,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
return try {
|
||||
super.visit(functionCall)
|
||||
typeCastConstArguments(functionCall)
|
||||
@@ -222,7 +220,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||
super.visit(functionCallStatement)
|
||||
typeCastConstArguments(functionCallStatement)
|
||||
return functionCallStatement
|
||||
@@ -246,7 +244,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
||||
override fun visit(memread: DirectMemoryRead): Expression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
@@ -259,7 +257,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
* Compile-time constant sub expressions will be evaluated on the spot.
|
||||
* For instance, the expression for "- 4.5" will be optimized into the float literal -4.5
|
||||
*/
|
||||
override fun visit(expr: PrefixExpression): IExpression {
|
||||
override fun visit(expr: PrefixExpression): Expression {
|
||||
return try {
|
||||
super.visit(expr)
|
||||
|
||||
@@ -317,7 +315,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
* (X / c1) * c2 -> X / (c2/c1)
|
||||
* (X + c1) - c2 -> X + (c1-c2)
|
||||
*/
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): Expression {
|
||||
return try {
|
||||
super.visit(expr)
|
||||
|
||||
@@ -364,7 +362,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
leftIsConst: Boolean,
|
||||
rightIsConst: Boolean,
|
||||
subleftIsConst: Boolean,
|
||||
subrightIsConst: Boolean): IExpression
|
||||
subrightIsConst: Boolean): Expression
|
||||
{
|
||||
// @todo this implements only a small set of possible reorderings for now
|
||||
if(expr.operator==subExpr.operator) {
|
||||
@@ -551,13 +549,13 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): Statement {
|
||||
|
||||
fun adjustRangeDt(rangeFrom: NumericLiteralValue, targetDt: DataType, rangeTo: NumericLiteralValue, stepLiteral: NumericLiteralValue?, range: RangeExpr): RangeExpr {
|
||||
val newFrom = rangeFrom.cast(targetDt)
|
||||
val newTo = rangeTo.cast(targetDt)
|
||||
if (newFrom != null && newTo != null) {
|
||||
val newStep: IExpression =
|
||||
val newStep: Expression =
|
||||
if (stepLiteral != null) (stepLiteral.cast(targetDt) ?: stepLiteral) else range.step
|
||||
return RangeExpr(newFrom, newTo, newStep, range.position)
|
||||
}
|
||||
@@ -605,7 +603,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
return resultStmt
|
||||
}
|
||||
|
||||
override fun visit(refLiteral: ReferenceLiteralValue): IExpression {
|
||||
override fun visit(refLiteral: ReferenceLiteralValue): Expression {
|
||||
val litval = super.visit(refLiteral)
|
||||
if(litval is ReferenceLiteralValue) {
|
||||
if (litval.isString) {
|
||||
@@ -670,7 +668,7 @@ class ConstantFolding(private val program: Program) : IAstModifyingVisitor {
|
||||
return litval
|
||||
}
|
||||
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): Statement {
|
||||
super.visit(assignment)
|
||||
val lv = assignment.value as? NumericLiteralValue
|
||||
if(lv!=null) {
|
||||
|
||||
@@ -1,7 +1,5 @@
|
||||
package prog8.optimizer
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.AstException
|
||||
import prog8.ast.base.DataType
|
||||
@@ -10,6 +8,7 @@ import prog8.ast.base.NumericDatatypes
|
||||
import prog8.ast.expressions.*
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.Assignment
|
||||
import prog8.ast.statements.Statement
|
||||
import kotlin.math.abs
|
||||
import kotlin.math.log2
|
||||
|
||||
@@ -23,13 +22,13 @@ import kotlin.math.log2
|
||||
internal class SimplifyExpressions(private val program: Program) : IAstModifyingVisitor {
|
||||
var optimizationsDone: Int = 0
|
||||
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): Statement {
|
||||
if (assignment.aug_op != null)
|
||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||
return super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun visit(memread: DirectMemoryRead): IExpression {
|
||||
override fun visit(memread: DirectMemoryRead): Expression {
|
||||
// @( &thing ) --> thing
|
||||
val addrOf = memread.addressExpression as? AddressOf
|
||||
if(addrOf!=null)
|
||||
@@ -37,7 +36,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return super.visit(memread)
|
||||
}
|
||||
|
||||
override fun visit(typecast: TypecastExpression): IExpression {
|
||||
override fun visit(typecast: TypecastExpression): Expression {
|
||||
var tc = typecast
|
||||
|
||||
// try to statically convert a literal value into one of the desired type
|
||||
@@ -83,7 +82,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(expr: PrefixExpression): IExpression {
|
||||
override fun visit(expr: PrefixExpression): Expression {
|
||||
if (expr.operator == "+") {
|
||||
// +X --> X
|
||||
optimizationsDone++
|
||||
@@ -130,7 +129,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return super.visit(expr)
|
||||
}
|
||||
|
||||
override fun visit(expr: BinaryExpression): IExpression {
|
||||
override fun visit(expr: BinaryExpression): Expression {
|
||||
super.visit(expr)
|
||||
val leftVal = expr.left.constValue(program)
|
||||
val rightVal = expr.right.constValue(program)
|
||||
@@ -343,7 +342,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun determineY(x: IExpression, subBinExpr: BinaryExpression): IExpression? {
|
||||
private fun determineY(x: Expression, subBinExpr: BinaryExpression): Expression? {
|
||||
return when {
|
||||
subBinExpr.left isSameAs x -> subBinExpr.right
|
||||
subBinExpr.right isSameAs x -> subBinExpr.left
|
||||
@@ -450,7 +449,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return ReorderedAssociativeBinaryExpr(expr, leftVal, expr.right.constValue(program))
|
||||
}
|
||||
|
||||
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizeAdd(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
||||
if(pleftVal==null && prightVal==null)
|
||||
return pexpr
|
||||
|
||||
@@ -471,7 +470,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizeSub(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||
if(leftVal==null && rightVal==null)
|
||||
return expr
|
||||
|
||||
@@ -500,7 +499,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizePower(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||
if(leftVal==null && rightVal==null)
|
||||
return expr
|
||||
|
||||
@@ -580,7 +579,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizeRemainder(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||
if(leftVal==null && rightVal==null)
|
||||
return expr
|
||||
|
||||
@@ -604,7 +603,7 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
|
||||
}
|
||||
|
||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizeDivision(expr: BinaryExpression, leftVal: NumericLiteralValue?, rightVal: NumericLiteralValue?): Expression {
|
||||
if(leftVal==null && rightVal==null)
|
||||
return expr
|
||||
|
||||
@@ -676,14 +675,14 @@ internal class SimplifyExpressions(private val program: Program) : IAstModifying
|
||||
return expr
|
||||
}
|
||||
|
||||
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): IExpression {
|
||||
private fun optimizeMultiplication(pexpr: BinaryExpression, pleftVal: NumericLiteralValue?, prightVal: NumericLiteralValue?): Expression {
|
||||
if(pleftVal==null && prightVal==null)
|
||||
return pexpr
|
||||
|
||||
val (expr, _, rightVal) = reorderAssociative(pexpr, pleftVal)
|
||||
if(rightVal!=null) {
|
||||
// right value is a constant, see if we can optimize
|
||||
val leftValue: IExpression = expr.left
|
||||
val leftValue: Expression = expr.left
|
||||
val rightConst: NumericLiteralValue = rightVal
|
||||
when(val cv = rightConst.number.toDouble()) {
|
||||
-1.0 -> {
|
||||
|
||||
@@ -59,7 +59,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
val scope = caller.definingScope()
|
||||
if(sub.calledBy.count { it.definingScope()===scope } > 1)
|
||||
return
|
||||
if(caller !is IFunctionCall || caller !is IStatement || sub.statements.any { it is Subroutine })
|
||||
if(caller !is IFunctionCall || caller !is Statement || sub.statements.any { it is Subroutine })
|
||||
return
|
||||
|
||||
if(sub.parameters.isEmpty() && sub.returntypes.isEmpty()) {
|
||||
@@ -136,7 +136,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
}
|
||||
}
|
||||
|
||||
override fun visit(block: Block): IStatement {
|
||||
override fun visit(block: Block): Statement {
|
||||
if("force_output" !in block.options()) {
|
||||
if (block.containsNoCodeNorVars()) {
|
||||
optimizationsDone++
|
||||
@@ -154,7 +154,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(block)
|
||||
}
|
||||
|
||||
override fun visit(subroutine: Subroutine): IStatement {
|
||||
override fun visit(subroutine: Subroutine): Statement {
|
||||
super.visit(subroutine)
|
||||
val forceOutput = "force_output" in subroutine.definingBlock().options()
|
||||
if(subroutine.asmAddress==null && !forceOutput) {
|
||||
@@ -191,7 +191,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return subroutine
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): Statement {
|
||||
val forceOutput = "force_output" in decl.definingBlock().options()
|
||||
if(decl !in callgraph.usedSymbols && !forceOutput) {
|
||||
if(decl.type == VarDeclType.VAR)
|
||||
@@ -203,7 +203,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
private fun deduplicateAssignments(statements: List<IStatement>): MutableList<Int> {
|
||||
private fun deduplicateAssignments(statements: List<Statement>): MutableList<Int> {
|
||||
// removes 'duplicate' assignments that assign the isSameAs target
|
||||
val linesToRemove = mutableListOf<Int>()
|
||||
var previousAssignmentLine: Int? = null
|
||||
@@ -228,7 +228,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return linesToRemove
|
||||
}
|
||||
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): IStatement {
|
||||
override fun visit(functionCallStatement: FunctionCallStatement): Statement {
|
||||
if(functionCallStatement.target.nameInSource.size==1 && functionCallStatement.target.nameInSource[0] in BuiltinFunctions) {
|
||||
val functionName = functionCallStatement.target.nameInSource[0]
|
||||
if (functionName in pureBuiltinFunctions) {
|
||||
@@ -286,7 +286,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(functionCallStatement)
|
||||
}
|
||||
|
||||
override fun visit(functionCall: FunctionCall): IExpression {
|
||||
override fun visit(functionCall: FunctionCall): Expression {
|
||||
// if it calls a subroutine,
|
||||
// and the first instruction in the subroutine is a jump, call that jump target instead
|
||||
// if the first instruction in the subroutine is a return statement with constant value, replace with the constant value
|
||||
@@ -306,7 +306,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(functionCall)
|
||||
}
|
||||
|
||||
override fun visit(ifStatement: IfStatement): IStatement {
|
||||
override fun visit(ifStatement: IfStatement): Statement {
|
||||
super.visit(ifStatement)
|
||||
|
||||
if(ifStatement.truepart.containsNoCodeNorVars() && ifStatement.elsepart.containsNoCodeNorVars()) {
|
||||
@@ -340,7 +340,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return ifStatement
|
||||
}
|
||||
|
||||
override fun visit(forLoop: ForLoop): IStatement {
|
||||
override fun visit(forLoop: ForLoop): Statement {
|
||||
super.visit(forLoop)
|
||||
if(forLoop.body.containsNoCodeNorVars()) {
|
||||
// remove empty for loop
|
||||
@@ -370,7 +370,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return forLoop
|
||||
}
|
||||
|
||||
override fun visit(whileLoop: WhileLoop): IStatement {
|
||||
override fun visit(whileLoop: WhileLoop): Statement {
|
||||
super.visit(whileLoop)
|
||||
val constvalue = whileLoop.condition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
@@ -396,7 +396,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return whileLoop
|
||||
}
|
||||
|
||||
override fun visit(repeatLoop: RepeatLoop): IStatement {
|
||||
override fun visit(repeatLoop: RepeatLoop): Statement {
|
||||
super.visit(repeatLoop)
|
||||
val constvalue = repeatLoop.untilCondition.constValue(program)
|
||||
if(constvalue!=null) {
|
||||
@@ -426,7 +426,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return repeatLoop
|
||||
}
|
||||
|
||||
override fun visit(whenStatement: WhenStatement): IStatement {
|
||||
override fun visit(whenStatement: WhenStatement): Statement {
|
||||
val choices = whenStatement.choices.toList()
|
||||
for(choice in choices) {
|
||||
if(choice.statements.containsNoCodeNorVars())
|
||||
@@ -441,12 +441,12 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
{
|
||||
var count=0
|
||||
|
||||
override fun visit(breakStmt: Break): IStatement {
|
||||
override fun visit(breakStmt: Break): Statement {
|
||||
count++
|
||||
return super.visit(breakStmt)
|
||||
}
|
||||
|
||||
override fun visit(contStmt: Continue): IStatement {
|
||||
override fun visit(contStmt: Continue): Statement {
|
||||
count++
|
||||
return super.visit(contStmt)
|
||||
}
|
||||
@@ -460,7 +460,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return s.count > 0
|
||||
}
|
||||
|
||||
override fun visit(jump: Jump): IStatement {
|
||||
override fun visit(jump: Jump): Statement {
|
||||
val subroutine = jump.identifier?.targetSubroutine(program.namespace)
|
||||
if(subroutine!=null) {
|
||||
// if the first instruction in the subroutine is another jump, shortcut this one
|
||||
@@ -484,7 +484,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return jump
|
||||
}
|
||||
|
||||
override fun visit(assignment: Assignment): IStatement {
|
||||
override fun visit(assignment: Assignment): Statement {
|
||||
if(assignment.aug_op!=null)
|
||||
throw AstException("augmented assignments should have been converted to normal assignments before this optimizer")
|
||||
|
||||
@@ -615,7 +615,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(assignment)
|
||||
}
|
||||
|
||||
override fun visit(scope: AnonymousScope): IStatement {
|
||||
override fun visit(scope: AnonymousScope): Statement {
|
||||
val linesToRemove = deduplicateAssignments(scope.statements)
|
||||
if(linesToRemove.isNotEmpty()) {
|
||||
linesToRemove.reversed().forEach{scope.statements.removeAt(it)}
|
||||
@@ -623,7 +623,7 @@ internal class StatementOptimizer(private val program: Program, private val opti
|
||||
return super.visit(scope)
|
||||
}
|
||||
|
||||
override fun visit(label: Label): IStatement {
|
||||
override fun visit(label: Label): Statement {
|
||||
// remove duplicate labels
|
||||
val stmts = label.definingScope().statements
|
||||
val startIdx = stmts.indexOf(label)
|
||||
@@ -644,7 +644,7 @@ internal class FlattenAnonymousScopesAndRemoveNops: IAstVisitor {
|
||||
super.visit(program)
|
||||
for(scope in scopesToFlatten.reversed()) {
|
||||
val namescope = scope.parent as INameScope
|
||||
val idx = namescope.statements.indexOf(scope as IStatement)
|
||||
val idx = namescope.statements.indexOf(scope as Statement)
|
||||
if(idx>=0) {
|
||||
val nop = NopStatement.insteadOf(namescope.statements[idx])
|
||||
nop.parent = namescope as Node
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.INameScope
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.*
|
||||
import prog8.ast.expressions.Expression
|
||||
import prog8.ast.expressions.IdentifierReference
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.statements.*
|
||||
@@ -314,7 +313,7 @@ class AstVm(val program: Program) {
|
||||
}
|
||||
|
||||
|
||||
private fun executeStatement(sub: INameScope, stmt: IStatement) {
|
||||
private fun executeStatement(sub: INameScope, stmt: Statement) {
|
||||
instructionCounter++
|
||||
if (instructionCounter % 200 == 0)
|
||||
Thread.sleep(1)
|
||||
@@ -545,7 +544,7 @@ class AstVm(val program: Program) {
|
||||
performAssignment(target2, value1, swap, evalCtx)
|
||||
}
|
||||
|
||||
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: IStatement, evalCtx: EvalContext) {
|
||||
fun performAssignment(target: AssignTarget, value: RuntimeValue, contextStmt: Statement, evalCtx: EvalContext) {
|
||||
when {
|
||||
target.identifier != null -> {
|
||||
val decl = contextStmt.definingScope().lookup(target.identifier.nameInSource, contextStmt) as? VarDecl
|
||||
@@ -642,7 +641,7 @@ class AstVm(val program: Program) {
|
||||
executeAnonymousScope(stmt.body)
|
||||
}
|
||||
|
||||
private fun evaluate(args: List<IExpression>) = args.map { evaluate(it, evalCtx) }
|
||||
private fun evaluate(args: List<Expression>) = args.map { evaluate(it, evalCtx) }
|
||||
|
||||
private fun performSyscall(sub: Subroutine, args: List<RuntimeValue>): RuntimeValue? {
|
||||
var result: RuntimeValue? = null
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.IExpression
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.ArrayElementTypes
|
||||
import prog8.ast.base.DataType
|
||||
@@ -25,7 +24,7 @@ class EvalContext(val program: Program, val mem: Memory, val statusflags: Status
|
||||
val performBuiltinFunction: BuiltinfunctionCaller,
|
||||
val executeSubroutine: SubroutineCaller)
|
||||
|
||||
fun evaluate(expr: IExpression, ctx: EvalContext): RuntimeValue {
|
||||
fun evaluate(expr: Expression, ctx: EvalContext): RuntimeValue {
|
||||
val constval = expr.constValue(ctx.program)
|
||||
if(constval!=null)
|
||||
return RuntimeValue.fromLv(constval)
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
package prog8.vm.astvm
|
||||
|
||||
import prog8.ast.IStatement
|
||||
import prog8.ast.Program
|
||||
import prog8.ast.base.DataType
|
||||
import prog8.ast.base.Position
|
||||
@@ -9,6 +8,7 @@ import prog8.ast.base.VarDeclType
|
||||
import prog8.ast.expressions.NumericLiteralValue
|
||||
import prog8.ast.expressions.ReferenceLiteralValue
|
||||
import prog8.ast.processing.IAstModifyingVisitor
|
||||
import prog8.ast.statements.Statement
|
||||
import prog8.ast.statements.StructDecl
|
||||
import prog8.ast.statements.VarDecl
|
||||
import prog8.ast.statements.ZeropageWish
|
||||
@@ -40,7 +40,7 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
||||
super.visit(program)
|
||||
}
|
||||
|
||||
override fun visit(decl: VarDecl): IStatement {
|
||||
override fun visit(decl: VarDecl): Statement {
|
||||
// if the decl is part of a struct, just skip it
|
||||
if(decl.parent !is StructDecl) {
|
||||
when (decl.type) {
|
||||
@@ -67,7 +67,7 @@ class VariablesCreator(private val runtimeVariables: RuntimeVariables, private v
|
||||
return super.visit(decl)
|
||||
}
|
||||
|
||||
// override fun accept(assignment: Assignment): IStatement {
|
||||
// override fun accept(assignment: Assignment): Statement {
|
||||
// if(assignment is VariableInitializationAssignment) {
|
||||
// println("INIT VAR $assignment")
|
||||
// }
|
||||
|
||||
Reference in New Issue
Block a user